]> git.cworth.org Git - apitrace/blob - common/os_backtrace.cpp
backtrace: Allow to build without libbacktrace.
[apitrace] / common / os_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 #include "os_backtrace.hpp"
34
35 #include <set>
36 #include <vector>
37 #include "os.hpp"
38
39 #if defined(ANDROID)
40 #  include <dlfcn.h>
41 #elif HAVE_BACKTRACE
42 #  include <stdint.h>
43 #  include <dlfcn.h>
44 #  include <unistd.h>
45 #  include <map>
46 #  include <vector>
47 #  include <cxxabi.h>
48 #  include <backtrace.h>
49 #endif
50
51
52 using trace::Id;
53
54 namespace os {
55
56 /*
57  * Pascal string (with zero terminator optionally omitted)
58  * This is a helper class for storing a set of exact strings or prefixes
59  * to match a zero-terminated string against later.
60  * Two zero-terminated pstrings compare equal iff they are the same.
61  * Otherwise, they compare equal iff one is a prefix of the other
62  * (a zero-terminated pstring cannot be a prefix)
63  */
64
65 struct pstring {
66     const char* s;
67     int n;
68     pstring(const char* s, int n)
69     {
70         this->s = s;
71         this->n = n;
72     }
73     bool operator<(const pstring q2) const
74     {
75         return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;
76     }
77 };
78
79
80 class StringPrefixes {
81 private:
82     std::set<pstring> pset;
83
84     void addPrefix(char* startbuf, int n) {
85         std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));
86         bool replace = elem != pset.end() && n < elem->n;
87         if (replace) {
88             pset.erase(elem);
89         }
90         if (replace || elem == pset.end()) {
91             pset.insert(pstring(startbuf, n));
92         }
93     }
94 public:
95     StringPrefixes();
96
97     bool contain(const char* s) {
98         return pset.find(pstring(s, strlen(s) + 1)) != pset.end();
99     }
100 };
101
102 StringPrefixes::StringPrefixes() {
103     char *list = getenv("APITRACE_BACKTRACE");
104     if (!list)
105         return;
106     for (char *t = strdup(list); ; t = NULL) {
107         char *tok = strtok(t, " \t\r\n");
108         if (!tok)
109             break;
110         if (tok[0] == '#')
111             continue;
112         if (tok[strlen(tok) - 1] == '*')
113             addPrefix(tok, strlen(tok) - 1);
114         else
115             addPrefix(tok, strlen(tok) + 1);
116     }
117 }
118
119
120 bool backtrace_is_needed(const char* fname) {
121     static StringPrefixes backtraceFunctionNamePrefixes;
122     return backtraceFunctionNamePrefixes.contain(fname);
123 }
124
125 #if defined(ANDROID)
126
127 /* The following two declarations are copied from Android sources */
128
129 enum DebugTargetKind {
130     kDebugTargetUnknown = 0,
131     kDebugTargetLog,
132     kDebugTargetFile,
133 };
134
135 struct DebugOutputTarget {
136     DebugTargetKind which;
137
138     union {
139         struct {
140             int priority;
141             const char* tag;
142         } log;
143         struct {
144             FILE* fp;
145         } file;
146     } data;
147 };
148
149 #define THREAD_SELF_NAME "_Z13dvmThreadSelfv"
150 #define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"
151 #define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
152
153
154 class DalvikBacktraceProvider {
155 private:
156     bool errorOccured;
157     void* (*threadself)(void);
158     FILE* streamInMemory;
159     char* buf;
160     size_t bufSize;
161     void (*dumpBacktrace)(const DebugOutputTarget*, void*);
162     DebugOutputTarget debugTarget;
163     Id nextFrameId;
164 public:
165     DalvikBacktraceProvider() {
166         nextFrameId = 0;
167         FILE* (*open_memstream_exp)(char**, size_t*);
168         void (*createDebugTarget)(DebugOutputTarget*, FILE*);
169         void* handle = dlopen("/system/lib/libdvm.so", 0);
170         errorOccured = true;
171         if (!handle) {
172             os::log("dlopen failed\n");
173             return;
174         }
175         threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);
176         if (threadself == NULL) {
177             os::log("dlsym ThreadSelf failed\n");
178             return;
179         }
180         createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);
181         if (createDebugTarget == NULL) {
182             os::log("dlsym CreateFileOutput failed\n");
183             return;
184         }
185         dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);
186         if (dumpBacktrace == NULL) {
187             os::log("dlsym DumpThreadStack failed\n");
188             return;
189         }
190         void* handle2 = dlopen("/system/lib/libcutils.so", 0);
191         if (!handle2) {
192             os::log("dlopen failed\n");
193             return;
194         }
195         open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
196         if (open_memstream_exp == NULL) {
197             os::log("dlsym open_memstream failed\n");
198             return;
199         }
200         streamInMemory = open_memstream_exp(&buf, &bufSize);
201         if (streamInMemory == NULL) {
202             os::log("open_memstream failed\n");
203             return;
204         }
205         createDebugTarget(&debugTarget, streamInMemory);
206         errorOccured = false;
207     }
208
209     inline char* getBacktrace() {
210         if (errorOccured) {
211             return NULL;
212         }
213         rewind(streamInMemory);
214         dumpBacktrace(&debugTarget, threadself());
215         fflush(streamInMemory);
216         return buf;
217     }
218 /*
219  * Parse a stack frame, expecting:
220  * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"
221  * or
222  * "  at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"
223  */
224     std::vector<RawStackFrame> parseBacktrace(char *rawBacktrace) {
225         std::vector<RawStackFrame> parsedBacktrace;
226         char* rawBacktrace_it = rawBacktrace;
227         while (*rawBacktrace_it != '\0') {
228             RawStackFrame stackFrame;
229             // TODO: Keep a cache of stack frames
230             stackFrame.id = nextFrameId++;
231             /* skip leading space */
232             while (*rawBacktrace_it == ' ') {
233                 rawBacktrace_it++;
234             }
235             /* Skip "at " */
236             rawBacktrace_it += 3;
237             stackFrame.function = rawBacktrace_it;
238             while (*rawBacktrace_it != '(') {
239                 rawBacktrace_it++;
240             }
241             *rawBacktrace_it = '\0';
242             stackFrame.filename = rawBacktrace_it + 1;
243             while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {
244                 rawBacktrace_it++;
245             }
246             if (*rawBacktrace_it == ':') {
247                 const char *linenumber = rawBacktrace_it + 1;
248                 *rawBacktrace_it = '\0';
249                 while (*rawBacktrace_it != ')') {
250                     rawBacktrace_it++;
251                 }
252                 *rawBacktrace_it = '\0';
253                 rawBacktrace_it++;
254                 stackFrame.linenumber = atoi(linenumber);
255             }
256             else {
257                 stackFrame.filename = NULL;
258                 while (*rawBacktrace_it != '\n') {
259                     rawBacktrace_it++;
260                 }
261             }
262             while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {
263                     rawBacktrace_it++;
264             }
265             parsedBacktrace.push_back(stackFrame); /* module */
266         }
267         return parsedBacktrace;
268     }
269 };
270
271 std::vector<RawStackFrame> get_backtrace() {
272     static DalvikBacktraceProvider backtraceProvider;
273     return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());
274 }
275
276 void dump_backtrace() {
277     /* TODO */
278 }
279
280
281 #elif HAVE_BACKTRACE
282
283
284 static char* format(uintptr_t num, int base, char *buf, int maxlen)
285 {
286     static const char digits[] = "0123456789abcdef";
287     buf += maxlen;
288     do {
289         *--buf = digits[num % base];
290         num /= base;
291         maxlen--;
292     } while (num != 0 && maxlen != 0);
293     return buf;
294 }
295
296 static void dump(const char *str, int len)
297 {
298     static int fd = dup(STDERR_FILENO);
299     if (write(fd, str, len) != len) {
300         // Do nothing
301     }
302 }
303
304 static void dumpFrame(const RawStackFrame &frame)
305 {
306     char buf[sizeof(long long) * 2], *p;
307 #define DUMP(string) dump(string, strlen(string))
308     DUMP(frame.module ? frame.module : "?");
309     if (frame.function) {
310         DUMP(": ");
311         DUMP(frame.function);
312     }
313     if (frame.offset >= 0) {
314         DUMP("+0x");
315         p = format((uintptr_t) frame.offset, 16, buf, sizeof(buf));
316         dump(p, buf + sizeof(buf) - p);
317     }
318     if (frame.filename) {
319         DUMP(": ");
320         DUMP(frame.filename);
321         if (frame.linenumber >= 0) {
322             DUMP(":");
323             p = format((uintptr_t) frame.linenumber, 10, buf, sizeof(buf));
324             dump(p, buf + sizeof(buf) - p);
325         }
326     }
327     DUMP("\n");
328 #undef DUMP
329 }
330
331
332 #define BT_DEPTH 10
333
334 class libbacktraceProvider {
335     struct backtrace_state *state;
336     int skipFrames;
337     Id nextFrameId;
338     std::map<uintptr_t, std::vector<RawStackFrame> > cache;
339     std::vector<RawStackFrame> *current, *current_frames;
340     RawStackFrame *current_frame;
341     bool missingDwarf;
342
343     static void bt_err_callback(void *vdata, const char *msg, int errnum)
344     {
345         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
346         if (errnum == -1)
347             this_->missingDwarf = true;
348         else if (errnum)
349             os::log("libbacktrace: %s: %s\n", msg, strerror(errnum));
350         else
351             os::log("libbacktrace: %s\n", msg);
352     }
353
354     static int bt_countskip(void *vdata, uintptr_t pc)
355     {
356         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
357         Dl_info info1, info2;
358         if (!dladdr((void*)bt_countskip, &info2)) {
359             os::log("dladdr failed, cannot cull stack traces\n");
360             return 1;
361         }
362         if (!dladdr((void*)pc, &info1))
363             return 1;
364         if (info1.dli_fbase != info2.dli_fbase)
365             return 1;
366         this_->skipFrames++;
367         return 0;
368     }
369
370     static int bt_full_callback(void *vdata, uintptr_t pc,
371                                  const char *file, int line, const char *func)
372     {
373         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
374         RawStackFrame frame = *this_->current_frame;
375         frame.id = this_->nextFrameId++;
376         frame.filename = file;
377         frame.linenumber = line;
378         if (func)
379             frame.function = func;
380         int status;
381         if (func && (func = abi::__cxa_demangle(func, NULL, NULL, &status)))
382             frame.function = func;
383         this_->current_frames->push_back(frame);
384         return 0;
385     }
386
387     static void dl_fill(RawStackFrame *frame, uintptr_t pc)
388     {
389         Dl_info info = {0};
390         dladdr((void*)pc, &info);
391         frame->module = info.dli_fname;
392         frame->function = info.dli_sname;
393         frame->offset = info.dli_saddr ? pc - (uintptr_t)info.dli_saddr
394                                        : pc - (uintptr_t)info.dli_fbase;
395     }
396
397     static int bt_callback(void *vdata, uintptr_t pc)
398     {
399         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
400         std::vector<RawStackFrame> &frames = this_->cache[pc];
401         if (!frames.size()) {
402             RawStackFrame frame;
403             dl_fill(&frame, pc);
404             this_->current_frame = &frame;
405             this_->current_frames = &frames;
406             backtrace_pcinfo(this_->state, pc, bt_full_callback, bt_err_callback, vdata);
407             if (!frames.size()) {
408                 frame.id = this_->nextFrameId++;
409                 frames.push_back(frame);
410             }
411         }
412         this_->current->insert(this_->current->end(), frames.begin(), frames.end());
413         return this_->current->size() >= BT_DEPTH;
414     }
415
416     static int bt_full_dump_callback(void *vdata, uintptr_t pc,
417                                      const char *file, int line, const char *func)
418     {
419         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
420         RawStackFrame *frame = this_->current_frame;
421         frame->filename = file;
422         frame->linenumber = line;
423         if (func)
424             frame->function = func;
425         dumpFrame(*frame);
426         return 0;
427     }
428
429     static int bt_dump_callback(void *vdata, uintptr_t pc)
430     {
431         libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
432         RawStackFrame frame;
433         dl_fill(&frame, pc);
434         this_->current_frame = &frame;
435         this_->missingDwarf = false;
436         backtrace_pcinfo(this_->state, pc, bt_full_dump_callback, bt_err_callback, vdata);
437         if (this_->missingDwarf)
438             dumpFrame(frame);
439         return 0;
440     }
441
442 public:
443     libbacktraceProvider():
444         state(backtrace_create_state(NULL, 0, bt_err_callback, NULL))
445     {
446         backtrace_simple(state, 0, bt_countskip, bt_err_callback, this);
447     }
448
449     std::vector<RawStackFrame> getParsedBacktrace()
450     {
451         std::vector<RawStackFrame> parsedBacktrace;
452         current = &parsedBacktrace;
453         backtrace_simple(state, skipFrames, bt_callback, bt_err_callback, this);
454         return parsedBacktrace;
455     }
456
457     void dumpBacktrace()
458     {
459         backtrace_simple(state, 0, bt_dump_callback, bt_err_callback, this);
460     }
461 };
462
463 std::vector<RawStackFrame> get_backtrace() {
464     static libbacktraceProvider backtraceProvider;
465     return backtraceProvider.getParsedBacktrace();
466 }
467
468 void dump_backtrace() {
469     static libbacktraceProvider backtraceProvider;
470     backtraceProvider.dumpBacktrace();
471 }
472
473
474 #else /* !HAVE_BACKTRACE */
475
476 std::vector<RawStackFrame> get_backtrace() {
477     return std::vector<RawStackFrame>();
478 }
479
480 void dump_backtrace() {
481 }
482
483 #endif
484
485
486 } /* namespace os */