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
19 #include "generic_types.h"
20 #include "platform_mutex.h"
21 #include "platform_thread.h"
23 #include "sysync_noncopyable.h"
28 /// @brief Debug output formats
30 dbgfmt_text, ///< plain text format (but can be indented)
31 dbgfmt_xml, ///< XML format
32 dbgfmt_html, ///< HTML format
34 dbgfmt_dlt, ///< GENIVI Diagnostic Log and Trace
39 /// @brief HTML dynamic folding
41 dbgfold_none, ///< do not include dynamic folding into HTML logs
42 dbgfold_collapsed, ///< include folding - all collapsed by default
43 dbgfold_expanded, ///< include folding - all expanded by default
44 dbgfold_auto, ///< include folding - collapse/expand state predefined on a block-by-block basis
48 /// @brief Debug flush modes
50 dbgflush_none, ///< no flush, keep open as long as possible
51 dbgflush_flush, ///< flush every debug message
52 dbgflush_openclose, ///< open and close debug channel separately for every message (as in 2.x engine)
56 /// @brief Debug subthread logging modes
58 dbgsubthread_none, ///< do not handle output from subthread specially
59 dbgsubthread_suppress, ///< suppress output from subthreads
60 dbgsubthread_separate, ///< create separate output stream (=file) for each subthread
61 dbgsubthread_linemix, ///< mix output on a line by line basis (forcing output to slow openclose mode)
62 dbgsubthread_bufferandmix, ///< buffer thread's output and mix it into main stream when appropriate
67 /// @brief HTML linking into source code
69 dbgsource_none, ///< do not include links into source code in HTML logs
70 dbgsource_hint, ///< no links, but info about what file/line number the message comes from
71 dbgsource_doxygen, ///< include link into doxygen prepared HTML version of source code
72 dbgsource_txmt, ///< include txmt:// link (understood by TextMate and BBEdit) into source code
77 #ifndef HARDCODED_CONFIG
78 extern cAppCharP const DbgOutFormatNames[numDbgOutFormats];
79 extern cAppCharP const DbgFoldingModeNames[numDbgFoldingModes];
80 extern cAppCharP const DbgFlushModeNames[numDbgFlushModes];
81 extern cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes];
82 extern cAppCharP const DbgSourceModeNames[numDbgSourceModes];
84 extern cAppCharP const DbgOutFormatExtensions[numDbgOutFormats];
86 /// @brief Debug options container
94 TDbgOutFormats fOutputFormat; ///< format
95 string fIndentString; ///< indent string
96 string fCustomPrefix; ///< custom prefix (different xml header or html with different styles for example)
97 string fCustomSuffix; ///< custom suffix (should match prefix)
98 string fBasename; ///< the initial part of the log file name, can override the hard-coded TARGETID (empty if unset)
99 bool fSeparateMsgs; ///< separate message lines (needed especially in XML to avoid unformatted PCDATA block)
100 bool fTimestampStructure; ///< include timestamp for structure elements (blocks)
101 bool fTimestampForAll; ///< include timestamp information for every message
102 bool fThreadIDForAll; ///< include thread ID information for every message
103 TDbgFlushModes fFlushMode; ///< how and when to flush
104 TDbgFoldingModes fFoldingMode; ///< if and how to fold HTML output
105 TDbgSourceModes fSourceLinkMode; ///< if and how to link with source code
106 string fSourceRootPath; ///< defines root path for source links
107 bool fAppend; ///< if set, existing debug files will not be overwritten, but appended to
108 TDbgSubthreadModes fSubThreadMode; ///< how to handle debug messages from subthreads
109 uInt32 fSubThreadBufferMax; ///< how much to buffer for subthread maximally
113 /// @brief Debug output channel
114 class TDbgOut : noncopyable {
115 // construction/destruction
117 bool fDestructed; // flag which will be set once destruct() has been called - by the outermost derivate's destructor
121 virtual void doDestruct(void); // will be called by destruct, derived must call inherited if they implement it
122 void destruct(void); // to be called by ALL destructors of derivates.
124 /// @brief duplicate output channel
125 virtual TDbgOut *clone(void) { return new TDbgOut; };
126 /// @brief open debug output channel
128 /// - Autocloses current channel if already open
129 /// - May not actually open the channel, but should test if channel is writable
130 /// @return false if debug channel cannot be opened and written to
131 /// @param aDbgOutputName[in] name (usually file name) of debug output channel
132 /// @param aSuggestedExtension[in] file extension suggested (may not be used depending on channel type)
133 /// @param aFlushMode[in] flush mode to be used on the channel
134 /// @param aOverWrite[in] if true, debug output channel (=file) will be overwritten (otherwise: appended to)
135 /// @param aRawMode[in] if true, debug output channel (=file) is opened in binary raw mode (for message dumps etc.)
136 virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false) { return true; };
137 /// @brief get current size of output file
138 /// @return number of bytes, 0 if file is empty or for non-files (like console)
139 virtual uInt32 dbgFileSize(void) { return 0; };
140 /// @brief close and flush all log output
141 virtual void closeDbg(void) { /* nop */ };
142 /// @brief write single line to debug output channel
143 /// @param aLine[in] text for line to be written out (must not contain line ends)
144 /// @param aForceFlush[in] if true, debug output will be flushed to permanent storage regardless of current flush mode
145 virtual void putLine(cAppCharP aLine, bool aForceFlush) { /* nop */};
146 /// @brief write raw data to debug output channel (usually makes sense only when channel is opened in raw mode)
147 /// @param aData[in] pointer to data to be written
148 /// @param aSize[in] size in bytes of data block at aData to be written
149 virtual void putRawData(cAppPointer aData, memSize aSize) { /* nop */};
158 /// @brief Standard file debug output channel
159 class TStdFileDbgOut : public TDbgOut {
160 typedef TDbgOut inherited;
162 // constructor/destructor
164 virtual ~TStdFileDbgOut();
166 virtual TDbgOut *clone(void) { return new TStdFileDbgOut; };
167 virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
168 virtual uInt32 dbgFileSize(void);
169 virtual void closeDbg(void);
170 virtual void putLine(cAppCharP aLine, bool aForceFlush);
171 virtual void putRawData(cAppPointer aData, memSize aSize);
173 TDbgFlushModes fFlushMode;
182 /// @brief Output to console
183 class TConsoleDbgOut : public TDbgOut {
184 typedef TDbgOut inherited;
186 // constructor/destructor
189 virtual TDbgOut *clone(void) { return new TConsoleDbgOut; };
190 virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
191 virtual void closeDbg(void);
192 virtual void putLine(cAppCharP aLine, bool aForceFlush);
193 virtual void putRawData(cAppPointer aData, memSize aSize) { /* not supported on console, just NOP */ };
197 // Debug logger class
198 // ------------------
200 /// @brief hierachical block history
201 typedef struct BlockLevel {
204 struct BlockLevel *fNext;
210 /// @brief Debug logger base class (without subthread handling)
211 class TDebugLoggerBase : noncopyable {
213 // constructor/destructor
214 TDebugLoggerBase(GZones *aGZonesP);
215 virtual ~TDebugLoggerBase();
217 /// @brief install output channel handler object (and pass it's ownership!)
218 /// @param aDbgOutP[in] output channel to be used for this logger (will be owned and finally destroyed by the logger)
219 void installOutput(TDbgOut *aDbgOutP);
220 /// @brief link this logger to another logger and redirect output to that logger
221 /// @param aDebugLoggerP[in] another logger, that must be alive as long as this logger is alive
222 void outputVia(TDebugLoggerBase *aDebugLoggerP);
223 /// @brief check if an output channel is already established other than with default values
224 bool outputEstablished(void) { return fOutStarted; };
225 /// @brief set debug options
226 virtual void setOptions(const TDbgOptions *aDbgOptionsP) { fDbgOptionsP = aDbgOptionsP; };
227 /// @brief get debug options pointer
228 const TDbgOptions *getOptions(void) { return fDbgOptionsP; };
229 // @brief convenience version for getting time
230 lineartime_t getSystemNowAs(timecontext_t aContext);
231 /// @brief get current debug mask for this logger.
232 /// Note that setEnabled(false) will cause this to return 0 even if the mask itself is non-zero
233 uInt32 getMask(void) { return fDebugEnabled ? fDebugMask : 0; };
234 uInt32 getRealMask(void) { return fDebugMask; };
235 /// @brief set new debug mask for this logger
236 void setMask(uInt32 aDbgMask)
237 { fDebugMask=aDbgMask; };
238 /// @brief enable or disable this logger (but leave dbgMask intact)
239 void setEnabled(bool aEnabled)
240 { fDebugEnabled=aEnabled; };
241 /// @brief set debug output path + filename (no extension, please)
242 void setDebugPath(cAppCharP aPath) { fDbgPath = aPath; };
243 /// @brief append to debug output path + filename (no extension, please)
244 void appendToDebugPath(cAppCharP aPathElement) { fDbgPath += aPathElement; };
245 /// @brief get debug output file path (w/o extension)
246 cAppCharP getDebugPath(void) { return fOutputLoggerP ? fOutputLoggerP->getDebugPath() : fDbgPath.c_str(); };
247 /// @brief get debug output file name (w/o path or extension)
248 cAppCharP getDebugFilename(void) { if (fOutputLoggerP) return fOutputLoggerP->getDebugFilename(); size_t n=fDbgPath.find_last_of("\\/:"); return fDbgPath.c_str()+(n!=string::npos ? n+1 : 0); };
249 /// @brief get debug output file extension
250 cAppCharP getDebugExt(void) { return fOutputLoggerP ? fOutputLoggerP->getDebugExt() : fDbgOptionsP ? DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat] : ""; };
252 /// @brief Write text to debug output channel.
254 /// - Line will be terminated by linefeed automatically (no need to include a linefeed for single line message)
255 /// - \n chars can be used to separate multi-line output. Formatter will take care that
256 /// all lines are equally indented/formatted/prefixed
257 /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
258 /// @param aText[in] text to be written out
259 /// @param aTextSize[in] if>0, this is the maximum number of chars to output from aText
260 virtual void DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
261 /// @brief Write formatted text to debug output channel.
262 /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
263 /// @param aFormat[in] format text in vprintf style to be written out
264 /// @param aArgs[in] varargs in vprintf style
265 virtual void DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
266 /// @brief Write formatted text to debug output channel.
267 /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
268 /// @param aFormat[in] format text in printf style to be written out
269 void DebugPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, ...)
271 __attribute__((format(printf, TDBG_LOCATION_ARG_NUM + 3, TDBG_LOCATION_ARG_NUM + 4)))
274 /// @brief set debug mask to be used for next DebugPrintfLastMask() call
275 /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
276 virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
277 /// @brief like DebugPrintf(), but using mask previously set by setNextMask()
278 /// @param aFormat[in] format text in printf style to be written out
279 void DebugPrintfLastMask(TDBG_LOCATION_PROTO cAppCharP aFormat, ...)
281 __attribute__((format(printf, TDBG_LOCATION_ARG_NUM + 2, TDBG_LOCATION_ARG_NUM + 3)))
285 /// @brief Open structure Block. Depending on the output format, this will generate indent, XML tags, HTML headers etc.
286 /// @param aBlockName[in] Name of Block. Will be used e.g. for tag name in XML. Intention is to group similar entities with the same BlockName
287 /// @param aBlockTitle[in] Title (descriptive text) of Block.
288 /// @param aCollapsed[in] If set, and folding mode is auto, block will be initially collapsed when log is opened in browser.
289 /// @param aBlockFmt[in] Format string for additional Block info. Should contain one or multiple tag=value pairs, separated by the pipe char |.
290 /// This will be used to generate XML attributes or other identifiers.
291 /// @param aArgs[in] varargs in vprintf style for aBlockFmt
292 virtual void DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
293 /// @brief Open structure Block, printf style variant
294 void DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
296 __attribute__((format(printf, TDBG_LOCATION_ARG_NUM + 5, TDBG_LOCATION_ARG_NUM + 6)))
299 void DebugOpenBlockExpanded(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
301 __attribute__((format(printf, TDBG_LOCATION_ARG_NUM + 4, TDBG_LOCATION_ARG_NUM + 5)))
304 void DebugOpenBlockCollapsed(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
306 __attribute__((format(printf, TDBG_LOCATION_ARG_NUM + 4, TDBG_LOCATION_ARG_NUM + 5)))
309 /// @brief Open structure Block, without any attributes
310 virtual void DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle=NULL, bool aCollapsed=false);
311 /// @brief Close structure Block. Name is used to close possibly unclosed contained Blocks automatically.
312 virtual void DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName);
315 /// @brief start debugging output if needed and sets fOutStarted
316 bool DebugStartOutput(void);
317 /// @brief Output single line to debug channel (includes indenting, but no other formatting)
318 void DebugPutLine(TDBG_LOCATION_PROTO cAppCharP aText, stringSize aTextSize=0, bool aPre=false);
319 /// @brief finalize debugging output
320 void DebugFinalizeOutput(void);
321 /// @brief get block number
322 uInt32 getBlockNo(void) { return fOutputLoggerP ? fOutputLoggerP->getBlockNo() : fBlockNo; };
323 /// @brief increment block number
324 void nextBlock(void) { if (fOutputLoggerP) fOutputLoggerP->nextBlock(); else fBlockNo++; };
325 /// @brief internal helper for closing debug Blocks
326 /// @param aBlockName[in] Name of Block to close. All Blocks including the first with given name will be closed. If NULL, all Blocks will be closed.
327 /// @param aCloseComment[in] Comment about closing Block. If NULL, no comment will be shown (unless implicit closes occur, which auto-creates a comment)
328 void internalCloseBlocks(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aCloseComment);
329 #ifdef SYDEBUG_LOCATION
330 /// @brief turn text into link to source code
331 string dbg2Link(const TDbgLocation &aTDbgLoc, const string &aTxt);
332 #endif // SYDEBUG_LOCATION
334 TDbgOut *fDbgOutP; // the debug output
335 string fDbgPath; // the output path+filename (w/o extension)
336 const TDbgOptions *fDbgOptionsP; // the debug options
337 uInt32 fDebugMask; // the debug mask
338 bool fDebugEnabled; // on-off-switch for debugging output
339 uInt32 fNextDebugMask; // debug mask to be used for next DebugPrintfLastMask()
340 uInt16 fIndent; // the current indent
341 TBlockLevel *fBlockHistory; // the linked list of Block history entries
342 bool fOutStarted; // set if output has started
343 uInt32 fBlockNo; // block count for folding
344 GZones *fGZonesP; // zones list for time conversions
345 TDebugLoggerBase *fOutputLoggerP; // another logger to be used for output
346 }; // TDebugLoggerBase
349 #ifdef MULTI_THREAD_SUPPORT
350 /// @brief Subthread log handling
351 typedef struct SubThreadLog {
353 struct SubThreadLog *fNext;
354 TDebugLoggerBase *fSubThreadLogger;
360 /// @brief Debug logger class
361 class TDebugLogger : public TDebugLoggerBase {
362 typedef TDebugLoggerBase inherited;
364 // constructor/destructor
365 TDebugLogger(GZones *aGZonesP);
366 virtual ~TDebugLogger();
368 #ifdef MULTI_THREAD_SUPPORT
369 /// @brief set debug options in this logger and all sub thread loggers
370 virtual void setOptions(const TDbgOptions *aDbgOptionsP);
371 virtual void DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
372 virtual void DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
373 virtual void DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
374 virtual void DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName);
375 virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
377 // - thread debug output serializing
378 /// @brief output all buffered subthread's output in a special subthread Block in the main output
379 void DebugShowSubThreadOutput(void);
380 /// @brief signals the calling thread that it is done with doing output for now.
381 /// @param aRemoveIt[in] if set, do remove thread from the subthread logger list
383 /// - If the main thread is doing this and we have bufferandmix mode, the next subthread will be allowed
384 /// to write into the output channel until a new main thread gains control via DebugDefineMainThread();
385 void DebugThreadOutputDone(bool aRemoveIt=false);
386 /// @brief Define the current calling thread as the main debug thread
387 /// Note: This is used for example when starting to process the next request of a session which possibly
388 // occurs from another thread).
389 void DebugDefineMainThread(void);
391 #ifdef MULTI_THREAD_SUPPORT
393 /// @brief find (and possibly delete) subthread record
394 /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
395 /// IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
396 /// Note that the caller must take care of deleting the subthread record
397 TSubThreadLog *findSubThread(uInt32 aThreadID, bool aAndRemove=false);
398 /// @brief find or create logger for subthread
399 TDebugLoggerBase *getThreadLogger(bool aCreateNew=true);
401 uIntArch fMainThreadID;
402 TSubThreadLog *fSubThreadLogs; // the linked list of active subthreads
403 TDebugLoggerBase *fSilentLoggerP; // a silent (inactive) logger required for suppressed subthreads
410 } // namespace sysync
414 #endif // DEBUGLOGGER_H