debug_cmd.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /////////////////////////////////////////////////////////////////////////EA-V1
  19. // $File: //depot/GeneralsMD/Staging/code/Libraries/Source/debug/debug_cmd.cpp $
  20. // $Author: mhoffe $
  21. // $Revision: #1 $
  22. // $DateTime: 2003/07/03 11:55:26 $
  23. //
  24. // ©2003 Electronic Arts
  25. //
  26. // Debug command group 'debug'
  27. //////////////////////////////////////////////////////////////////////////////
  28. #include "_pch.h"
  29. #include <process.h>
  30. bool DebugCmdInterfaceDebug::Execute(class Debug& dbg, const char *cmd,
  31. CommandMode cmdmode, unsigned argn,
  32. const char * const * argv)
  33. {
  34. // just for convenience...
  35. bool normalMode=cmdmode==CommandMode::Normal;
  36. if (!strcmp(cmd,"help"))
  37. {
  38. if (!normalMode)
  39. return true;
  40. if (!argn)
  41. {
  42. dbg << "debug group help:\n"
  43. " list, io, alwaysflush, timestamp, exit, clear, add, view\n";
  44. return true;
  45. }
  46. else if (!strcmp(argv[0],"list"))
  47. {
  48. dbg << "list (g|l|d|a|c) [ <pattern> ]\n"
  49. "\n"
  50. "Shows some or all items of a specific type.\n"
  51. "\n"
  52. "The following items are supported:\n"
  53. "- g: command groups\n"
  54. "- l: log groups (only those encountered yet)\n"
  55. "- d: log groups with descriptions (only those that have descriptions)\n"
  56. "- a: asserts/crashes (only those hit yet)\n"
  57. "- c: checks (only those failed yet)\n"
  58. "\n"
  59. "If a pattern is specified only items matching\n"
  60. "that pattern are shown. A pattern can contain\n"
  61. "any character, letter, or a wildcard '*'.\n"
  62. "\n"
  63. "Please note that assert, crashes, and check items have\n"
  64. "their line number appended to the current file name,\n"
  65. "e.g. debug.cpp(13).\n";
  66. return true;
  67. }
  68. else if (!strcmp(argv[0],"io"))
  69. {
  70. dbg << "io <I/O Class> <cmd> { <param> }]\n"
  71. "\n"
  72. "Issues a I/O class command. I/O class commands are used\n"
  73. "for determining where all log output should be sent. \n"
  74. "Please check the list of \ref debug_ioclasses for a list\n"
  75. "of existing I/O classes.\n"
  76. "\n"
  77. "Each existing I/O class must accept at least the\n"
  78. "following two commands: 'add' and 'remove'. Usually\n"
  79. "after a class has been added it reacts to the 'help'\n"
  80. "command as well.\n"
  81. "\n"
  82. "If the command is entered without any parameters a list\n"
  83. "of active I/O classes is shown. Typing 'io ?' retrieves\n"
  84. "a list of possible I/O classes.\n";
  85. return true;
  86. }
  87. else if (!strcmp(argv[0],"alwaysflush"))
  88. {
  89. dbg << "alwaysflush [ (+|-) ]\n\n"
  90. "Enables/disables flushing after each new entry in\n"
  91. "the log file (default: off).\n";
  92. return true;
  93. }
  94. else if (!strcmp(argv[0],"timestamp"))
  95. {
  96. dbg << "timestamp [ (+|-) ]\n\n"
  97. "Enables/disables timestamping each log entry\n"
  98. "(default: off).\n";
  99. return true;
  100. }
  101. else if (!strcmp(argv[0],"exit"))
  102. {
  103. dbg << "exit\n\nExits program immediately.\n";
  104. return true;
  105. }
  106. else if (!strcmp(argv[0],"clear"))
  107. {
  108. dbg << "clear (l|a|c)\n\n"
  109. "Clears the given inclusion/exclusion list\n"
  110. "(l=logs, a=asserts/crashes, c=checks).\n";
  111. return true;
  112. }
  113. else if (!strcmp(argv[0],"add"))
  114. {
  115. dbg << "add (l|a|c) (+|-) <pattern>\n"
  116. "\n"
  117. "Adds a pattern to the given list (l=logs, \n"
  118. "a=asserts/crashes, c=checks). By default all\n"
  119. "asserts, crashes, and checks are active, all logs\n"
  120. "inactive. Each item is then checked \n"
  121. "against all pattern in the respective\n"
  122. "list. If a match is found the active/inactive\n"
  123. "state is modified accordingly (+ for active,\n"
  124. "- for inactive). The final state is always\n"
  125. "the last match.";
  126. return true;
  127. }
  128. else if (!strcmp(argv[0],"view"))
  129. {
  130. dbg << "view [ (l|a|c) ]\n\n"
  131. "Shows the active pattern for the given list\n"
  132. "(l=logs, a=asserts/crashes, c=checks).\n";
  133. return true;
  134. }
  135. return false;
  136. }
  137. if (!strcmp(cmd,"list"))
  138. {
  139. const char *pattern=argn>=2?argv[1]:"*";
  140. switch(argn?*argv[0]:0)
  141. {
  142. case 'g':
  143. {
  144. if (normalMode)
  145. dbg << "Command groups:\n";
  146. for (Debug::CmdInterfaceListEntry *cur=dbg.firstCmdGroup;cur;cur=cur->next)
  147. if (Debug::SimpleMatch(cur->group,pattern))
  148. dbg << cur->group << "\n";
  149. }
  150. break;
  151. case 'l':
  152. case 'd':
  153. {
  154. if (normalMode)
  155. dbg << "Logs:\n";
  156. for (Debug::KnownLogGroupList *cur=dbg.firstLogGroup;cur;cur=cur->next)
  157. if (Debug::SimpleMatch(cur->nameGroup,pattern)&&
  158. (*argv[0]=='l'||cur->descr))
  159. {
  160. dbg << cur->nameGroup;
  161. if (cur->descr)
  162. dbg << " (" << cur->descr << ")";
  163. dbg << "\n";
  164. }
  165. }
  166. break;
  167. case 'a':
  168. case 'c':
  169. {
  170. if (normalMode)
  171. dbg << (*argv[0]=='a'?"Asserts/Crashes:\n":"Checks:\n");
  172. unsigned mask=*argv[0]=='a'?Debug::FrameTypeAssert:Debug::FrameTypeCheck;
  173. for (unsigned k=0;k<Debug::FRAME_HASH_SIZE;k++)
  174. {
  175. for (Debug::FrameHashEntry *cur=dbg.frameHash[k];cur;cur=cur->next)
  176. {
  177. if (!(cur->frameType&mask))
  178. continue;
  179. char help[256];
  180. wsprintf(help,"%s(%i)",cur->fileOrGroup,cur->line);
  181. if (Debug::SimpleMatch(help,pattern))
  182. {
  183. dbg << help << " (" << cur->hits << " hits)";
  184. if (cur->status==Debug::Skip)
  185. dbg << " [off]";
  186. dbg << "\n";
  187. }
  188. }
  189. }
  190. }
  191. break;
  192. default:
  193. dbg << "Unknown item type, see help.";
  194. }
  195. return true;
  196. }
  197. if (!strcmp(cmd,"io"))
  198. {
  199. // cmd: io
  200. if (!argn||!strcmp(argv[0],"?"))
  201. {
  202. // show active/all I/O classes
  203. if (normalMode)
  204. dbg << (argn?"Possible:\n":"Active:\n");
  205. bool hadItem=false;
  206. for (Debug::IOFactoryListEntry *cur=dbg.firstIOFactory;cur;cur=cur->next)
  207. {
  208. if (!argn&&!cur->io)
  209. continue;
  210. hadItem=true;
  211. dbg << cur->ioID << " (" << cur->descr << ")\n";
  212. }
  213. if (normalMode&&!hadItem)
  214. dbg << "(none)\n";
  215. }
  216. else
  217. {
  218. // regular I/O command
  219. // find I/O class
  220. for (Debug::IOFactoryListEntry *cur=dbg.firstIOFactory;cur;cur=cur->next)
  221. if (!strcmp(argv[0],cur->ioID))
  222. break;
  223. if (!cur)
  224. {
  225. dbg << "Unknown I/O class " << argv[0];
  226. return true; // still return true because we knew the command
  227. }
  228. if (argn>1)
  229. {
  230. // 'add' command?
  231. if (!strcmp(argv[1],"add"))
  232. {
  233. if (cur->io)
  234. {
  235. dbg << "I/O class already added";
  236. return true;
  237. }
  238. cur->io=cur->factory();
  239. if (!cur->io)
  240. {
  241. dbg << "I/O class factory failed";
  242. return true;
  243. }
  244. }
  245. // 'remove' command?
  246. if (!strcmp(argv[1],"remove"))
  247. {
  248. if (cur->io)
  249. {
  250. cur->io->Delete();
  251. cur->io=NULL;
  252. }
  253. return true;
  254. }
  255. }
  256. // now pass along I/O command
  257. if (!cur->io)
  258. {
  259. dbg << "Add I/O class first";
  260. return true;
  261. }
  262. cur->io->Execute(dbg,argn>1?argv[1]:NULL,!normalMode,argn>1?argn-2:0,argv+2);
  263. }
  264. return true;
  265. }
  266. if (!strcmp(cmd,"alwaysflush"))
  267. {
  268. if (argn)
  269. {
  270. if (*argv[0]=='+')
  271. dbg.alwaysFlush=true;
  272. if (*argv[0]=='-')
  273. dbg.alwaysFlush=false;
  274. }
  275. if (normalMode)
  276. dbg << "Always flush: " << (dbg.alwaysFlush?"on":"off");
  277. else
  278. dbg << (dbg.alwaysFlush?"1":"0");
  279. return true;
  280. }
  281. if (!strcmp(cmd,"timestamp"))
  282. {
  283. if (argn)
  284. {
  285. if (*argv[0]=='+')
  286. dbg.timeStamp=true;
  287. if (*argv[0]=='-')
  288. dbg.timeStamp=false;
  289. }
  290. if (normalMode)
  291. dbg << "Timestamp: " << (dbg.timeStamp?"on":"off");
  292. else
  293. dbg << (dbg.timeStamp?"1":"0");
  294. return true;
  295. }
  296. if (!strcmp(cmd,"exit"))
  297. {
  298. exit(1);
  299. return true;
  300. }
  301. if (!strcmp(cmd,"clear")||
  302. !strcmp(cmd,"add")||
  303. !strcmp(cmd,"view"))
  304. {
  305. unsigned mask=0;
  306. if (argn)
  307. {
  308. for (const char *p=argv[0];*p;p++)
  309. {
  310. switch(*p)
  311. {
  312. case 'l': mask|=Debug::FrameTypeLog; break;
  313. case 'a': mask|=Debug::FrameTypeAssert; break;
  314. case 'c': mask|=Debug::FrameTypeCheck; break;
  315. }
  316. }
  317. }
  318. if (!mask)
  319. mask=0xffffffff;
  320. bool modified=false;
  321. if (!strcmp(cmd,"clear"))
  322. {
  323. // remove some (or all) pattern
  324. const char *pattern=argn<2?"*":argv[1];
  325. for (Debug::PatternListEntry **entryPtr=&dbg.firstPatternEntry;*entryPtr;)
  326. {
  327. if ( (((*entryPtr)->frameTypes&mask)!=0)
  328. && Debug::SimpleMatch((*entryPtr)->pattern,pattern) )
  329. {
  330. // remove this entry
  331. modified=true;
  332. Debug::PatternListEntry *cur=*entryPtr;
  333. *entryPtr=cur->next;
  334. DebugFreeMemory(cur->pattern);
  335. DebugFreeMemory(cur);
  336. }
  337. else
  338. entryPtr=&((*entryPtr)->next);
  339. }
  340. // must fixup lastPatternEntry now
  341. if (dbg.firstPatternEntry)
  342. {
  343. for (Debug::PatternListEntry *cur=dbg.firstPatternEntry;cur->next;cur=cur->next);
  344. dbg.lastPatternEntry=cur;
  345. }
  346. else
  347. dbg.lastPatternEntry=NULL;
  348. }
  349. if (!strcmp(cmd,"add"))
  350. {
  351. // add a pattern
  352. if (argn<3)
  353. dbg << "Please specify mode and pattern";
  354. else
  355. {
  356. dbg.AddPatternEntry(mask,*argv[1]=='+',argv[2]);
  357. modified=true;
  358. }
  359. }
  360. if (!strcmp(cmd,"view"))
  361. {
  362. // show list of defined patterns
  363. for (Debug::PatternListEntry *cur=dbg.firstPatternEntry;cur;cur=cur->next)
  364. {
  365. if (!(cur->frameTypes&mask))
  366. continue;
  367. if (cur->frameTypes&Debug::FrameTypeLog) dbg << "l";
  368. if (cur->frameTypes&Debug::FrameTypeAssert) dbg << "a";
  369. if (cur->frameTypes&Debug::FrameTypeCheck) dbg << "c";
  370. dbg << (cur->isActive?" + ":" - ") << cur->pattern << "\n";
  371. }
  372. }
  373. if (modified)
  374. {
  375. // pattern list was modified, set all frame entries statuses to Unknown
  376. for (unsigned k=0;k<Debug::FRAME_HASH_SIZE;k++)
  377. for (Debug::FrameHashEntry *cur=dbg.frameHash[k];cur;cur=cur->next)
  378. cur->status=Debug::Unknown;
  379. }
  380. return true;
  381. }
  382. // unknown command
  383. return false;
  384. }