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
21namespace Poco {
22
23
24NestedDiagnosticContext::NestedDiagnosticContext()
25{
26}
27
28
29NestedDiagnosticContext::NestedDiagnosticContext(const NestedDiagnosticContext& ctx)
30{
31 _stack = ctx._stack;
32}
33
34
35NestedDiagnosticContext::~NestedDiagnosticContext()
36{
37}
38
39
40NestedDiagnosticContext& NestedDiagnosticContext::operator = (const NestedDiagnosticContext& ctx)
41{
42 if (&ctx != this)
43 _stack = ctx._stack;
44 return *this;
45}
46
47
48void 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
58void 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
68void NestedDiagnosticContext::pop()
69{
70 if (!_stack.empty())
71 _stack.pop_back();
72}
73
74
75int NestedDiagnosticContext::depth() const
76{
77 return int(_stack.size());
78}
79
80
81std::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
94void NestedDiagnosticContext::dump(std::ostream& ostr) const
95{
96 dump(ostr, "\n");
97}
98
99
100void 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
118void NestedDiagnosticContext::clear()
119{
120 _stack.clear();
121}
122
123
124std::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
234namespace
235{
236 static ThreadLocal<NestedDiagnosticContext> ndc;
237}
238
239
240NestedDiagnosticContext& NestedDiagnosticContext::current()
241{
242 return ndc.get();
243}
244
245
246} // namespace Poco
247