debug_debug.cpp 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  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_debug.cpp $
  20. // $Author: mhoffe $
  21. // $Revision: #2 $
  22. // $DateTime: 2003/07/09 10:57:23 $
  23. //
  24. // ©2003 Electronic Arts
  25. //
  26. // Debug class implementation
  27. //////////////////////////////////////////////////////////////////////////////
  28. #include "_pch.h"
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <new> // needed for placement new prototype
  33. // a little dummy variable that makes the linker actually include
  34. // us...
  35. extern "C" bool __DebugIncludeInLink1;
  36. bool __DebugIncludeInLink1;
  37. // This part is a little tricky (and not portable to other compilers).
  38. // MSVC initializes all static C++ variables by calling a list of
  39. // function pointers contained in data segments called .CRT$XCA to
  40. // .CRT$XCZ. We jam in our own two functions at the very beginning
  41. // and end of this list (B and Y respectively since the A and Z segments
  42. // contain list delimiters).
  43. #pragma data_seg(".CRT$XCB")
  44. void *Debug::PreStatic=&Debug::PreStaticInit;
  45. #pragma data_seg(".CRT$XCY")
  46. void *Debug::PostStatic=&Debug::PostStaticInit;
  47. #pragma data_seg()
  48. Debug::LogDescription::LogDescription(const char *fileOrGroup, const char *description)
  49. {
  50. Debug::Instance.AddLogGroup(fileOrGroup,description);
  51. }
  52. // our global Debug instance
  53. Debug Debug::Instance;
  54. // more class static members
  55. unsigned Debug::curStackFrame;
  56. // this constructor is empty on purpose because all construction
  57. // work is done in PreStaticInit (and some in PostStaticInit)
  58. Debug::Debug(void)
  59. {
  60. // do not put any code in here (but it's good for keeping module global todo's)
  61. /// @todo what about frame based logging?
  62. /// @todo have new DLOG with category, add DWARN, DPERF, DERR etc. based on that,
  63. /// make it possible to enable/disable categories by adding category to log ID
  64. }
  65. void Debug::PreStaticInit(void)
  66. {
  67. // do not change any member variables that have constructors
  68. // because they are not constructed yet!
  69. // make sure this function gets called on exit
  70. // (we might still have to call it manually if there's
  71. // an exception and we're not calling exit)
  72. atexit(StaticExit);
  73. // init vars
  74. Instance.hrTranslators=NULL;
  75. Instance.numHrTranslators=0;
  76. Instance.firstIOFactory=NULL;
  77. Instance.firstCmdGroup=NULL;
  78. memset(Instance.frameHash,0,sizeof(Instance.frameHash));
  79. Instance.nextUnusedFrameHash=NULL;
  80. Instance.numAvailableFrameHash=0;
  81. Instance.firstLogGroup=NULL;
  82. memset(Instance.ioBuffer,0,sizeof(Instance.ioBuffer));
  83. Instance.curType=DebugIOInterface::StringType::MAX;
  84. *Instance.curSource=0;
  85. Instance.disableAssertsEtc=0;
  86. Instance.curFrameEntry=NULL;
  87. Instance.firstPatternEntry=NULL;
  88. Instance.lastPatternEntry=NULL;
  89. *Instance.curCommandGroup=0;
  90. Instance.alwaysFlush=false;
  91. Instance.timeStamp=false;
  92. Instance.m_radix=10;
  93. Instance.m_fillChar=' ';
  94. /// install exception handler
  95. SetUnhandledExceptionFilter(DebugExceptionhandler::ExceptionFilter);
  96. }
  97. void Debug::PostStaticInit(void)
  98. {
  99. InstallExceptionHandler();
  100. // register our default IO classes
  101. AddIOFactory("con","Console window",DebugIOCon::Create);
  102. AddIOFactory("flat","Flat local file(s)",DebugIOFlat::Create);
  103. AddIOFactory("net","Network via named pipe",DebugIONet::Create);
  104. AddIOFactory("ods","OutputDebugString function",DebugIOOds::Create);
  105. // add debug command handler
  106. AddCommands("debug",new (DebugAllocMemory(sizeof(DebugCmdInterfaceDebug))) DebugCmdInterfaceDebug);
  107. /// exec dbgcmd file
  108. char ioBuffer[2048];
  109. GetModuleFileName(NULL,ioBuffer,sizeof(ioBuffer));
  110. char *q=strrchr(ioBuffer,'.');
  111. if (q)
  112. strcpy(q,".dbgcmd");
  113. HANDLE h=CreateFile(ioBuffer,GENERIC_READ,0,NULL,OPEN_EXISTING,
  114. FILE_ATTRIBUTE_NORMAL,NULL);
  115. if (h==INVALID_HANDLE_VALUE)
  116. h=CreateFile("default.dbgcmd",GENERIC_READ,0,NULL,OPEN_EXISTING,
  117. FILE_ATTRIBUTE_NORMAL,NULL);
  118. if (h!=INVALID_HANDLE_VALUE)
  119. {
  120. char cmdBuffer[512];
  121. unsigned long ioCur=0,ioUsed=0,cmdCur=0;
  122. ReadFile(h,ioBuffer,sizeof(ioBuffer),&ioUsed,NULL);
  123. for (;;)
  124. {
  125. if (ioCur==ioUsed)
  126. {
  127. ReadFile(h,ioBuffer,sizeof(ioBuffer),&ioUsed,NULL);
  128. ioCur=0;
  129. }
  130. if (ioCur==ioUsed||ioBuffer[ioCur]=='\n'||ioBuffer[ioCur]=='\r')
  131. {
  132. if (cmdCur)
  133. {
  134. Instance.ExecCommand(cmdBuffer,cmdBuffer+cmdCur);
  135. cmdCur=0;
  136. }
  137. if (ioCur==ioUsed)
  138. break;
  139. ioCur++;
  140. }
  141. else
  142. {
  143. if (cmdCur<sizeof(cmdBuffer))
  144. cmdBuffer[cmdCur++]=ioBuffer[ioCur];
  145. ioCur++;
  146. }
  147. }
  148. CloseHandle(h);
  149. }
  150. else
  151. {
  152. // exec default commands
  153. const char *p=DebugGetDefaultCommands();
  154. while (p&&*p)
  155. {
  156. const char *q=strchr(p,'\n');
  157. if (!q)
  158. q=p+strlen(p);
  159. if (p!=q)
  160. {
  161. Instance.ExecCommand(p,q);
  162. p=*q?q+1:NULL;
  163. }
  164. }
  165. }
  166. // check: are we using an old dbghelp.dll?
  167. if (DebugStackwalk::IsOldDbghelp())
  168. {
  169. // give a serious hint
  170. Instance.StartOutput(DebugIOInterface::Other,"");
  171. Instance << RepeatChar('=',79) <<
  172. "\nYou are using an older version of the DBGHELP.DLL library.\n"
  173. "Please update to the newest available version in order to\n"
  174. "get reliable stack and symbol information.\n\n";
  175. char buf[256];
  176. GetModuleFileName((HMODULE)DebugStackwalk::GetDbghelpHandle(),buf,sizeof(buf));
  177. Instance <<
  178. "Hint: The DLL got loaded as:\n" << buf << "\n" << RepeatChar('=',79) << "\n\n";
  179. // flush output only if there is already an active I/O class
  180. Instance.FlushOutput(false);
  181. }
  182. }
  183. void Debug::StaticExit(void)
  184. {
  185. // yes, we do leave memory 'leaks' but Win32 will take care of these
  186. // however, I/O classes must be actively shut down
  187. if (Instance.curType!=DebugIOInterface::StringType::MAX)
  188. Instance.FlushOutput();
  189. for (IOFactoryListEntry *io=Instance.firstIOFactory;io;io=io->next)
  190. if (io->io)
  191. {
  192. io->io->Delete();
  193. io->io=NULL;
  194. }
  195. // and command group interfaces...
  196. for (CmdInterfaceListEntry *cmd=Instance.firstCmdGroup;cmd;cmd=cmd->next)
  197. if (cmd->cmdif)
  198. {
  199. cmd->cmdif->Delete();
  200. cmd->cmdif=NULL;
  201. }
  202. }
  203. Debug& Debug::operator<<(RepeatChar &c)
  204. {
  205. if (c.m_count>=10)
  206. {
  207. char help[10];
  208. memset(help,c.m_char,10);
  209. while ((c.m_count-=10)>=0)
  210. AddOutput(help,10);
  211. }
  212. while (c.m_count-->0)
  213. AddOutput(&c.m_char,1);
  214. return *this;
  215. }
  216. Debug::Format::Format(const char *format, ...)
  217. {
  218. va_list va;
  219. va_start(va,format);
  220. _vsnprintf(m_buffer,sizeof(m_buffer)-1,format,va);
  221. va_end(va);
  222. }
  223. Debug::~Debug()
  224. {
  225. // again, do not put any code in here
  226. }
  227. static void LocalSETranslator(unsigned, struct _EXCEPTION_POINTERS *pExPtrs)
  228. {
  229. // simply call our regular exception handler
  230. DebugExceptionhandler::ExceptionFilter(pExPtrs);
  231. }
  232. void Debug::InstallExceptionHandler(void)
  233. {
  234. _set_se_translator(LocalSETranslator);
  235. }
  236. bool Debug::SkipNext(void)
  237. {
  238. // this is typically set while an assertion
  239. // is running
  240. if (Instance.disableAssertsEtc)
  241. return true;
  242. // do not implement this function inline, we do need
  243. // a valid frame pointer here!
  244. unsigned help;
  245. _asm
  246. {
  247. mov eax,[ebp+4] // return address
  248. mov help,eax
  249. };
  250. curStackFrame=help;
  251. // do we know if to skip the following code?
  252. FrameHashEntry *e=Instance.LookupFrame(curStackFrame);
  253. if (!e|| // unknown frame, will be added later
  254. e->status==NoSkip) // frame known but active
  255. return false;
  256. // status is unknown, must update
  257. if (e->status==Unknown)
  258. Instance.UpdateFrameStatus(*e);
  259. // now we now wether to skip or not
  260. return e->status==Skip;
  261. }
  262. Debug& Debug::AssertBegin(const char *file, int line, const char *expr)
  263. {
  264. // avoid infinite recursion...
  265. ++Instance.disableAssertsEtc;
  266. // anything to flush first?
  267. if (Instance.curType!=DebugIOInterface::StringType::MAX)
  268. Instance.FlushOutput();
  269. // set new output
  270. __ASSERT(Instance.curFrameEntry==NULL);
  271. Instance.curFrameEntry=Instance.GetFrameEntry(curStackFrame,FrameTypeAssert,file,line);
  272. if (Instance.curFrameEntry->status==NoSkip)
  273. {
  274. Instance.StartOutput(DebugIOInterface::StringType::Assert,"%s(%i)",
  275. Instance.curFrameEntry->fileOrGroup,
  276. Instance.curFrameEntry->line);
  277. ++Instance.curFrameEntry->hits;
  278. // if there is a \code\ section in the filename truncate
  279. // everything before that (including \code\)
  280. const char *p=strstr(file,"\\code\\");
  281. p=p?p+6:file;
  282. Instance << "\n" << RepeatChar('=',80) << "\nAssertion failed in " << p << ", line " << line
  283. << ",\nexpression " << expr;
  284. }
  285. return Instance;
  286. }
  287. bool Debug::AssertDone(void)
  288. {
  289. --disableAssertsEtc;
  290. // did we have an active assertion?
  291. if (curType==DebugIOInterface::StringType::Assert)
  292. {
  293. __ASSERT(curFrameEntry!=NULL);
  294. // hit info?
  295. if (curFrameEntry->hits>1)
  296. (*this) << " (hit #" << curFrameEntry->hits << ")";
  297. // need CR?
  298. if (!ioBuffer[curType].lastWasCR)
  299. operator<<("\n");
  300. // yes, duplicate message
  301. const char *addInfo="\nPress 'abort' to abort the program,\n"
  302. "'retry' for breaking into the debugger, or\n"
  303. "'ignore' for ignoring this assertion for the\n"
  304. "time being (stops logging this assertion as well).";
  305. char *help=(char *)DebugAllocMemory(ioBuffer[curType].used+strlen(addInfo)+1);
  306. strcpy(help,ioBuffer[curType].buffer+82);
  307. strcat(help,addInfo);
  308. // First hit? Then do a stack trace
  309. if (curFrameEntry->hits==1)
  310. {
  311. DebugStackwalk::Signature sig;
  312. if (m_stackWalk.StackWalk(sig))
  313. (*this) << sig;
  314. }
  315. // ... and flush out
  316. operator<<("\n\n");
  317. FlushOutput();
  318. // show dialog box only if running windowed
  319. if (IsWindowed())
  320. {
  321. /// @todo replace MessageBox with custom dialog w/ 4 options: abort, skip 1, skip all, break
  322. // now display message, wait for user input
  323. int result=MessageBox(NULL,help,"Assertion failed",
  324. MB_ABORTRETRYIGNORE|MB_ICONSTOP|MB_TASKMODAL|MB_SETFOREGROUND);
  325. switch(result)
  326. {
  327. case IDABORT:
  328. curFrameEntry=NULL;
  329. exit(1);
  330. break;
  331. case IDIGNORE:
  332. {
  333. // build 'pattern'
  334. char help[200];
  335. __ASSERT(strlen(curFrameEntry->fileOrGroup)<190);
  336. wsprintf(help,"%s(%i)",curFrameEntry->fileOrGroup,
  337. curFrameEntry->line);
  338. AddPatternEntry(FrameTypeAssert,false,help);
  339. curFrameEntry->status=Skip;
  340. }
  341. break;
  342. case IDRETRY:
  343. _asm int 0x03
  344. break;
  345. default:
  346. ((void)0);
  347. }
  348. }
  349. else
  350. {
  351. // we're running fullscreen
  352. // hit too often?
  353. if (curFrameEntry->hits==MAX_CHECK_HITS)
  354. {
  355. // yup, turn off then
  356. StartOutput(DebugIOInterface::StringType::Other,"");
  357. Instance << "Assert hit too often - turning check off.\n";
  358. FlushOutput();
  359. // build 'pattern'
  360. char help[200];
  361. __ASSERT(strlen(curFrameEntry->fileOrGroup)<190);
  362. wsprintf(help,"%s(%i)",curFrameEntry->fileOrGroup,
  363. curFrameEntry->line);
  364. AddPatternEntry(FrameTypeAssert,false,help);
  365. curFrameEntry->status=Skip;
  366. }
  367. }
  368. }
  369. curFrameEntry=NULL;
  370. return false;
  371. }
  372. Debug& Debug::CheckBegin(const char *file, int line, const char *expr)
  373. {
  374. // avoid infinite recursion...
  375. ++Instance.disableAssertsEtc;
  376. // anything to flush first?
  377. if (Instance.curType!=DebugIOInterface::StringType::MAX)
  378. Instance.FlushOutput();
  379. // set new output
  380. __ASSERT(Instance.curFrameEntry==NULL);
  381. Instance.curFrameEntry=Instance.GetFrameEntry(curStackFrame,FrameTypeCheck,file,line);
  382. if (Instance.curFrameEntry->status==NoSkip)
  383. {
  384. ++Instance.curFrameEntry->hits;
  385. Instance.StartOutput(DebugIOInterface::StringType::Check,"%s(%i)",
  386. Instance.curFrameEntry->fileOrGroup,
  387. Instance.curFrameEntry->line);
  388. // if there is a \code\ section in the filename truncate
  389. // everything before that (including \code\)
  390. const char *p=strstr(file,"\\code\\");
  391. p=p?p+6:file;
  392. Instance << "\n" << RepeatChar('=',80) << "\nCheck failed in " << p << ", line " << line
  393. << ",\nexpression " << expr;
  394. }
  395. return Instance;
  396. }
  397. bool Debug::CheckDone(void)
  398. {
  399. --disableAssertsEtc;
  400. // did we have an active check?
  401. if (curType==DebugIOInterface::StringType::Check)
  402. {
  403. __ASSERT(curFrameEntry!=NULL);
  404. // hit info?
  405. if (curFrameEntry->hits>1)
  406. (*this) << " (hit #" << curFrameEntry->hits << ")";
  407. // need CR?
  408. if (!ioBuffer[curType].lastWasCR)
  409. operator<<("\n");
  410. // First hit? Then do a stack trace
  411. if (curFrameEntry->hits==1)
  412. {
  413. DebugStackwalk::Signature sig;
  414. if (m_stackWalk.StackWalk(sig))
  415. (*this) << sig;
  416. }
  417. // flush out
  418. operator<<("\n\n");
  419. FlushOutput();
  420. // hit too often?
  421. if (curFrameEntry->hits==MAX_CHECK_HITS)
  422. {
  423. // yup, turn off then
  424. StartOutput(DebugIOInterface::StringType::Other,"");
  425. Instance << "Check hit too often - turning check off.\n";
  426. FlushOutput();
  427. // build 'pattern'
  428. char help[200];
  429. __ASSERT(strlen(curFrameEntry->fileOrGroup)<190);
  430. wsprintf(help,"%s(%i)",curFrameEntry->fileOrGroup,
  431. curFrameEntry->line);
  432. AddPatternEntry(FrameTypeCheck,false,help);
  433. curFrameEntry->status=Skip;
  434. }
  435. }
  436. curFrameEntry=NULL;
  437. return false;
  438. }
  439. Debug& Debug::LogBegin(const char *fileOrGroup)
  440. {
  441. // avoid infinite recursion...
  442. ++Instance.disableAssertsEtc;
  443. // anything to flush first?
  444. if (Instance.curType!=DebugIOInterface::StringType::MAX&&
  445. Instance.curType!=DebugIOInterface::StringType::Log)
  446. Instance.FlushOutput();
  447. // set new output
  448. __ASSERT(Instance.curFrameEntry==NULL);
  449. Instance.curFrameEntry=Instance.GetFrameEntry(curStackFrame,FrameTypeLog,fileOrGroup,0);
  450. if (Instance.curFrameEntry->status==NoSkip)
  451. {
  452. ++Instance.curFrameEntry->hits;
  453. // we're doing all this extra work so that DLOGs can be spread across
  454. // multiple calls
  455. if (Instance.curType==DebugIOInterface::StringType::Log&&
  456. strcmp(Instance.curSource,Instance.curFrameEntry->fileOrGroup))
  457. Instance.FlushOutput();
  458. if (Instance.curType!=DebugIOInterface::StringType::Log)
  459. Instance.StartOutput(DebugIOInterface::StringType::Log,"%s",
  460. Instance.curFrameEntry->fileOrGroup);
  461. }
  462. else if (Instance.curType!=DebugIOInterface::StringType::MAX)
  463. Instance.FlushOutput();
  464. return Instance;
  465. }
  466. bool Debug::LogDone(void)
  467. {
  468. --disableAssertsEtc;
  469. // we're not flushing here on intention!
  470. curFrameEntry=NULL;
  471. return false;
  472. }
  473. Debug& Debug::CrashBegin(const char *file, int line)
  474. {
  475. // avoid infinite recursion...
  476. ++Instance.disableAssertsEtc;
  477. // anything to flush first?
  478. if (Instance.curType!=DebugIOInterface::StringType::MAX)
  479. Instance.FlushOutput();
  480. // set new output
  481. __ASSERT(Instance.curFrameEntry==NULL);
  482. Instance.curFrameEntry=Instance.GetFrameEntry(curStackFrame,FrameTypeAssert,file,line);
  483. if (Instance.curFrameEntry->status==NoSkip)
  484. {
  485. Instance.StartOutput(DebugIOInterface::StringType::Crash,"%s(%i)",file,line);
  486. ++Instance.curFrameEntry->hits;
  487. Instance << "\n" << RepeatChar('=',80) << "\n";
  488. if (file)
  489. {
  490. // if there is a \code\ section in the filename truncate
  491. // everything before that (including \code\)
  492. const char *p=strstr(file,"\\code\\");
  493. p=p?p+6:file;
  494. Instance << "Crash in " << p << ", line " << line
  495. << ", reason:\n";
  496. }
  497. }
  498. return Instance;
  499. }
  500. bool Debug::CrashDone(bool die)
  501. {
  502. --disableAssertsEtc;
  503. // did we have an active assertion?
  504. if (curType==DebugIOInterface::StringType::Crash)
  505. {
  506. __ASSERT(curFrameEntry!=NULL);
  507. // hit info?
  508. if (curFrameEntry->hits>1)
  509. (*this) << " (hit #" << curFrameEntry->hits << ")";
  510. // need CR?
  511. if (!ioBuffer[curType].lastWasCR)
  512. operator<<("\n");
  513. // duplicate message
  514. const char *addInfo=
  515. "\nBecause of the severity of this error the "
  516. "game will now exit.";
  517. #ifdef HAS_LOGS
  518. if (IsWindowed()&&!die)
  519. addInfo=
  520. "\nPress 'abort' to abort the program,\n"
  521. "'retry' for breaking into the debugger, or\n"
  522. "'ignore' for ignoring this assertion for the\n"
  523. "time being (stops logging this assertion as well).";
  524. #endif
  525. char *help=(char *)DebugAllocMemory(ioBuffer[curType].used+strlen(addInfo)+1);
  526. strcpy(help,ioBuffer[curType].buffer+82);
  527. strcat(help,addInfo);
  528. // First hit? Then do a stack trace
  529. if (curFrameEntry->hits==1)
  530. {
  531. DebugStackwalk::Signature sig;
  532. if (m_stackWalk.StackWalk(sig))
  533. (*this) << sig;
  534. }
  535. // ... and flush out
  536. operator<<("\n\n");
  537. FlushOutput();
  538. // now display message, wait for user input
  539. #ifdef HAS_LOGS
  540. // show dialog box only if running windowed
  541. if (!die)
  542. {
  543. if (IsWindowed())
  544. {
  545. /// @todo replace MessageBox with custom dialog w/ 4 options: abort, skip 1, skip all, break
  546. // now display message, wait for user input
  547. int result=MessageBox(NULL,help,"Crash hit",
  548. MB_ABORTRETRYIGNORE|MB_ICONSTOP|MB_TASKMODAL|MB_SETFOREGROUND);
  549. switch(result)
  550. {
  551. case IDABORT:
  552. curFrameEntry=NULL;
  553. exit(1);
  554. break;
  555. case IDIGNORE:
  556. {
  557. // build 'pattern'
  558. char help[200];
  559. __ASSERT(strlen(curFrameEntry->fileOrGroup)<190);
  560. wsprintf(help,"%s(%i)",curFrameEntry->fileOrGroup,
  561. curFrameEntry->line);
  562. AddPatternEntry(FrameTypeAssert,false,help);
  563. curFrameEntry->status=Skip;
  564. }
  565. break;
  566. case IDRETRY:
  567. _asm int 0x03
  568. break;
  569. default:
  570. ((void)0);
  571. }
  572. }
  573. else
  574. {
  575. // running fullscreen...
  576. // hit too often?
  577. if (curFrameEntry->hits==MAX_CHECK_HITS)
  578. {
  579. // yup, turn off then
  580. StartOutput(DebugIOInterface::StringType::Other,"");
  581. Instance << "Crash hit too often - turning check off.\n";
  582. FlushOutput();
  583. // build 'pattern'
  584. char help[200];
  585. __ASSERT(strlen(curFrameEntry->fileOrGroup)<190);
  586. wsprintf(help,"%s(%i)",curFrameEntry->fileOrGroup,
  587. curFrameEntry->line);
  588. AddPatternEntry(FrameTypeAssert,false,help);
  589. curFrameEntry->status=Skip;
  590. }
  591. }
  592. }
  593. else
  594. #endif
  595. {
  596. MessageBox(NULL,help,"Game crash",
  597. MB_OK|MB_ICONSTOP|MB_TASKMODAL|MB_SETFOREGROUND);
  598. curFrameEntry=NULL;
  599. _exit(1);
  600. }
  601. }
  602. curFrameEntry=NULL;
  603. return false;
  604. }
  605. Debug& Debug::operator<<(const char *str)
  606. {
  607. if (curType==DebugIOInterface::StringType::MAX)
  608. // yes, this is valid and simply means not to
  609. // write anything...
  610. return *this;
  611. // buffer large enough?
  612. if (!str)
  613. str="[NULL]";
  614. else if (!*str)
  615. return *this;
  616. unsigned len=strlen(str);
  617. // forced width?
  618. if (len<m_width)
  619. {
  620. for (unsigned k=len;k<m_width;k++)
  621. AddOutput(&m_fillChar,1);
  622. }
  623. // reset width after each insertion
  624. m_width=0;
  625. AddOutput(str,len);
  626. return *this;
  627. }
  628. void Debug::SetPrefixAndRadix(const char *prefix, int radix)
  629. {
  630. strncpy(m_prefix,prefix?prefix:"",sizeof(m_prefix)-1);
  631. m_prefix[sizeof(m_prefix)-1]=0;
  632. m_radix=radix;
  633. }
  634. Debug& Debug::operator<<(int val)
  635. {
  636. // usually having a fixed size buffer and a function
  637. // that doesn't check for buffer overflow isn't a good idea
  638. // but in this case we know how long it can be at max...
  639. char help[1+32+1]; // sign, 32 digits (binary), NUL
  640. AddOutput(m_prefix,strlen(m_prefix));
  641. return (*this) << _itoa(val,help,m_radix);
  642. }
  643. Debug& Debug::operator<<(unsigned val)
  644. {
  645. // usually having a fixed size buffer and a function
  646. // that doesn't check for buffer overflow isn't a good idea
  647. // but in this case we know how long it can be at max...
  648. char help[32+1]; // 32 digits, NUL
  649. AddOutput(m_prefix,strlen(m_prefix));
  650. return (*this) << _ultoa(val,help,m_radix);
  651. }
  652. Debug& Debug::operator<<(long val)
  653. {
  654. // usually having a fixed size buffer and a function
  655. // that doesn't check for buffer overflow isn't a good idea
  656. // but in this case we know how long it can be at max...
  657. char help[1+32+1]; // sign, 32 digits, NUL
  658. AddOutput(m_prefix,strlen(m_prefix));
  659. return (*this) << _itoa(val,help,m_radix);
  660. }
  661. Debug& Debug::operator<<(unsigned long val)
  662. {
  663. // usually having a fixed size buffer and a function
  664. // that doesn't check for buffer overflow isn't a good idea
  665. // but in this case we know how long it can be at max...
  666. char help[32+1]; // 32 digits, NUL
  667. AddOutput(m_prefix,strlen(m_prefix));
  668. return (*this) << _ultoa(val,help,m_radix);
  669. }
  670. Debug& Debug::operator<<(bool val)
  671. {
  672. return (*this) << (val?"true":"false");
  673. }
  674. Debug& Debug::operator<<(float val)
  675. {
  676. /// @todo_opt shouldn't use snprintf here - brings in most of the old C IO lib...
  677. char help[200];
  678. _snprintf(help,sizeof(help),"%f",val);
  679. return (*this) << help;
  680. }
  681. Debug& Debug::operator<<(double val)
  682. {
  683. /// @todo_opt shouldn't use snprintf here - brings in most of the old C IO lib...
  684. char help[200];
  685. _snprintf(help,sizeof(help),"%f",val);
  686. return (*this) << help;
  687. }
  688. Debug& Debug::operator<<(short val)
  689. {
  690. // usually having a fixed size buffer and a function
  691. // that doesn't check for buffer overflow isn't a good idea
  692. // but in this case we know how long it can be at max...
  693. char help[1+16+1]; // sign, 16 digits, NUL
  694. AddOutput(m_prefix,strlen(m_prefix));
  695. return (*this) << _itoa(val,help,m_radix);
  696. }
  697. Debug& Debug::operator<<(unsigned short val)
  698. {
  699. // usually having a fixed size buffer and a function
  700. // that doesn't check for buffer overflow isn't a good idea
  701. // but in this case we know how long it can be at max...
  702. char help[16+1]; // 16 digits, NUL
  703. AddOutput(m_prefix,strlen(m_prefix));
  704. return (*this) << _itoa(val,help,m_radix);
  705. }
  706. Debug& Debug::operator<<(__int64 val)
  707. {
  708. // usually having a fixed size buffer and a function
  709. // that doesn't check for buffer overflow isn't a good idea
  710. // but in this case we know how long it can be at max...
  711. char help[1+64+1]; // sign, 64 digits, NUL
  712. AddOutput(m_prefix,strlen(m_prefix));
  713. return (*this) << _i64toa(val,help,m_radix);
  714. }
  715. Debug& Debug::operator<<(unsigned __int64 val)
  716. {
  717. // usually having a fixed size buffer and a function
  718. // that doesn't check for buffer overflow isn't a good idea
  719. // but in this case we know how long it can be at max...
  720. char help[64+1]; // sign, 64 digits, NUL
  721. AddOutput(m_prefix,strlen(m_prefix));
  722. return (*this) << _ui64toa(val,help,m_radix);
  723. }
  724. Debug& Debug::operator<<(const void *ptr)
  725. {
  726. (*this) << "ptr:";
  727. if (ptr)
  728. {
  729. char help[9];
  730. (*this) << "0x" << _ultoa((unsigned long)ptr,help,16);
  731. }
  732. else
  733. (*this) << "NULL";
  734. return *this;
  735. }
  736. Debug& Debug::operator<<(const MemDump &dump)
  737. {
  738. if (curType==DebugIOInterface::StringType::MAX)
  739. return *this;
  740. // need CR?
  741. if (!ioBuffer[curType].lastWasCR)
  742. operator<<("\n");
  743. // How many items per line? We're assuming an output
  744. // width of 73 chars. Left border is address thus
  745. // leaving 65 chars effectively. If character dump is
  746. // enabled then an additional space is needed (64 chars
  747. // then).
  748. unsigned itemPerLine=(dump.m_withChars?64:65)/
  749. (1+2*dump.m_bytePerItem+(dump.m_withChars?1:0));
  750. if (!itemPerLine)
  751. itemPerLine=1;
  752. // now dump line by line
  753. const unsigned char *cur=dump.m_startPtr;
  754. for (unsigned i=0;i<dump.m_numItems;i+=itemPerLine,cur+=itemPerLine*dump.m_bytePerItem)
  755. {
  756. // address
  757. char buf[9];
  758. sprintf(buf,"%08x",dump.m_absAddr?unsigned(cur):cur-dump.m_startPtr);
  759. operator<<(buf);
  760. // items
  761. const unsigned char *curByte=cur;
  762. for (unsigned k=0;k<itemPerLine;k++,curByte+=dump.m_bytePerItem)
  763. {
  764. operator<<(" ");
  765. if (k+i>=dump.m_numItems)
  766. {
  767. for (unsigned l=dump.m_bytePerItem;l;--l)
  768. operator<<(" ");
  769. }
  770. else if (IsBadReadPtr(curByte,dump.m_bytePerItem))
  771. {
  772. for (unsigned l=dump.m_bytePerItem;l;--l)
  773. operator<<("??");
  774. }
  775. else
  776. {
  777. curByte+=dump.m_bytePerItem;
  778. for (unsigned l=0;l<dump.m_bytePerItem;++l)
  779. {
  780. sprintf(buf,"%02x",*--curByte);
  781. operator<<(buf);
  782. }
  783. }
  784. }
  785. // characters
  786. if (!dump.m_withChars)
  787. continue;
  788. operator<<(" ");
  789. curByte=cur;
  790. for (k=0;k<itemPerLine;k++,curByte+=dump.m_bytePerItem)
  791. {
  792. if (k+i>=dump.m_numItems)
  793. break;
  794. else if (IsBadReadPtr(curByte,dump.m_bytePerItem))
  795. {
  796. for (unsigned l=dump.m_bytePerItem;l;--l)
  797. operator<<("?");
  798. }
  799. else
  800. {
  801. buf[1]=0;
  802. for (unsigned l=0;l<dump.m_bytePerItem;++l)
  803. {
  804. *buf=curByte[l]>' '?curByte[l]:'.';
  805. operator<<(buf);
  806. }
  807. }
  808. }
  809. operator<<("\n");
  810. }
  811. return *this;
  812. }
  813. Debug& Debug::operator<<(HResult hres)
  814. {
  815. for (unsigned k=0;k<numHrTranslators;k++)
  816. if (hrTranslators[k].func(*this,hres.m_hresult,hrTranslators[k].user))
  817. return *this;
  818. (*this) << "HResult:0x";
  819. char help[9];
  820. return (*this) << _ultoa(hres.m_hresult,help,16);
  821. }
  822. bool Debug::IsLogEnabled(const char *fileOrGroup)
  823. {
  824. // now this isn't great but since IsLogEnabled is supposed
  825. // to be used from the D_ISLOG macros only and those guarantee
  826. // that we are having real static strings let's use
  827. // that strings address as frame address...
  828. FrameHashEntry *e=Instance.LookupFrame((unsigned)fileOrGroup);
  829. if (!e)
  830. e=Instance.AddFrameEntry((unsigned)fileOrGroup,FrameTypeLog,fileOrGroup,0);
  831. if (e->status==Unknown)
  832. Instance.UpdateFrameStatus(*e);
  833. return e->status==NoSkip;
  834. }
  835. void Debug::AddHResultTranslator(unsigned prio, HResultTranslator func, void *user)
  836. {
  837. // bail out if invalid parameter passed in
  838. if (!func)
  839. return;
  840. // just remove it first (if it's not in there nothing is done)
  841. // necessary in case we want to 'change' the priority of an
  842. // existing HR translator
  843. RemoveHResultTranslator(func,user);
  844. // now find the right place to insert the translator
  845. // (slow but this function is not time critical)
  846. for (unsigned k=0;k<Instance.numHrTranslators;++k)
  847. if (Instance.hrTranslators[k].prio<prio)
  848. break;
  849. // grow & move
  850. Instance.hrTranslators=(HResultTranslatorEntry *)
  851. DebugReAllocMemory(Instance.hrTranslators,(Instance.numHrTranslators+1)*sizeof(void *));
  852. memmove(Instance.hrTranslators+k+1,Instance.hrTranslators+k,(Instance.numHrTranslators-k)*sizeof(void *));
  853. // add new
  854. ++Instance.numHrTranslators;
  855. Instance.hrTranslators[k].prio=prio;
  856. Instance.hrTranslators[k].func=func;
  857. Instance.hrTranslators[k].user=user;
  858. }
  859. void Debug::RemoveHResultTranslator(HResultTranslator func, void *user)
  860. {
  861. // bail out if invalid parameter passed in
  862. if (!func)
  863. return;
  864. // look for func/user pair
  865. for (unsigned k=0;k<Instance.numHrTranslators;++k)
  866. if (Instance.hrTranslators[k].func==func&&
  867. Instance.hrTranslators[k].user==user)
  868. {
  869. // remove it
  870. memmove(Instance.hrTranslators+k,Instance.hrTranslators+k+1,
  871. (Instance.numHrTranslators-k-1)*sizeof(void *));
  872. --Instance.hrTranslators;
  873. Instance.hrTranslators=(HResultTranslatorEntry *)
  874. DebugReAllocMemory(Instance.hrTranslators,Instance.numHrTranslators*sizeof(void *));
  875. }
  876. }
  877. bool Debug::AddIOFactory(const char *io_id, const char *descr, DebugIOInterface* (*func)(void))
  878. {
  879. // bail out if invalid parameters passed in
  880. if (!io_id||!func)
  881. return true;
  882. // allocate & init new list entry
  883. IOFactoryListEntry *entry=(IOFactoryListEntry *)
  884. DebugAllocMemory(sizeof(IOFactoryListEntry));
  885. entry->next=Instance.firstIOFactory;
  886. entry->ioID=io_id;
  887. entry->descr=descr;
  888. entry->factory=func;
  889. entry->io=NULL;
  890. entry->input=NULL;
  891. entry->inputAlloc=0;
  892. entry->inputUsed=0;
  893. // add to list
  894. Instance.firstIOFactory=entry;
  895. return true;
  896. }
  897. bool Debug::AddCommands(const char *cmdgroup, DebugCmdInterface *cmdif)
  898. {
  899. // bail out if invalid parameters passed in
  900. if (!cmdgroup||!cmdif)
  901. return true;
  902. // walk to end of list, add there (unless interface pointer already in list)
  903. CmdInterfaceListEntry **listptr=&Instance.firstCmdGroup;
  904. while (*listptr)
  905. {
  906. if ((*listptr)->cmdif==cmdif)
  907. // interface already in list, don't add twice
  908. return true;
  909. listptr=&((*listptr)->next);
  910. }
  911. // allocate & init new list entry
  912. CmdInterfaceListEntry *entry=(CmdInterfaceListEntry *)
  913. DebugAllocMemory(sizeof(CmdInterfaceListEntry));
  914. entry->next=NULL;
  915. entry->group=cmdgroup;
  916. entry->cmdif=cmdif;
  917. // add to list
  918. *listptr=entry;
  919. return true;
  920. }
  921. void Debug::RemoveCommands(DebugCmdInterface *cmdif)
  922. {
  923. // bail out if invalid parameter passed in
  924. if (!cmdif)
  925. return;
  926. // walk the list, search for interface pointer
  927. CmdInterfaceListEntry **listptr=&Instance.firstCmdGroup;
  928. while (*listptr)
  929. {
  930. if ((*listptr)->cmdif==cmdif)
  931. {
  932. // found it, now remove it
  933. CmdInterfaceListEntry *cur=*listptr;
  934. *listptr=cur->next;
  935. // free list entry
  936. DebugFreeMemory(cur);
  937. // done
  938. break;
  939. }
  940. listptr=&((*listptr)->next);
  941. }
  942. }
  943. void Debug::Command(const char *cmd)
  944. {
  945. DFAIL_IF(!cmd) return;
  946. Instance.ExecCommand(cmd,cmd+strlen(cmd));
  947. }
  948. void Debug::Update(void)
  949. {
  950. // check all existing IO interfaces
  951. for (IOFactoryListEntry *cur=Instance.firstIOFactory;cur;cur=cur->next)
  952. {
  953. if (!cur->io)
  954. continue;
  955. // any input?
  956. bool hadInput=false;
  957. for (;;)
  958. {
  959. if (cur->inputAlloc-cur->inputUsed<64)
  960. // must grow input buffer...
  961. cur->input=(char *)DebugReAllocMemory(cur->input,(cur->inputAlloc+=64)+1);
  962. int numChars=cur->io->Read(cur->input+cur->inputUsed,cur->inputAlloc-cur->inputUsed);
  963. if (!numChars)
  964. break;
  965. cur->inputUsed+=numChars;
  966. cur->input[cur->inputUsed]=0;
  967. hadInput=true;
  968. }
  969. if (!hadInput)
  970. // skip then
  971. continue;
  972. // else look for completed commands and try to process them
  973. for (;;)
  974. {
  975. char *p=strchr(cur->input,'\n');
  976. if (!p)
  977. break;
  978. Instance.ExecCommand(cur->input,p);
  979. strcpy(cur->input,p+1);
  980. cur->inputUsed=strlen(cur->input);
  981. }
  982. }
  983. }
  984. Debug::FrameHashEntry* Debug::AddFrameEntry(unsigned addr, unsigned type,
  985. const char *fileOrGroup, int line)
  986. {
  987. __ASSERT(LookupFrame(addr)==NULL);
  988. // get new entry
  989. if (!numAvailableFrameHash)
  990. {
  991. numAvailableFrameHash=FRAME_HASH_ALLOC_COUNT;
  992. nextUnusedFrameHash=(FrameHashEntry *)
  993. DebugAllocMemory(numAvailableFrameHash*sizeof(FrameHashEntry));
  994. }
  995. FrameHashEntry *e=nextUnusedFrameHash++;
  996. --numAvailableFrameHash;
  997. // fill entry
  998. e->next=frameHash[addr%FRAME_HASH_SIZE];
  999. e->frameAddr=addr;
  1000. e->frameType=type;
  1001. e->line=line;
  1002. e->status=Unknown;
  1003. e->hits=0;
  1004. // log?
  1005. if (type&FrameTypeLog)
  1006. {
  1007. // must add to list of known logs,
  1008. // store translated name
  1009. e->fileOrGroup=AddLogGroup(fileOrGroup,NULL);
  1010. }
  1011. else
  1012. {
  1013. // no, just add file name (without path though)
  1014. e->fileOrGroup=fileOrGroup?strrchr(fileOrGroup,'\\'):NULL;
  1015. e->fileOrGroup=e->fileOrGroup?e->fileOrGroup+1:fileOrGroup;
  1016. }
  1017. // add to hash
  1018. frameHash[addr%FRAME_HASH_SIZE]=e;
  1019. return e;
  1020. }
  1021. void Debug::UpdateFrameStatus(FrameHashEntry &entry)
  1022. {
  1023. // build pattern match entry
  1024. char help[512];
  1025. if (entry.frameType==FrameTypeAssert||
  1026. entry.frameType==FrameTypeCheck)
  1027. wsprintf(help,"%s(%i)",entry.fileOrGroup,entry.line);
  1028. else
  1029. strcpy(help,entry.fileOrGroup);
  1030. // update frame status
  1031. bool active=entry.frameType!=FrameTypeLog;
  1032. for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
  1033. {
  1034. if (!(cur->frameTypes&entry.frameType))
  1035. continue;
  1036. if (SimpleMatch(help,cur->pattern))
  1037. active=cur->isActive;
  1038. }
  1039. entry.status=active?NoSkip:Skip;
  1040. }
  1041. const char *Debug::AddLogGroup(const char *fileOrGroup, const char *descr)
  1042. {
  1043. // helper buffer for stripping down fileOrGroup
  1044. char help[200];
  1045. // do we need to strip down fileOrGroup?
  1046. const char *p=strrchr(fileOrGroup,'\\');
  1047. const char *q=strchr(p?p:fileOrGroup,'.');
  1048. if (p||q)
  1049. {
  1050. // this extracts everything beyond the last backslash
  1051. // up to the first dot
  1052. p=p?p+1:fileOrGroup;
  1053. if (!q) q=p+strlen(p);
  1054. if (q-p>=sizeof(help))
  1055. q=p+sizeof(help)-1;
  1056. memcpy(help,p,q-p);
  1057. help[q-p]=0;
  1058. fileOrGroup=help;
  1059. }
  1060. // is that log group known?
  1061. for (KnownLogGroupList *cur=firstLogGroup;cur;cur=cur->next)
  1062. {
  1063. if (!strcmp(cur->nameGroup,fileOrGroup))
  1064. {
  1065. // yes, return translated name
  1066. return cur->nameGroup;
  1067. }
  1068. }
  1069. // no, add new entry
  1070. cur=(KnownLogGroupList *)DebugAllocMemory(sizeof(KnownLogGroupList));
  1071. cur->next=firstLogGroup;
  1072. cur->nameGroup=(char *)DebugAllocMemory(strlen(fileOrGroup)+1);
  1073. strcpy(cur->nameGroup,fileOrGroup);
  1074. cur->descr=descr;
  1075. firstLogGroup=cur;
  1076. return cur->nameGroup;
  1077. }
  1078. void Debug::StartOutput(DebugIOInterface::StringType type, const char *fmt, ...)
  1079. {
  1080. if (curType==DebugIOInterface::Log)
  1081. FlushOutput();
  1082. __ASSERT(curType==DebugIOInterface::StringType::MAX);
  1083. curType=type;
  1084. // potentially dangerous (fixed string buffer...)
  1085. va_list va;
  1086. va_start(va,fmt);
  1087. wvsprintf(curSource,fmt,va);
  1088. va_end(va);
  1089. __ASSERT(curSource[sizeof(curSource)-1]==0);
  1090. }
  1091. void Debug::AddOutput(const char *str, unsigned remainingLen)
  1092. {
  1093. // bail out if no valid destination type
  1094. // (valid, can happen if hitting a disabled log for the first time)
  1095. if (curType==DebugIOInterface::StringType::MAX)
  1096. return;
  1097. while (remainingLen)
  1098. {
  1099. // if we're doing timestamps we have to split at each '\n'
  1100. unsigned len;
  1101. if (timeStamp)
  1102. {
  1103. // add timestamp now?
  1104. if (ioBuffer[curType].lastWasCR)
  1105. {
  1106. SYSTEMTIME systime;
  1107. GetLocalTime(&systime);
  1108. char ts[40];
  1109. wsprintf(ts,"[%02i:%02i.%02i.%03i] ",systime.wHour,systime.wMinute,
  1110. systime.wSecond,systime.wMilliseconds);
  1111. unsigned tsLen=strlen(ts);
  1112. memcpy(ioBuffer[curType].buffer+ioBuffer[curType].used,ts,tsLen+1);
  1113. ioBuffer[curType].used+=tsLen;
  1114. }
  1115. // search for next '\n'
  1116. const char *p=strchr(str,'\n');
  1117. p=p?p+1:str+remainingLen;
  1118. len=p-str;
  1119. }
  1120. else
  1121. len=remainingLen;
  1122. if (ioBuffer[curType].used+len+64>=ioBuffer[curType].alloc)
  1123. {
  1124. // no, must grow buffer
  1125. ioBuffer[curType].alloc+=len+1024;
  1126. ioBuffer[curType].buffer=(char *)
  1127. DebugReAllocMemory(ioBuffer[curType].buffer,ioBuffer[curType].alloc);
  1128. }
  1129. // add to buffer (with NUL)
  1130. memcpy(ioBuffer[curType].buffer+ioBuffer[curType].used,str,len+1);
  1131. ioBuffer[curType].used+=len;
  1132. // last char CR?
  1133. ioBuffer[curType].lastWasCR=str[len-1]=='\n';
  1134. str+=len;
  1135. remainingLen-=len;
  1136. // are we writing a log string?
  1137. if (curType==DebugIOInterface::Log&&ioBuffer[curType].lastWasCR)
  1138. {
  1139. // yes, flush out now
  1140. FlushOutput();
  1141. curType=DebugIOInterface::Log;
  1142. }
  1143. }
  1144. }
  1145. void Debug::FlushOutput(bool defaultLog)
  1146. {
  1147. __ASSERT(curType!=DebugIOInterface::StringType::MAX);
  1148. // bail out early if buffer is still empty
  1149. if (!ioBuffer[curType].used)
  1150. {
  1151. curType=DebugIOInterface::StringType::MAX;
  1152. return;
  1153. }
  1154. // need CR?
  1155. if (!ioBuffer[curType].lastWasCR)
  1156. operator<<("\n");
  1157. // send string to all active I/O interfaces
  1158. bool hadWrite=!defaultLog;
  1159. for (IOFactoryListEntry *cur=firstIOFactory;cur;cur=cur->next)
  1160. {
  1161. if (!cur->io)
  1162. continue;
  1163. hadWrite=true;
  1164. cur->io->Write(curType,curSource,ioBuffer[curType].buffer);
  1165. if (alwaysFlush)
  1166. cur->io->Write(curType,curSource,NULL);
  1167. }
  1168. // written nowhere?
  1169. if (!hadWrite&&curType!=DebugIOInterface::StringType::StructuredCmdReply)
  1170. {
  1171. #ifdef HAS_LOGS
  1172. // then force output to a very simple default log file
  1173. // (non-Release builds only)
  1174. HANDLE h=CreateFile("default.log",GENERIC_WRITE,0,NULL,
  1175. OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  1176. SetFilePointer(h,0,NULL,FILE_END);
  1177. DWORD dwDummy;
  1178. WriteFile(h,ioBuffer[curType].buffer,strlen(ioBuffer[curType].buffer),&dwDummy,NULL);
  1179. CloseHandle(h);
  1180. #endif
  1181. }
  1182. // empty buffer etc.
  1183. ioBuffer[curType].used=0;
  1184. *ioBuffer[curType].buffer=0;
  1185. curType=DebugIOInterface::StringType::MAX;
  1186. *curSource=0;
  1187. }
  1188. void Debug::AddPatternEntry(unsigned types, bool isActive, const char *pattern)
  1189. {
  1190. __ASSERT(pattern);
  1191. // alloc new pattern entry
  1192. PatternListEntry *cur=(PatternListEntry *)
  1193. DebugAllocMemory(sizeof(PatternListEntry));
  1194. // init
  1195. cur->next=NULL;
  1196. cur->frameTypes=types;
  1197. cur->isActive=isActive;
  1198. cur->pattern=(char *)DebugAllocMemory(strlen(pattern)+1);
  1199. strcpy(cur->pattern,pattern);
  1200. // add to list
  1201. if (lastPatternEntry)
  1202. lastPatternEntry->next=cur;
  1203. else
  1204. firstPatternEntry=cur;
  1205. lastPatternEntry=cur;
  1206. }
  1207. bool Debug::SimpleMatch(const char *str, const char *pattern)
  1208. {
  1209. __ASSERT(str);
  1210. __ASSERT(pattern);
  1211. while (*str&&*pattern)
  1212. {
  1213. if (*pattern=='*')
  1214. {
  1215. pattern++;
  1216. while (*str)
  1217. if (SimpleMatch(str++,pattern))
  1218. return true;
  1219. return *str==*pattern;
  1220. }
  1221. else
  1222. {
  1223. if (*str++!=*pattern++)
  1224. return false;
  1225. }
  1226. }
  1227. return *str==*pattern;
  1228. }
  1229. void Debug::SetBuildInfo(const char *version,
  1230. const char *internalVersion,
  1231. const char *buildDate)
  1232. {
  1233. if (version)
  1234. strncpy(Instance.m_version,version,sizeof(Instance.m_version)-1);
  1235. if (internalVersion)
  1236. strncpy(Instance.m_intVersion,internalVersion,sizeof(Instance.m_intVersion)-1);
  1237. if (buildDate)
  1238. strncpy(Instance.m_buildDate,buildDate,sizeof(Instance.m_buildDate)-1);
  1239. }
  1240. void Debug::WriteBuildInfo(void)
  1241. {
  1242. operator<<("Version:");
  1243. if (*m_version)
  1244. (*this) << " " << m_version;
  1245. if (*m_intVersion)
  1246. (*this) << " internal " << m_intVersion;
  1247. #if defined(_INTERNAL)
  1248. operator<<(" internal");
  1249. #elif defined(_DEBUG)
  1250. operator<<(" debug");
  1251. #elif defined(_PROFILE)
  1252. operator<<(" profile");
  1253. #else
  1254. operator<<(" release");
  1255. #endif
  1256. if (*m_buildDate)
  1257. (*this) << " build " << m_buildDate;
  1258. }
  1259. void Debug::ExecCommand(const char *cmdstart, const char *cmdend)
  1260. {
  1261. // split off into command and arguments
  1262. // alloc & copy string
  1263. char *strbuf=(char *)DebugAllocMemory(cmdend-cmdstart+1);
  1264. memcpy(strbuf,cmdstart,cmdend-cmdstart);
  1265. strbuf[cmdend-cmdstart]=0;
  1266. // for simplicity I'm using a fixed size argv array here...
  1267. // if there are more arguments given than we have we're
  1268. // just dropping the excess arguments
  1269. char *parts[100];
  1270. int numParts=0;
  1271. char *lastNonWhitespace=NULL;
  1272. char *cur=strbuf;
  1273. // regular reply or structured reply?
  1274. DebugIOInterface::StringType reply;
  1275. DebugCmdInterface::CommandMode mode;
  1276. if (*cur=='!')
  1277. {
  1278. cur++;
  1279. reply=DebugIOInterface::StringType::StructuredCmdReply;
  1280. mode=DebugCmdInterface::CommandMode::Structured;
  1281. }
  1282. else
  1283. {
  1284. reply=DebugIOInterface::StringType::CmdReply;
  1285. mode=DebugCmdInterface::CommandMode::Normal;
  1286. }
  1287. for (;;)
  1288. {
  1289. if (!lastNonWhitespace&&(*cur=='\''||*cur=='"'))
  1290. {
  1291. char quote=*cur++;
  1292. if (numParts<sizeof(parts)/sizeof(*parts))
  1293. parts[numParts++]=cur;
  1294. while (*cur&&*cur!=quote)
  1295. ++cur;
  1296. if (*cur)
  1297. *cur++=0;
  1298. }
  1299. else if (*cur==' '||*cur=='\t'||!*cur||*cur==';')
  1300. {
  1301. if (*cur==';')
  1302. *cur=0;
  1303. if (lastNonWhitespace)
  1304. {
  1305. if (numParts<sizeof(parts)/sizeof(*parts))
  1306. parts[numParts++]=lastNonWhitespace;
  1307. lastNonWhitespace=NULL;
  1308. if (*cur)
  1309. *cur++=0;
  1310. }
  1311. else if (*cur)
  1312. ++cur;
  1313. else
  1314. break;
  1315. }
  1316. else
  1317. {
  1318. if (!lastNonWhitespace)
  1319. lastNonWhitespace=cur;
  1320. ++cur;
  1321. }
  1322. }
  1323. if (numParts)
  1324. {
  1325. // part[0] is the command, part[1..numParts] are arguments
  1326. // split off command group (if any)
  1327. char *p=strchr(parts[0],'.');
  1328. if (p&&p-parts[0]<sizeof(curCommandGroup))
  1329. {
  1330. memcpy(curCommandGroup,parts[0],p-parts[0]);
  1331. curCommandGroup[p-parts[0]]=0;
  1332. ++p;
  1333. }
  1334. else
  1335. p=parts[0];
  1336. StartOutput(reply,"%s.%s",curCommandGroup,p);
  1337. if (mode!=DebugCmdInterface::CommandMode::Structured)
  1338. AddOutput("> ",2);
  1339. // repeat current command first
  1340. AddOutput(cmdstart,cmdend-cmdstart);
  1341. AddOutput("\n",1);
  1342. // command group known?
  1343. for (CmdInterfaceListEntry *cur=firstCmdGroup;cur;cur=cur->next)
  1344. if (!strcmp(curCommandGroup,cur->group))
  1345. break;
  1346. if (!cur)
  1347. {
  1348. // nope, show error message
  1349. (*this) << "Unknown command group " << curCommandGroup;
  1350. *p=0;
  1351. }
  1352. if (*p)
  1353. {
  1354. // must have command...
  1355. // search for a matching command handler
  1356. for (CmdInterfaceListEntry *cur=firstCmdGroup;cur;cur=cur->next)
  1357. {
  1358. if (strcmp(curCommandGroup,cur->group))
  1359. continue;
  1360. bool doneCommand=cur->cmdif->Execute(*this,p,mode,numParts-1,parts+1);
  1361. if (doneCommand&&(strcmp(p,"help")||numParts>1))
  1362. break;
  1363. }
  1364. // display error message if command not found, break away
  1365. if (!cur&&mode==DebugCmdInterface::CommandMode::Normal)
  1366. {
  1367. if (strcmp(p,"help"))
  1368. operator<<("Unknown command");
  1369. else if (numParts>1)
  1370. operator<<("Unknown command, help not available");
  1371. }
  1372. }
  1373. // flush output only if there is already an active I/O class
  1374. FlushOutput(false);
  1375. }
  1376. // cleanup
  1377. DebugFreeMemory(strbuf);
  1378. }
  1379. // little helper to get app window
  1380. static BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM lParam)
  1381. {
  1382. *(HWND *)lParam=hwnd;
  1383. return FALSE;
  1384. }
  1385. bool Debug::IsWindowed(void)
  1386. {
  1387. // use cached result if possible
  1388. if (m_isWindowed)
  1389. return m_isWindowed>0;
  1390. // find main app window
  1391. HWND appHWnd=NULL;
  1392. EnumThreadWindows(GetCurrentThreadId(),EnumThreadWndProc,(LPARAM)&appHWnd);
  1393. if (!appHWnd)
  1394. {
  1395. // couldn't find main window, assume we're windowed anyway
  1396. m_isWindowed=1;
  1397. return true;
  1398. }
  1399. // we assume full screen if WS_CAPTION is not set
  1400. m_isWindowed=(GetWindowLong(appHWnd,GWL_STYLE)&WS_CAPTION)?1:-1;
  1401. return m_isWindowed>0;
  1402. }
  1403. //////////////////////////////////////////////////////////////////////////////
  1404. // And finally for a little list of C/C++ runtime replacement functions.
  1405. // Abort process due to fatal heap error
  1406. void __cdecl _heap_abort(void)
  1407. {
  1408. DCRASH_RELEASE("Fatal heap error.");
  1409. }