]> git.cworth.org Git - apitrace/blob - common/trace_backtrace.cpp
thirdparty/libbacktrace: update from upstream
[apitrace] / common / trace_backtrace.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013 Samsung
4  * Contributed by Eugene Velesevich
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 /*
28  *  Wrapper for platform-specific code for obtaining symbolic backtraces
29  *  on Android and Linux
30  */
31
32
33
34 #include "trace_backtrace.hpp"
35
36 #if defined(ANDROID) || defined(__ELF__)
37
38 #include <set>
39 #include "os.hpp"
40
41
42 namespace trace {
43
44 /*
45  * Pascal string (with zero terminator optionally omitted)
46  * This is a helper class for storing a set of exact strings or prefixes
47  * to match a zero-terminated string against later.
48  * Two zero-terminated pstrings compare equal iff they are the same.
49  * Otherwise, they compare equal iff one is a prefix of the other
50  * (a zero-terminated pstring cannot be a prefix)
51  */
52
53 struct pstring {
54     const char* s;
55     int n;
56     pstring(const char* s, int n)
57     {
58         this->s = s;
59         this->n = n;
60     }
61     bool operator<(const pstring q2) const
62     {
63         return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;
64     }
65 };
66
67
68 class StringPrefixes {
69 private:
70     std::set<pstring> pset;
71
72     void addPrefix(char* startbuf, int n) {
73         std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));
74         bool replace = elem != pset.end() && n < elem->n;
75         if (replace) {
76             pset.erase(elem);
77         }
78         if (replace || elem == pset.end()) {
79             pset.insert(pstring(startbuf, n));
80         }
81     }
82 public:
83     StringPrefixes();
84
85     bool contain(const char* s) {
86         return pset.find(pstring(s, strlen(s) + 1)) != pset.end();
87     }
88 };
89
90 StringPrefixes::StringPrefixes() {
91     char *list = getenv("APITRACE_BACKTRACE");
92     if (!list)
93         return;
94     for (char *t = strdup(list); ; t = NULL) {
95         char *tok = strtok(t, " \t\r\n");
96         if (!tok)
97             break;
98         if (tok[0] == '#')
99             continue;
100         if (tok[strlen(tok) - 1] == '*')
101             addPrefix(tok, strlen(tok) - 1);
102         else
103             addPrefix(tok, strlen(tok) + 1);
104     }
105 }
106
107
108 bool backtrace_is_needed(const char* fname) {
109     static StringPrefixes backtraceFunctionNamePrefixes;
110     return backtraceFunctionNamePrefixes.contain(fname);
111 }
112
113 } /* namespace trace */
114
115 #if defined(ANDROID)
116
117 #include <dlfcn.h>
118 #include "os.hpp"
119 #include <vector>
120
121 namespace trace {
122
123 /* The following two declarations are copied from Android sources */
124
125 enum DebugTargetKind {
126     kDebugTargetUnknown = 0,
127     kDebugTargetLog,
128     kDebugTargetFile,
129 };
130
131 struct DebugOutputTarget {
132     DebugTargetKind which;
133
134     union {
135         struct {
136             int priority;
137             const char* tag;
138         } log;
139         struct {
140             FILE* fp;
141         } file;
142     } data;
143 };
144
145 #define THREAD_SELF_NAME "_Z13dvmThreadSelfv"
146 #define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"
147 #define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
148
149
150 class DalvikBacktraceProvider {
151 private:
152     bool errorOccured;
153     void* (*threadself)(void);
154     FILE* streamInMemory;
155     char* buf;
156     size_t bufSize;
157     void (*dumpBacktrace)(const DebugOutputTarget*, void*);
158     DebugOutputTarget debugTarget;
159     Id nextFrameId;
160 public:
161     DalvikBacktraceProvider() {
162         nextFrameId = 0;
163         FILE* (*open_memstream_exp)(char**, size_t*);
164         void (*createDebugTarget)(DebugOutputTarget*, FILE*);
165         void* handle = dlopen("/system/lib/libdvm.so", 0);
166         errorOccured = true;
167         if (!handle) {
168             os::log("dlopen failed\n");
169             return;
170         }
171         threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);
172         if (threadself == NULL) {
173             os::log("dlsym ThreadSelf failed\n");
174             return;
175         }
176         createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);
177         if (createDebugTarget == NULL) {
178             os::log("dlsym CreateFileOutput failed\n");
179             return;
180         }
181         dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);
182         if (dumpBacktrace == NULL) {
183             os::log("dlsym DumpThreadStack failed\n");
184             return;
185         }
186         void* handle2 = dlopen("/system/lib/libcutils.so", 0);
187         if (!handle2) {
188             os::log("dlopen failed\n");
189             return;
190         }
191         open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
192         if (open_memstream_exp == NULL) {
193             os::log("dlsym open_memstream failed\n");
194             return;
195         }
196         streamInMemory = open_memstream_exp(&buf, &bufSize);
197         if (streamInMemory == NULL) {
198             os::log("open_memstream failed\n");
199             return;
200         }
201         createDebugTarget(&debugTarget, streamInMemory);
202         errorOccured = false;
203     }
204
205     inline char* getBacktrace() {
206         if (errorOccured) {
207             return NULL;
208         }
209         rewind(streamInMemory);
210         dumpBacktrace(&debugTarget, threadself());
211         fflush(streamInMemory);
212         return buf;
213     }
214 /*
215  * Parse a stack frame, expecting:
216  * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"
217  * or
218  * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"
219  */
220     std::vector<RawStackFrame> parseBacktrace(char *rawBacktrace) {
221         std::vector<RawStackFrame> parsedBacktrace;
222         char* rawBacktrace_it = rawBacktrace;
223         while (*rawBacktrace_it != '\0') {
224             RawStackFrame stackFrame;
225             // TODO: Keep a cache of stack frames
226             stackFrame.id = nextFrameId++;
227             /* skip leading space */
228             while (*rawBacktrace_it == ' ') {
229                 rawBacktrace_it++;
230             }
231             /* Skip "at " */
232             rawBacktrace_it += 3;
233             stackFrame.function = rawBacktrace_it;
234             while (*rawBacktrace_it != '(') {
235                 rawBacktrace_it++;
236             }
237             *rawBacktrace_it = '\0';
238             stackFrame.filename = rawBacktrace_it + 1;
239             while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {
240                 rawBacktrace_it++;
241             }
242             if (*rawBacktrace_it == ':') {
243                 const char *linenumber = rawBacktrace_it + 1;
244                 *rawBacktrace_it = '\0';
245                 while (*rawBacktrace_it != ')') {
246                     rawBacktrace_it++;
247                 }
248                 *rawBacktrace_it = '\0';
249                 rawBacktrace_it++;
250                 stackFrame.linenumber = atoi(linenumber);
251             }
252             else {
253                 stackFrame.filename = NULL;
254                 while (*rawBacktrace_it != '\n') {
255                     rawBacktrace_it++;
256                 }
257             }
258             while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {
259                     rawBacktrace_it++;
260             }
261             parsedBacktrace.push_back(stackFrame); /* module */
262         }
263         return parsedBacktrace;
264     }
265 };
266
267 std::vector<RawStackFrame> get_backtrace() {
268     static DalvikBacktraceProvider backtraceProvider;
269     return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());
270 }
271
272 /* end ANDROID */
273 #elif defined(__ELF__)
274
275 #include <stdint.h>
276 #include <dlfcn.h>
277 #include <map>
278 #include <vector>
279 #include <cxxabi.h>
280
281 #include <backtrace.h>
282
283 namespace trace {
284
285
286
287
288 #define BT_DEPTH 10
289
290 class libbacktraceProvider {
291     struct backtrace_state *state;
292     int skipFrames;
293     Id nextFrameId;
294     std::map<uintptr_t, std::vector<RawStackFrame> > cache;
295     std::vector<RawStackFrame> *current, *current_frames;
296     RawStackFrame *current_frame;
297
298     static void bt_err_callback(void *vdata, const char *msg, int errnum)
299     {
300         if (errnum == -1)
301             return;// no debug/sym info
302         else if (errnum)
303             os::log("libbacktrace: %s: %s\n", msg, strerror(errnum));
304         else
305             os::log("libbacktrace: %s\n", msg);
306     }
307
308     static int bt_countskip(void *vdata, uintptr_t pc)
309     {
310         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
311         Dl_info info1, info2;
312         if (!dladdr((void*)bt_countskip, &info2)) {
313             os::log("dladdr failed, cannot cull stack traces\n");
314             return 1;
315         }
316         if (!dladdr((void*)pc, &info1))
317             return 1;
318         if (info1.dli_fbase != info2.dli_fbase)
319             return 1;
320         this_->skipFrames++;
321         return 0;
322     }
323
324     static int bt_full_callback(void *vdata, uintptr_t pc,
325                                  const char *file, int line, const char *func)
326     {
327         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
328         RawStackFrame frame = *this_->current_frame;
329         frame.id = this_->nextFrameId++;
330         frame.filename = file;
331         frame.linenumber = line;
332         if (func)
333             frame.function = func;
334         int status;
335         if (func && (func = abi::__cxa_demangle(func, NULL, NULL, &status)))
336             frame.function = func;
337         this_->current_frames->push_back(frame);
338         return 0;
339     }
340
341     static int bt_callback(void *vdata, uintptr_t pc)
342     {
343         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
344         std::vector<RawStackFrame> &frames = this_->cache[pc];
345         if (!frames.size()) {
346             RawStackFrame frame;
347             Dl_info info = {0};
348             dladdr((void*)pc, &info);
349             frame.module = info.dli_fname;
350             frame.function = info.dli_sname;
351             frame.offset = info.dli_saddr ? pc - (uintptr_t)info.dli_saddr
352                                           : pc - (uintptr_t)info.dli_fbase;
353             this_->current_frame = &frame;
354             this_->current_frames = &frames;
355             backtrace_pcinfo(this_->state, pc, bt_full_callback, bt_err_callback, vdata);
356             if (!frames.size()) {
357                 frame.id = this_->nextFrameId++;
358                 frames.push_back(frame);
359             }
360         }
361         this_->current->insert(this_->current->end(), frames.begin(), frames.end());
362         return this_->current->size() >= BT_DEPTH;
363     }
364
365 public:
366     libbacktraceProvider():
367         state(backtrace_create_state(NULL, 0, bt_err_callback, NULL))
368     {
369         backtrace_simple(state, 0, bt_countskip, bt_err_callback, this);
370     }
371
372     std::vector<RawStackFrame> getParsedBacktrace()
373     {
374         std::vector<RawStackFrame> parsedBacktrace;
375         current = &parsedBacktrace;
376         backtrace_simple(state, skipFrames, bt_callback, bt_err_callback, this);
377         return parsedBacktrace;
378     }
379 };
380
381 std::vector<RawStackFrame> get_backtrace() {
382     static libbacktraceProvider backtraceProvider;
383     return backtraceProvider.getParsedBacktrace();
384 }
385
386 #endif /* LINUX */
387
388 } /* namespace trace */
389
390 #endif /* ANDROID or LINUX */