Home | History | Annotate | Download | only in ndk-stack
      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*
    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
    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
    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
    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*
    269 next_separator(const char* str)
    270 {
    271   return str + strcspn(str, " \t");
    272 }
    273 
    274 static const char*
    275 next_token(const char* str)
    276 {
    277   str = next_separator(str);
    278   return str + strspn(str, " \t");
    279 }
    280 
    281 static const char*
    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 *
    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
    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