File: | libsynthesis/src/sysync/debuglogger.cpp |
Warning: | line 1106, column 17 Value stored to 's' is never read |
1 | /* |
2 | * File: debuglogger.cpp |
3 | * |
4 | * Author: Lukas Zeller (luz@plan44.ch) |
5 | * |
6 | * Global debug mechanisms |
7 | * |
8 | * Copyright (c) 2005-2011 by Synthesis AG + plan44.ch |
9 | * |
10 | * 2005-08-04 : luz : created |
11 | * |
12 | */ |
13 | |
14 | |
15 | #include "prefix_file.h" |
16 | |
17 | #ifdef SYDEBUG2 |
18 | |
19 | #include "debuglogger.h" |
20 | |
21 | |
22 | #ifdef MULTI_THREAD_SUPPORT1 |
23 | #include "platform_thread.h" |
24 | #endif |
25 | |
26 | #ifdef USE_DLT |
27 | #include <dlt.h> |
28 | #endif |
29 | |
30 | namespace sysync { |
31 | |
32 | #ifdef USE_DLT |
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; |
44 | #endif |
45 | |
46 | |
47 | #ifndef HARDCODED_CONFIG |
48 | |
49 | // debug format modes |
50 | cAppCharP const DbgOutFormatNames[numDbgOutFormats] = { |
51 | "text", // plain text format (but can be indented) |
52 | "xml", // XML format |
53 | "html" // HTML format |
54 | #ifdef USE_DLT |
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" |
58 | // would be nicer. |
59 | , "dlt" // GENIVI Diagnostic Log and Trace |
60 | #endif |
61 | }; |
62 | |
63 | |
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 |
70 | }; |
71 | |
72 | |
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 |
78 | }; |
79 | |
80 | |
81 | // debug flush modes |
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) |
86 | }; |
87 | |
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 |
95 | }; |
96 | |
97 | #endif |
98 | |
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 |
104 | }; |
105 | |
106 | |
107 | cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = { |
108 | "*** Start of log", |
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"3" "." "4" "." "0" "." "47" " 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) |
140 | ".exp {\n" |
141 | " color: #FF0000;\n" |
142 | " font-weight: bold;\n" |
143 | " font-size: 90%;\n" |
144 | " width: 1em;\n" |
145 | " height: 1em;\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" |
153 | "}\n" |
154 | ".coll {\n" |
155 | " color: #754242;\n" |
156 | " font-weight: bold;\n" |
157 | " font-size: 90%;\n" |
158 | " width: 1em;\n" |
159 | " height: 1em;\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" |
167 | "}\n" |
168 | ".doall { color: #754242; }\n" |
169 | "--></style>\n" |
170 | "</head><body><h2>Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING"3" "." "4" "." "0" "." "47" "</h2>\n<ul>\n" |
171 | }; |
172 | |
173 | cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = { |
174 | "*** End of log", |
175 | "</sysync_log>", |
176 | "</ul><h2>End of log</h2></html>" |
177 | }; |
178 | |
179 | |
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" |
187 | "}\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" |
193 | " }\n" |
194 | "}\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" |
200 | " }\n" |
201 | "}\n" |
202 | "function doall(id,collapse) {\n" |
203 | " // get parent element\n" |
204 | " if (id=='') {\n" |
205 | " mydiv=document;\n" |
206 | " }\n" |
207 | " else {\n" |
208 | " mydiv=document.getElementById('B'+id); // get div to collapse or expand\n" |
209 | " if (collapse) {\n" |
210 | " coll(id);\n" |
211 | " }\n" |
212 | " else {\n" |
213 | " exp(id);\n" |
214 | " }\n" |
215 | " }\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" |
222 | " if (collapse) {\n" |
223 | " coll(bid);\n" |
224 | " }\n" |
225 | " else {\n" |
226 | " exp(bid);\n" |
227 | " }\n" |
228 | " }\n" |
229 | " }\n" |
230 | "}\n" |
231 | "--></script>\n" |
232 | "<li><span class=\"doall\" onclick=\"doall('',true)\">[-- collapse all --]</span><span class=\"doall\" onclick=\"doall('',false)\">[++ expand all ++]</span></li>\n"; |
233 | |
234 | |
235 | // externals |
236 | |
237 | #ifdef CONSOLEINFO |
238 | // privately redefined here to avoid circular headers (would need syncappbase.h) |
239 | extern "C" void ConsolePuts(const char *text); |
240 | #endif |
241 | |
242 | // TDbgOptions implementation |
243 | // -------------------------- |
244 | |
245 | TDbgOptions::TDbgOptions() |
246 | { |
247 | // set defaults |
248 | clear(); |
249 | } // TDbgOptions::TDbgOptions |
250 | |
251 | |
252 | void TDbgOptions::clear(void) |
253 | { |
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 |
267 | #endif |
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 |
272 | |
273 | |
274 | |
275 | // TDbgOut implementation |
276 | // ---------------------- |
277 | |
278 | TDbgOut::TDbgOut() : |
279 | fDestructed(false) |
280 | { |
281 | // init |
282 | fIsOpen=false; |
283 | } // TDbgOut::TDbgOut |
284 | |
285 | |
286 | TDbgOut::~TDbgOut() |
287 | { |
288 | destruct(); |
289 | } // TDbgOut::~TDbgOut |
290 | |
291 | |
292 | void TDbgOut::destruct(void) |
293 | { |
294 | if (!fDestructed) doDestruct(); |
295 | fDestructed=true; |
296 | } // TDbgOut::destruct |
297 | |
298 | |
299 | void TDbgOut::doDestruct(void) |
300 | { |
301 | // make sure files are closed |
302 | closeDbg(); |
303 | } // TDbgOut::doDestruct |
304 | |
305 | |
306 | // TStdFileDbgOut implementation |
307 | // ----------------------------- |
308 | |
309 | #ifndef NO_C_FILES |
310 | |
311 | TStdFileDbgOut::TStdFileDbgOut() |
312 | { |
313 | // init |
314 | fFileName.erase(); |
315 | fFile=NULL__null; |
316 | mutex=newMutex(); |
317 | } // TStdFileDbgOut::TStdFileDbgOut |
318 | |
319 | |
320 | TStdFileDbgOut::~TStdFileDbgOut() |
321 | { |
322 | destruct(); |
323 | freeMutex(mutex); |
324 | } // TStdFileDbgOut::~TStdFileDbgOut |
325 | |
326 | |
327 | #ifdef LINUX |
328 | # include <unistd.h> |
329 | # include <fcntl.h> |
330 | #endif |
331 | |
332 | static FILE *FOpen(const char *path, const char *mode) |
333 | { |
334 | FILE *file = fopen(path, mode); |
335 | #ifdef LINUX |
336 | // Be careful not to leak this file descriptor into forked |
337 | // processes. |
338 | int fd = file ? fileno(file) : -1; |
339 | if (fd >= 0) { |
340 | int flags = fcntl(fd, F_GETFD1); |
341 | if (flags != -1) { |
342 | fcntl(fd, F_SETFD2, flags | FD_CLOEXEC1); |
343 | } |
344 | } |
345 | #endif |
346 | return file; |
347 | } |
348 | |
349 | // open standard C file based debug output channel |
350 | bool TStdFileDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode) |
351 | { |
352 | if (fIsOpen) { |
353 | // first close |
354 | closeDbg(); |
355 | } |
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; |
362 | // open |
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 |
365 | fIsOpen=fFile!=NULL__null; |
366 | // For openclose mode, we have opened here only to check for logfile writability - close again |
367 | if (fIsOpen && fFlushMode==dbgflush_openclose) { |
368 | fclose(fFile); |
369 | fFile=NULL__null; |
370 | } |
371 | // return false if we haven't been successful opening the channel |
372 | return fIsOpen; |
373 | } // TStdFileDbgOut::openDbg |
374 | |
375 | |
376 | // return current size of debug file |
377 | uInt32 TStdFileDbgOut::dbgFileSize(void) |
378 | { |
379 | if (!fIsOpen) return 0; // no file, no size |
380 | uInt32 sz; |
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_END2); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode) |
385 | sz=ftell(fFile); |
386 | fclose(fFile); |
387 | fFile=NULL__null; |
388 | } |
389 | else { |
390 | fseek(fFile,0,SEEK_END2); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode) |
391 | sz=ftell(fFile); // return size |
392 | } |
393 | return sz; |
394 | } // TStdFileDbgOut::dbgFileSize |
395 | |
396 | |
397 | // close standard C file based debug channel |
398 | void TStdFileDbgOut::closeDbg(void) |
399 | { |
400 | if (fIsOpen) { |
401 | if (fFile) { |
402 | fclose(fFile); |
403 | fFile=NULL__null; |
404 | } |
405 | fIsOpen=false; |
406 | } |
407 | } // TStdFileDbgOut::closeDbg |
408 | |
409 | |
410 | // write single line to standard file based output channel |
411 | void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush) |
412 | { |
413 | // if not open, just NOP |
414 | if (fIsOpen) { |
415 | if (fFlushMode==dbgflush_openclose) { |
416 | // we need to open the file for append first |
417 | lockMutex(mutex); |
418 | fFile=FOpen(fFileName.c_str(),"a"); |
419 | if (!fFile) |
420 | unlockMutex(mutex); |
421 | } |
422 | if (fFile) { |
423 | // now output |
424 | fputs(aLine,fFile); |
425 | fputs("\n",fFile); |
426 | |
427 | // do required flushing |
428 | if (fFlushMode==dbgflush_openclose) { |
429 | // we need to close the file after every line of output |
430 | fclose(fFile); |
431 | fFile=NULL__null; |
432 | unlockMutex(mutex); |
433 | } |
434 | else if (aForceFlush || fFlushMode==dbgflush_flush) { |
435 | // simply flush |
436 | fflush(fFile); |
437 | } |
438 | } |
439 | } |
440 | } // TStdFileDbgOut::putLine |
441 | |
442 | |
443 | // write raw data to output file |
444 | void TStdFileDbgOut::putRawData(cAppPointer aData, memSize aSize) |
445 | { |
446 | if (fIsOpen) { |
447 | if (fFlushMode==dbgflush_openclose) { |
448 | // we need to open the file for append first |
449 | lockMutex(mutex); |
450 | fFile=FOpen(fFileName.c_str(),"a"); |
451 | } |
452 | if (fFile) { |
453 | if (fwrite(aData, 1, aSize, fFile) != 1) { |
454 | // error ignored |
455 | } |
456 | } |
457 | // do required flushing |
458 | if (fFlushMode==dbgflush_openclose) { |
459 | // we need to close the file after every line of output |
460 | fclose(fFile); |
461 | fFile=NULL__null; |
462 | unlockMutex(mutex); |
463 | } |
464 | else if (fFlushMode==dbgflush_flush) { |
465 | // simply flush |
466 | fflush(fFile); |
467 | } |
468 | } |
469 | } // TStdFileDbgOut::putRawData |
470 | |
471 | |
472 | #endif |
473 | |
474 | |
475 | // TConsoleDbgOut implementation |
476 | // ----------------------------- |
477 | |
478 | TConsoleDbgOut::TConsoleDbgOut() |
479 | { |
480 | // init |
481 | } // TStdFileDbgOut::TStdFileDbgOut |
482 | |
483 | |
484 | // open standard C file based debug output channel |
485 | bool TConsoleDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode) |
486 | { |
487 | if (fIsOpen) { |
488 | // first close |
489 | closeDbg(); |
490 | } |
491 | // raw mode is not supported |
492 | if (!aRawMode) |
493 | fIsOpen=true; |
494 | // return false if we haven't been successful opening the channel |
495 | return fIsOpen; |
496 | } // TConsoleDbgOut::openDbg |
497 | |
498 | |
499 | // close standard C file based debug channel |
500 | void TConsoleDbgOut::closeDbg(void) |
501 | { |
502 | fIsOpen=false; |
503 | } // TConsoleDbgOut::closeDbg |
504 | |
505 | |
506 | // write single line to standard file based output channel |
507 | void TConsoleDbgOut::putLine(cAppCharP aLine, bool aForceFlush) |
508 | { |
509 | // if not open, just NOP |
510 | if (fIsOpen) { |
511 | CONSOLEPUTS(aLine)SySync_ConsolePrintf(stderr, "SYSYNC " "%s" "\n", (aLine)); |
512 | } |
513 | } // TConsoleDbgOut::putLine |
514 | |
515 | |
516 | |
517 | |
518 | // TDebugLoggerBase implementation |
519 | // ------------------------------- |
520 | |
521 | // constructor |
522 | TDebugLoggerBase::TDebugLoggerBase(GZones *aGZonesP) : |
523 | fGZonesP(aGZonesP) |
524 | { |
525 | fDebugMask=0; |
526 | fDebugEnabled=true; // enabled by default |
527 | fNextDebugMask=0; |
528 | fDbgOutP=NULL__null; |
529 | fDbgOptionsP=NULL__null; |
530 | fIndent=0; |
531 | fBlockHistory=NULL__null; // no Block open yet |
532 | fOutStarted=false; // not yet started |
533 | fBlockNo=0; |
534 | fGZonesP=NULL__null; |
535 | fOutputLoggerP=NULL__null; // no redirected output yet |
536 | } // TDebugLoggerBase::TDebugLoggerBase |
537 | |
538 | |
539 | // destructor |
540 | TDebugLoggerBase::~TDebugLoggerBase() |
541 | { |
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; |
548 | delete bl; |
549 | } |
550 | // get rid of output object |
551 | if (fDbgOutP) delete fDbgOutP; |
552 | fDbgOutP=NULL__null; |
553 | } // TDebugLoggerBase::TDebugLoggerBase |
554 | |
555 | |
556 | // @brief convenience version for getting time |
557 | lineartime_t TDebugLoggerBase::getSystemNowAs(timecontext_t aContext) |
558 | { |
559 | return sysync::getSystemNowAs(aContext,fGZonesP); |
560 | } // TDebugLoggerBase::getSystemNowAs |
561 | |
562 | |
563 | // install outputter |
564 | void TDebugLoggerBase::installOutput(TDbgOut *aDbgOutP) |
565 | { |
566 | // get rid of possibly installed previous outputter |
567 | if (fDbgOutP) delete fDbgOutP; |
568 | fDbgOutP=aDbgOutP; |
569 | } // TDebugLoggerBase::installOutput |
570 | |
571 | |
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) |
575 | { |
576 | // save logger and prefix |
577 | fOutputLoggerP = aDebugLoggerP; |
578 | } // TDebugLoggerBase::outputVia |
579 | |
580 | #if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC) |
581 | extern "C" { |
582 | int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...) = fprintf; |
583 | } |
584 | #endif |
585 | |
586 | // output formatted text |
587 | void TDebugLoggerBase::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs) |
588 | { |
589 | // we need a format and debug not completely off |
590 | if ((getMask() & aDbgMask)==aDbgMask && aFormat) { |
591 | const sInt16 maxmsglen=1024; |
592 | char msg[maxmsglen]; |
593 | msg[0]='\0'; |
594 | // assemble the message string |
595 | vsnprintf(msg, maxmsglen, aFormat, aArgs); |
596 | // write the string |
597 | DebugPuts(TDBG_LOCATION_ARG aDbgMask,msg); |
598 | } |
599 | } // TDebugLoggerBase::DebugVPrintf |
600 | |
601 | |
602 | // helper needed for maintaining old DEBUGPRINTFX() macro syntax |
603 | TDebugLoggerBase &TDebugLoggerBase::setNextMask(uInt32 aDbgMask) |
604 | { |
605 | fNextDebugMask=aDbgMask; |
606 | return *this; |
607 | } // TDebugLoggerBase::setNextMask |
608 | |
609 | |
610 | // like DebugPrintf(), but using mask previously set by setNextMask() |
611 | void TDebugLoggerBase::DebugPrintfLastMask(TDBG_LOCATION_PROTO cAppCharP aFormat, ...) |
612 | { |
613 | va_list args; |
614 | // we need a format and debug not completely off |
615 | if ((getMask() & fNextDebugMask)==fNextDebugMask && aFormat) { |
616 | va_start(args, aFormat)__builtin_va_start(args, aFormat); |
617 | DebugVPrintf(TDBG_LOCATION_ARG fNextDebugMask,aFormat,args); |
618 | va_end(args)__builtin_va_end(args); |
619 | } |
620 | fNextDebugMask=0; |
621 | } // TDebugLoggerBase::DebugPrintfLastMask |
622 | |
623 | |
624 | // output formatted text |
625 | void TDebugLoggerBase::DebugPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, ...) |
626 | { |
627 | va_list args; |
628 | // we need a format and debug not completely off |
629 | if ((getMask() & aDbgMask)==aDbgMask && aFormat) { |
630 | va_start(args, aFormat)__builtin_va_start(args, aFormat); |
631 | DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,args); |
632 | va_end(args)__builtin_va_end(args); |
633 | } |
634 | } // TDebugLoggerBase::DebugVPrintf |
635 | |
636 | |
637 | // open new Block without attribute list |
638 | void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed) |
639 | { |
640 | // we need a format and debug not completely off |
641 | if (getMask() && aBlockName) { |
642 | #ifdef __clang__1 |
643 | #pragma clang diagnostic push |
644 | #pragma clang diagnostic ignored "-Wformat-security" |
645 | #endif |
646 | DebugOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,NULL__null); |
647 | #ifdef __clang__1 |
648 | #pragma clang diagnostic pop |
649 | #endif |
650 | } |
651 | } // TDebugLoggerBase::DebugOpenBlock |
652 | |
653 | |
654 | // open new Block with attribute list, printf style |
655 | void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...) |
656 | { |
657 | va_list args; |
658 | // we need a format and debug not completely off |
659 | if (getMask() && aBlockName) { |
660 | va_start(args, aBlockFmt)__builtin_va_start(args, aBlockFmt); |
661 | DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,aBlockFmt,args); |
662 | va_end(args)__builtin_va_end(args); |
663 | } |
664 | } // TDebugLoggerBase::DebugOpenBlock |
665 | |
666 | |
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, ...) |
669 | { |
670 | va_list args; |
671 | // we need a format and debug not completely off |
672 | if (getMask() && aBlockName) { |
673 | va_start(args, aBlockFmt)__builtin_va_start(args, aBlockFmt); |
674 | DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,false,aBlockFmt,args); |
675 | va_end(args)__builtin_va_end(args); |
676 | } |
677 | } // TDebugLoggerBase::DebugOpenBlockExpanded |
678 | |
679 | |
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, ...) |
682 | { |
683 | va_list args; |
684 | // we need a format and debug not completely off |
685 | if (getMask() && aBlockName) { |
686 | va_start(args, aBlockFmt)__builtin_va_start(args, aBlockFmt); |
687 | DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,true,aBlockFmt,args); |
688 | va_end(args)__builtin_va_end(args); |
689 | } |
690 | } // TDebugLoggerBase::DebugOpenBlockCollapsed |
691 | |
692 | |
693 | #ifdef SYDEBUG_LOCATION |
694 | |
695 | #define MAKEDBGLINK(txt)(txt) dbg2Link(TDBG_LOCATION_ARG txt) |
696 | |
697 | /// turn text into link to source code |
698 | string TDebugLoggerBase::dbg2Link(const TDbgLocation &aTDbgLoc, const string &aTxt) |
699 | { |
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 |
703 | string line; |
704 | |
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); |
711 | line += '"'; |
712 | goto closelink; |
713 | } |
714 | case dbgsource_doxygen: { |
715 | // create link into doxygen |
716 | line = "<a href=\""; |
717 | // replace path with path to Doxygen HTML pages, |
718 | // mangle base name like Doxygen does |
719 | line += fDbgOptionsP->fSourceRootPath; |
720 | line += "/"; |
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++) { |
726 | switch(file[off]) { |
727 | case '_': |
728 | line+="__"; |
729 | break; |
730 | case '.': |
731 | line+="_8"; |
732 | break; |
733 | default: |
734 | line+=file[off]; |
735 | break; |
736 | } |
737 | } |
738 | StringObjAppendPrintf(line,"-source.html#l%05d",aTDbgLoc.fLine); |
739 | line+="\""; |
740 | if (aTDbgLoc.fFunction) { |
741 | line+=" title=\""; |
742 | line+=aTDbgLoc.fFunction; |
743 | line+="\""; |
744 | } |
745 | goto closelink; |
746 | } |
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://"; |
750 | // - create path |
751 | string path = fDbgOptionsP->fSourceRootPath; |
752 | path += aTDbgLoc.fFile; |
753 | // - add path CGI encoded |
754 | line += encodeForCGI(path.c_str()); |
755 | // - add line number |
756 | if (aTDbgLoc.fLine>0) |
757 | StringObjAppendPrintf(line,"&line=%d",aTDbgLoc.fLine); |
758 | line+="\""; |
759 | if (aTDbgLoc.fFunction) { |
760 | line+=" title=\""; |
761 | line+=aTDbgLoc.fFunction; |
762 | line+='"'; |
763 | } |
764 | } |
765 | closelink: { |
766 | line+=">"; |
767 | line+=aTxt; |
768 | line+="</a>"; |
769 | break; |
770 | } |
771 | default: |
772 | line = aTxt; |
773 | } // switch |
774 | // return |
775 | return line; |
776 | } // TDebugLoggerBase::dbg2Link |
777 | |
778 | #else |
779 | |
780 | #define MAKEDBGLINK(txt)(txt) (txt) |
781 | |
782 | #endif // SYDEBUG_LOCATION |
783 | |
784 | #ifdef USE_DLT |
785 | static void DbgText2PlainText(const char *in, size_t len, std::string &out) |
786 | { |
787 | const char *q=in; |
788 | const char *s=q; |
789 | const char *end=in + len; |
790 | |
791 | while (q<end) { |
792 | if (*q=='&') { |
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; |
797 | s=q=q+6; |
798 | while(q+6<=end && strucmp(q,"&html;",6)!=0) q++; |
799 | s=q=q+6; |
800 | } |
801 | else if (end-q>=4 && strucmp(q,"&sp;",4)==0) { |
802 | if (q>s) out.append(s,q-s); |
803 | s=q=q+4; |
804 | out += ' '; // convert to plain space |
805 | } |
806 | else |
807 | q++; |
808 | } |
809 | else { |
810 | q++; |
811 | } |
812 | } |
813 | if (q>s) out.append(s,q-s); |
814 | } |
815 | #endif // USE_DLT |
816 | |
817 | |
818 | // output text to debug channel |
819 | void TDebugLoggerBase::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted) |
820 | { |
821 | // we need a text and debug not completely off |
822 | if (!((getMask() & aDbgMask)==aDbgMask && aText && fDbgOptionsP)) { |
823 | // cannot output |
824 | //#ifdef __MWERKS__ |
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>"); |
829 | //#endif |
830 | } |
831 | else { |
832 | // make sure output is started |
833 | if (!fOutStarted) { |
834 | // try starting output |
835 | DebugStartOutput(); |
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) |
838 | if (!fOutStarted) { |
839 | fDebugEnabled = false; |
840 | return; // stop all efforts here |
841 | } |
842 | } |
843 | |
844 | #ifdef USE_DLT |
845 | // DLT logging logs everything in one chunk |
846 | if (fDbgOptionsP->fOutputFormat == dbgfmt_dlt) { |
847 | DltContext *context = &DbgDefaultContext; |
848 | if (aDbgMask & DBG_PROTO0x00000010) { |
849 | context = &DbgProtoContext; |
850 | } |
851 | else if (aDbgMask & DBG_SESSION0x00000020) { |
852 | context = &DbgSessionContext; |
853 | } |
854 | else if (aDbgMask & DBG_ADMIN0x00000040) { |
855 | context = &DbgAdminContext; |
856 | } |
857 | else if (aDbgMask & DBG_DATA0x00000080) { |
858 | context = &DbgDataContext; |
859 | } |
860 | else if (aDbgMask & DBG_REMOTEINFO0x00000100) { |
861 | context = &DbgRemoteInfoContext; |
862 | } |
863 | else if (aDbgMask & DBG_PARSE0x00000200) { |
864 | context = &DbgParseContext; |
865 | } |
866 | else if (aDbgMask & DBG_GEN0x00000400) { |
867 | context = &DbgGenerateContext; |
868 | } |
869 | else if (aDbgMask & DBG_TRANSP0x00002000) { |
870 | context = &DbgTranspContext; |
871 | } |
872 | else if (aDbgMask & (DBG_RTK_SML0x00100000|DBG_RTK_XPT0x00200000)) { |
873 | context = &DbgSyncMLTKContext; |
874 | } |
875 | |
876 | DltLogLevelType level = DLT_LOG_VERBOSE; |
877 | if (level > DLT_LOG_INFO && |
878 | (aDbgMask & DBG_HOT0x00000001)) { |
879 | level = DLT_LOG_INFO; |
880 | } |
881 | if (level > DLT_LOG_ERROR && |
882 | (aDbgMask & DBG_ERROR0x00000002)) { |
883 | level = DLT_LOG_ERROR; |
884 | } |
885 | if (level > DLT_LOG_DEBUG && |
886 | (aDbgMask & (DBG_USERDATA0x01000000|DBG_PLUGIN0x04000000|DBG_FILTER0x08000000|DBG_CONFLICT0x20000000))) { |
887 | level = DLT_LOG_DEBUG; |
888 | } |
889 | if (level < DLT_LOG_VERBOSE && |
890 | (aDbgMask & DBG_DETAILS0x40000000)) { |
891 | level = (DltLogLevelType)((int)level + 1); |
892 | } |
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. |
897 | string buffer; |
898 | buffer.reserve(aTextSize); |
899 | DbgText2PlainText(aText, aTextSize ? aTextSize : strlen(aText), buffer); |
900 | DLT_LOG(*context, level, DLT_STRING(buffer.c_str())); |
901 | } else { |
902 | // Fast path: log directly. |
903 | DLT_LOG(*context, level, DLT_STRING(aText)); |
904 | } |
905 | return; |
906 | } |
907 | #endif // USE_DLT |
908 | |
909 | // dissect into lines |
910 | cAppCharP end=aTextSize ? aText+aTextSize : NULL__null; |
911 | bool firstLine=true; |
912 | // check for preformatted message |
913 | bool pre=strnncmp(aText,"⪯",5)==0; |
914 | if (pre) aText+=5; |
915 | pre = pre || aPreFormatted; |
916 | // now process text |
917 | while ((!end || aText<end) && *aText) { |
918 | // search for line end or end of string |
919 | cAppCharP p=aText; |
920 | while ((!end || p<end) && *p && *p!='\n' && *p!='\r') p++; |
921 | // output this line, properly formatted |
922 | string line; |
923 | line.erase(); |
924 | cAppCharP q,s; |
925 | string ts; |
926 | switch (fDbgOptionsP->fOutputFormat) { |
927 | // HTML |
928 | case dbgfmt_html: |
929 | // prefix first line with <li>, second and further with <br/> |
930 | if (firstLine) { |
931 | line="<li>"; |
932 | // add timestamp if needed for every line |
933 | if ( |
934 | fDbgOptionsP->fTimestampForAll |
935 | || fDbgOptionsP->fThreadIDForAll |
936 | #ifdef SYDEBUG_LOCATION |
937 | || fDbgOptionsP->fSourceLinkMode!=dbgsource_none |
938 | #endif |
939 | ) { |
940 | string prefix; |
941 | prefix = "<i>["; |
942 | #ifdef MULTI_THREAD_SUPPORT1 |
943 | if (fDbgOptionsP->fThreadIDForAll) { |
944 | StringObjAppendPrintf(prefix,"%09lu",myThreadID()); |
945 | if (fDbgOptionsP->fTimestampForAll) prefix += ", "; |
946 | } |
947 | #endif |
948 | if (fDbgOptionsP->fTimestampForAll) { |
949 | StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)))); |
950 | prefix += ts; |
951 | } |
952 | #ifdef SYDEBUG_LOCATION |
953 | else if (!fDbgOptionsP->fThreadIDForAll) { |
954 | // neither threadID nor timestamp, but source requested -> put small text here |
955 | prefix += "src"; |
956 | } |
957 | #endif |
958 | prefix+="]</i> "; |
959 | // if we have links into source code, add it here |
960 | line += MAKEDBGLINK(prefix)(prefix); |
961 | } |
962 | // colorize some messages |
963 | string cl=""; |
964 | // colors, not mixable, most relevant first |
965 | if (aDbgMask & DBG_ERROR0x00000002) { |
966 | cl="error"; |
967 | } |
968 | else if (aDbgMask & DBG_EXOTIC0x80000000) { |
969 | cl="exotic"; |
970 | } |
971 | else if (aDbgMask & DBG_SCRIPTS0x00001000) { |
972 | cl="script"; |
973 | } |
974 | else if (aDbgMask & DBG_PLUGIN0x04000000) { |
975 | cl="plugin"; |
976 | } |
977 | else if (aDbgMask & DBG_DBAPI0x02000000) { |
978 | cl="dbapi"; |
979 | } |
980 | else if (aDbgMask & DBG_CONFLICT0x20000000) { |
981 | cl="conflict"; |
982 | } |
983 | else if (aDbgMask & DBG_MATCH0x10000000) { |
984 | cl="match"; |
985 | } |
986 | else if (aDbgMask & DBG_REMOTEINFO0x00000100) { |
987 | cl="remote"; |
988 | } |
989 | else if (aDbgMask & DBG_PROTO0x00000010) { |
990 | cl="proto"; |
991 | } |
992 | else if (aDbgMask & DBG_FILTER0x08000000) { |
993 | cl="filter"; |
994 | } |
995 | else if (aDbgMask & DBG_PARSE0x00000200) { |
996 | cl="incoming"; |
997 | } |
998 | else if (aDbgMask & DBG_GEN0x00000400) { |
999 | cl="outgoing"; |
1000 | } |
1001 | else if (aDbgMask & DBG_REST0x00008000) { |
1002 | cl="rest"; |
1003 | } |
1004 | // apply basic color style |
1005 | if (!cl.empty()) { |
1006 | line+="<span class=\""; line+=cl; line+="\">"; |
1007 | } |
1008 | // aditional style modifiers that can be combined with colors |
1009 | if (aDbgMask & DBG_HOT0x00000001) { |
1010 | if (cl.empty()) |
1011 | line+="<span class=\"hotalone\">"; |
1012 | else |
1013 | line+="<span class=\"hot\">"; |
1014 | } |
1015 | // start preformatted if selected |
1016 | if (pre) |
1017 | line+="<pre>"; |
1018 | } |
1019 | else { |
1020 | if (!pre) line="<br/>"; |
1021 | } |
1022 | goto xmlize; |
1023 | // XML, just output and replace special chars as needed |
1024 | case dbgfmt_xml: |
1025 | if (firstLine) { |
1026 | #ifdef MULTI_THREAD_SUPPORT1 |
1027 | if (fDbgOptionsP->fThreadIDForAll) { |
1028 | line+="<thread>"; |
1029 | StringObjAppendPrintf(line,"%09lu",myThreadID()); |
1030 | line+="</thread>"; |
1031 | } |
1032 | #endif |
1033 | if (fDbgOptionsP->fTimestampForAll) { |
1034 | StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)))); |
1035 | line+="<time>"; |
1036 | line+=ts; |
1037 | line+="</time>"; |
1038 | } |
1039 | DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size()); |
1040 | line.erase(); |
1041 | } |
1042 | if (fDbgOptionsP->fSeparateMsgs) { |
1043 | line+="<msg>"; |
1044 | } |
1045 | xmlize: |
1046 | q=aText; |
1047 | s=q; |
1048 | while (q<p) { |
1049 | if (*q=='&') { |
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; |
1054 | s=q=q+6; |
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) |
1058 | line.append(s,q-s); |
1059 | s=q=q+6; |
1060 | } |
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) |
1065 | line += " "; |
1066 | else |
1067 | line += ' '; |
1068 | s=q=q+4; // skip &sp; |
1069 | } |
1070 | else { |
1071 | if (q>s) line.append(s,q-s); |
1072 | line+="&"; |
1073 | s=++q; |
1074 | } |
1075 | } |
1076 | else if (*q=='<') { |
1077 | if (q>s) line.append(s,q-s); |
1078 | line+="<"; |
1079 | s=++q; |
1080 | } |
1081 | else if (*q=='>') { |
1082 | if (q>s) line.append(s,q-s); |
1083 | line+=">"; |
1084 | s=++q; |
1085 | } |
1086 | else { |
1087 | q++; |
1088 | } |
1089 | } |
1090 | if (q>s) line.append(s,q-s); |
1091 | if (fDbgOptionsP->fSeparateMsgs && fDbgOptionsP->fOutputFormat==dbgfmt_xml) { |
1092 | line+="</msg>"; |
1093 | } |
1094 | break; |
1095 | // plain text |
1096 | default: |
1097 | case dbgfmt_text: |
1098 | q=aText; |
1099 | s=q; |
1100 | while (q<p) { |
1101 | if (*q=='&') { |
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; |
1106 | s=q=q+6; |
Value stored to 's' is never read | |
1107 | while(*q && strucmp(q,"&html;",6)!=0) q++; |
1108 | s=q=q+6; |
1109 | } |
1110 | else if (strucmp(q,"&sp;",4)==0) { |
1111 | if (q>s) line.append(s,q-s); |
1112 | s=q=q+4; |
1113 | line += ' '; // convert to plain space |
1114 | } |
1115 | else |
1116 | q++; |
1117 | } |
1118 | else { |
1119 | q++; |
1120 | } |
1121 | } |
1122 | if (q>s) line.append(s,q-s); |
1123 | break; |
1124 | } // switch text output |
1125 | firstLine=false; |
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)) { |
1129 | // end preformatted |
1130 | if (pre) |
1131 | line+="</pre>"; |
1132 | // colors |
1133 | if (aDbgMask & ( |
1134 | DBG_ERROR0x00000002 | |
1135 | DBG_SCRIPTS0x00001000 | |
1136 | DBG_REST0x00008000 | |
1137 | DBG_EXOTIC0x80000000 | |
1138 | DBG_DBAPI0x02000000 | |
1139 | DBG_PLUGIN0x04000000 | |
1140 | DBG_PARSE0x00000200 | |
1141 | DBG_GEN0x00000400 | |
1142 | DBG_CONFLICT0x20000000 | |
1143 | DBG_MATCH0x10000000 | |
1144 | DBG_REMOTEINFO0x00000100 | |
1145 | DBG_PROTO0x00000010 | |
1146 | DBG_FILTER0x08000000 |
1147 | )) { |
1148 | line+="</span>"; // end special style |
1149 | } |
1150 | // HOT modifier |
1151 | if (aDbgMask & ( |
1152 | DBG_HOT0x00000001 |
1153 | )) { |
1154 | line+="</span>"; // end special style |
1155 | } |
1156 | line+="</li>"; // we need to close the list entry |
1157 | } |
1158 | DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size(),pre); |
1159 | // next line, if any |
1160 | aText=p; |
1161 | } // loop until all text done |
1162 | } |
1163 | } // TDebugLoggerBase::DebugPuts |
1164 | |
1165 | |
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) |
1168 | { |
1169 | if (!fDbgOptionsP) |
1170 | return; |
1171 | if (fDbgOptionsP->fFoldingMode==dbgfold_collapsed) |
1172 | aCollapsed=true; |
1173 | else if (fDbgOptionsP->fFoldingMode==dbgfold_expanded) |
1174 | aCollapsed=false; |
1175 | if (getMask() && aBlockName && fDbgOptionsP) { |
1176 | // make sure output is started |
1177 | if (!fOutStarted) DebugStartOutput(); |
1178 | // create Block line on current indent level |
1179 | string bl; |
1180 | string ts; |
1181 | // - preamble, possibly with timestamp |
1182 | bool withTime = fDbgOptionsP->fTimestampStructure; |
1183 | if (withTime) |
1184 | StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)))); |
1185 | switch (fDbgOptionsP->fOutputFormat) { |
1186 | // XML |
1187 | case dbgfmt_xml: |
1188 | bl="<"; bl+=aBlockName; |
1189 | if (withTime) { |
1190 | bl+=" time=\"" + ts + "\""; |
1191 | } |
1192 | if (aBlockTitle) { |
1193 | bl+=" title=\""; |
1194 | bl+=aBlockTitle; |
1195 | bl+="\""; |
1196 | } |
1197 | break; |
1198 | // HTML |
1199 | case dbgfmt_html: |
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()) |
1206 | ); |
1207 | } |
1208 | StringObjAppendPrintf(bl,"<a name=\"H%ld\">", long(getBlockNo())); |
1209 | if (withTime) { |
1210 | bl += MAKEDBGLINK(string("[") + ts + "] ")(string("[") + ts + "] "); |
1211 | } |
1212 | #ifdef SYDEBUG_LOCATION |
1213 | else if (fDbgOptionsP->fSourceLinkMode!=dbgsource_none) { |
1214 | bl += MAKEDBGLINK(string("[src] "))(string("[src] ")); |
1215 | } |
1216 | #endif |
1217 | bl+="'"; |
1218 | bl+=aBlockName; |
1219 | bl+="'"; |
1220 | if (aBlockTitle) { |
1221 | bl+=" - "; |
1222 | bl+=aBlockTitle; |
1223 | } |
1224 | bl+="</a></span><span class=\"attribute\">"; |
1225 | break; |
1226 | // plain text |
1227 | default: |
1228 | case dbgfmt_text: |
1229 | bl.erase(); |
1230 | if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway |
1231 | bl+="[" + ts + "] "; |
1232 | } |
1233 | bl+=aBlockName; |
1234 | if (aBlockTitle) { |
1235 | bl+=" - "; |
1236 | bl+=aBlockTitle; |
1237 | } |
1238 | break; |
1239 | } // switch preamble |
1240 | // - attributes |
1241 | if (aBlockFmt) { |
1242 | // first expand all printf parameters |
1243 | string attrs; |
1244 | vStringObjPrintf(attrs,aBlockFmt,true,aArgs); |
1245 | // isolate |-separated attribute format strings |
1246 | cAppCharP q,r,s,p=attrs.c_str(); |
1247 | while (*p) { |
1248 | // search for beginning of value |
1249 | q=p; |
1250 | while(*q && *q!='=' && *q!='|') q++; |
1251 | // search for end of value |
1252 | r=q; |
1253 | s=q; // in case we don't have a = |
1254 | if (*q=='=') { |
1255 | s=q+1; |
1256 | r=s; |
1257 | while (*r && *r!='|') r++; |
1258 | } |
1259 | // now: p=start of attrname, q=end of attrname |
1260 | // s=start of value, r=end of value |
1261 | // output an attribute now |
1262 | if (q>p && r>s) { |
1263 | switch (fDbgOptionsP->fOutputFormat) { |
1264 | // XML |
1265 | case dbgfmt_xml: |
1266 | bl+=" "; |
1267 | bl.append(p,q-p); |
1268 | bl+="=\""; |
1269 | bl.append(s,r-s); |
1270 | bl+="\""; |
1271 | break; |
1272 | case dbgfmt_html: |
1273 | bl+=", "; |
1274 | bl.append(p,q-p); |
1275 | bl+="=<span class=\"attrval\">"; |
1276 | bl.append(s,r-s); |
1277 | bl+="</span>"; |
1278 | break; |
1279 | case dbgfmt_text: |
1280 | default: |
1281 | bl+=", "; |
1282 | bl.append(p,q-p); |
1283 | bl+="="; |
1284 | bl.append(s,r-s); |
1285 | break; |
1286 | } // switch attribute |
1287 | } // non-empty attribute |
1288 | // more attributes to come? |
1289 | if (*r=='|') r++; // skip separator |
1290 | p=r; |
1291 | } // while |
1292 | } // attributes present |
1293 | // - finalize Block |
1294 | switch (fDbgOptionsP->fOutputFormat) { |
1295 | // XML |
1296 | case dbgfmt_xml: |
1297 | bl+=">"; |
1298 | break; |
1299 | // HTML |
1300 | case dbgfmt_html: |
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()) |
1306 | ); |
1307 | } |
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)); |
1313 | } |
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\">", |
1318 | long(getBlockNo()), |
1319 | aCollapsed ? "none" : "inline" |
1320 | ); |
1321 | } |
1322 | bl+="<ul>"; // now start list for block's contents |
1323 | break; |
1324 | // plain text |
1325 | default: |
1326 | case dbgfmt_text: |
1327 | break; |
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) |
1332 | fIndent++; |
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 |
1340 | } |
1341 | } // TDebugLoggerBase::DebugVOpenBlock |
1342 | |
1343 | |
1344 | // close named Block. If no name given, topmost Block will be closed |
1345 | void TDebugLoggerBase::DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName) |
1346 | { |
1347 | if (fOutStarted && getMask() && fDbgOptionsP && fBlockHistory) { |
1348 | if (aBlockName==NULL__null) { |
1349 | #if SYDEBUG2>1 |
1350 | internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),"Block Nest Warning: Missing Block name at close"); |
1351 | #else |
1352 | internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),NULL__null); |
1353 | #endif |
1354 | } |
1355 | else { |
1356 | internalCloseBlocks(TDBG_LOCATION_ARG aBlockName,NULL__null); |
1357 | } |
1358 | } |
1359 | } // TDebugLoggerBase::DebugCloseBlock |
1360 | |
1361 | |
1362 | |
1363 | // internal helper used to close all or some Blocks |
1364 | void TDebugLoggerBase::internalCloseBlocks(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aCloseComment) |
1365 | { |
1366 | if (!fDbgOptionsP) return; // security |
1367 | bool withTime = fDbgOptionsP->fTimestampStructure; |
1368 | string comment; |
1369 | #if SYDEBUG2>1 |
1370 | if (!fBlockHistory && aBlockName) { |
1371 | // no blocks open any more and not close-all-remaining call (log close...) |
1372 | DebugPrintf(TDBG_LOCATION_ARG DBG_EXOTIC0x80000000+DBG_ERROR0x00000002,"Block Nest Warning: Trying to close block '%s', but no block is open",aBlockName); |
1373 | } |
1374 | #endif |
1375 | while (fBlockHistory) { |
1376 | // prepare comment |
1377 | comment.erase(); |
1378 | if (aCloseComment) { |
1379 | comment += " - "; |
1380 | comment += aCloseComment; |
1381 | } |
1382 | // check if closing top-of-stack Block now |
1383 | bool found= |
1384 | (aBlockName && strucmp(aBlockName,fBlockHistory->fBlockName.c_str())==0); |
1385 | if (!found && fBlockHistory->fNext==NULL__null) { |
1386 | // last Block always counts as "found"... |
1387 | found = true; |
1388 | #if SYDEBUG2>1 |
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()); |
1391 | #endif |
1392 | } |
1393 | // now close topmost Block |
1394 | string ts,bl; |
1395 | // - get time if needed and possibly put it within indented block |
1396 | if (withTime) { |
1397 | StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)))); |
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 |
1402 | } |
1403 | } |
1404 | // - now unindent |
1405 | if (fIndent>0) fIndent--; |
1406 | // - then create closing Block |
1407 | #if SYDEBUG2>1 |
1408 | if (!found) StringObjAppendPrintf(comment," - Block Nest Warning: implicitly closed (by explicitly closing '%s')",aBlockName ? aBlockName : "<unknown parent>"); |
1409 | #endif |
1410 | switch (fDbgOptionsP->fOutputFormat) { |
1411 | // XML |
1412 | case dbgfmt_xml: |
1413 | bl="</"; |
1414 | bl+=fBlockHistory->fBlockName; |
1415 | bl+=">"; |
1416 | if (!comment.empty()) { |
1417 | bl+=" <!-- "; |
1418 | bl+=comment; |
1419 | bl+=" -->"; |
1420 | } |
1421 | break; |
1422 | // HTML |
1423 | case dbgfmt_html: |
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) |
1429 | ); |
1430 | } |
1431 | StringObjAppendPrintf(bl,"<a name=\"F%ld\">",long(fBlockHistory->fBlockNo)); |
1432 | if (withTime) { |
1433 | bl += MAKEDBGLINK(string("[") + ts + "] ")(string("[") + ts + "] "); |
1434 | } |
1435 | bl += "End of '"; |
1436 | bl+=fBlockHistory->fBlockName; |
1437 | bl+="'"; |
1438 | bl+=comment; |
1439 | bl+="</a></span>"; |
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)); |
1445 | } |
1446 | if (fDbgOptionsP->fFoldingMode!=dbgfold_none) { |
1447 | bl+="</div>"; // end of folding division |
1448 | } |
1449 | bl+="</li>"; // end of list entry containing entire block |
1450 | break; |
1451 | // plain text |
1452 | default: |
1453 | case dbgfmt_text: |
1454 | bl.erase(); |
1455 | if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway |
1456 | bl+="[" + ts + "] "; |
1457 | } |
1458 | bl+="End of '"; |
1459 | bl+=fBlockHistory->fBlockName; |
1460 | bl+="'"; |
1461 | bl+=comment; |
1462 | break; |
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; |
1469 | delete closedLevel; |
1470 | // if we have found the Block, exit here |
1471 | if (found) break; |
1472 | } |
1473 | } // TDebugLoggerBase::internalCloseBlocks |
1474 | |
1475 | #ifdef USE_DLT |
1476 | static void RegisterContext(DltContext *aHandle, const char *aContextID, const char *aDescription) |
1477 | { |
1478 | std::string envName = "LIBSYNTHESIS_"; |
1479 | envName += aContextID; |
1480 | const char *value = getenv(envName.c_str()); |
1481 | if (value) { |
1482 | // Explicit level. |
1483 | DltLogLevelType level = (DltLogLevelType)atoi(value); |
1484 | dlt_register_context_ll_ts(aHandle, aContextID, aDescription, level, DLT_TRACE_STATUS_OFF); |
1485 | } else { |
1486 | // Default level. |
1487 | dlt_register_context(aHandle, aContextID, aDescription); |
1488 | } |
1489 | } |
1490 | #endif // USE_DLT |
1491 | |
1492 | // start debugging output if needed and sets fOutStarted |
1493 | bool TDebugLoggerBase::DebugStartOutput(void) |
1494 | { |
1495 | if (!fOutStarted) { |
1496 | #ifdef USE_DLT |
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; |
1511 | } |
1512 | fOutStarted = true; |
1513 | } |
1514 | else |
1515 | #endif // USE_DLT |
1516 | if (fOutputLoggerP) { |
1517 | // using another logger, call it to start output |
1518 | fOutStarted = fOutputLoggerP->DebugStartOutput(); |
1519 | if (fOutStarted) { |
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 |
1524 | } |
1525 | } |
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( |
1529 | fDbgPath.c_str(), |
1530 | DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat], |
1531 | fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix ? dbgflush_openclose : fDbgOptionsP->fFlushMode, |
1532 | !fDbgOptionsP->fAppend |
1533 | )) { |
1534 | // make sure we don't recurse when we produce some output |
1535 | fOutStarted = true; |
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); |
1547 | } |
1548 | } // debug channel opened successfully |
1549 | } // use own debug channel |
1550 | } // environment ready to start output |
1551 | return fOutStarted; |
1552 | } // TDebugLoggerBase::DebugStartOutput |
1553 | |
1554 | |
1555 | // @brief finalize debugging output (close Blocks, close output channel) |
1556 | void TDebugLoggerBase::DebugFinalizeOutput(void) |
1557 | { |
1558 | if (fOutputLoggerP) { |
1559 | // just close my own blocks |
1560 | internalCloseBlocks(TDBG_LOCATION_NONE NULL__null,"closed because sub-log ends here"); |
1561 | } |
1562 | if (fOutStarted && fDbgOptionsP && fDbgOutP) { |
1563 | // close all left-open open Blocks |
1564 | internalCloseBlocks(TDBG_LOCATION_NONE NULL__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) |
1569 | // - then suffix |
1570 | DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomSuffix.empty() ? DbgOutDefaultSuffixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomSuffix.c_str()); |
1571 | // now close the debug channel |
1572 | fDbgOutP->closeDbg(); |
1573 | } |
1574 | // whatever happened, we are not started any more |
1575 | fOutStarted=false; |
1576 | } // TDebugLoggerBase::DebugFinalizeOutput |
1577 | |
1578 | |
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) |
1581 | { |
1582 | #ifdef USE_DLT |
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. |
1587 | if (aText) { |
1588 | if (aTextSize > 0 && aTextSize < strlen(aText)) { |
1589 | string buffer(aText, aTextSize); |
1590 | DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(buffer.c_str())); |
1591 | } else { |
1592 | DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(aText)); |
1593 | } |
1594 | } |
1595 | return; |
1596 | } |
1597 | #endif // USE_DLT |
1598 | |
1599 | if (!aText || (!fDbgOutP && !fOutputLoggerP)) return; |
1600 | if (*aText) { |
1601 | // not an empty line |
1602 | string msg; |
1603 | msg.erase(); |
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 |
1607 | string ts; |
1608 | StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)))); |
1609 | msg='['; |
1610 | msg+=ts; |
1611 | msg+="] "; |
1612 | } |
1613 | // Indent if selected |
1614 | if (fDbgOptionsP && !fDbgOptionsP->fIndentString.empty() && !(fDbgOptionsP->fOutputFormat==dbgfmt_html && aPre)) { |
1615 | // with indent |
1616 | for (uInt16 n=0; n<fIndent; n++) { |
1617 | msg+=fDbgOptionsP->fIndentString; |
1618 | } |
1619 | } |
1620 | // add message itself |
1621 | if (aTextSize) |
1622 | msg.append(aText,aTextSize); |
1623 | else |
1624 | msg.append(aText); |
1625 | // now output |
1626 | if (fOutputLoggerP) { |
1627 | // use parent's output |
1628 | fOutputLoggerP->fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now |
1629 | } |
1630 | else { |
1631 | // use my own output channel |
1632 | fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now |
1633 | } |
1634 | } |
1635 | } // TDebugLoggerBase::DebugPutLine |
1636 | |
1637 | |
1638 | // TDebugLogger implementation |
1639 | // --------------------------- |
1640 | |
1641 | // constructor |
1642 | TDebugLogger::TDebugLogger(GZones *aGZonesP) : |
1643 | inherited(aGZonesP) |
1644 | { |
1645 | #ifdef MULTI_THREAD_SUPPORT1 |
1646 | fMainThreadID=0; |
1647 | fSubThreadLogs=NULL__null; |
1648 | fSilentLoggerP=NULL__null; |
1649 | #endif |
1650 | } // TDebugLogger::TDebugLogger |
1651 | |
1652 | |
1653 | // destructor |
1654 | TDebugLogger::~TDebugLogger() |
1655 | { |
1656 | #ifdef MULTI_THREAD_SUPPORT1 |
1657 | // remove subthread loggers |
1658 | TSubThreadLog* subThreadP = fSubThreadLogs; |
1659 | fSubThreadLogs = NULL__null; |
1660 | while (subThreadP) { |
1661 | // delete logger if any |
1662 | if (subThreadP->fSubThreadLogger) { |
1663 | SYSYNC_TRYtry { |
1664 | delete subThreadP->fSubThreadLogger; |
1665 | } |
1666 | SYSYNC_CATCH(...)catch(...) { |
1667 | // nop |
1668 | SYSYNC_ENDCATCH} |
1669 | } |
1670 | TSubThreadLog* delP = subThreadP; |
1671 | subThreadP = subThreadP->fNext; |
1672 | delete delP; |
1673 | } |
1674 | // |
1675 | if (fSilentLoggerP) { |
1676 | delete fSilentLoggerP; |
1677 | fSilentLoggerP = NULL__null; |
1678 | } |
1679 | #endif |
1680 | } // TDebugLogger::~TDebugLogger |
1681 | |
1682 | |
1683 | #ifdef MULTI_THREAD_SUPPORT1 |
1684 | |
1685 | void TDebugLogger::setOptions(const TDbgOptions *aDbgOptionsP) |
1686 | { |
1687 | TDebugLoggerBase::setOptions(aDbgOptionsP); |
1688 | TSubThreadLog* subThreadP = fSubThreadLogs; |
1689 | while (subThreadP) { |
1690 | if (subThreadP->fSubThreadLogger) { |
1691 | subThreadP->fSubThreadLogger->setOptions(aDbgOptionsP); |
1692 | } |
1693 | subThreadP = subThreadP->fNext; |
1694 | } |
1695 | } |
1696 | |
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) |
1702 | { |
1703 | TSubThreadLog* subThreadP = fSubThreadLogs; |
1704 | TSubThreadLog** subThreadLinkPP = &fSubThreadLogs; |
1705 | while (subThreadP) { |
1706 | if (subThreadP->fThreadID == aThreadID) { |
1707 | if (aAndRemove) { |
1708 | // bridge previous with next in one single assignment (i.e. thread safe) |
1709 | *subThreadLinkPP = subThreadP->fNext; |
1710 | } |
1711 | // return found record (note that it MUST BE DELETED by caller if no longer used) |
1712 | return subThreadP; |
1713 | } |
1714 | subThreadLinkPP = &subThreadP->fNext; |
1715 | subThreadP = *subThreadLinkPP; |
1716 | } |
1717 | return NULL__null; // none found |
1718 | } // TDebugLogger::findSubThread |
1719 | |
1720 | |
1721 | /// @brief find or create logger for subthread |
1722 | TDebugLoggerBase *TDebugLogger::getThreadLogger(bool aCreateNew) |
1723 | { |
1724 | if (fOutputLoggerP) { |
1725 | TDebugLoggerBase *logger = fOutputLoggerP->getThreadLogger(aCreateNew); |
1726 | if (logger) |
1727 | return logger; |
1728 | } |
1729 | |
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! |
1735 | return this; |
1736 | } |
1737 | TSubThreadLog* subThreadP = findSubThread(threadID); |
1738 | if (subThreadP) { |
1739 | // we know this subthread, return its logger |
1740 | return subThreadP->fSubThreadLogger; // can be NULL if subthread logging is disabled |
1741 | } |
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. |
1748 | return this; |
1749 | } |
1750 | // new subthread, create entry in list |
1751 | if (aCreateNew) { |
1752 | string s; |
1753 | // create new entry |
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__null); |
1765 | // - same options |
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()); |
1774 | break; |
1775 | case dbgsubthread_suppress: |
1776 | default: |
1777 | // no output from subthreads |
1778 | subThreadP->fSubThreadLogger=NULL__null; // no logger |
1779 | break; |
1780 | } |
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; |
1785 | } |
1786 | return NULL__null; // no logger for this thread |
1787 | } // TDebugLogger::getThreadLogger |
1788 | |
1789 | |
1790 | // helper needed for maintaining old DEBUGPRINTFX() macro syntax |
1791 | TDebugLoggerBase &TDebugLogger::setNextMask(uInt32 aDbgMask) |
1792 | { |
1793 | TDebugLoggerBase *loggerP = getThreadLogger(); |
1794 | if (loggerP) { |
1795 | // return pointer to loggerbase whose DebugPrintfLastMask() must be called |
1796 | return loggerP->inherited::setNextMask(aDbgMask); |
1797 | } |
1798 | else { |
1799 | // we have no logger but still need to return something |
1800 | if (!fSilentLoggerP) { |
1801 | fSilentLoggerP = new TDebugLoggerBase(fGZonesP); |
1802 | fSilentLoggerP->setEnabled(false); |
1803 | } |
1804 | fSilentLoggerP->setNextMask(DBG_ERROR0x00000002); // must set non-zero to make sure it is NOT output! |
1805 | return *fSilentLoggerP; |
1806 | } |
1807 | } // TDebugLoggerBase::setNextMask |
1808 | |
1809 | |
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) |
1812 | { |
1813 | TDebugLoggerBase *loggerP = getThreadLogger(); |
1814 | if (loggerP) loggerP->inherited::DebugPuts(TDBG_LOCATION_ARG aDbgMask,aText,aTextSize,aPreFormatted); |
1815 | } // TDebugLogger::DebugPuts |
1816 | |
1817 | |
1818 | void TDebugLogger::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs) |
1819 | { |
1820 | TDebugLoggerBase *loggerP = getThreadLogger(); |
1821 | if (loggerP) loggerP->inherited::DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,aArgs); |
1822 | } // TDebugLogger::DebugVPrintf |
1823 | |
1824 | |
1825 | void TDebugLogger::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs) |
1826 | { |
1827 | TDebugLoggerBase *loggerP = getThreadLogger(); |
1828 | if (loggerP) loggerP->inherited::DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName, aBlockTitle, aCollapsed, aBlockFmt, aArgs); |
1829 | } // TDebugLogger::DebugVOpenBlock |
1830 | |
1831 | |
1832 | void TDebugLogger:: DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName) |
1833 | { |
1834 | TDebugLoggerBase *loggerP = getThreadLogger(); |
1835 | if (loggerP) loggerP->inherited::DebugCloseBlock(TDBG_LOCATION_ARG aBlockName); |
1836 | } // TDebugLogger::DebugCloseBlock |
1837 | |
1838 | #endif |
1839 | |
1840 | |
1841 | // output all buffered subthread's output in a special subthread Block in the main output |
1842 | void TDebugLogger::DebugShowSubThreadOutput(void) |
1843 | { |
1844 | #ifdef MULTI_THREAD_SUPPORT1 |
1845 | // nop as long mixed-block mode is not implemented |
1846 | #endif |
1847 | } // TDebugLogger::DebugShowSubThreadOutput |
1848 | |
1849 | |
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) |
1855 | { |
1856 | #ifdef MULTI_THREAD_SUPPORT1 |
1857 | uIntArch threadID = myThreadID(); |
1858 | if (threadID==fMainThreadID) { |
1859 | // current main thread done |
1860 | fMainThreadID = 0; |
1861 | } |
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) |
1864 | if (aRemoveIt) { |
1865 | TSubThreadLog* tP = findSubThread(threadID,true); |
1866 | if (tP) { |
1867 | if (tP->fSubThreadLogger) { |
1868 | SYSYNC_TRYtry { |
1869 | delete tP->fSubThreadLogger; |
1870 | } |
1871 | SYSYNC_CATCH(...)catch(...) { |
1872 | // nop |
1873 | SYSYNC_ENDCATCH} |
1874 | } |
1875 | delete tP; |
1876 | } |
1877 | } |
1878 | #endif |
1879 | } // TDebugLogger::DebugThreadOutputDone |
1880 | |
1881 | |
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) |
1885 | { |
1886 | #ifdef MULTI_THREAD_SUPPORT1 |
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__null) { |
1897 | // this is not a new thread, but a known subthread, can't get main thread now |
1898 | return; // no further op |
1899 | } |
1900 | else { |
1901 | // this is not a known subthread, so it can become the main thread |
1902 | fMainThreadID = threadID; |
1903 | return; // done |
1904 | } |
1905 | } |
1906 | else { |
1907 | // cannot become main thread, will be treated as subthread if it generates output |
1908 | // - no op required |
1909 | } |
1910 | #endif |
1911 | } // TDebugLogger::DebugDefineMainThread |
1912 | |
1913 | |
1914 | } // namespace sysync |
1915 | |
1916 | #endif // SYDEBUG |
1917 | |
1918 | // eof |
1919 |