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