2 * File: debuglogger.cpp
4 * Author: Lukas Zeller (luz@plan44.ch)
6 * Global debug mechanisms
8 * Copyright (c) 2005-2011 by Synthesis AG + plan44.ch
10 * 2005-08-04 : luz : created
15 #include "prefix_file.h"
19 #include "debuglogger.h"
22 #ifdef MULTI_THREAD_SUPPORT
23 #include "platform_thread.h"
28 #ifndef HARDCODED_CONFIG
31 cAppCharP const DbgOutFormatNames[numDbgOutFormats] = {
32 "text", // plain text format (but can be indented)
38 // HTML dynamic folding modes
39 cAppCharP const DbgFoldingModeNames[numDbgFoldingModes] = {
40 "none", // do not include dynamic folding into HTML logs
41 "collapsed", // include folding - all collapsed by default
42 "expanded", // include folding - all expanded by default
43 "auto" // include folding - collapse/expand state predefined on a block-by-block basis
47 cAppCharP const DbgSourceModeNames[numDbgSourceModes] = {
48 "none", // do not include links into source code in HTML logs
49 "hint", // no links, but info about what file/line number the message comes from
50 "doxygen", // include link into doxygen prepared HTML version of source code
51 "txmt", // include txmt:// link (understood by TextMate and BBEdit) into source code
56 cAppCharP const DbgFlushModeNames[numDbgFlushModes] = {
57 "buffered", // no flush, keep open as long as possible, output buffered (fast, needed for network drives)
58 "flush", // flush every debug message
59 "openclose" // open and close debug channel separately for every message (as in 2.x engine)
62 // debug subthread isolation modes
63 cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes] = {
64 "none", // do not handle output from subthread specially
65 "suppress", // suppress output from subthreads
66 "separate", // create separate output stream (=file) for each subthread
67 "mix", // mix on a line by line basis
68 "mixblocks" // buffer thread's output and mix block-wise it into main stream when appropriate
73 // file extentsions for debug format modes
74 cAppCharP const DbgOutFormatExtensions[numDbgOutFormats] = {
75 ".log", // plain text format (but can be indented)
77 ".html" // HTML format
81 cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = {
83 "<?xml version=\"1.0\"?>\n"
84 "<sysync_log version=\"1.0\">",
85 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
86 "<html><head><title>SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log</title>\n"
87 "<meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\">\n"
88 "<style type=\"text/css\" media=\"screen\"><!--\n"
89 ".block { color: #0000FF; font-weight: bold; }\n"
90 ".attribute { color: #A5002C; }\n"
91 ".attrval { color: #D80039; font-weight: bold; }\n"
92 ".error { color: red; font-weight: bold; }\n"
93 ".hotalone { color: #000000; font-weight: bold; }\n"
94 ".hot { font-weight: bold; }\n"
95 ".script { color: #996633; }\n" // brownish
96 ".source { color: #3333FF; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // keyword blue
97 ".comment { color: #669933; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // comment green
98 ".skipped { color: #BBBBBB; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // skipped code
99 ".value { color: #FF3300; }\n" // bright orange
100 ".filter { color: #997F66; }\n" // brownish pale
101 ".match { color: #A95E38; }\n" // brownish orange
102 ".dbapi { color: #CC3366; }\n" // dark reddish/pink (pink/violet = database)
103 ".plugin { color: #9151A3; }\n" // dark violet (pink/violet = database)
104 ".incoming { color: #196D00; }\n" // really dark green (green = remote)
105 ".outgoing { color: #002C84; }\n" // really dark blue (blue = local)
106 ".conflict { color: #990000; }\n" // dark red
107 ".remote { color: #709900; }\n" // greenish (green = remote)
108 ".proto { color: #777100; }\n" // dark yellowish/brown
109 ".rest { color: #AAAAAA; }\n" // greyed
110 ".exotic { color: #FF9900; }\n" // mango
111 "a.jump { color: #5D82BA; }\n"
112 "pre { font-size: 90%; }\n"
113 // for folding (always included, as it must be in header)
116 " font-weight: bold;\n"
120 " display: inline;\n"
121 " border-width: 0.2em;\n"
122 " border-style: solid;\n"
123 " text-align: center;\n"
124 " vertical-align: middle;\n"
125 " padding: 0px 0.2em 0px 0.2em;\n"
126 " margin: 0 4px 2px 0;\n"
130 " font-weight: bold;\n"
134 " display: inline;\n"
135 " border-width: 0.2em;\n"
136 " border-style: solid;\n"
137 " text-align: center;\n"
138 " vertical-align: middle;\n"
139 " padding: 0px 0.2em 0px 0.2em;\n"
140 " margin: 0 4px 2px 0;\n"
142 ".doall { color: #754242; }\n"
144 "</head><body><h2>Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING "</h2>\n<ul>\n"
147 cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = {
150 "</ul><h2>End of log</h2></html>"
154 cAppCharP FoldingPrefix =
155 "<script language=javascript1.2 type=text/javascript><!--\n"
156 "function div_ref_style (id) {\n"
157 " if (document.layers) return document.layers[id];\n"
158 " else if (document.all) return document.all[id].style;\n"
159 " else if (document.getElementById) return document.getElementById(id).style;\n"
160 " else return null;\n"
162 "function exp(id) {\n"
163 " if(div_ref_style('B'+id).display!='block') {\n"
164 " div_ref_style('B'+id).display='block';\n"
165 " div_ref_style('E'+id).display='none';\n"
166 " div_ref_style('C'+id).display='inline';\n"
169 "function coll(id) {\n"
170 " if(div_ref_style('B'+id).display!='none') {\n"
171 " div_ref_style('B'+id).display='none';\n"
172 " div_ref_style('E'+id).display='inline';\n"
173 " div_ref_style('C'+id).display='none';\n"
176 "function doall(id,collapse) {\n"
177 " // get parent element\n"
182 " mydiv=document.getElementById('B'+id); // get div to collapse or expand\n"
190 " // get all contained blocks\n"
191 " divs=mydiv.getElementsByTagName('div') // all divs\n"
192 " for (i=0 ; i<divs.length ; i++) {\n"
193 " if (divs[i].className=='blk') {\n"
194 " // this is a foldable block div\n"
195 " bid = divs[i].id.substring(1);\n"
206 "<li><span class=\"doall\" onclick=\"doall('',true)\">[-- collapse all --]</span><span class=\"doall\" onclick=\"doall('',false)\">[++ expand all ++]</span></li>\n";
212 // privately redefined here to avoid circular headers (would need syncappbase.h)
213 extern "C" void ConsolePuts(const char *text);
216 // TDbgOptions implementation
217 // --------------------------
219 TDbgOptions::TDbgOptions()
223 } // TDbgOptions::TDbgOptions
226 void TDbgOptions::clear(void)
228 fOutputFormat = dbgfmt_html; // most universally readable and convenient
229 fIndentString = " "; // two spaces
230 fCustomPrefix.erase(); // no custom prefix
231 fCustomSuffix.erase(); // no custom suffix
232 fSeparateMsgs = true; // separate text message lines (<msg></msg> in xml)
233 fTimestampStructure = true; // timestamps in structure...
234 fTimestampForAll = false; // ..but not for every line
235 fThreadIDForAll = false; // not by default
236 fFlushMode = dbgflush_none; // no special flush or openclose (fast, but might loose info on process abort)
237 fFoldingMode = dbgfold_auto; // dynamic folding enabled, expanded/collapsed defaults automatically set on block-by-block basis
238 #ifdef SYDEBUG_LOCATION
239 fSourceLinkMode = dbgsource_none; // no links into source code
240 fSourceRootPath = SYDEBUG_LOCATION; // use default path from build
242 fAppend = false; // default to overwrite existing logfiles
243 fSubThreadMode = dbgsubthread_suppress; // simply suppress subthread info
244 fSubThreadBufferMax = 1024*1024; // don't buffer more than one meg.
245 } // TDbgOptions::clear
249 // TDbgOut implementation
250 // ----------------------
257 } // TDbgOut::TDbgOut
263 } // TDbgOut::~TDbgOut
266 void TDbgOut::destruct(void)
268 if (!fDestructed) doDestruct();
270 } // TDbgOut::destruct
273 void TDbgOut::doDestruct(void)
275 // make sure files are closed
277 } // TDbgOut::doDestruct
280 // TStdFileDbgOut implementation
281 // -----------------------------
285 TStdFileDbgOut::TStdFileDbgOut()
291 } // TStdFileDbgOut::TStdFileDbgOut
294 TStdFileDbgOut::~TStdFileDbgOut()
298 } // TStdFileDbgOut::~TStdFileDbgOut
301 // open standard C file based debug output channel
302 bool TStdFileDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
308 // now apply new flush mode
309 fFlushMode=aFlushMode;
310 // save new file name
311 fFileName=aDbgOutputName;
312 // for C files, use the extension provided
313 fFileName+=aSuggestedExtension;
315 fFile=fopen(fFileName.c_str(),aRawMode ? (aOverWrite ? "wb" : "ab") : (aOverWrite ? "w" : "a"));
316 // in case this fails, we'll have a NULL fFile. We can't do anything more here
318 // For openclose mode, we have opened here only to check for logfile writability - close again
319 if (fIsOpen && fFlushMode==dbgflush_openclose) {
323 // return false if we haven't been successful opening the channel
325 } // TStdFileDbgOut::openDbg
328 // return current size of debug file
329 uInt32 TStdFileDbgOut::dbgFileSize(void)
331 if (!fIsOpen) return 0; // no file, no size
333 if (fFlushMode==dbgflush_openclose) {
334 // we need to open the file for append first
335 fFile=fopen(fFileName.c_str(),"a");
336 fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
342 fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
343 sz=ftell(fFile); // return size
346 } // TStdFileDbgOut::dbgFileSize
349 // close standard C file based debug channel
350 void TStdFileDbgOut::closeDbg(void)
359 } // TStdFileDbgOut::closeDbg
362 // write single line to standard file based output channel
363 void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
365 // if not open, just NOP
367 if (fFlushMode==dbgflush_openclose) {
368 // we need to open the file for append first
370 fFile=fopen(fFileName.c_str(),"a");
379 // do required flushing
380 if (fFlushMode==dbgflush_openclose) {
381 // we need to close the file after every line of output
386 else if (aForceFlush || fFlushMode==dbgflush_flush) {
392 } // TStdFileDbgOut::putLine
395 // write raw data to output file
396 void TStdFileDbgOut::putRawData(cAppPointer aData, memSize aSize)
399 if (fFlushMode==dbgflush_openclose) {
400 // we need to open the file for append first
402 fFile=fopen(fFileName.c_str(),"a");
405 if (fwrite(aData, 1, aSize, fFile) != 1) {
409 // do required flushing
410 if (fFlushMode==dbgflush_openclose) {
411 // we need to close the file after every line of output
416 else if (fFlushMode==dbgflush_flush) {
421 } // TStdFileDbgOut::putRawData
427 // TConsoleDbgOut implementation
428 // -----------------------------
430 TConsoleDbgOut::TConsoleDbgOut()
433 } // TStdFileDbgOut::TStdFileDbgOut
436 // open standard C file based debug output channel
437 bool TConsoleDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
443 // raw mode is not supported
446 // return false if we haven't been successful opening the channel
448 } // TConsoleDbgOut::openDbg
451 // close standard C file based debug channel
452 void TConsoleDbgOut::closeDbg(void)
455 } // TConsoleDbgOut::closeDbg
458 // write single line to standard file based output channel
459 void TConsoleDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
461 // if not open, just NOP
465 } // TConsoleDbgOut::putLine
470 // TDebugLoggerBase implementation
471 // -------------------------------
474 TDebugLoggerBase::TDebugLoggerBase(GZones *aGZonesP) :
478 fDebugEnabled=true; // enabled by default
483 fBlockHistory=NULL; // no Block open yet
484 fOutStarted=false; // not yet started
487 fOutputLoggerP=NULL; // no redirected output yet
488 } // TDebugLoggerBase::TDebugLoggerBase
492 TDebugLoggerBase::~TDebugLoggerBase()
494 // make sure debug is finalized
495 DebugFinalizeOutput();
496 // make sure possibly left-over history elements are erased
497 while (fBlockHistory) {
498 TBlockLevel *bl=fBlockHistory;
499 fBlockHistory=bl->fNext;
502 // get rid of output object
503 if (fDbgOutP) delete fDbgOutP;
505 } // TDebugLoggerBase::TDebugLoggerBase
508 // @brief convenience version for getting time
509 lineartime_t TDebugLoggerBase::getSystemNowAs(timecontext_t aContext)
511 return sysync::getSystemNowAs(aContext,fGZonesP);
512 } // TDebugLoggerBase::getSystemNowAs
516 void TDebugLoggerBase::installOutput(TDbgOut *aDbgOutP)
518 // get rid of possibly installed previous outputter
519 if (fDbgOutP) delete fDbgOutP;
521 } // TDebugLoggerBase::installOutput
524 /// @brief link this logger to another logger and redirect output to that logger
525 /// @param aDebugLoggerP[in] another logger, that must be alive as long as this logger is alive
526 void TDebugLoggerBase::outputVia(TDebugLoggerBase *aDebugLoggerP)
528 // save logger and prefix
529 fOutputLoggerP = aDebugLoggerP;
530 } // TDebugLoggerBase::outputVia
532 #if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC)
534 int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...) = fprintf;
538 // output formatted text
539 void TDebugLoggerBase::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
541 // we need a format and debug not completely off
542 if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
543 const sInt16 maxmsglen=1024;
546 // assemble the message string
547 vsnprintf(msg, maxmsglen, aFormat, aArgs);
549 DebugPuts(TDBG_LOCATION_ARG aDbgMask,msg);
551 } // TDebugLoggerBase::DebugVPrintf
554 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
555 TDebugLoggerBase &TDebugLoggerBase::setNextMask(uInt32 aDbgMask)
557 fNextDebugMask=aDbgMask;
559 } // TDebugLoggerBase::setNextMask
562 // like DebugPrintf(), but using mask previously set by setNextMask()
563 void TDebugLoggerBase::DebugPrintfLastMask(TDBG_LOCATION_PROTO cAppCharP aFormat, ...)
566 // we need a format and debug not completely off
567 if ((getMask() & fNextDebugMask)==fNextDebugMask && aFormat) {
568 va_start(args, aFormat);
569 DebugVPrintf(TDBG_LOCATION_ARG fNextDebugMask,aFormat,args);
573 } // TDebugLoggerBase::DebugPrintfLastMask
576 // output formatted text
577 void TDebugLoggerBase::DebugPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, ...)
580 // we need a format and debug not completely off
581 if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
582 va_start(args, aFormat);
583 DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,args);
586 } // TDebugLoggerBase::DebugVPrintf
589 // open new Block without attribute list
590 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
592 // we need a format and debug not completely off
593 if (getMask() && aBlockName) {
595 #pragma clang diagnostic push
596 #pragma clang diagnostic ignored "-Wformat-security"
598 DebugOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,NULL);
600 #pragma clang diagnostic pop
603 } // TDebugLoggerBase::DebugOpenBlock
606 // open new Block with attribute list, printf style
607 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
610 // we need a format and debug not completely off
611 if (getMask() && aBlockName) {
612 va_start(args, aBlockFmt);
613 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,aBlockFmt,args);
616 } // TDebugLoggerBase::DebugOpenBlock
619 // open new Block with attribute list, printf style, expanded by default
620 void TDebugLoggerBase::DebugOpenBlockExpanded(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
623 // we need a format and debug not completely off
624 if (getMask() && aBlockName) {
625 va_start(args, aBlockFmt);
626 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,false,aBlockFmt,args);
629 } // TDebugLoggerBase::DebugOpenBlockExpanded
632 // open new Block with attribute list, printf style, collapsed by default
633 void TDebugLoggerBase::DebugOpenBlockCollapsed(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
636 // we need a format and debug not completely off
637 if (getMask() && aBlockName) {
638 va_start(args, aBlockFmt);
639 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,true,aBlockFmt,args);
642 } // TDebugLoggerBase::DebugOpenBlockCollapsed
645 #ifdef SYDEBUG_LOCATION
647 #define MAKEDBGLINK(txt) dbg2Link(TDBG_LOCATION_ARG txt)
649 /// turn text into link to source code
650 string TDebugLoggerBase::dbg2Link(const TDbgLocation &aTDbgLoc, const string &aTxt)
652 if (!aTDbgLoc.fFile || !fDbgOptionsP || fDbgOptionsP->fSourceLinkMode==dbgsource_none || fDbgOptionsP->fOutputFormat!=dbgfmt_html)
653 return aTxt; // disabled, non-html or no information to create source link
654 // create link or hint to source code
657 switch(fDbgOptionsP->fSourceLinkMode) {
658 case dbgsource_hint: {
659 // only add name/line number/function as title hint (in a otherwise inactive link)
660 line = "<a href=\"#\" title=";
661 StringObjPrintf(line,"<a href=\"#\" title=\"%s:%d",aTDbgLoc.fFile,aTDbgLoc.fLine);
662 StringObjAppendPrintf(line," in %s",aTDbgLoc.fFunction);
666 case dbgsource_doxygen: {
667 // create link into doxygen
669 // replace path with path to Doxygen HTML pages,
670 // mangle base name like Doxygen does
671 line += fDbgOptionsP->fSourceRootPath;
673 string file = aTDbgLoc.fFile;
674 size_t off = file.rfind('/');
675 if (off != file.npos)
676 file = file.substr(off + 1);
677 for (off = 0; off < file.size(); off++) {
690 StringObjAppendPrintf(line,"-source.html#l%05d",aTDbgLoc.fLine);
692 if (aTDbgLoc.fFunction) {
694 line+=aTDbgLoc.fFunction;
699 case dbgsource_txmt: {
700 // create txmt:// URL scheme link, which opens TextMate or BBEdit at the correct line in MacOS X
701 line = "<a href=\"txmt://open/?url=file://";
703 string path = fDbgOptionsP->fSourceRootPath;
704 path += aTDbgLoc.fFile;
705 // - add path CGI encoded
706 line += encodeForCGI(path.c_str());
708 if (aTDbgLoc.fLine>0)
709 StringObjAppendPrintf(line,"&line=%d",aTDbgLoc.fLine);
711 if (aTDbgLoc.fFunction) {
713 line+=aTDbgLoc.fFunction;
728 } // TDebugLoggerBase::dbg2Link
732 #define MAKEDBGLINK(txt) (txt)
734 #endif // SYDEBUG_LOCATION
737 // output text to debug channel
738 void TDebugLoggerBase::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
740 // we need a text and debug not completely off
741 if (!((getMask() & aDbgMask)==aDbgMask && aText && fDbgOptionsP)) {
744 //#warning "ugly hack"
745 DebugPutLine(TDBG_LOCATION_NONE "<li><span class=\"error\">Warning: Dbg output system already half shut down (limited formatting)!</span></li><li>");
746 if (aText) DebugPutLine(TDBG_LOCATION_NONE aText);
747 DebugPutLine(TDBG_LOCATION_NONE "</li>");
751 // make sure output is started
753 // try starting output
755 // disable debugging in this logger if starting output failed
756 // (prevents endless re-trying to open debug logs e.g. when log directory does not exist)
758 fDebugEnabled = false;
759 return; // stop all efforts here
762 // dissect into lines
763 cAppCharP end=aTextSize ? aText+aTextSize : NULL;
765 // check for preformatted message
766 bool pre=strnncmp(aText,"⪯",5)==0;
768 pre = pre || aPreFormatted;
770 while ((!end || aText<end) && *aText) {
771 // search for line end or end of string
773 while ((!end || p<end) && *p && *p!='\n' && *p!='\r') p++;
774 // output this line, properly formatted
779 switch (fDbgOptionsP->fOutputFormat) {
782 // prefix first line with <li>, second and further with <br/>
785 // add timestamp if needed for every line
787 fDbgOptionsP->fTimestampForAll
788 || fDbgOptionsP->fThreadIDForAll
789 #ifdef SYDEBUG_LOCATION
790 || fDbgOptionsP->fSourceLinkMode!=dbgsource_none
795 #ifdef MULTI_THREAD_SUPPORT
796 if (fDbgOptionsP->fThreadIDForAll) {
797 StringObjAppendPrintf(prefix,"%09lu",myThreadID());
798 if (fDbgOptionsP->fTimestampForAll) prefix += ", ";
801 if (fDbgOptionsP->fTimestampForAll) {
802 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
805 #ifdef SYDEBUG_LOCATION
806 else if (!fDbgOptionsP->fThreadIDForAll) {
807 // neither threadID nor timestamp, but source requested -> put small text here
811 prefix+="]</i> ";
812 // if we have links into source code, add it here
813 line += MAKEDBGLINK(prefix);
815 // colorize some messages
817 // colors, not mixable, most relevant first
818 if (aDbgMask & DBG_ERROR) {
821 else if (aDbgMask & DBG_EXOTIC) {
824 else if (aDbgMask & DBG_SCRIPTS) {
827 else if (aDbgMask & DBG_PLUGIN) {
830 else if (aDbgMask & DBG_DBAPI) {
833 else if (aDbgMask & DBG_CONFLICT) {
836 else if (aDbgMask & DBG_MATCH) {
839 else if (aDbgMask & DBG_REMOTEINFO) {
842 else if (aDbgMask & DBG_PROTO) {
845 else if (aDbgMask & DBG_FILTER) {
848 else if (aDbgMask & DBG_PARSE) {
851 else if (aDbgMask & DBG_GEN) {
854 else if (aDbgMask & DBG_REST) {
857 // apply basic color style
859 line+="<span class=\""; line+=cl; line+="\">";
861 // aditional style modifiers that can be combined with colors
862 if (aDbgMask & DBG_HOT) {
864 line+="<span class=\"hotalone\">";
866 line+="<span class=\"hot\">";
868 // start preformatted if selected
873 if (!pre) line="<br/>";
876 // XML, just output and replace special chars as needed
879 #ifdef MULTI_THREAD_SUPPORT
880 if (fDbgOptionsP->fThreadIDForAll) {
882 StringObjAppendPrintf(line,"%09lu",myThreadID());
886 if (fDbgOptionsP->fTimestampForAll) {
887 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
892 DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size());
895 if (fDbgOptionsP->fSeparateMsgs) {
903 if (strucmp(q,"&html;",6)==0) {
904 if (q>s) line.append(s,q-s); // flush stuff scanned so far
905 // everything until next &html; does not need or want escaping, copy it as is
906 // - search next &html;
908 while(*q && strucmp(q,"&html;",6)!=0) q++;
909 // - append everything between if we are in HTML mode
910 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && q>s)
914 else if (strucmp(q,"&sp;",4)==0) {
915 if (q>s) line.append(s,q-s); // flush stuff scanned so far
916 // non-breaking space in HTML, normal space otherwise
917 if (fDbgOptionsP->fOutputFormat==dbgfmt_html)
921 s=q=q+4; // skip &sp;
924 if (q>s) line.append(s,q-s);
930 if (q>s) line.append(s,q-s);
935 if (q>s) line.append(s,q-s);
943 if (q>s) line.append(s,q-s);
944 if (fDbgOptionsP->fSeparateMsgs && fDbgOptionsP->fOutputFormat==dbgfmt_xml) {
955 if (strucmp(q,"&html;",6)==0) {
956 if (q>s) line.append(s,q-s);
957 // everything until next &html; must be filtered out
958 // - search next &html;
960 while(*q && strucmp(q,"&html;",6)!=0) q++;
963 else if (strucmp(q,"&sp;",4)==0) {
964 if (q>s) line.append(s,q-s);
966 line += ' '; // convert to plain space
975 if (q>s) line.append(s,q-s);
977 } // switch text output
979 // skip the lineend, if any
980 while (((!end || p<end) && *p=='\n') || *p=='\r') p++;
981 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && ((end && p>=end) || *p==0)) {
1001 line+="</span>"; // end special style
1007 line+="</span>"; // end special style
1009 line+="</li>"; // we need to close the list entry
1011 DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size(),pre);
1012 // next line, if any
1014 } // loop until all text done
1016 } // TDebugLoggerBase::DebugPuts
1019 // open new Block with attribute list, varargs passed
1020 void TDebugLoggerBase::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1024 if (fDbgOptionsP->fFoldingMode==dbgfold_collapsed)
1026 else if (fDbgOptionsP->fFoldingMode==dbgfold_expanded)
1028 if (getMask() && aBlockName && fDbgOptionsP) {
1029 // make sure output is started
1030 if (!fOutStarted) DebugStartOutput();
1031 // create Block line on current indent level
1034 // - preamble, possibly with timestamp
1035 bool withTime = fDbgOptionsP->fTimestampStructure;
1037 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1038 switch (fDbgOptionsP->fOutputFormat) {
1041 bl="<"; bl+=aBlockName;
1043 bl+=" time=\"" + ts + "\"";
1053 bl="<li><span class=\"block\">";
1054 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1055 StringObjAppendPrintf(bl,
1056 "<div id=\"E%ld\" style=\"display:%s\" class=\"exp\" onclick=\"exp('%ld')\">+</div><div id=\"C%ld\" style=\"display:%s\" class=\"coll\" onclick=\"coll('%ld')\">–</div>",
1057 long(getBlockNo()), aCollapsed ? "inline" : "none", long(getBlockNo()),
1058 long(getBlockNo()), aCollapsed ? "none" : "inline", long(getBlockNo())
1061 StringObjAppendPrintf(bl,"<a name=\"H%ld\">", long(getBlockNo()));
1063 bl += MAKEDBGLINK(string("[") + ts + "] ");
1065 #ifdef SYDEBUG_LOCATION
1066 else if (fDbgOptionsP->fSourceLinkMode!=dbgsource_none) {
1067 bl += MAKEDBGLINK(string("[src] "));
1077 bl+="</a></span><span class=\"attribute\">";
1083 if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1084 bl+="[" + ts + "] ";
1092 } // switch preamble
1095 // first expand all printf parameters
1097 vStringObjPrintf(attrs,aBlockFmt,true,aArgs);
1098 // isolate |-separated attribute format strings
1099 cAppCharP q,r,s,p=attrs.c_str();
1101 // search for beginning of value
1103 while(*q && *q!='=' && *q!='|') q++;
1104 // search for end of value
1106 s=q; // in case we don't have a =
1110 while (*r && *r!='|') r++;
1112 // now: p=start of attrname, q=end of attrname
1113 // s=start of value, r=end of value
1114 // output an attribute now
1116 switch (fDbgOptionsP->fOutputFormat) {
1128 bl+="=<span class=\"attrval\">";
1139 } // switch attribute
1140 } // non-empty attribute
1141 // more attributes to come?
1142 if (*r=='|') r++; // skip separator
1145 } // attributes present
1147 switch (fDbgOptionsP->fOutputFormat) {
1154 bl+="</span>"; // end span for attributes
1155 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1156 StringObjAppendPrintf(bl,
1157 " <span class=\"doall\" onclick=\"doall('%ld',true)\">[--]</span><span class=\"doall\" onclick=\"doall('%ld',false)\">[++]</span>",
1158 long(getBlockNo()), long(getBlockNo())
1161 // link to end of block
1162 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#F%ld\">[->end]</a>", long(getBlockNo()));
1163 // link to start of enclosing block (if any)
1164 if (fBlockHistory) {
1165 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#H%ld\">[->enclosing]</a>", long(fBlockHistory->fBlockNo));
1167 // start div for content folding
1168 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1169 StringObjAppendPrintf(bl,
1170 "<div class=\"blk\" id=\"B%ld\" style=\"display:%s\">",
1172 aCollapsed ? "none" : "inline"
1175 bl+="<ul>"; // now start list for block's contents
1181 } // switch preamble
1182 // now output Block line (on current indent level)
1183 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1184 // increase indent level (applies to all Block contents)
1186 // save Block on stack
1187 TBlockLevel *newLevel = new TBlockLevel;
1188 newLevel->fBlockName=aBlockName;
1189 newLevel->fNext=fBlockHistory;
1190 newLevel->fBlockNo=getBlockNo(); // save block number to reference block in collapse box at end of block
1191 nextBlock(); // increment block number
1192 fBlockHistory=newLevel; // insert new level at start of list
1194 } // TDebugLoggerBase::DebugVOpenBlock
1197 // close named Block. If no name given, topmost Block will be closed
1198 void TDebugLoggerBase::DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1200 if (fOutStarted && getMask() && fDbgOptionsP && fBlockHistory) {
1201 if (aBlockName==NULL) {
1203 internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),"Block Nest Warning: Missing Block name at close");
1205 internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),NULL);
1209 internalCloseBlocks(TDBG_LOCATION_ARG aBlockName,NULL);
1212 } // TDebugLoggerBase::DebugCloseBlock
1216 // internal helper used to close all or some Blocks
1217 void TDebugLoggerBase::internalCloseBlocks(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aCloseComment)
1219 if (!fDbgOptionsP) return; // security
1220 bool withTime = fDbgOptionsP->fTimestampStructure;
1223 if (!fBlockHistory && aBlockName) {
1224 // no blocks open any more and not close-all-remaining call (log close...)
1225 DebugPrintf(TDBG_LOCATION_ARG DBG_EXOTIC+DBG_ERROR,"Block Nest Warning: Trying to close block '%s', but no block is open",aBlockName);
1228 while (fBlockHistory) {
1231 if (aCloseComment) {
1233 comment += aCloseComment;
1235 // check if closing top-of-stack Block now
1237 (aBlockName && strucmp(aBlockName,fBlockHistory->fBlockName.c_str())==0);
1238 if (!found && fBlockHistory->fNext==NULL) {
1239 // last Block always counts as "found"...
1242 // ...but issue warning as name is not what we would have expected
1243 StringObjAppendPrintf(comment, " - Block Nest Warning: closing '%s', but expected '%s'",aBlockName ? aBlockName : "<unknown>", fBlockHistory->fBlockName.c_str());
1246 // now close topmost Block
1248 // - get time if needed and possibly put it within indented block
1250 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1251 // for XML, the time must be shown before the close tag on a separate line
1252 if (fDbgOptionsP->fOutputFormat == dbgfmt_xml) {
1253 StringObjPrintf(bl,"<endblock time=\"%s\"/>",ts.c_str());
1254 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size()); // still within block, indented
1258 if (fIndent>0) fIndent--;
1259 // - then create closing Block
1261 if (!found) StringObjAppendPrintf(comment," - Block Nest Warning: implicitly closed (by explicitly closing '%s')",aBlockName ? aBlockName : "<unknown parent>");
1263 switch (fDbgOptionsP->fOutputFormat) {
1267 bl+=fBlockHistory->fBlockName;
1269 if (!comment.empty()) {
1277 bl="</ul><span class=\"block\">"; // end of content list
1278 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1279 StringObjAppendPrintf(bl,
1280 "<span class=\"coll\" onclick=\"coll('%ld')\">–</span>",
1281 long(fBlockHistory->fBlockNo)
1284 StringObjAppendPrintf(bl,"<a name=\"F%ld\">",long(fBlockHistory->fBlockNo));
1286 bl += MAKEDBGLINK(string("[") + ts + "] ");
1289 bl+=fBlockHistory->fBlockName;
1293 // link to top of block
1294 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#H%ld\">[->top]</a>",long(fBlockHistory->fBlockNo));
1295 // link to end of enclosing block (if any)
1296 if (fBlockHistory->fNext) {
1297 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#F%ld\">[->enclosing]</a>",long(fBlockHistory->fNext->fBlockNo));
1299 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1300 bl+="</div>"; // end of folding division
1302 bl+="</li>"; // end of list entry containing entire block
1308 if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1309 bl+="[" + ts + "] ";
1312 bl+=fBlockHistory->fBlockName;
1316 } // switch Block close
1317 // - output closing Block line
1318 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1319 // - remove Block level
1320 TBlockLevel *closedLevel = fBlockHistory;
1321 fBlockHistory = closedLevel->fNext;
1323 // if we have found the Block, exit here
1326 } // TDebugLoggerBase::internalCloseBlocks
1329 // start debugging output if needed and sets fOutStarted
1330 bool TDebugLoggerBase::DebugStartOutput(void)
1333 if (fOutputLoggerP) {
1334 // using another logger, call it to start output
1335 fOutStarted = fOutputLoggerP->DebugStartOutput();
1337 // start with indent level of parent logger
1338 fIndent = fOutputLoggerP->fIndent;
1339 // note: we'll use the parent logger's block number...
1340 fBlockNo = 0; // ...but init to something just in case
1343 else if (fDbgOptionsP && fDbgOutP && !fDbgPath.empty()) {
1344 // try to open the debug channel (force to openclose if we have multiple threads mixed in one file)
1345 if (fDbgOutP->openDbg(
1347 DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat],
1348 fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix ? dbgflush_openclose : fDbgOptionsP->fFlushMode,
1349 !fDbgOptionsP->fAppend
1351 // make sure we don't recurse when we produce some output
1353 fIndent = 0; // reset to make sure
1354 // create a block number that is unique in the file, even if we append multiple times.
1355 // We assume that a block consumes at least 256 bytes, so size_of_file/256 always gets
1356 // an unused block ID within that file
1357 // 256 is a safe assumption because the "fold" button <divs> alone are around 250 bytes
1358 fBlockNo = 1 + (fDbgOutP->dbgFileSize()/256);
1359 // now create required prefix
1360 DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomPrefix.empty() ? DbgOutDefaultPrefixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomPrefix.c_str());
1361 // add folding javascript if needed
1362 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1363 DebugPutLine(TDBG_LOCATION_NONE FoldingPrefix);
1365 } // debug channel opened successfully
1366 } // use own debug channel
1367 } // environment ready to start output
1369 } // TDebugLoggerBase::DebugStartOutput
1372 // @brief finalize debugging output (close Blocks, close output channel)
1373 void TDebugLoggerBase::DebugFinalizeOutput(void)
1375 if (fOutputLoggerP) {
1376 // just close my own blocks
1377 internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because sub-log ends here");
1379 if (fOutStarted && fDbgOptionsP && fDbgOutP) {
1380 // close all left-open open Blocks
1381 internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because log ends here");
1382 // now finalize output
1383 // - special stuff before
1384 if (fDbgOptionsP->fOutputFormat == dbgfmt_xml)
1385 fIndent=0; // unindent to zero (document is not a real Block)
1387 DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomSuffix.empty() ? DbgOutDefaultSuffixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomSuffix.c_str());
1388 // now close the debug channel
1389 fDbgOutP->closeDbg();
1391 // whatever happened, we are not started any more
1393 } // TDebugLoggerBase::DebugFinalizeOutput
1396 // Output single line to debug channel (includes indenting and other prefixing, but no further formatting)
1397 void TDebugLoggerBase::DebugPutLine(TDBG_LOCATION_PROTO cAppCharP aText, stringSize aTextSize, bool aPre)
1399 if (!aText || (!fDbgOutP && !fOutputLoggerP)) return;
1401 // not an empty line
1404 // prefix with timestamp if selected in text format
1405 if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_text && fDbgOptionsP->fTimestampForAll) {
1406 // prefix each line (before the indent!) with a timestamp
1408 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1413 // Indent if selected
1414 if (fDbgOptionsP && !fDbgOptionsP->fIndentString.empty() && !(fDbgOptionsP->fOutputFormat==dbgfmt_html && aPre)) {
1416 for (uInt16 n=0; n<fIndent; n++) {
1417 msg+=fDbgOptionsP->fIndentString;
1420 // add message itself
1422 msg.append(aText,aTextSize);
1426 if (fOutputLoggerP) {
1427 // use parent's output
1428 fOutputLoggerP->fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1431 // use my own output channel
1432 fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1435 } // TDebugLoggerBase::DebugPutLine
1438 // TDebugLogger implementation
1439 // ---------------------------
1442 TDebugLogger::TDebugLogger(GZones *aGZonesP) :
1445 #ifdef MULTI_THREAD_SUPPORT
1447 fSubThreadLogs=NULL;
1448 fSilentLoggerP=NULL;
1450 } // TDebugLogger::TDebugLogger
1454 TDebugLogger::~TDebugLogger()
1456 #ifdef MULTI_THREAD_SUPPORT
1457 // remove subthread loggers
1458 TSubThreadLog* subThreadP = fSubThreadLogs;
1459 fSubThreadLogs = NULL;
1460 while (subThreadP) {
1461 // delete logger if any
1462 if (subThreadP->fSubThreadLogger) {
1464 delete subThreadP->fSubThreadLogger;
1470 TSubThreadLog* delP = subThreadP;
1471 subThreadP = subThreadP->fNext;
1475 if (fSilentLoggerP) {
1476 delete fSilentLoggerP;
1477 fSilentLoggerP = NULL;
1480 } // TDebugLogger::~TDebugLogger
1483 #ifdef MULTI_THREAD_SUPPORT
1485 void TDebugLogger::setOptions(const TDbgOptions *aDbgOptionsP)
1487 TDebugLoggerBase::setOptions(aDbgOptionsP);
1488 TSubThreadLog* subThreadP = fSubThreadLogs;
1489 while (subThreadP) {
1490 if (subThreadP->fSubThreadLogger) {
1491 subThreadP->fSubThreadLogger->setOptions(aDbgOptionsP);
1493 subThreadP = subThreadP->fNext;
1497 /// @brief find (and possibly delete) subthread record
1498 /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
1499 /// IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
1500 /// Note that the caller must take care of deleting the subthread record
1501 TSubThreadLog *TDebugLogger::findSubThread(uInt32 aThreadID, bool aAndRemove)
1503 TSubThreadLog* subThreadP = fSubThreadLogs;
1504 TSubThreadLog** subThreadLinkPP = &fSubThreadLogs;
1505 while (subThreadP) {
1506 if (subThreadP->fThreadID == aThreadID) {
1508 // bridge previous with next in one single assignment (i.e. thread safe)
1509 *subThreadLinkPP = subThreadP->fNext;
1511 // return found record (note that it MUST BE DELETED by caller if no longer used)
1514 subThreadLinkPP = &subThreadP->fNext;
1515 subThreadP = *subThreadLinkPP;
1517 return NULL; // none found
1518 } // TDebugLogger::findSubThread
1521 /// @brief find or create logger for subthread
1522 TDebugLoggerBase *TDebugLogger::getThreadLogger(bool aCreateNew)
1524 if (!fDbgOptionsP || fDbgOptionsP->fSubThreadMode==dbgsubthread_none)
1525 return this; // no options, do not handle subthreads specially
1526 uIntArch threadID = myThreadID();
1527 if (fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix || threadID==fMainThreadID) {
1528 // In line mix and for mainthread - I am the logger for this thread!
1531 TSubThreadLog* subThreadP = findSubThread(threadID);
1533 // we know this subthread, return its logger
1534 return subThreadP->fSubThreadLogger; // can be NULL if subthread logging is disabled
1536 // unknown subthread
1537 if (fMainThreadID==0) {
1538 // no current mainthread, let subthread write to main log
1539 // Note: this makes sure log info possibly trailing the DebugThreadOutputDone()
1540 // also lands in the main log. This is not critical - the only thing that must be
1541 // ensured is that starting new threads is made only with DebugDefineMainThread set.
1544 // new subthread, create entry in list
1548 subThreadP = new TSubThreadLog;
1549 subThreadP->fThreadID=threadID;
1550 subThreadP->fNext=fSubThreadLogs; // link current list behind this new entry
1551 // create logger for the thread (or none)
1552 switch (fDbgOptionsP->fSubThreadMode) {
1553 case dbgsubthread_separate:
1554 // separate file for subthread output
1555 // - create new base logger
1556 subThreadP->fSubThreadLogger = new TDebugLoggerBase(fGZonesP);
1557 // - install output (copy)
1558 subThreadP->fSubThreadLogger->installOutput(fDbgOutP ? fDbgOutP->clone() : NULL);
1560 subThreadP->fSubThreadLogger->setOptions(getOptions());
1561 // - inherit current mask/enable
1562 subThreadP->fSubThreadLogger->setMask(getMask());
1563 subThreadP->fSubThreadLogger->setEnabled(fDebugEnabled);
1564 // - debug path is same as myself plus Thread ID
1565 subThreadP->fSubThreadLogger->setDebugPath(fDbgPath.c_str());
1566 StringObjPrintf(s,"_%lu",(long unsigned)threadID);
1567 subThreadP->fSubThreadLogger->appendToDebugPath(s.c_str());
1569 case dbgsubthread_suppress:
1571 // no output from subthreads
1572 subThreadP->fSubThreadLogger=NULL; // no logger
1575 // now activate by linking it at top of list (this is thread safe)
1576 fSubThreadLogs = subThreadP;
1577 // return the logger
1578 return subThreadP->fSubThreadLogger;
1580 return NULL; // no logger for this thread
1581 } // TDebugLogger::getThreadLogger
1584 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
1585 TDebugLoggerBase &TDebugLogger::setNextMask(uInt32 aDbgMask)
1587 TDebugLoggerBase *loggerP = getThreadLogger();
1589 // return pointer to loggerbase whose DebugPrintfLastMask() must be called
1590 return loggerP->inherited::setNextMask(aDbgMask);
1593 // we have no logger but still need to return something
1594 if (!fSilentLoggerP) {
1595 fSilentLoggerP = new TDebugLoggerBase(fGZonesP);
1596 fSilentLoggerP->setEnabled(false);
1598 fSilentLoggerP->setNextMask(DBG_ERROR); // must set non-zero to make sure it is NOT output!
1599 return *fSilentLoggerP;
1601 } // TDebugLoggerBase::setNextMask
1604 // output text to debug channel, with checking for subthreads
1605 void TDebugLogger::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
1607 TDebugLoggerBase *loggerP = getThreadLogger();
1608 if (loggerP) loggerP->inherited::DebugPuts(TDBG_LOCATION_ARG aDbgMask,aText,aTextSize,aPreFormatted);
1609 } // TDebugLogger::DebugPuts
1612 void TDebugLogger::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
1614 TDebugLoggerBase *loggerP = getThreadLogger();
1615 if (loggerP) loggerP->inherited::DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,aArgs);
1616 } // TDebugLogger::DebugVPrintf
1619 void TDebugLogger::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1621 TDebugLoggerBase *loggerP = getThreadLogger();
1622 if (loggerP) loggerP->inherited::DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName, aBlockTitle, aCollapsed, aBlockFmt, aArgs);
1623 } // TDebugLogger::DebugVOpenBlock
1626 void TDebugLogger:: DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1628 TDebugLoggerBase *loggerP = getThreadLogger();
1629 if (loggerP) loggerP->inherited::DebugCloseBlock(TDBG_LOCATION_ARG aBlockName);
1630 } // TDebugLogger::DebugCloseBlock
1635 // output all buffered subthread's output in a special subthread Block in the main output
1636 void TDebugLogger::DebugShowSubThreadOutput(void)
1638 #ifdef MULTI_THREAD_SUPPORT
1639 // nop as long mixed-block mode is not implemented
1641 } // TDebugLogger::DebugShowSubThreadOutput
1644 // the calling thread signals that it is done with doing output for now. If the main
1645 // thread is doing this and we have bufferandmix mode, the next subthread will be allowed
1646 // to write into the output channel until a new main thread gains control via
1647 // DebugDefineMainThread();
1648 void TDebugLogger::DebugThreadOutputDone(bool aRemoveIt)
1650 #ifdef MULTI_THREAD_SUPPORT
1651 uIntArch threadID = myThreadID();
1652 if (threadID==fMainThreadID) {
1653 // current main thread done
1656 // for session logs, subthreads are usually left in the list at this time (aRemoveIt==false)
1657 // (as they will get deleted with the session logger later anyway)
1659 TSubThreadLog* tP = findSubThread(threadID,true);
1661 if (tP->fSubThreadLogger) {
1663 delete tP->fSubThreadLogger;
1673 } // TDebugLogger::DebugThreadOutputDone
1676 // Used to regain control as main thread (e.g. for the next request of a session which
1677 // possibly occurs from another thread).
1678 void TDebugLogger::DebugDefineMainThread(void)
1680 #ifdef MULTI_THREAD_SUPPORT
1681 uIntArch threadID = myThreadID();
1682 // if this is already the main thread, no op
1683 if (threadID == fMainThreadID)
1684 return; // nop, done
1685 // thread is not the current main thread
1686 // - search if it is a registered subthread
1687 TSubThreadLog *subThreadP = findSubThread(threadID);
1688 if (fMainThreadID==0) {
1689 // no main thread currently registered
1690 if (subThreadP!=NULL) {
1691 // this is not a new thread, but a known subthread, can't get main thread now
1692 return; // no further op
1695 // this is not a known subthread, so it can become the main thread
1696 fMainThreadID = threadID;
1701 // cannot become main thread, will be treated as subthread if it generates output
1705 } // TDebugLogger::DebugDefineMainThread
1708 } // namespace sysync