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