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 | |
42 | namespace Poco { |
43 | |
44 | |
45 | class NDCScope; |
46 | |
47 | |
48 | class 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 | { |
69 | public: |
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 | |
160 | private: |
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 | |
175 | typedef NestedDiagnosticContext NDC; |
176 | |
177 | |
178 | class 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 | { |
183 | public: |
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 | |
199 | inline bool NestedDiagnosticContext::hasBacktrace() |
200 | { |
201 | #ifdef POCO_HAS_BACKTRACE |
202 | return true; |
203 | #endif |
204 | return false; |
205 | } |
206 | |
207 | |
208 | inline NDCScope::NDCScope(const std::string& info) |
209 | { |
210 | NestedDiagnosticContext::current().push(info); |
211 | } |
212 | |
213 | |
214 | inline NDCScope::NDCScope(const std::string& info, int line, const char* filename) |
215 | { |
216 | NestedDiagnosticContext::current().push(info, line, filename); |
217 | } |
218 | |
219 | |
220 | inline 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 | |