1 /* Copyright (C) 2007-2011 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 ** GNU General Public License for more details.
11 */
12 
13 /*
14  * Contains implementation of a class DumpFile of routines that implements
15  * access to a log file.
16  */
17 
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include "regex/regex.h"
24 #include "elff/elff_api.h"
25 
26 #include "ndk-stack-parser.h"
27 
28 /* Enumerates states of the crash parser.
29  */
30 typedef enum NDK_CRASH_PARSER_STATE {
31   /* Parser expects the beginning of the crash dump. */
32   EXPECTS_CRASH_DUMP,
33   /* Parser expects the build fingerprint, or process and thread information. */
34   EXPECTS_BUILD_FINGREPRINT_OR_PID,
35   /* Parser expects the process and thread information. */
36   EXPECTS_PID,
37   /* Parser expects the signal information, or the first crash frame. */
38   EXPECTS_SIGNAL_OR_FRAME,
39   /* Parser expects a crash frame. */
40   EXPECTS_FRAME,
41 } NDK_CRASH_PARSER_STATE;
42 
43 /* Crash parser descriptor.
44  */
45 struct NdkCrashParser {
46   /* Handle to the stream where to print the output. */
47   FILE*                 out_handle;
48 
49   /* Path to the root folder where symbols are stored. */
50   char*                 sym_root;
51 
52   /* Current state of the parser. */
53   NDK_CRASH_PARSER_STATE state;
54 
55   /* Compiled regular expressions */
56   regex_t     re_pid_header;
57   regex_t     re_sig_header;
58   regex_t     re_frame_header;
59 };
60 
61 /* Crash dumps begin with a string containing this substring. */
62 static const char _crash_dump_header[] =
63   "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***";
64 
65 /* Build fingerprint contains this substring. */
66 static const char _build_fingerprint_header[] = "Build fingerprint:";
67 
68 /* Regular expression for the process ID information line. */
69 static const char _pid_header[] = "pid: [0-9]+, tid: [0-9]+.*";
70 
71 /* Regular expression for the signal information line. */
72 static const char _sig_header[] = "signal*[ \t][0-9]+";
73 
74 /* Regular expression for the frame information line. */
75 static const char _frame_header[] = "\\#[0-9]+[ |\t]+[pc|eip]+:*[ |\t]+([0-9a-f]{8})*";
76 
77 #ifndef min
78 #define min(a,b) (((a) < (b)) ? a : b)
79 #endif
80 
81 /* Parses a line representing a crash frame.
82  * This routine will try to obtain source file / line information for the
83  * frame's address, and print that information to the specified output handle.
84  * Param:
85  *  parser - NdkCrashParser descriptor, created and initialized with a call to
86  *    NdkCrashParser routine.
87  *  frame - Line containing crash frame.
88  * Return:
89  *  0 If source file information has been found and printed, or -1 if that
90  *  information was not available.
91  */
92 static int ParseFrame(NdkCrashParser* parser, const char* frame);
93 
94 /* Matches a string against a regular expression.
95  * Param:
96  *  line - String to matches against the regular expression.
97  *  regex - Regular expression to match the string against.
98  *  match - Upon successful match contains information about the part of the
99  *    string that matches the regular expression.
100  * Return:
101  *  Boolean: 1 if a match has been found, or 0 if match has not been found in
102  *  the string.
103  */
104 static int MatchRegex(const char* line, const regex_t* regex, regmatch_t* match);
105 
106 /* Returns pointer to the next separator (a space, or a tab) in the string. */
107 static const char* next_separator(const char* str);
108 
109 /* Returns pointer to the next token (a character other than space, or a tab)
110  * in the string.
111  */
112 static const char* next_token(const char* str);
113 
114 /* Gets next token from the string.
115  * param:
116  *  str - String where to get the next token from. Note that if string begins
117  *    with a separator, this routine will return first token after that
118  *    separator. If string begins with a token, this routine will return next
119  *    token after that.
120  *  token - Upon success contains a copy of the next token in the string.
121  *  size - Size of the 'token' buffer.
122  * Return:
123  *  Beginning of the returned token in the string.
124  */
125 static const char* get_next_token(const char* str, char* token, size_t size);
126 
127 /* Return pointer to first word "pc", "eip", or "ip" in string "frame"
128  * param:
129  *  frame - a line from dump
130  * Return:
131  *  The first occurrence of "pc", "eip", or "ip"
132  */
133 static const char* find_pc(const char *frame);
134 
135 NdkCrashParser*
CreateNdkCrashParser(FILE * out_handle,const char * sym_root)136 CreateNdkCrashParser(FILE* out_handle, const char* sym_root)
137 {
138   NdkCrashParser* parser;
139 
140   parser = (NdkCrashParser*)calloc(sizeof(*parser), 1);
141   if (parser == NULL)
142       return NULL;
143 
144   parser->out_handle = out_handle;
145   parser->state      = EXPECTS_CRASH_DUMP;
146 
147   parser->sym_root = strdup(sym_root);
148   if (!parser->sym_root)
149       goto BAD_INIT;
150 
151   if (regcomp(&parser->re_pid_header, _pid_header, REG_EXTENDED | REG_NEWLINE) ||
152       regcomp(&parser->re_sig_header, _sig_header, REG_EXTENDED | REG_NEWLINE) ||
153       regcomp(&parser->re_frame_header, _frame_header, REG_EXTENDED | REG_NEWLINE))
154       goto BAD_INIT;
155 
156   return parser;
157 
158 BAD_INIT:
159   DestroyNdkCrashParser(parser);
160   return NULL;
161 }
162 
163 void
DestroyNdkCrashParser(NdkCrashParser * parser)164 DestroyNdkCrashParser(NdkCrashParser* parser)
165 {
166   if (parser != NULL) {
167     /* Release compiled regular expressions */
168     regfree(&parser->re_frame_header);
169     regfree(&parser->re_sig_header);
170     regfree(&parser->re_pid_header);
171     /* Release symbol path */
172     free(parser->sym_root);
173     /* Release parser itself */
174     free(parser);
175   }
176 }
177 
178 int
ParseLine(NdkCrashParser * parser,const char * line)179 ParseLine(NdkCrashParser* parser, const char* line)
180 {
181   regmatch_t match;
182   int found = 0;
183 
184   if (line == NULL || *line == '\0') {
185     // Nothing to parse.
186     return 1;
187   }
188 
189   // Lets see if this is the beginning of a crash dump.
190   if (strstr(line, _crash_dump_header) != NULL) {
191     if (parser->state != EXPECTS_CRASH_DUMP) {
192       // Printing another crash dump was in progress. Mark the end of it.
193       fprintf(parser->out_handle, "Crash dump is completed\n\n");
194     }
195 
196     // New crash dump begins.
197     fprintf(parser->out_handle, "********** Crash dump: **********\n");
198     parser->state = EXPECTS_BUILD_FINGREPRINT_OR_PID;
199 
200     return 0;
201   }
202 
203   switch (parser->state) {
204     case EXPECTS_BUILD_FINGREPRINT_OR_PID:
205       if (strstr(line, _build_fingerprint_header) != NULL) {
206         fprintf(parser->out_handle, "%s\n",
207                 strstr(line, _build_fingerprint_header));
208         parser->state = EXPECTS_PID;
209         found = 1;
210       }
211       // Let it fall through to the EXPECTS_PID, in case the dump doesn't
212       // contain build fingerprint.
213     case EXPECTS_PID:
214       if (MatchRegex(line, &parser->re_pid_header, &match)) {
215         fprintf(parser->out_handle, "%s\n", line + match.rm_so);
216         parser->state = EXPECTS_SIGNAL_OR_FRAME;
217         return 0;
218       } else {
219         return !found;
220       }
221 
222     case EXPECTS_SIGNAL_OR_FRAME:
223       if (MatchRegex(line, &parser->re_sig_header, &match)) {
224         fprintf(parser->out_handle, "%s\n", line + match.rm_so);
225         parser->state = EXPECTS_FRAME;
226         found = 1;
227       }
228       // Let it fall through to the EXPECTS_FRAME, in case the dump doesn't
229       // contain signal fingerprint.
230     case EXPECTS_FRAME:
231       if (!MatchRegex(line, &parser->re_frame_header, &match))
232         return !found;
233       // Regex generated by x86_64-w64-mingw32 compiler erroneously match
234       // frame line with #[0-9]+ in "stack:" section even when the line has
235       //  no word "pc", "eip", or "ip" in it.
236       //
237       //   stack:
238       //      I/DEBUG   ( 1151):     #00  5f09db68  401f01c4  /system/lib/libc.so
239       //
240       // To workaround, let's double check if pc is found!
241       //
242       if (!(find_pc(line)))
243         return !found;
244 
245       parser->state = EXPECTS_FRAME;
246       return ParseFrame(parser, line + match.rm_so);
247 
248     default:
249       return 1;
250   }
251 }
252 
253 static int
MatchRegex(const char * line,const regex_t * regex,regmatch_t * match)254 MatchRegex(const char* line, const regex_t* regex, regmatch_t* match)
255 {
256   int err = regexec(regex, line, 1, match, 0x00400/*REG_TRACE*/);
257 #if 0
258   char rerr[4096];
259   if (err) {
260     regerror(err, regex, rerr, sizeof(rerr));
261     fprintf(stderr, "regexec(%s, %s) has failed: %s\n", line, regex, rerr);
262   }
263 #endif
264 
265   return err == 0;
266 }
267 
268 static const char*
next_separator(const char * str)269 next_separator(const char* str)
270 {
271   return str + strcspn(str, " \t");
272 }
273 
274 static const char*
next_token(const char * str)275 next_token(const char* str)
276 {
277   str = next_separator(str);
278   return str + strspn(str, " \t");
279 }
280 
281 static const char*
get_next_token(const char * str,char * token,size_t size)282 get_next_token(const char* str, char* token, size_t size)
283 {
284   const char* start = next_token(str);
285   const char* end = next_separator(start);
286   if (start != end) {
287     const size_t to_copy = min((size_t)(end - start), (size - 1));
288     memcpy(token, start, to_copy);
289     token[to_copy] = '\0';
290     return start;
291   } else {
292     return NULL;
293   }
294 }
295 
296 static const char *
find_pc(const char * frame)297 find_pc(const char *frame)
298 {
299   const char *pcstrs[] = { "pc", "eip", "ip" };
300   int i;
301   for (i=0; i<sizeof(pcstrs)/sizeof(pcstrs[0]); i++) {
302     const char *p = strstr(frame, pcstrs[i]);
303     // check it's a word, not part of filename or something
304     if (p && p!=frame) {
305       char l = p[-1];
306       char r = p[strlen(pcstrs[i])];
307       if ((l==' ' || l=='\t') && (r==' ' || r=='\t'))
308         return p;
309     }
310   }
311   return NULL;
312 }
313 
314 int
ParseFrame(NdkCrashParser * parser,const char * frame)315 ParseFrame(NdkCrashParser* parser, const char* frame)
316 {
317   uint64_t address;
318   const char* wrk;
319   char* eptr;
320   char pc_address[17];
321   char module_path[2048];
322   char* module_name;
323   char sym_file[2048];
324 #if !defined(WITH_LIBBFD)
325   ELFF_HANDLE elff_handle;
326   Elf_AddressInfo pc_info;
327 #else
328   const int ac = 5;
329   char *av[ac];
330   FILE *f;
331 #endif
332 
333   fprintf(parser->out_handle, "Stack frame %s", frame);
334 
335   // Advance to the instruction pointer token.
336   if ((wrk=find_pc(frame)) == NULL) {
337     fprintf(parser->out_handle,
338             "Parser is unable to locate instruction pointer token.\n");
339     return -1;
340   }
341 
342   // Next token after the instruction pointer token is its address.
343   wrk = get_next_token(wrk, pc_address, sizeof(pc_address));
344   // PC address is a hex value. Get it.
345   eptr = pc_address + strlen(pc_address);
346   address = strtoul(pc_address, &eptr, 16);
347 
348   // Next token is module path.
349   get_next_token(wrk, module_path, sizeof(module_path));
350 
351   // Extract basename of module, we should not care about its path
352   // on the device.
353   module_name = strrchr(module_path,'/');
354   if (module_name == NULL)
355       module_name = module_path;
356   else {
357       module_name += 1;
358       if (*module_name == '\0') {
359           /* Trailing slash in the module path, this should not happen */
360           /* Back-off with the full module-path */
361           module_name = module_path;
362       }
363   }
364 
365   // Build path to the symbol file.
366   snprintf(sym_file, sizeof(sym_file), "%s/%s", parser->sym_root, module_name);
367 
368 #if defined(WITH_LIBBFD)
369   if ((f=fopen(sym_file, "r")) == NULL) {
370     if (errno == ENOENT) {
371         printf("\n");
372     } else {
373         printf(": Unable to open symbol file %s. Error (%d): %s\n",
374                 sym_file, errno, strerror(errno));
375     }
376     return -1;
377   }
378 
379   // call addr2line if sym_file exist
380   extern int addr2line_main (int argc, char **argv);
381 
382   av[0] = "ndk-stack";
383   av[1] = "-fpC";  // f:function, p:pretty-print, C:demangle
384   av[2] = "-e";    // e:exe-filename
385   av[3] = sym_file;
386   av[4] = pc_address;
387   (void)address;
388 
389   printf(": Routine ");
390   return addr2line_main(ac, av);
391 #else
392   // Init ELFF wrapper for the symbol file.
393   elff_handle = elff_init(sym_file);
394   if (elff_handle == NULL) {
395     if (errno == ENOENT) {
396         fprintf(parser->out_handle, "\n");
397     } else {
398         fprintf(parser->out_handle,
399                 ": Unable to open symbol file %s. Error (%d): %s\n",
400                 sym_file, errno, strerror(errno));
401     }
402     return -1;
403   }
404   // Extract address info from the symbol file.
405   if (!elff_get_pc_address_info(elff_handle, address, &pc_info)) {
406     if (pc_info.dir_name != NULL) {
407       fprintf(parser->out_handle, ": Routine %s in %s/%s:%d\n",
408               pc_info.routine_name, pc_info.dir_name, pc_info.file_name,
409               pc_info.line_number);
410     } else {
411       fprintf(parser->out_handle, ": Routine %s in %s:%d\n",
412               pc_info.routine_name, pc_info.file_name, pc_info.line_number);
413     }
414     elff_free_pc_address_info(elff_handle, &pc_info);
415     elff_close(elff_handle);
416     return 0;
417   } else {
418     fprintf(parser->out_handle,
419             ": Unable to locate routine information for address %x in module %s\n",
420             (uint32_t)address, sym_file);
421     elff_close(elff_handle);
422     return -1;
423   }
424 #endif // WITH_LIBBFD
425 }
426