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 <stdio.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <errno.h>
     22 #include "regex/regex.h"
     23 #include "elff/elff_api.h"
     24 
     25 #include "ndk-stack-parser.h"
     26 
     27 /* Enumerates states of the crash parser.
     28  */
     29 typedef enum NDK_CRASH_PARSER_STATE {
     30   /* Parser expects the beginning of the crash dump. */
     31   EXPECTS_CRASH_DUMP,
     32   /* Parser expects the build fingerprint, or process and thread information. */
     33   EXPECTS_BUILD_FINGREPRINT_OR_PID,
     34   /* Parser expects the process and thread information. */
     35   EXPECTS_PID,
     36   /* Parser expects the signal information, or the first crash frame. */
     37   EXPECTS_SIGNAL_OR_FRAME,
     38   /* Parser expects a crash frame. */
     39   EXPECTS_FRAME,
     40 } NDK_CRASH_PARSER_STATE;
     41 
     42 /* Crash parser descriptor.
     43  */
     44 struct NdkCrashParser {
     45   /* Handle to the stream where to print the output. */
     46   FILE*                 out_handle;
     47 
     48   /* Path to the root folder where symbols are stored. */
     49   char*                 sym_root;
     50 
     51   /* Current state of the parser. */
     52   NDK_CRASH_PARSER_STATE state;
     53 
     54   /* Compiled regular expressions */
     55   regex_t     re_pid_header;
     56   regex_t     re_sig_header;
     57   regex_t     re_frame_header;
     58 };
     59 
     60 /* Crash dumps begin with a string containing this substring. */
     61 static const char _crash_dump_header[] =
     62   "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***";
     63 
     64 /* Build fingerprint contains this substring. */
     65 static const char _build_fingerprint_header[] = "Build fingerprint:";
     66 
     67 /* Regular expression for the process ID information line. */
     68 static const char _pid_header[] = "pid: [0-9]+, tid: [0-9]+.*";
     69 
     70 /* Regular expression for the signal information line. */
     71 static const char _sig_header[] = "signal*[ \t][0-9]+";
     72 
     73 /* Regular expression for the frame information line. */
     74 static const char _frame_header[] = "\\#[0-9]+[ |\t]+[pc|eip]+:*[ |\t]+([0-9a-f]{8})*";
     75 
     76 #ifndef min
     77 #define min(a,b) (((a) < (b)) ? a : b)
     78 #endif
     79 
     80 /* Parses a line representing a crash frame.
     81  * This routine will try to obtain source file / line information for the
     82  * frame's address, and print that information to the specified output handle.
     83  * Param:
     84  *  parser - NdkCrashParser descriptor, created and initialized with a call to
     85  *    NdkCrashParser routine.
     86  *  frame - Line containing crash frame.
     87  * Return:
     88  *  0 If source file information has been found and printed, or -1 if that
     89  *  information was not available.
     90  */
     91 static int ParseFrame(NdkCrashParser* parser, const char* frame);
     92 
     93 /* Matches a string against a regular expression.
     94  * Param:
     95  *  line - String to matches against the regular expression.
     96  *  regex - Regular expression to match the string against.
     97  *  match - Upon successful match contains information about the part of the
     98  *    string that matches the regular expression.
     99  * Return:
    100  *  Boolean: 1 if a match has been found, or 0 if match has not been found in
    101  *  the string.
    102  */
    103 static int MatchRegex(const char* line, const regex_t* regex, regmatch_t* match);
    104 
    105 /* Returns pointer to the next separator (a space, or a tab) in the string. */
    106 static const char* next_separator(const char* str);
    107 
    108 /* Returns pointer to the next token (a character other than space, or a tab)
    109  * in the string.
    110  */
    111 static const char* next_token(const char* str);
    112 
    113 /* Gets next token from the string.
    114  * param:
    115  *  str - String where to get the next token from. Note that if string begins
    116  *    with a separator, this routine will return first token after that
    117  *    separator. If string begins with a token, this routine will return next
    118  *    token after that.
    119  *  token - Upon success contains a copy of the next token in the string.
    120  *  size - Size of the 'token' buffer.
    121  * Return:
    122  *  Beginning of the returned token in the string.
    123  */
    124 static const char* get_next_token(const char* str, char* token, size_t size);
    125 
    126 NdkCrashParser*
    127 CreateNdkCrashParser(FILE* out_handle, const char* sym_root)
    128 {
    129   int ok;
    130   NdkCrashParser* parser;
    131 
    132   parser = (NdkCrashParser*)calloc(sizeof(*parser), 1);
    133   if (parser == NULL)
    134       return NULL;
    135 
    136   parser->out_handle = out_handle;
    137   parser->state      = EXPECTS_CRASH_DUMP;
    138 
    139   parser->sym_root = strdup(sym_root);
    140   if (!parser->sym_root)
    141       goto BAD_INIT;
    142 
    143   if (regcomp(&parser->re_pid_header, _pid_header, REG_EXTENDED | REG_NEWLINE) ||
    144       regcomp(&parser->re_sig_header, _sig_header, REG_EXTENDED | REG_NEWLINE) ||
    145       regcomp(&parser->re_frame_header, _frame_header, REG_EXTENDED | REG_NEWLINE))
    146       goto BAD_INIT;
    147 
    148   return parser;
    149 
    150 BAD_INIT:
    151   DestroyNdkCrashParser(parser);
    152   return NULL;
    153 }
    154 
    155 void
    156 DestroyNdkCrashParser(NdkCrashParser* parser)
    157 {
    158   if (parser != NULL) {
    159     /* Release compiled regular expressions */
    160     regfree(&parser->re_frame_header);
    161     regfree(&parser->re_sig_header);
    162     regfree(&parser->re_pid_header);
    163     /* Release symbol path */
    164     free(parser->sym_root);
    165     /* Release parser itself */
    166     free(parser);
    167   }
    168 }
    169 
    170 int
    171 ParseLine(NdkCrashParser* parser, const char* line)
    172 {
    173   regmatch_t match;
    174 
    175   if (line == NULL || *line == '\0') {
    176     // Nothing to parse.
    177     return 1;
    178   }
    179 
    180   // Lets see if this is the beginning of a crash dump.
    181   if (strstr(line, _crash_dump_header) != NULL) {
    182     if (parser->state != EXPECTS_CRASH_DUMP) {
    183       // Printing another crash dump was in progress. Mark the end of it.
    184       fprintf(parser->out_handle, "Crash dump is completed\n\n");
    185     }
    186 
    187     // New crash dump begins.
    188     fprintf(parser->out_handle, "********** Crash dump: **********\n");
    189     parser->state = EXPECTS_BUILD_FINGREPRINT_OR_PID;
    190 
    191     return 0;
    192   }
    193 
    194   switch (parser->state) {
    195     case EXPECTS_BUILD_FINGREPRINT_OR_PID:
    196       if (strstr(line, _build_fingerprint_header) != NULL) {
    197         fprintf(parser->out_handle, "%s\n",
    198                 strstr(line, _build_fingerprint_header));
    199         parser->state = EXPECTS_PID;
    200       }
    201       // Let it fall through to the EXPECTS_PID, in case the dump doesn't
    202       // contain build fingerprint.
    203     case EXPECTS_PID:
    204       if (MatchRegex(line, &parser->re_pid_header, &match)) {
    205         fprintf(parser->out_handle, "%s\n", line + match.rm_so);
    206         parser->state = EXPECTS_SIGNAL_OR_FRAME;
    207         return 0;
    208       } else {
    209         return 1;
    210       }
    211 
    212     case EXPECTS_SIGNAL_OR_FRAME:
    213       if (MatchRegex(line, &parser->re_sig_header, &match)) {
    214         fprintf(parser->out_handle, "%s\n", line + match.rm_so);
    215         parser->state = EXPECTS_FRAME;
    216       }
    217       // Let it fall through to the EXPECTS_FRAME, in case the dump doesn't
    218       // contain signal fingerprint.
    219     case EXPECTS_FRAME:
    220       if (MatchRegex(line, &parser->re_frame_header, &match)) {
    221         parser->state = EXPECTS_FRAME;
    222         return ParseFrame(parser, line + match.rm_so);
    223       } else {
    224         return 1;
    225       }
    226 
    227     default:
    228       return 1;
    229   }
    230 }
    231 
    232 static int
    233 MatchRegex(const char* line, const regex_t* regex, regmatch_t* match)
    234 {
    235   char rerr[4096];
    236   regex_t rex;
    237   int err = regexec(regex, line, 1, match, 0x00400/*REG_TRACE*/);
    238 #if 0
    239     if (err) {
    240         regerror(err, regex, rerr, sizeof(rerr));
    241         fprintf(stderr, "regexec(%s, %s) has failed: %s\n", line, regex, rerr);
    242     }
    243 #endif
    244 
    245   return err == 0;
    246 }
    247 
    248 static const char*
    249 next_separator(const char* str)
    250 {
    251   return str + strcspn(str, " \t");
    252 }
    253 
    254 static const char*
    255 next_token(const char* str)
    256 {
    257   str = next_separator(str);
    258   return str + strspn(str, " \t");
    259 }
    260 
    261 static const char*
    262 get_next_token(const char* str, char* token, size_t size)
    263 {
    264   const char* start = next_token(str);
    265   const char* end = next_separator(start);
    266   if (start != end) {
    267     const size_t to_copy = min((end - start), (size - 1));
    268     memcpy(token, start, to_copy);
    269     token[to_copy] = '\0';
    270     return start;
    271   } else {
    272     return NULL;
    273   }
    274 }
    275 
    276 int
    277 ParseFrame(NdkCrashParser* parser, const char* frame)
    278 {
    279   uint64_t address;
    280   const char* wrk;
    281   char* eptr;
    282   char pc_address[17];
    283   char module_path[2048];
    284   char* module_name;
    285   char sym_file[2048];
    286   ELFF_HANDLE elff_handle;
    287   Elf_AddressInfo pc_info;
    288 
    289   fprintf(parser->out_handle, "Stack frame %s", frame);
    290 
    291   // Advance to the instruction pointer token.
    292   wrk = strstr(frame, "pc");
    293   if (wrk == NULL) {
    294     wrk = strstr(frame, "eip");
    295     if (wrk == NULL) {
    296       wrk = strstr(frame, "ip");
    297       if (wrk == NULL) {
    298         fprintf(parser->out_handle,
    299                 "Parser is unable to locate instruction pointer token.\n");
    300         return -1;
    301       }
    302     }
    303   }
    304 
    305   // Next token after the instruction pointer token is its address.
    306   wrk = get_next_token(wrk, pc_address, sizeof(pc_address));
    307   // PC address is a hex value. Get it.
    308   eptr = pc_address + strlen(pc_address);
    309   address = strtoul(pc_address, &eptr, 16);
    310 
    311   // Next token is module path.
    312   get_next_token(wrk, module_path, sizeof(module_path));
    313 
    314   // Extract basename of module, we should not care about its path
    315   // on the device.
    316   module_name = strrchr(module_path,'/');
    317   if (module_name == NULL)
    318       module_name = module_path;
    319   else {
    320       module_name += 1;
    321       if (*module_name == '\0') {
    322           /* Trailing slash in the module path, this should not happen */
    323           /* Back-off with the full module-path */
    324           module_name = module_path;
    325       }
    326   }
    327 
    328   // Build path to the symbol file.
    329   strncpy(sym_file, parser->sym_root, sizeof(sym_file));
    330   strncat(sym_file, "/", sizeof(sym_file));
    331   strncat(sym_file, module_name, sizeof(sym_file));
    332   sym_file[sizeof(sym_file)-1] = '\0';
    333 
    334   // Init ELFF wrapper for the symbol file.
    335   elff_handle = elff_init(sym_file);
    336   if (elff_handle == NULL) {
    337     if (errno == ENOENT) {
    338         fprintf(parser->out_handle, "\n");
    339     } else {
    340         fprintf(parser->out_handle,
    341                 ": Unable to open symbol file %s. Error (%d): %s\n",
    342                 sym_file, errno, strerror(errno));
    343     }
    344     return -1;
    345   }
    346   // Extract address info from the symbol file.
    347   if (!elff_get_pc_address_info(elff_handle, address, &pc_info)) {
    348     if (pc_info.dir_name != NULL) {
    349       fprintf(parser->out_handle, ": Routine %s in %s/%s:%d\n",
    350               pc_info.routine_name, pc_info.dir_name, pc_info.file_name,
    351               pc_info.line_number);
    352     } else {
    353       fprintf(parser->out_handle, ": Routine %s in %s:%d\n",
    354               pc_info.routine_name, pc_info.file_name, pc_info.line_number);
    355     }
    356     elff_free_pc_address_info(elff_handle, &pc_info);
    357     elff_close(elff_handle);
    358     return 0;
    359   } else {
    360     fprintf(parser->out_handle,
    361             ": Unable to locate routine information for address %x in module %s\n",
    362             (uint32_t)address, sym_file);
    363     elff_close(elff_handle);
    364     return -1;
    365   }
    366 }
    367