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