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