]> git.cworth.org Git - apitrace/blob - common/trace_backtrace.cpp
Cleanup backtrace setup code
[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) or defined(__linux__)
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 #ifdef 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 __linux__
274
275 #include <execinfo.h>
276 #include <string.h>
277 #include <stdlib.h>
278 #include <map>
279 #include <vector>
280 #include <stdio.h>
281
282 namespace trace {
283
284
285
286
287 #define BT_DEPTH 10
288
289 class GlibcBacktraceProvider {
290 private:
291     std::map<void*, RawStackFrame*> cache;
292     /*
293      * Backtrace being returned by glibc backtrace() contains stack frames
294      * belonging to apitrace wrapper module. We count the number of apitrace
295      * functions on the stack to avoid recording these frames.
296      */
297     int numOfNestedFunctions;
298     Id nextFrameId;
299 private:
300 /*
301  * Parse a stack frame, expecting:
302  * /lib/libc.so.6.1(__libc_start_main+0x50308) [0x2000000000097630]
303  * or
304  * /lib/libc.so.6.1(+0x50308) [0x2000000000097630]
305  * or
306  * /lib/libc.so.6.1() [0x2000000000097630]
307  */
308     RawStackFrame* parseFrame(void* frame, char* frame_symbol) {
309         if (cache.find(frame) == cache.end()) {
310             char* frame_symbol_copy = new char[strlen(frame_symbol) + 1];
311             strcpy(frame_symbol_copy, frame_symbol);
312             RawStackFrame* parsedFrame = new RawStackFrame;
313             parsedFrame->id = nextFrameId++;
314             char* frame_it = frame_symbol_copy;
315             parsedFrame->module = frame_it;
316             char* offset = NULL;
317             while (true) {
318                 switch (*frame_it) {
319                 case '(':
320                     *frame_it = '\0';
321                     frame_it++;
322                     if (*frame_it != ')' && *frame_it != '+') {
323                         parsedFrame->function = frame_it;
324                         while (*frame_it != '+' && *frame_it != ')') {
325                             frame_it++;
326                         }
327                         *frame_it = '\0';
328                         frame_it++;
329                     }
330                     break;
331                 case '[':
332                     *frame_it = '\0';
333                     frame_it++;
334                     offset = frame_it;
335                     break;
336                 case ']':
337                     *frame_it = '\0';
338                     sscanf(offset, "%llx", &parsedFrame->offset);
339                     cache[frame] = parsedFrame;
340                     return parsedFrame;
341                 case '\0':
342                     cache[frame] = NULL;
343                     delete[] frame_symbol_copy;
344                     delete[] parsedFrame;
345                     return NULL;
346                 default:
347                     frame_it++;
348                 }
349             }
350         }
351         else {
352             return cache[frame];
353         }
354     }
355 public:
356     GlibcBacktraceProvider() :
357       numOfNestedFunctions(0),
358       nextFrameId(0)
359     {}
360
361     std::vector<RawStackFrame> getParsedBacktrace() {
362         std::vector<RawStackFrame> parsedBacktrace;
363         void *array[numOfNestedFunctions + BT_DEPTH];
364         size_t size;
365         char **strings;
366         size_t i;
367         const char* firstModule;
368         size = backtrace(array, numOfNestedFunctions + BT_DEPTH);
369         strings = backtrace_symbols(array, size);
370         for (i = numOfNestedFunctions; i < size; i++) {
371             RawStackFrame* parsedFrame = parseFrame(array[i], strings[i]);
372             if (numOfNestedFunctions == 0) {
373                 if (i == 0) {
374                     firstModule = parsedFrame->module;
375                 }
376                 else {
377                     if (strcmp(firstModule, parsedFrame->module)) {
378                         numOfNestedFunctions = i;
379                         free(strings);
380                         parsedBacktrace = getParsedBacktrace();
381                         numOfNestedFunctions--;
382                         return parsedBacktrace;
383                     }
384                 }
385             } else {
386                 if (parsedFrame != NULL) {
387                     parsedBacktrace.push_back(*parsedFrame);
388                 }
389             }
390         }
391         free(strings);
392         return parsedBacktrace;
393     }
394 };
395
396
397 std::vector<RawStackFrame> get_backtrace() {
398     static GlibcBacktraceProvider backtraceProvider;
399     return backtraceProvider.getParsedBacktrace();
400 }
401
402 #endif /* LINUX */
403
404 } /* namespace trace */
405
406 #endif /* ANDROID or LINUX */