1//
2// NestedDiagnosticContext.h
3//
4// Library: Foundation
5// Package: Core
6// Module: NestedDiagnosticContext
7//
8// Definition of the NestedDiagnosticContext class.
9//
10// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15// g++ backtrace implementation adapted from
16// https://panthema.net/2008/0901-stacktrace-demangled/
17// License: "You can freely use it for whatever purpose"
18
19#ifndef Foundation_NestedDiagnosticContext_INCLUDED
20#define Foundation_NestedDiagnosticContext_INCLUDED
21
22
23#include "Poco/Foundation.h"
24#include "Poco/OrderedMap.h"
25#include <vector>
26#include <ostream>
27#include <typeinfo>
28#include <utility>
29
30#if defined(POCO_COMPILER_GCC) && (POCO_OS == POCO_OS_LINUX) && (POCO_OS != POCO_OS_CYGWIN)
31 #define POCO_HAS_BACKTRACE
32#endif
33
34#ifdef POCO_HAS_BACKTRACE
35 #ifdef POCO_COMPILER_GCC
36 #include <cxxabi.h>
37 #include <execinfo.h>
38 #include <dlfcn.h>
39 #endif
40#endif
41
42namespace Poco {
43
44
45class NDCScope;
46
47
48class Foundation_API NestedDiagnosticContext
49 /// This class implements a Nested Diagnostic Context (NDC),
50 /// as described in Neil Harrison's article "Patterns for Logging
51 /// Diagnostic Messages" in "Pattern Languages of Program Design 3"
52 /// (Addison-Wesley).
53 ///
54 /// A NDC maintains a stack of context information, consisting of
55 /// an informational string (e.g., a method name), as well as an
56 /// optional source code line number and file name.
57 /// NDCs are especially useful for tagging log messages with
58 /// context information which is very helpful in a multithreaded
59 /// server scenario.
60 /// Every thread has its own private NDC, which is automatically
61 /// created when needed and destroyed when the thread terminates.
62 ///
63 /// The NDCScope (or NDC::Scope) class can be used to automatically
64 /// push information at the beginning of a scope, and to pop it
65 /// at the end.
66 /// The poco_ndc(info) macro augments the information with a
67 /// source code line number and file name.
68{
69public:
70 typedef NDCScope Scope;
71
72 NestedDiagnosticContext();
73 /// Creates the NestedDiagnosticContext.
74
75 NestedDiagnosticContext(const NestedDiagnosticContext& ctx);
76 /// Copy constructor.
77
78 ~NestedDiagnosticContext();
79 /// Destroys the NestedDiagnosticContext.
80
81 NestedDiagnosticContext& operator = (const NestedDiagnosticContext& ctx);
82 /// Assignment operator.
83
84 void push(const std::string& info);
85 /// Pushes a context (without line number and filename) onto the stack.
86
87 void push(const std::string& info, int line, const char* filename);
88 /// Pushes a context (including line number and filename)
89 /// onto the stack. Filename must be a static string, such as the
90 /// one produced by the __FILE__ preprocessor macro.
91
92 void pop();
93 /// Pops the top-most context off the stack.
94
95 int depth() const;
96 /// Returns the depth (number of contexts) of the stack.
97
98 std::string toString() const;
99 /// Returns the stack as a string with entries
100 /// delimited by colons. The string does not contain
101 /// line numbers and filenames.
102
103 void dump(std::ostream& ostr) const;
104 /// Dumps the stack (including line number and filenames)
105 /// to the given stream. The entries are delimited by
106 /// a newline.
107
108 void dump(std::ostream& ostr, const std::string& delimiter, bool nameOnly = false) const;
109 /// Dumps the stack (including line number and filenames)
110 /// to the given stream.
111 /// If nameOnly is false (default), the whole path to file is printed,
112 /// otherwise only the file name.
113
114 void clear();
115 /// Clears the NDC stack.
116
117 static NestedDiagnosticContext& current();
118 /// Returns the current thread's NDC.
119
120 template <typename T>
121 static std::string typeName(bool full = true)
122 /// Returns type name for the provided type.
123 /// Names are demangled for g++ only at this time.
124 /// If full is false, the scope is trimmed off.
125 {
126 std::string name(typeid(T).name());
127#ifdef POCO_HAS_BACKTRACE
128 #ifdef POCO_COMPILER_GCC
129 int status = 0;
130 #if (POCO_OS == POCO_OS_CYGWIN)
131 char* pName = __cxxabiv1::__cxa_demangle(typeid(T).name(), 0, 0, &status);
132 #else
133 char* pName = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
134 #endif
135 if (status == 0) name = pName;
136 free(pName);
137 if (!full) // strip scope, if any
138 {
139 std::size_t pos = name.rfind("::");
140 if (pos != std::string::npos) name = name.substr(pos+2);
141 }
142 #endif // TODO: demangle other compilers
143#endif
144 return name;
145 }
146
147 static std::string backtrace(int skipEnd = 1, int skipBegin = 0, int stackSize = 128, int bufSize = 1024);
148 /// Returns string containing the formatted current thread stack trace. To "trim" returned stack
149 /// frames, set skipEnd and skipBegin to the appropriate values; trace entries are sorted in
150 /// reverse order (last entry is first). So, to omit this function from the returned string,
151 /// skipEnd defaults to 1. For larger stacks, adjust stackSize and bufSize accordingly.
152 ///
153 /// Note that the output may depend on many factors, mostly on the compiler/linker settings,
154 /// but also on the code being analyzed in some cases; for example, g++ stack trace will not
155 /// show static (or the ones in anonymous namespaces) functions by name, but only by address.
156
157 static bool hasBacktrace();
158 /// Returns true if backtrace functionality is available, false otherwise.
159
160private:
161 struct Context
162 {
163 std::string info;
164 const char* file;
165 int line;
166 std::string trace; // TODO: add backtrace
167 };
168
169 typedef std::vector<Context> Stack;
170
171 Stack _stack;
172};
173
174
175typedef NestedDiagnosticContext NDC;
176
177
178class Foundation_API NDCScope
179 /// This class can be used to automatically push a context onto
180 /// the NDC stack at the beginning of a scope, and to pop
181 /// the context at the end of the scope.
182{
183public:
184 NDCScope(const std::string& info);
185 /// Pushes a context on the stack.
186
187 NDCScope(const std::string& info, int line, const char* filename);
188 /// Pushes a context on the stack.
189
190 ~NDCScope();
191 /// Pops the top-most context off the stack.
192};
193
194
195//
196// inlines
197//
198
199inline bool NestedDiagnosticContext::hasBacktrace()
200{
201#ifdef POCO_HAS_BACKTRACE
202 return true;
203#endif
204 return false;
205}
206
207
208inline NDCScope::NDCScope(const std::string& info)
209{
210 NestedDiagnosticContext::current().push(info);
211}
212
213
214inline NDCScope::NDCScope(const std::string& info, int line, const char* filename)
215{
216 NestedDiagnosticContext::current().push(info, line, filename);
217}
218
219
220inline NDCScope::~NDCScope()
221{
222 try
223 {
224 NestedDiagnosticContext::current().pop();
225 }
226 catch (...)
227 {
228 poco_unexpected();
229 }
230}
231
232
233//
234// helper macros
235//
236#define poco_ndc_func \
237 Poco::NDCScope _theNdcScope(__func__), __LINE__, __FILE__)
238
239#define poco_ndc(func) \
240 Poco::NDCScope _theNdcScope(#func, __LINE__, __FILE__)
241
242#define poco_ndc_str(str) \
243 Poco::NDCScope _theNdcScope(str, __LINE__, __FILE__)
244
245#define poco_ndc_bt(from, to) \
246 Poco::NDCScope _theNdcScope(NDC::backTrace(from, to), __LINE__, __FILE__)
247
248
249#if defined(_DEBUG)
250 #define poco_ndc_func_dbg \
251 Poco::NDCScope _theNdcScope(__func__, __LINE__, __FILE__)
252
253 #define poco_ndc_dbg(func) \
254 Poco::NDCScope _theNdcScope(#func, __LINE__, __FILE__)
255
256 #define poco_ndc_dbg_str(str) \
257 Poco::NDCScope _theNdcScope(str, __LINE__, __FILE__)
258
259 #define poco_ndc_bt_dbg(from, to) \
260 Poco::NDCScope _theNdcScope(NDC::backTrace(from, to), __LINE__, __FILE__)
261#else
262 #define poco_ndc_func_dbg
263 #define poco_ndc_dbg(func)
264 #define poco_ndc_dbg_str(str)
265 #define poco_ndc_bt_dbg(from, to)
266#endif
267
268
269} // namespace Poco
270
271
272#endif // Foundation_NestedDiagnosticContext_INCLUDED
273