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"
33 static bool DbgDLTInitialized;
34 static DltContext DbgProtoContext;
35 static DltContext DbgSessionContext;
36 static DltContext DbgAdminContext;
37 static DltContext DbgDataContext;
38 static DltContext DbgRemoteInfoContext;
39 static DltContext DbgParseContext;
40 static DltContext DbgGenerateContext;
41 static DltContext DbgTranspContext;
42 static DltContext DbgSyncMLTKContext;
43 static DltContext DbgDefaultContext;
47 #ifndef HARDCODED_CONFIG
50 cAppCharP const DbgOutFormatNames[numDbgOutFormats] = {
51 "text", // plain text format (but can be indented)
55 // If DLT support is not enabled, then trying to uses it in a config
56 // will lead to a generic parse error. Might be good enough, although
57 // a dedicated error message about "DLT being disabled in this build"
59 , "dlt" // GENIVI Diagnostic Log and Trace
64 // HTML dynamic folding modes
65 cAppCharP const DbgFoldingModeNames[numDbgFoldingModes] = {
66 "none", // do not include dynamic folding into HTML logs
67 "collapsed", // include folding - all collapsed by default
68 "expanded", // include folding - all expanded by default
69 "auto" // include folding - collapse/expand state predefined on a block-by-block basis
73 cAppCharP const DbgSourceModeNames[numDbgSourceModes] = {
74 "none", // do not include links into source code in HTML logs
75 "hint", // no links, but info about what file/line number the message comes from
76 "doxygen", // include link into doxygen prepared HTML version of source code
77 "txmt", // include txmt:// link (understood by TextMate and BBEdit) into source code
82 cAppCharP const DbgFlushModeNames[numDbgFlushModes] = {
83 "buffered", // no flush, keep open as long as possible, output buffered (fast, needed for network drives)
84 "flush", // flush every debug message
85 "openclose" // open and close debug channel separately for every message (as in 2.x engine)
88 // debug subthread isolation modes
89 cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes] = {
90 "none", // do not handle output from subthread specially
91 "suppress", // suppress output from subthreads
92 "separate", // create separate output stream (=file) for each subthread
93 "mix", // mix on a line by line basis
94 "mixblocks" // buffer thread's output and mix block-wise it into main stream when appropriate
99 // file extentsions for debug format modes
100 cAppCharP const DbgOutFormatExtensions[numDbgOutFormats] = {
101 ".log", // plain text format (but can be indented)
102 ".xml", // XML format
103 ".html" // HTML format
107 cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = {
109 "<?xml version=\"1.0\"?>\n"
110 "<sysync_log version=\"1.0\">",
111 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
112 "<html><head><title>SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log</title>\n"
113 "<meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\">\n"
114 "<style type=\"text/css\" media=\"screen\"><!--\n"
115 ".block { color: #0000FF; font-weight: bold; }\n"
116 ".attribute { color: #A5002C; }\n"
117 ".attrval { color: #D80039; font-weight: bold; }\n"
118 ".error { color: red; font-weight: bold; }\n"
119 ".hotalone { color: #000000; font-weight: bold; }\n"
120 ".hot { font-weight: bold; }\n"
121 ".script { color: #996633; }\n" // brownish
122 ".source { color: #3333FF; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // keyword blue
123 ".comment { color: #669933; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // comment green
124 ".skipped { color: #BBBBBB; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // skipped code
125 ".value { color: #FF3300; }\n" // bright orange
126 ".filter { color: #997F66; }\n" // brownish pale
127 ".match { color: #A95E38; }\n" // brownish orange
128 ".dbapi { color: #CC3366; }\n" // dark reddish/pink (pink/violet = database)
129 ".plugin { color: #9151A3; }\n" // dark violet (pink/violet = database)
130 ".incoming { color: #196D00; }\n" // really dark green (green = remote)
131 ".outgoing { color: #002C84; }\n" // really dark blue (blue = local)
132 ".conflict { color: #990000; }\n" // dark red
133 ".remote { color: #709900; }\n" // greenish (green = remote)
134 ".proto { color: #777100; }\n" // dark yellowish/brown
135 ".rest { color: #AAAAAA; }\n" // greyed
136 ".exotic { color: #FF9900; }\n" // mango
137 "a.jump { color: #5D82BA; }\n"
138 "pre { font-size: 90%; }\n"
139 // for folding (always included, as it must be in header)
142 " font-weight: bold;\n"
146 " display: inline;\n"
147 " border-width: 0.2em;\n"
148 " border-style: solid;\n"
149 " text-align: center;\n"
150 " vertical-align: middle;\n"
151 " padding: 0px 0.2em 0px 0.2em;\n"
152 " margin: 0 4px 2px 0;\n"
156 " font-weight: bold;\n"
160 " display: inline;\n"
161 " border-width: 0.2em;\n"
162 " border-style: solid;\n"
163 " text-align: center;\n"
164 " vertical-align: middle;\n"
165 " padding: 0px 0.2em 0px 0.2em;\n"
166 " margin: 0 4px 2px 0;\n"
168 ".doall { color: #754242; }\n"
170 "</head><body><h2>Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING "</h2>\n<ul>\n"
173 cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = {
176 "</ul><h2>End of log</h2></html>"
180 cAppCharP FoldingPrefix =
181 "<script language=javascript1.2 type=text/javascript><!--\n"
182 "function div_ref_style (id) {\n"
183 " if (document.layers) return document.layers[id];\n"
184 " else if (document.all) return document.all[id].style;\n"
185 " else if (document.getElementById) return document.getElementById(id).style;\n"
186 " else return null;\n"
188 "function exp(id) {\n"
189 " if(div_ref_style('B'+id).display!='block') {\n"
190 " div_ref_style('B'+id).display='block';\n"
191 " div_ref_style('E'+id).display='none';\n"
192 " div_ref_style('C'+id).display='inline';\n"
195 "function coll(id) {\n"
196 " if(div_ref_style('B'+id).display!='none') {\n"
197 " div_ref_style('B'+id).display='none';\n"
198 " div_ref_style('E'+id).display='inline';\n"
199 " div_ref_style('C'+id).display='none';\n"
202 "function doall(id,collapse) {\n"
203 " // get parent element\n"
208 " mydiv=document.getElementById('B'+id); // get div to collapse or expand\n"
216 " // get all contained blocks\n"
217 " divs=mydiv.getElementsByTagName('div') // all divs\n"
218 " for (i=0 ; i<divs.length ; i++) {\n"
219 " if (divs[i].className=='blk') {\n"
220 " // this is a foldable block div\n"
221 " bid = divs[i].id.substring(1);\n"
232 "<li><span class=\"doall\" onclick=\"doall('',true)\">[-- collapse all --]</span><span class=\"doall\" onclick=\"doall('',false)\">[++ expand all ++]</span></li>\n";
238 // privately redefined here to avoid circular headers (would need syncappbase.h)
239 extern "C" void ConsolePuts(const char *text);
242 // TDbgOptions implementation
243 // --------------------------
245 TDbgOptions::TDbgOptions()
249 } // TDbgOptions::TDbgOptions
252 void TDbgOptions::clear(void)
254 fOutputFormat = dbgfmt_html; // most universally readable and convenient
255 fIndentString = " "; // two spaces
256 fCustomPrefix.erase(); // no custom prefix
257 fCustomSuffix.erase(); // no custom suffix
258 fSeparateMsgs = true; // separate text message lines (<msg></msg> in xml)
259 fTimestampStructure = true; // timestamps in structure...
260 fTimestampForAll = false; // ..but not for every line
261 fThreadIDForAll = false; // not by default
262 fFlushMode = dbgflush_none; // no special flush or openclose (fast, but might loose info on process abort)
263 fFoldingMode = dbgfold_auto; // dynamic folding enabled, expanded/collapsed defaults automatically set on block-by-block basis
264 #ifdef SYDEBUG_LOCATION
265 fSourceLinkMode = dbgsource_none; // no links into source code
266 fSourceRootPath = SYDEBUG_LOCATION; // use default path from build
268 fAppend = false; // default to overwrite existing logfiles
269 fSubThreadMode = dbgsubthread_suppress; // simply suppress subthread info
270 fSubThreadBufferMax = 1024*1024; // don't buffer more than one meg.
271 } // TDbgOptions::clear
275 // TDbgOut implementation
276 // ----------------------
283 } // TDbgOut::TDbgOut
289 } // TDbgOut::~TDbgOut
292 void TDbgOut::destruct(void)
294 if (!fDestructed) doDestruct();
296 } // TDbgOut::destruct
299 void TDbgOut::doDestruct(void)
301 // make sure files are closed
303 } // TDbgOut::doDestruct
306 // TStdFileDbgOut implementation
307 // -----------------------------
311 TStdFileDbgOut::TStdFileDbgOut()
317 } // TStdFileDbgOut::TStdFileDbgOut
320 TStdFileDbgOut::~TStdFileDbgOut()
324 } // TStdFileDbgOut::~TStdFileDbgOut
332 static FILE *FOpen(const char *path, const char *mode)
334 FILE *file = fopen(path, mode);
336 // Be careful not to leak this file descriptor into forked
338 int fd = file ? fileno(file) : -1;
340 int flags = fcntl(fd, F_GETFD);
342 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
349 // open standard C file based debug output channel
350 bool TStdFileDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
356 // now apply new flush mode
357 fFlushMode=aFlushMode;
358 // save new file name
359 fFileName=aDbgOutputName;
360 // for C files, use the extension provided
361 fFileName+=aSuggestedExtension;
363 fFile=FOpen(fFileName.c_str(),aRawMode ? (aOverWrite ? "wb" : "ab") : (aOverWrite ? "w" : "a"));
364 // in case this fails, we'll have a NULL fFile. We can't do anything more here
366 // For openclose mode, we have opened here only to check for logfile writability - close again
367 if (fIsOpen && fFlushMode==dbgflush_openclose) {
371 // return false if we haven't been successful opening the channel
373 } // TStdFileDbgOut::openDbg
376 // return current size of debug file
377 uInt32 TStdFileDbgOut::dbgFileSize(void)
379 if (!fIsOpen) return 0; // no file, no size
381 if (fFlushMode==dbgflush_openclose) {
382 // we need to open the file for append first
383 fFile=FOpen(fFileName.c_str(),"a");
384 fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
390 fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
391 sz=ftell(fFile); // return size
394 } // TStdFileDbgOut::dbgFileSize
397 // close standard C file based debug channel
398 void TStdFileDbgOut::closeDbg(void)
407 } // TStdFileDbgOut::closeDbg
410 // write single line to standard file based output channel
411 void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
413 // if not open, just NOP
415 if (fFlushMode==dbgflush_openclose) {
416 // we need to open the file for append first
418 fFile=FOpen(fFileName.c_str(),"a");
427 // do required flushing
428 if (fFlushMode==dbgflush_openclose) {
429 // we need to close the file after every line of output
434 else if (aForceFlush || fFlushMode==dbgflush_flush) {
440 } // TStdFileDbgOut::putLine
443 // write raw data to output file
444 void TStdFileDbgOut::putRawData(cAppPointer aData, memSize aSize)
447 if (fFlushMode==dbgflush_openclose) {
448 // we need to open the file for append first
450 fFile=FOpen(fFileName.c_str(),"a");
453 if (fwrite(aData, 1, aSize, fFile) != 1) {
457 // do required flushing
458 if (fFlushMode==dbgflush_openclose) {
459 // we need to close the file after every line of output
464 else if (fFlushMode==dbgflush_flush) {
469 } // TStdFileDbgOut::putRawData
475 // TConsoleDbgOut implementation
476 // -----------------------------
478 TConsoleDbgOut::TConsoleDbgOut()
481 } // TStdFileDbgOut::TStdFileDbgOut
484 // open standard C file based debug output channel
485 bool TConsoleDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
491 // raw mode is not supported
494 // return false if we haven't been successful opening the channel
496 } // TConsoleDbgOut::openDbg
499 // close standard C file based debug channel
500 void TConsoleDbgOut::closeDbg(void)
503 } // TConsoleDbgOut::closeDbg
506 // write single line to standard file based output channel
507 void TConsoleDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
509 // if not open, just NOP
513 } // TConsoleDbgOut::putLine
518 // TDebugLoggerBase implementation
519 // -------------------------------
522 TDebugLoggerBase::TDebugLoggerBase(GZones *aGZonesP) :
526 fDebugEnabled=true; // enabled by default
531 fBlockHistory=NULL; // no Block open yet
532 fOutStarted=false; // not yet started
535 fOutputLoggerP=NULL; // no redirected output yet
536 } // TDebugLoggerBase::TDebugLoggerBase
540 TDebugLoggerBase::~TDebugLoggerBase()
542 // make sure debug is finalized
543 DebugFinalizeOutput();
544 // make sure possibly left-over history elements are erased
545 while (fBlockHistory) {
546 TBlockLevel *bl=fBlockHistory;
547 fBlockHistory=bl->fNext;
550 // get rid of output object
551 if (fDbgOutP) delete fDbgOutP;
553 } // TDebugLoggerBase::TDebugLoggerBase
556 // @brief convenience version for getting time
557 lineartime_t TDebugLoggerBase::getSystemNowAs(timecontext_t aContext)
559 return sysync::getSystemNowAs(aContext,fGZonesP);
560 } // TDebugLoggerBase::getSystemNowAs
564 void TDebugLoggerBase::installOutput(TDbgOut *aDbgOutP)
566 // get rid of possibly installed previous outputter
567 if (fDbgOutP) delete fDbgOutP;
569 } // TDebugLoggerBase::installOutput
572 /// @brief link this logger to another logger and redirect output to that logger
573 /// @param aDebugLoggerP[in] another logger, that must be alive as long as this logger is alive
574 void TDebugLoggerBase::outputVia(TDebugLoggerBase *aDebugLoggerP)
576 // save logger and prefix
577 fOutputLoggerP = aDebugLoggerP;
578 } // TDebugLoggerBase::outputVia
580 #if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC)
582 int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...) = fprintf;
586 // output formatted text
587 void TDebugLoggerBase::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
589 // we need a format and debug not completely off
590 if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
591 const sInt16 maxmsglen=1024;
594 // assemble the message string
595 vsnprintf(msg, maxmsglen, aFormat, aArgs);
597 DebugPuts(TDBG_LOCATION_ARG aDbgMask,msg);
599 } // TDebugLoggerBase::DebugVPrintf
602 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
603 TDebugLoggerBase &TDebugLoggerBase::setNextMask(uInt32 aDbgMask)
605 fNextDebugMask=aDbgMask;
607 } // TDebugLoggerBase::setNextMask
610 // like DebugPrintf(), but using mask previously set by setNextMask()
611 void TDebugLoggerBase::DebugPrintfLastMask(TDBG_LOCATION_PROTO cAppCharP aFormat, ...)
614 // we need a format and debug not completely off
615 if ((getMask() & fNextDebugMask)==fNextDebugMask && aFormat) {
616 va_start(args, aFormat);
617 DebugVPrintf(TDBG_LOCATION_ARG fNextDebugMask,aFormat,args);
621 } // TDebugLoggerBase::DebugPrintfLastMask
624 // output formatted text
625 void TDebugLoggerBase::DebugPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, ...)
628 // we need a format and debug not completely off
629 if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
630 va_start(args, aFormat);
631 DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,args);
634 } // TDebugLoggerBase::DebugVPrintf
637 // open new Block without attribute list
638 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
640 // we need a format and debug not completely off
641 if (getMask() && aBlockName) {
643 #pragma clang diagnostic push
644 #pragma clang diagnostic ignored "-Wformat-security"
646 DebugOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,NULL);
648 #pragma clang diagnostic pop
651 } // TDebugLoggerBase::DebugOpenBlock
654 // open new Block with attribute list, printf style
655 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
658 // we need a format and debug not completely off
659 if (getMask() && aBlockName) {
660 va_start(args, aBlockFmt);
661 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,aBlockFmt,args);
664 } // TDebugLoggerBase::DebugOpenBlock
667 // open new Block with attribute list, printf style, expanded by default
668 void TDebugLoggerBase::DebugOpenBlockExpanded(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
671 // we need a format and debug not completely off
672 if (getMask() && aBlockName) {
673 va_start(args, aBlockFmt);
674 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,false,aBlockFmt,args);
677 } // TDebugLoggerBase::DebugOpenBlockExpanded
680 // open new Block with attribute list, printf style, collapsed by default
681 void TDebugLoggerBase::DebugOpenBlockCollapsed(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
684 // we need a format and debug not completely off
685 if (getMask() && aBlockName) {
686 va_start(args, aBlockFmt);
687 DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,true,aBlockFmt,args);
690 } // TDebugLoggerBase::DebugOpenBlockCollapsed
693 #ifdef SYDEBUG_LOCATION
695 #define MAKEDBGLINK(txt) dbg2Link(TDBG_LOCATION_ARG txt)
697 /// turn text into link to source code
698 string TDebugLoggerBase::dbg2Link(const TDbgLocation &aTDbgLoc, const string &aTxt)
700 if (!aTDbgLoc.fFile || !fDbgOptionsP || fDbgOptionsP->fSourceLinkMode==dbgsource_none || fDbgOptionsP->fOutputFormat!=dbgfmt_html)
701 return aTxt; // disabled, non-html or no information to create source link
702 // create link or hint to source code
705 switch(fDbgOptionsP->fSourceLinkMode) {
706 case dbgsource_hint: {
707 // only add name/line number/function as title hint (in a otherwise inactive link)
708 line = "<a href=\"#\" title=";
709 StringObjPrintf(line,"<a href=\"#\" title=\"%s:%d",aTDbgLoc.fFile,aTDbgLoc.fLine);
710 StringObjAppendPrintf(line," in %s",aTDbgLoc.fFunction);
714 case dbgsource_doxygen: {
715 // create link into doxygen
717 // replace path with path to Doxygen HTML pages,
718 // mangle base name like Doxygen does
719 line += fDbgOptionsP->fSourceRootPath;
721 string file = aTDbgLoc.fFile;
722 size_t off = file.rfind('/');
723 if (off != file.npos)
724 file = file.substr(off + 1);
725 for (off = 0; off < file.size(); off++) {
738 StringObjAppendPrintf(line,"-source.html#l%05d",aTDbgLoc.fLine);
740 if (aTDbgLoc.fFunction) {
742 line+=aTDbgLoc.fFunction;
747 case dbgsource_txmt: {
748 // create txmt:// URL scheme link, which opens TextMate or BBEdit at the correct line in MacOS X
749 line = "<a href=\"txmt://open/?url=file://";
751 string path = fDbgOptionsP->fSourceRootPath;
752 path += aTDbgLoc.fFile;
753 // - add path CGI encoded
754 line += encodeForCGI(path.c_str());
756 if (aTDbgLoc.fLine>0)
757 StringObjAppendPrintf(line,"&line=%d",aTDbgLoc.fLine);
759 if (aTDbgLoc.fFunction) {
761 line+=aTDbgLoc.fFunction;
776 } // TDebugLoggerBase::dbg2Link
780 #define MAKEDBGLINK(txt) (txt)
782 #endif // SYDEBUG_LOCATION
785 static void DbgText2PlainText(const char *in, size_t len, std::string &out)
789 const char *end=in + len;
793 if (end-q>=6 && strucmp(q,"&html;",6)==0) {
794 if (q>s) out.append(s,q-s);
795 // everything until next &html; must be filtered out
796 // - search next &html;
798 while(q+6<=end && strucmp(q,"&html;",6)!=0) q++;
801 else if (end-q>=4 && strucmp(q,"&sp;",4)==0) {
802 if (q>s) out.append(s,q-s);
804 out += ' '; // convert to plain space
813 if (q>s) out.append(s,q-s);
818 // output text to debug channel
819 void TDebugLoggerBase::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
821 // we need a text and debug not completely off
822 if (!((getMask() & aDbgMask)==aDbgMask && aText && fDbgOptionsP)) {
825 //#warning "ugly hack"
826 DebugPutLine(TDBG_LOCATION_NONE "<li><span class=\"error\">Warning: Dbg output system already half shut down (limited formatting)!</span></li><li>");
827 if (aText) DebugPutLine(TDBG_LOCATION_NONE aText);
828 DebugPutLine(TDBG_LOCATION_NONE "</li>");
832 // make sure output is started
834 // try starting output
836 // disable debugging in this logger if starting output failed
837 // (prevents endless re-trying to open debug logs e.g. when log directory does not exist)
839 fDebugEnabled = false;
840 return; // stop all efforts here
845 // DLT logging logs everything in one chunk
846 if (fDbgOptionsP->fOutputFormat == dbgfmt_dlt) {
847 DltContext *context = &DbgDefaultContext;
848 if (aDbgMask & DBG_PROTO) {
849 context = &DbgProtoContext;
851 else if (aDbgMask & DBG_SESSION) {
852 context = &DbgSessionContext;
854 else if (aDbgMask & DBG_ADMIN) {
855 context = &DbgAdminContext;
857 else if (aDbgMask & DBG_DATA) {
858 context = &DbgDataContext;
860 else if (aDbgMask & DBG_REMOTEINFO) {
861 context = &DbgRemoteInfoContext;
863 else if (aDbgMask & DBG_PARSE) {
864 context = &DbgParseContext;
866 else if (aDbgMask & DBG_GEN) {
867 context = &DbgGenerateContext;
869 else if (aDbgMask & DBG_TRANSP) {
870 context = &DbgTranspContext;
872 else if (aDbgMask & (DBG_RTK_SML|DBG_RTK_XPT)) {
873 context = &DbgSyncMLTKContext;
876 DltLogLevelType level = DLT_LOG_VERBOSE;
877 if (level > DLT_LOG_INFO &&
878 (aDbgMask & DBG_HOT)) {
879 level = DLT_LOG_INFO;
881 if (level > DLT_LOG_ERROR &&
882 (aDbgMask & DBG_ERROR)) {
883 level = DLT_LOG_ERROR;
885 if (level > DLT_LOG_DEBUG &&
886 (aDbgMask & (DBG_USERDATA|DBG_PLUGIN|DBG_FILTER|DBG_CONFLICT))) {
887 level = DLT_LOG_DEBUG;
889 if (level < DLT_LOG_VERBOSE &&
890 (aDbgMask & DBG_DETAILS)) {
891 level = (DltLogLevelType)((int)level + 1);
893 if ((aTextSize > 0 && strlen(aText) > aTextSize) ||
894 strstr(aText, "&html;") ||
895 strstr(aText, "&sp;")) {
896 // Must make a copy and potentially filter out html markup.
898 buffer.reserve(aTextSize);
899 DbgText2PlainText(aText, aTextSize ? aTextSize : strlen(aText), buffer);
900 DLT_LOG(*context, level, DLT_STRING(buffer.c_str()));
902 // Fast path: log directly.
903 DLT_LOG(*context, level, DLT_STRING(aText));
909 // dissect into lines
910 cAppCharP end=aTextSize ? aText+aTextSize : NULL;
912 // check for preformatted message
913 bool pre=strnncmp(aText,"⪯",5)==0;
915 pre = pre || aPreFormatted;
917 while ((!end || aText<end) && *aText) {
918 // search for line end or end of string
920 while ((!end || p<end) && *p && *p!='\n' && *p!='\r') p++;
921 // output this line, properly formatted
926 switch (fDbgOptionsP->fOutputFormat) {
929 // prefix first line with <li>, second and further with <br/>
932 // add timestamp if needed for every line
934 fDbgOptionsP->fTimestampForAll
935 || fDbgOptionsP->fThreadIDForAll
936 #ifdef SYDEBUG_LOCATION
937 || fDbgOptionsP->fSourceLinkMode!=dbgsource_none
942 #ifdef MULTI_THREAD_SUPPORT
943 if (fDbgOptionsP->fThreadIDForAll) {
944 StringObjAppendPrintf(prefix,"%09lu",myThreadID());
945 if (fDbgOptionsP->fTimestampForAll) prefix += ", ";
948 if (fDbgOptionsP->fTimestampForAll) {
949 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
952 #ifdef SYDEBUG_LOCATION
953 else if (!fDbgOptionsP->fThreadIDForAll) {
954 // neither threadID nor timestamp, but source requested -> put small text here
958 prefix+="]</i> ";
959 // if we have links into source code, add it here
960 line += MAKEDBGLINK(prefix);
962 // colorize some messages
964 // colors, not mixable, most relevant first
965 if (aDbgMask & DBG_ERROR) {
968 else if (aDbgMask & DBG_EXOTIC) {
971 else if (aDbgMask & DBG_SCRIPTS) {
974 else if (aDbgMask & DBG_PLUGIN) {
977 else if (aDbgMask & DBG_DBAPI) {
980 else if (aDbgMask & DBG_CONFLICT) {
983 else if (aDbgMask & DBG_MATCH) {
986 else if (aDbgMask & DBG_REMOTEINFO) {
989 else if (aDbgMask & DBG_PROTO) {
992 else if (aDbgMask & DBG_FILTER) {
995 else if (aDbgMask & DBG_PARSE) {
998 else if (aDbgMask & DBG_GEN) {
1001 else if (aDbgMask & DBG_REST) {
1004 // apply basic color style
1006 line+="<span class=\""; line+=cl; line+="\">";
1008 // aditional style modifiers that can be combined with colors
1009 if (aDbgMask & DBG_HOT) {
1011 line+="<span class=\"hotalone\">";
1013 line+="<span class=\"hot\">";
1015 // start preformatted if selected
1020 if (!pre) line="<br/>";
1023 // XML, just output and replace special chars as needed
1026 #ifdef MULTI_THREAD_SUPPORT
1027 if (fDbgOptionsP->fThreadIDForAll) {
1029 StringObjAppendPrintf(line,"%09lu",myThreadID());
1033 if (fDbgOptionsP->fTimestampForAll) {
1034 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1039 DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size());
1042 if (fDbgOptionsP->fSeparateMsgs) {
1050 if (strucmp(q,"&html;",6)==0) {
1051 if (q>s) line.append(s,q-s); // flush stuff scanned so far
1052 // everything until next &html; does not need or want escaping, copy it as is
1053 // - search next &html;
1055 while(*q && strucmp(q,"&html;",6)!=0) q++;
1056 // - append everything between if we are in HTML mode
1057 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && q>s)
1061 else if (strucmp(q,"&sp;",4)==0) {
1062 if (q>s) line.append(s,q-s); // flush stuff scanned so far
1063 // non-breaking space in HTML, normal space otherwise
1064 if (fDbgOptionsP->fOutputFormat==dbgfmt_html)
1068 s=q=q+4; // skip &sp;
1071 if (q>s) line.append(s,q-s);
1077 if (q>s) line.append(s,q-s);
1082 if (q>s) line.append(s,q-s);
1090 if (q>s) line.append(s,q-s);
1091 if (fDbgOptionsP->fSeparateMsgs && fDbgOptionsP->fOutputFormat==dbgfmt_xml) {
1102 if (strucmp(q,"&html;",6)==0) {
1103 if (q>s) line.append(s,q-s);
1104 // everything until next &html; must be filtered out
1105 // - search next &html;
1107 while(*q && strucmp(q,"&html;",6)!=0) q++;
1110 else if (strucmp(q,"&sp;",4)==0) {
1111 if (q>s) line.append(s,q-s);
1113 line += ' '; // convert to plain space
1122 if (q>s) line.append(s,q-s);
1124 } // switch text output
1126 // skip the lineend, if any
1127 while (((!end || p<end) && *p=='\n') || *p=='\r') p++;
1128 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && ((end && p>=end) || *p==0)) {
1148 line+="</span>"; // end special style
1154 line+="</span>"; // end special style
1156 line+="</li>"; // we need to close the list entry
1158 DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size(),pre);
1159 // next line, if any
1161 } // loop until all text done
1163 } // TDebugLoggerBase::DebugPuts
1166 // open new Block with attribute list, varargs passed
1167 void TDebugLoggerBase::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1171 if (fDbgOptionsP->fFoldingMode==dbgfold_collapsed)
1173 else if (fDbgOptionsP->fFoldingMode==dbgfold_expanded)
1175 if (getMask() && aBlockName && fDbgOptionsP) {
1176 // make sure output is started
1177 if (!fOutStarted) DebugStartOutput();
1178 // create Block line on current indent level
1181 // - preamble, possibly with timestamp
1182 bool withTime = fDbgOptionsP->fTimestampStructure;
1184 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1185 switch (fDbgOptionsP->fOutputFormat) {
1188 bl="<"; bl+=aBlockName;
1190 bl+=" time=\"" + ts + "\"";
1200 bl="<li><span class=\"block\">";
1201 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1202 StringObjAppendPrintf(bl,
1203 "<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>",
1204 long(getBlockNo()), aCollapsed ? "inline" : "none", long(getBlockNo()),
1205 long(getBlockNo()), aCollapsed ? "none" : "inline", long(getBlockNo())
1208 StringObjAppendPrintf(bl,"<a name=\"H%ld\">", long(getBlockNo()));
1210 bl += MAKEDBGLINK(string("[") + ts + "] ");
1212 #ifdef SYDEBUG_LOCATION
1213 else if (fDbgOptionsP->fSourceLinkMode!=dbgsource_none) {
1214 bl += MAKEDBGLINK(string("[src] "));
1224 bl+="</a></span><span class=\"attribute\">";
1230 if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1231 bl+="[" + ts + "] ";
1239 } // switch preamble
1242 // first expand all printf parameters
1244 vStringObjPrintf(attrs,aBlockFmt,true,aArgs);
1245 // isolate |-separated attribute format strings
1246 cAppCharP q,r,s,p=attrs.c_str();
1248 // search for beginning of value
1250 while(*q && *q!='=' && *q!='|') q++;
1251 // search for end of value
1253 s=q; // in case we don't have a =
1257 while (*r && *r!='|') r++;
1259 // now: p=start of attrname, q=end of attrname
1260 // s=start of value, r=end of value
1261 // output an attribute now
1263 switch (fDbgOptionsP->fOutputFormat) {
1275 bl+="=<span class=\"attrval\">";
1286 } // switch attribute
1287 } // non-empty attribute
1288 // more attributes to come?
1289 if (*r=='|') r++; // skip separator
1292 } // attributes present
1294 switch (fDbgOptionsP->fOutputFormat) {
1301 bl+="</span>"; // end span for attributes
1302 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1303 StringObjAppendPrintf(bl,
1304 " <span class=\"doall\" onclick=\"doall('%ld',true)\">[--]</span><span class=\"doall\" onclick=\"doall('%ld',false)\">[++]</span>",
1305 long(getBlockNo()), long(getBlockNo())
1308 // link to end of block
1309 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#F%ld\">[->end]</a>", long(getBlockNo()));
1310 // link to start of enclosing block (if any)
1311 if (fBlockHistory) {
1312 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#H%ld\">[->enclosing]</a>", long(fBlockHistory->fBlockNo));
1314 // start div for content folding
1315 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1316 StringObjAppendPrintf(bl,
1317 "<div class=\"blk\" id=\"B%ld\" style=\"display:%s\">",
1319 aCollapsed ? "none" : "inline"
1322 bl+="<ul>"; // now start list for block's contents
1328 } // switch preamble
1329 // now output Block line (on current indent level)
1330 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1331 // increase indent level (applies to all Block contents)
1333 // save Block on stack
1334 TBlockLevel *newLevel = new TBlockLevel;
1335 newLevel->fBlockName=aBlockName;
1336 newLevel->fNext=fBlockHistory;
1337 newLevel->fBlockNo=getBlockNo(); // save block number to reference block in collapse box at end of block
1338 nextBlock(); // increment block number
1339 fBlockHistory=newLevel; // insert new level at start of list
1341 } // TDebugLoggerBase::DebugVOpenBlock
1344 // close named Block. If no name given, topmost Block will be closed
1345 void TDebugLoggerBase::DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1347 if (fOutStarted && getMask() && fDbgOptionsP && fBlockHistory) {
1348 if (aBlockName==NULL) {
1350 internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),"Block Nest Warning: Missing Block name at close");
1352 internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),NULL);
1356 internalCloseBlocks(TDBG_LOCATION_ARG aBlockName,NULL);
1359 } // TDebugLoggerBase::DebugCloseBlock
1363 // internal helper used to close all or some Blocks
1364 void TDebugLoggerBase::internalCloseBlocks(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aCloseComment)
1366 if (!fDbgOptionsP) return; // security
1367 bool withTime = fDbgOptionsP->fTimestampStructure;
1370 if (!fBlockHistory && aBlockName) {
1371 // no blocks open any more and not close-all-remaining call (log close...)
1372 DebugPrintf(TDBG_LOCATION_ARG DBG_EXOTIC+DBG_ERROR,"Block Nest Warning: Trying to close block '%s', but no block is open",aBlockName);
1375 while (fBlockHistory) {
1378 if (aCloseComment) {
1380 comment += aCloseComment;
1382 // check if closing top-of-stack Block now
1384 (aBlockName && strucmp(aBlockName,fBlockHistory->fBlockName.c_str())==0);
1385 if (!found && fBlockHistory->fNext==NULL) {
1386 // last Block always counts as "found"...
1389 // ...but issue warning as name is not what we would have expected
1390 StringObjAppendPrintf(comment, " - Block Nest Warning: closing '%s', but expected '%s'",aBlockName ? aBlockName : "<unknown>", fBlockHistory->fBlockName.c_str());
1393 // now close topmost Block
1395 // - get time if needed and possibly put it within indented block
1397 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1398 // for XML, the time must be shown before the close tag on a separate line
1399 if (fDbgOptionsP->fOutputFormat == dbgfmt_xml) {
1400 StringObjPrintf(bl,"<endblock time=\"%s\"/>",ts.c_str());
1401 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size()); // still within block, indented
1405 if (fIndent>0) fIndent--;
1406 // - then create closing Block
1408 if (!found) StringObjAppendPrintf(comment," - Block Nest Warning: implicitly closed (by explicitly closing '%s')",aBlockName ? aBlockName : "<unknown parent>");
1410 switch (fDbgOptionsP->fOutputFormat) {
1414 bl+=fBlockHistory->fBlockName;
1416 if (!comment.empty()) {
1424 bl="</ul><span class=\"block\">"; // end of content list
1425 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1426 StringObjAppendPrintf(bl,
1427 "<span class=\"coll\" onclick=\"coll('%ld')\">–</span>",
1428 long(fBlockHistory->fBlockNo)
1431 StringObjAppendPrintf(bl,"<a name=\"F%ld\">",long(fBlockHistory->fBlockNo));
1433 bl += MAKEDBGLINK(string("[") + ts + "] ");
1436 bl+=fBlockHistory->fBlockName;
1440 // link to top of block
1441 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#H%ld\">[->top]</a>",long(fBlockHistory->fBlockNo));
1442 // link to end of enclosing block (if any)
1443 if (fBlockHistory->fNext) {
1444 StringObjAppendPrintf(bl," <a class=\"jump\" href=\"#F%ld\">[->enclosing]</a>",long(fBlockHistory->fNext->fBlockNo));
1446 if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1447 bl+="</div>"; // end of folding division
1449 bl+="</li>"; // end of list entry containing entire block
1455 if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1456 bl+="[" + ts + "] ";
1459 bl+=fBlockHistory->fBlockName;
1463 } // switch Block close
1464 // - output closing Block line
1465 DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1466 // - remove Block level
1467 TBlockLevel *closedLevel = fBlockHistory;
1468 fBlockHistory = closedLevel->fNext;
1470 // if we have found the Block, exit here
1473 } // TDebugLoggerBase::internalCloseBlocks
1476 static void RegisterContext(DltContext *aHandle, const char *aContextID, const char *aDescription)
1478 std::string envName = "LIBSYNTHESIS_";
1479 envName += aContextID;
1480 const char *value = getenv(envName.c_str());
1483 DltLogLevelType level = (DltLogLevelType)atoi(value);
1484 dlt_register_context_ll_ts(aHandle, aContextID, aDescription, level, DLT_TRACE_STATUS_OFF);
1487 dlt_register_context(aHandle, aContextID, aDescription);
1492 // start debugging output if needed and sets fOutStarted
1493 bool TDebugLoggerBase::DebugStartOutput(void)
1497 if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_dlt) {
1498 // Register our logging contexts.
1499 if (!DbgDLTInitialized) {
1500 RegisterContext(&DbgProtoContext, "PROT", "SyncML protocol related information");
1501 RegisterContext(&DbgSessionContext, "SESS", "session management related information");
1502 RegisterContext(&DbgAdminContext, "ADMN", "verything that has to do with administrative data (anchors, targets, map table)");
1503 RegisterContext(&DbgDataContext, "DATA", "Everything that has to do with handling user data (data objects). Actual user data will however be shown only if loglevel >= debug.");
1504 RegisterContext(&DbgRemoteInfoContext, "REMI", "This shows information delivered in the remote party's device information, such as manufacturer name, datatypes supported, fields supported etc.");
1505 RegisterContext(&DbgParseContext, "PARS", "This shows information related to parsing and processing incoming data from the remote party. Actual user data will however be shown only if loglevel >= debug.");
1506 RegisterContext(&DbgGenerateContext, "GEN", "This shows information related to generating outgoing data for the remote party. Actual user data will however be shown only if loglevel >= debug.");
1507 RegisterContext(&DbgTranspContext, "TRNS", "shows transport (http and TCP communication) related information");
1508 RegisterContext(&DbgSyncMLTKContext, "SMLT", "messages generated by the SyncML Toolkit code");
1509 RegisterContext(&DbgDefaultContext, "SYS", "any other libsynthesis debug log message that does not fit in any of the other contexts");
1510 DbgDLTInitialized = true;
1516 if (fOutputLoggerP) {
1517 // using another logger, call it to start output
1518 fOutStarted = fOutputLoggerP->DebugStartOutput();
1520 // start with indent level of parent logger
1521 fIndent = fOutputLoggerP->fIndent;
1522 // note: we'll use the parent logger's block number...
1523 fBlockNo = 0; // ...but init to something just in case
1526 else if (fDbgOptionsP && fDbgOutP && !fDbgPath.empty()) {
1527 // try to open the debug channel (force to openclose if we have multiple threads mixed in one file)
1528 if (fDbgOutP->openDbg(
1530 DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat],
1531 fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix ? dbgflush_openclose : fDbgOptionsP->fFlushMode,
1532 !fDbgOptionsP->fAppend
1534 // make sure we don't recurse when we produce some output
1536 fIndent = 0; // reset to make sure
1537 // create a block number that is unique in the file, even if we append multiple times.
1538 // We assume that a block consumes at least 256 bytes, so size_of_file/256 always gets
1539 // an unused block ID within that file
1540 // 256 is a safe assumption because the "fold" button <divs> alone are around 250 bytes
1541 fBlockNo = 1 + (fDbgOutP->dbgFileSize()/256);
1542 // now create required prefix
1543 DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomPrefix.empty() ? DbgOutDefaultPrefixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomPrefix.c_str());
1544 // add folding javascript if needed
1545 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1546 DebugPutLine(TDBG_LOCATION_NONE FoldingPrefix);
1548 } // debug channel opened successfully
1549 } // use own debug channel
1550 } // environment ready to start output
1552 } // TDebugLoggerBase::DebugStartOutput
1555 // @brief finalize debugging output (close Blocks, close output channel)
1556 void TDebugLoggerBase::DebugFinalizeOutput(void)
1558 if (fOutputLoggerP) {
1559 // just close my own blocks
1560 internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because sub-log ends here");
1562 if (fOutStarted && fDbgOptionsP && fDbgOutP) {
1563 // close all left-open open Blocks
1564 internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because log ends here");
1565 // now finalize output
1566 // - special stuff before
1567 if (fDbgOptionsP->fOutputFormat == dbgfmt_xml)
1568 fIndent=0; // unindent to zero (document is not a real Block)
1570 DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomSuffix.empty() ? DbgOutDefaultSuffixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomSuffix.c_str());
1571 // now close the debug channel
1572 fDbgOutP->closeDbg();
1574 // whatever happened, we are not started any more
1576 } // TDebugLoggerBase::DebugFinalizeOutput
1579 // Output single line to debug channel (includes indenting and other prefixing, but no further formatting)
1580 void TDebugLoggerBase::DebugPutLine(TDBG_LOCATION_PROTO cAppCharP aText, stringSize aTextSize, bool aPre)
1583 if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_dlt) {
1584 // One example where this gets called is DebugVOpen/CloseBlock()
1585 // with lines prepared as if we are printing plain text. Use
1586 // a fairly neutral log level here.
1588 if (aTextSize > 0 && aTextSize < strlen(aText)) {
1589 string buffer(aText, aTextSize);
1590 DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(buffer.c_str()));
1592 DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(aText));
1599 if (!aText || (!fDbgOutP && !fOutputLoggerP)) return;
1601 // not an empty line
1604 // prefix with timestamp if selected in text format
1605 if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_text && fDbgOptionsP->fTimestampForAll) {
1606 // prefix each line (before the indent!) with a timestamp
1608 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1613 // Indent if selected
1614 if (fDbgOptionsP && !fDbgOptionsP->fIndentString.empty() && !(fDbgOptionsP->fOutputFormat==dbgfmt_html && aPre)) {
1616 for (uInt16 n=0; n<fIndent; n++) {
1617 msg+=fDbgOptionsP->fIndentString;
1620 // add message itself
1622 msg.append(aText,aTextSize);
1626 if (fOutputLoggerP) {
1627 // use parent's output
1628 fOutputLoggerP->fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1631 // use my own output channel
1632 fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1635 } // TDebugLoggerBase::DebugPutLine
1638 // TDebugLogger implementation
1639 // ---------------------------
1642 TDebugLogger::TDebugLogger(GZones *aGZonesP) :
1645 #ifdef MULTI_THREAD_SUPPORT
1647 fSubThreadLogs=NULL;
1648 fSilentLoggerP=NULL;
1650 } // TDebugLogger::TDebugLogger
1654 TDebugLogger::~TDebugLogger()
1656 #ifdef MULTI_THREAD_SUPPORT
1657 // remove subthread loggers
1658 TSubThreadLog* subThreadP = fSubThreadLogs;
1659 fSubThreadLogs = NULL;
1660 while (subThreadP) {
1661 // delete logger if any
1662 if (subThreadP->fSubThreadLogger) {
1664 delete subThreadP->fSubThreadLogger;
1670 TSubThreadLog* delP = subThreadP;
1671 subThreadP = subThreadP->fNext;
1675 if (fSilentLoggerP) {
1676 delete fSilentLoggerP;
1677 fSilentLoggerP = NULL;
1680 } // TDebugLogger::~TDebugLogger
1683 #ifdef MULTI_THREAD_SUPPORT
1685 void TDebugLogger::setOptions(const TDbgOptions *aDbgOptionsP)
1687 TDebugLoggerBase::setOptions(aDbgOptionsP);
1688 TSubThreadLog* subThreadP = fSubThreadLogs;
1689 while (subThreadP) {
1690 if (subThreadP->fSubThreadLogger) {
1691 subThreadP->fSubThreadLogger->setOptions(aDbgOptionsP);
1693 subThreadP = subThreadP->fNext;
1697 /// @brief find (and possibly delete) subthread record
1698 /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
1699 /// IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
1700 /// Note that the caller must take care of deleting the subthread record
1701 TSubThreadLog *TDebugLogger::findSubThread(uInt32 aThreadID, bool aAndRemove)
1703 TSubThreadLog* subThreadP = fSubThreadLogs;
1704 TSubThreadLog** subThreadLinkPP = &fSubThreadLogs;
1705 while (subThreadP) {
1706 if (subThreadP->fThreadID == aThreadID) {
1708 // bridge previous with next in one single assignment (i.e. thread safe)
1709 *subThreadLinkPP = subThreadP->fNext;
1711 // return found record (note that it MUST BE DELETED by caller if no longer used)
1714 subThreadLinkPP = &subThreadP->fNext;
1715 subThreadP = *subThreadLinkPP;
1717 return NULL; // none found
1718 } // TDebugLogger::findSubThread
1721 /// @brief find or create logger for subthread
1722 TDebugLoggerBase *TDebugLogger::getThreadLogger(bool aCreateNew)
1724 if (fOutputLoggerP) {
1725 TDebugLoggerBase *logger = fOutputLoggerP->getThreadLogger(aCreateNew);
1730 if (!fDbgOptionsP || fDbgOptionsP->fSubThreadMode==dbgsubthread_none)
1731 return this; // no options, do not handle subthreads specially
1732 uIntArch threadID = myThreadID();
1733 if (fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix || threadID==fMainThreadID) {
1734 // In line mix and for mainthread - I am the logger for this thread!
1737 TSubThreadLog* subThreadP = findSubThread(threadID);
1739 // we know this subthread, return its logger
1740 return subThreadP->fSubThreadLogger; // can be NULL if subthread logging is disabled
1742 // unknown subthread
1743 if (fMainThreadID==0) {
1744 // no current mainthread, let subthread write to main log
1745 // Note: this makes sure log info possibly trailing the DebugThreadOutputDone()
1746 // also lands in the main log. This is not critical - the only thing that must be
1747 // ensured is that starting new threads is made only with DebugDefineMainThread set.
1750 // new subthread, create entry in list
1754 subThreadP = new TSubThreadLog;
1755 subThreadP->fThreadID=threadID;
1756 subThreadP->fNext=fSubThreadLogs; // link current list behind this new entry
1757 // create logger for the thread (or none)
1758 switch (fDbgOptionsP->fSubThreadMode) {
1759 case dbgsubthread_separate:
1760 // separate file for subthread output
1761 // - create new base logger
1762 subThreadP->fSubThreadLogger = new TDebugLoggerBase(fGZonesP);
1763 // - install output (copy)
1764 subThreadP->fSubThreadLogger->installOutput(fDbgOutP ? fDbgOutP->clone() : NULL);
1766 subThreadP->fSubThreadLogger->setOptions(getOptions());
1767 // - inherit current mask/enable
1768 subThreadP->fSubThreadLogger->setMask(getMask());
1769 subThreadP->fSubThreadLogger->setEnabled(fDebugEnabled);
1770 // - debug path is same as myself plus Thread ID
1771 subThreadP->fSubThreadLogger->setDebugPath(fDbgPath.c_str());
1772 StringObjPrintf(s,"_%lu",(long unsigned)threadID);
1773 subThreadP->fSubThreadLogger->appendToDebugPath(s.c_str());
1775 case dbgsubthread_suppress:
1777 // no output from subthreads
1778 subThreadP->fSubThreadLogger=NULL; // no logger
1781 // now activate by linking it at top of list (this is thread safe)
1782 fSubThreadLogs = subThreadP;
1783 // return the logger
1784 return subThreadP->fSubThreadLogger;
1786 return NULL; // no logger for this thread
1787 } // TDebugLogger::getThreadLogger
1790 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
1791 TDebugLoggerBase &TDebugLogger::setNextMask(uInt32 aDbgMask)
1793 TDebugLoggerBase *loggerP = getThreadLogger();
1795 // return pointer to loggerbase whose DebugPrintfLastMask() must be called
1796 return loggerP->inherited::setNextMask(aDbgMask);
1799 // we have no logger but still need to return something
1800 if (!fSilentLoggerP) {
1801 fSilentLoggerP = new TDebugLoggerBase(fGZonesP);
1802 fSilentLoggerP->setEnabled(false);
1804 fSilentLoggerP->setNextMask(DBG_ERROR); // must set non-zero to make sure it is NOT output!
1805 return *fSilentLoggerP;
1807 } // TDebugLoggerBase::setNextMask
1810 // output text to debug channel, with checking for subthreads
1811 void TDebugLogger::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
1813 TDebugLoggerBase *loggerP = getThreadLogger();
1814 if (loggerP) loggerP->inherited::DebugPuts(TDBG_LOCATION_ARG aDbgMask,aText,aTextSize,aPreFormatted);
1815 } // TDebugLogger::DebugPuts
1818 void TDebugLogger::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
1820 TDebugLoggerBase *loggerP = getThreadLogger();
1821 if (loggerP) loggerP->inherited::DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,aArgs);
1822 } // TDebugLogger::DebugVPrintf
1825 void TDebugLogger::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1827 TDebugLoggerBase *loggerP = getThreadLogger();
1828 if (loggerP) loggerP->inherited::DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName, aBlockTitle, aCollapsed, aBlockFmt, aArgs);
1829 } // TDebugLogger::DebugVOpenBlock
1832 void TDebugLogger:: DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1834 TDebugLoggerBase *loggerP = getThreadLogger();
1835 if (loggerP) loggerP->inherited::DebugCloseBlock(TDBG_LOCATION_ARG aBlockName);
1836 } // TDebugLogger::DebugCloseBlock
1841 // output all buffered subthread's output in a special subthread Block in the main output
1842 void TDebugLogger::DebugShowSubThreadOutput(void)
1844 #ifdef MULTI_THREAD_SUPPORT
1845 // nop as long mixed-block mode is not implemented
1847 } // TDebugLogger::DebugShowSubThreadOutput
1850 // the calling thread signals that it is done with doing output for now. If the main
1851 // thread is doing this and we have bufferandmix mode, the next subthread will be allowed
1852 // to write into the output channel until a new main thread gains control via
1853 // DebugDefineMainThread();
1854 void TDebugLogger::DebugThreadOutputDone(bool aRemoveIt)
1856 #ifdef MULTI_THREAD_SUPPORT
1857 uIntArch threadID = myThreadID();
1858 if (threadID==fMainThreadID) {
1859 // current main thread done
1862 // for session logs, subthreads are usually left in the list at this time (aRemoveIt==false)
1863 // (as they will get deleted with the session logger later anyway)
1865 TSubThreadLog* tP = findSubThread(threadID,true);
1867 if (tP->fSubThreadLogger) {
1869 delete tP->fSubThreadLogger;
1879 } // TDebugLogger::DebugThreadOutputDone
1882 // Used to regain control as main thread (e.g. for the next request of a session which
1883 // possibly occurs from another thread).
1884 void TDebugLogger::DebugDefineMainThread(void)
1886 #ifdef MULTI_THREAD_SUPPORT
1887 uIntArch threadID = myThreadID();
1888 // if this is already the main thread, no op
1889 if (threadID == fMainThreadID)
1890 return; // nop, done
1891 // thread is not the current main thread
1892 // - search if it is a registered subthread
1893 TSubThreadLog *subThreadP = findSubThread(threadID);
1894 if (fMainThreadID==0) {
1895 // no main thread currently registered
1896 if (subThreadP!=NULL) {
1897 // this is not a new thread, but a known subthread, can't get main thread now
1898 return; // no further op
1901 // this is not a known subthread, so it can become the main thread
1902 fMainThreadID = threadID;
1907 // cannot become main thread, will be treated as subthread if it generates output
1911 } // TDebugLogger::DebugDefineMainThread
1914 } // namespace sysync