1 | // |
2 | // NestedDiagnosticContext.cpp |
3 | // |
4 | // Library: Foundation |
5 | // Package: Core |
6 | // Module: NestedDiagnosticContext |
7 | // |
8 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/NestedDiagnosticContext.h" |
16 | #include "Poco/SingletonHolder.h" |
17 | #include "Poco/String.h" |
18 | #include "Poco/ThreadLocal.h" |
19 | #include "Poco/Path.h" |
20 | |
21 | namespace Poco { |
22 | |
23 | |
24 | NestedDiagnosticContext::NestedDiagnosticContext() |
25 | { |
26 | } |
27 | |
28 | |
29 | NestedDiagnosticContext::NestedDiagnosticContext(const NestedDiagnosticContext& ctx) |
30 | { |
31 | _stack = ctx._stack; |
32 | } |
33 | |
34 | |
35 | NestedDiagnosticContext::~NestedDiagnosticContext() |
36 | { |
37 | } |
38 | |
39 | |
40 | NestedDiagnosticContext& NestedDiagnosticContext::operator = (const NestedDiagnosticContext& ctx) |
41 | { |
42 | if (&ctx != this) |
43 | _stack = ctx._stack; |
44 | return *this; |
45 | } |
46 | |
47 | |
48 | void NestedDiagnosticContext::push(const std::string& info) |
49 | { |
50 | Context ctx; |
51 | ctx.info = info; |
52 | ctx.line = -1; |
53 | ctx.file = 0; |
54 | _stack.push_back(ctx); |
55 | } |
56 | |
57 | |
58 | void NestedDiagnosticContext::push(const std::string& info, int line, const char* filename) |
59 | { |
60 | Context ctx; |
61 | ctx.info = info; |
62 | ctx.line = line; |
63 | ctx.file = filename; |
64 | _stack.push_back(ctx); |
65 | } |
66 | |
67 | |
68 | void NestedDiagnosticContext::pop() |
69 | { |
70 | if (!_stack.empty()) |
71 | _stack.pop_back(); |
72 | } |
73 | |
74 | |
75 | int NestedDiagnosticContext::depth() const |
76 | { |
77 | return int(_stack.size()); |
78 | } |
79 | |
80 | |
81 | std::string NestedDiagnosticContext::toString() const |
82 | { |
83 | std::string result; |
84 | for (Stack::const_iterator it = _stack.begin(); it != _stack.end(); ++it) |
85 | { |
86 | if (!result.empty()) |
87 | result.append(":" ); |
88 | result.append(it->info); |
89 | } |
90 | return result; |
91 | } |
92 | |
93 | |
94 | void NestedDiagnosticContext::dump(std::ostream& ostr) const |
95 | { |
96 | dump(ostr, "\n" ); |
97 | } |
98 | |
99 | |
100 | void NestedDiagnosticContext::dump(std::ostream& ostr, const std::string& delimiter, bool nameOnly) const |
101 | { |
102 | for (Stack::const_iterator it = _stack.begin();;) |
103 | { |
104 | std::string file = it->file; |
105 | if (nameOnly) file = Path(file).getFileName(); |
106 | ostr << it->info; |
107 | if (it->file) |
108 | ostr << " (in \"" << file << "\", line " << it->line << ")" ; |
109 | if (!it->trace.empty()) |
110 | ostr << "\nbacktrace:" << it->trace; |
111 | ++it; |
112 | if (it == _stack.end()) break; |
113 | ostr << delimiter; |
114 | } |
115 | } |
116 | |
117 | |
118 | void NestedDiagnosticContext::clear() |
119 | { |
120 | _stack.clear(); |
121 | } |
122 | |
123 | |
124 | std::string NestedDiagnosticContext::backtrace(int skipEnd, int skipBegin, int stackSize, int bufSize) |
125 | { |
126 | std::string traceBuf; |
127 | #ifdef POCO_HAS_BACKTRACE |
128 | #ifdef POCO_COMPILER_GCC |
129 | const int maxFrames = sizeof(void*) * stackSize; |
130 | void* addrList[maxFrames+1]; |
131 | int addrLen = ::backtrace(addrList, maxFrames); |
132 | |
133 | if (addrLen != 0) |
134 | { |
135 | char** symbollist = backtrace_symbols(addrList, addrLen); |
136 | |
137 | size_t funcNameSize = 256; |
138 | char* funcName = (char*)malloc(funcNameSize); |
139 | |
140 | int begin = skipEnd; |
141 | int end = addrLen - ((skipBegin <= addrLen) ? skipBegin : 0); |
142 | traceBuf.append("backtrace" ); |
143 | if (begin > 1) // 0 is this function |
144 | { |
145 | int skip = begin-1; |
146 | if (skip == 1) |
147 | traceBuf.append(" (entry 1 skipped)" ); |
148 | else |
149 | traceBuf.append(" (entries 1-" + std::to_string(begin-1) + " skipped)" ); |
150 | } |
151 | traceBuf.append(":\n" ); |
152 | |
153 | std::string prevSymbol; |
154 | for (int i = begin; i < end; ++i) |
155 | { |
156 | char *beginName = 0, *beginOffset = 0, *endOffset = 0; |
157 | |
158 | // find parentheses and +address offset surrounding the mangled name: |
159 | // ./module(function+0x15c) [0x8048a6d] |
160 | for (char*p = symbollist[i]; *p; ++p) |
161 | { |
162 | if (*p == '(') beginName = p; |
163 | else |
164 | { |
165 | if (*p == '+') beginOffset = p; |
166 | else if (*p == ')' && beginOffset) { endOffset = p; break; } |
167 | } |
168 | } |
169 | |
170 | if (beginName && beginOffset && endOffset && beginName < beginOffset) |
171 | { |
172 | *beginName++ = '\0'; |
173 | *beginOffset++ = '\0'; |
174 | *endOffset = '\0'; |
175 | |
176 | // mangled name: [beginName, beginOffset), |
177 | // caller offset: [beginOffset, endOffset) |
178 | int status; |
179 | char* ret = abi::__cxa_demangle(beginName, funcName, &funcNameSize, &status); |
180 | if (status == 0) // demangled |
181 | { |
182 | funcName = ret; |
183 | if (prevSymbol != symbollist[i]) // output module once |
184 | { |
185 | traceBuf.append("\n> " ); |
186 | traceBuf.append(symbollist[i]); |
187 | traceBuf.append(":\n" ); |
188 | prevSymbol = symbollist[i]; |
189 | } |
190 | traceBuf.append(1, ' ').append(std::to_string(i)).append(1, ' '); |
191 | traceBuf.append(funcName).append(1, '+').append(beginOffset); |
192 | traceBuf.append(1, '\n'); |
193 | } |
194 | else // demangling failed, output mangled |
195 | { |
196 | traceBuf.append("\n> " ); |
197 | traceBuf.append(symbollist[i]); |
198 | traceBuf.append(":\n" ); |
199 | traceBuf.append(1, ' ').append(std::to_string(i)).append(1, ' '); |
200 | traceBuf.append(beginName).append(1, '+').append(beginOffset); |
201 | traceBuf.append(1, '\n'); |
202 | } |
203 | } |
204 | else // couldn't parse, output everything |
205 | { |
206 | traceBuf.append(1, ' ').append(std::to_string(i)).append(1, ' '); |
207 | traceBuf.append(symbollist[i]).append(1, '\n'); |
208 | } |
209 | } |
210 | if (traceBuf.size()) traceBuf.erase(traceBuf.end()-1); |
211 | free(funcName); |
212 | free(symbollist); |
213 | |
214 | if (end < addrLen) |
215 | { |
216 | if (end != addrLen-1) |
217 | traceBuf.append("\n " + std::to_string(end).append(1, '-') + std::to_string(addrLen-1) + " (skipped)" ); |
218 | else |
219 | traceBuf.append("\n " + std::to_string(end).append(" (skipped)" )); |
220 | } |
221 | } |
222 | #endif // POCO_COMPILER_GCC |
223 | #else |
224 | traceBuf = "[call trace not available]" ; |
225 | (void)skipEnd; |
226 | (void)skipBegin; |
227 | (void)stackSize; |
228 | (void)bufSize; |
229 | #endif |
230 | return traceBuf; |
231 | } |
232 | |
233 | |
234 | namespace |
235 | { |
236 | static ThreadLocal<NestedDiagnosticContext> ndc; |
237 | } |
238 | |
239 | |
240 | NestedDiagnosticContext& NestedDiagnosticContext::current() |
241 | { |
242 | return ndc.get(); |
243 | } |
244 | |
245 | |
246 | } // namespace Poco |
247 | |