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 NdkCrashParser* parser; 130 131 parser = (NdkCrashParser*)calloc(sizeof(*parser), 1); 132 if (parser == NULL) 133 return NULL; 134 135 parser->out_handle = out_handle; 136 parser->state = EXPECTS_CRASH_DUMP; 137 138 parser->sym_root = strdup(sym_root); 139 if (!parser->sym_root) 140 goto BAD_INIT; 141 142 if (regcomp(&parser->re_pid_header, _pid_header, REG_EXTENDED | REG_NEWLINE) || 143 regcomp(&parser->re_sig_header, _sig_header, REG_EXTENDED | REG_NEWLINE) || 144 regcomp(&parser->re_frame_header, _frame_header, REG_EXTENDED | REG_NEWLINE)) 145 goto BAD_INIT; 146 147 return parser; 148 149 BAD_INIT: 150 DestroyNdkCrashParser(parser); 151 return NULL; 152 } 153 154 void 155 DestroyNdkCrashParser(NdkCrashParser* parser) 156 { 157 if (parser != NULL) { 158 /* Release compiled regular expressions */ 159 regfree(&parser->re_frame_header); 160 regfree(&parser->re_sig_header); 161 regfree(&parser->re_pid_header); 162 /* Release symbol path */ 163 free(parser->sym_root); 164 /* Release parser itself */ 165 free(parser); 166 } 167 } 168 169 int 170 ParseLine(NdkCrashParser* parser, const char* line) 171 { 172 regmatch_t match; 173 174 if (line == NULL || *line == '\0') { 175 // Nothing to parse. 176 return 1; 177 } 178 179 // Lets see if this is the beginning of a crash dump. 180 if (strstr(line, _crash_dump_header) != NULL) { 181 if (parser->state != EXPECTS_CRASH_DUMP) { 182 // Printing another crash dump was in progress. Mark the end of it. 183 fprintf(parser->out_handle, "Crash dump is completed\n\n"); 184 } 185 186 // New crash dump begins. 187 fprintf(parser->out_handle, "********** Crash dump: **********\n"); 188 parser->state = EXPECTS_BUILD_FINGREPRINT_OR_PID; 189 190 return 0; 191 } 192 193 switch (parser->state) { 194 case EXPECTS_BUILD_FINGREPRINT_OR_PID: 195 if (strstr(line, _build_fingerprint_header) != NULL) { 196 fprintf(parser->out_handle, "%s\n", 197 strstr(line, _build_fingerprint_header)); 198 parser->state = EXPECTS_PID; 199 } 200 // Let it fall through to the EXPECTS_PID, in case the dump doesn't 201 // contain build fingerprint. 202 case EXPECTS_PID: 203 if (MatchRegex(line, &parser->re_pid_header, &match)) { 204 fprintf(parser->out_handle, "%s\n", line + match.rm_so); 205 parser->state = EXPECTS_SIGNAL_OR_FRAME; 206 return 0; 207 } else { 208 return 1; 209 } 210 211 case EXPECTS_SIGNAL_OR_FRAME: 212 if (MatchRegex(line, &parser->re_sig_header, &match)) { 213 fprintf(parser->out_handle, "%s\n", line + match.rm_so); 214 parser->state = EXPECTS_FRAME; 215 } 216 // Let it fall through to the EXPECTS_FRAME, in case the dump doesn't 217 // contain signal fingerprint. 218 case EXPECTS_FRAME: 219 if (MatchRegex(line, &parser->re_frame_header, &match)) { 220 parser->state = EXPECTS_FRAME; 221 return ParseFrame(parser, line + match.rm_so); 222 } else { 223 return 1; 224 } 225 226 default: 227 return 1; 228 } 229 } 230 231 static int 232 MatchRegex(const char* line, const regex_t* regex, regmatch_t* match) 233 { 234 int err = regexec(regex, line, 1, match, 0x00400/*REG_TRACE*/); 235 #if 0 236 char rerr[4096]; 237 if (err) { 238 regerror(err, regex, rerr, sizeof(rerr)); 239 fprintf(stderr, "regexec(%s, %s) has failed: %s\n", line, regex, rerr); 240 } 241 #endif 242 243 return err == 0; 244 } 245 246 static const char* 247 next_separator(const char* str) 248 { 249 return str + strcspn(str, " \t"); 250 } 251 252 static const char* 253 next_token(const char* str) 254 { 255 str = next_separator(str); 256 return str + strspn(str, " \t"); 257 } 258 259 static const char* 260 get_next_token(const char* str, char* token, size_t size) 261 { 262 const char* start = next_token(str); 263 const char* end = next_separator(start); 264 if (start != end) { 265 const size_t to_copy = min((size_t)(end - start), (size - 1)); 266 memcpy(token, start, to_copy); 267 token[to_copy] = '\0'; 268 return start; 269 } else { 270 return NULL; 271 } 272 } 273 274 int 275 ParseFrame(NdkCrashParser* parser, const char* frame) 276 { 277 uint64_t address; 278 const char* wrk; 279 char* eptr; 280 char pc_address[17]; 281 char module_path[2048]; 282 char* module_name; 283 char sym_file[2048]; 284 ELFF_HANDLE elff_handle; 285 Elf_AddressInfo pc_info; 286 287 fprintf(parser->out_handle, "Stack frame %s", frame); 288 289 // Advance to the instruction pointer token. 290 wrk = strstr(frame, "pc"); 291 if (wrk == NULL) { 292 wrk = strstr(frame, "eip"); 293 if (wrk == NULL) { 294 wrk = strstr(frame, "ip"); 295 if (wrk == NULL) { 296 fprintf(parser->out_handle, 297 "Parser is unable to locate instruction pointer token.\n"); 298 return -1; 299 } 300 } 301 } 302 303 // Next token after the instruction pointer token is its address. 304 wrk = get_next_token(wrk, pc_address, sizeof(pc_address)); 305 // PC address is a hex value. Get it. 306 eptr = pc_address + strlen(pc_address); 307 address = strtoul(pc_address, &eptr, 16); 308 309 // Next token is module path. 310 get_next_token(wrk, module_path, sizeof(module_path)); 311 312 // Extract basename of module, we should not care about its path 313 // on the device. 314 module_name = strrchr(module_path,'/'); 315 if (module_name == NULL) 316 module_name = module_path; 317 else { 318 module_name += 1; 319 if (*module_name == '\0') { 320 /* Trailing slash in the module path, this should not happen */ 321 /* Back-off with the full module-path */ 322 module_name = module_path; 323 } 324 } 325 326 // Build path to the symbol file. 327 snprintf(sym_file, sizeof(sym_file), "%s/%s", parser->sym_root, module_name); 328 329 // Init ELFF wrapper for the symbol file. 330 elff_handle = elff_init(sym_file); 331 if (elff_handle == NULL) { 332 if (errno == ENOENT) { 333 fprintf(parser->out_handle, "\n"); 334 } else { 335 fprintf(parser->out_handle, 336 ": Unable to open symbol file %s. Error (%d): %s\n", 337 sym_file, errno, strerror(errno)); 338 } 339 return -1; 340 } 341 // Extract address info from the symbol file. 342 if (!elff_get_pc_address_info(elff_handle, address, &pc_info)) { 343 if (pc_info.dir_name != NULL) { 344 fprintf(parser->out_handle, ": Routine %s in %s/%s:%d\n", 345 pc_info.routine_name, pc_info.dir_name, pc_info.file_name, 346 pc_info.line_number); 347 } else { 348 fprintf(parser->out_handle, ": Routine %s in %s:%d\n", 349 pc_info.routine_name, pc_info.file_name, pc_info.line_number); 350 } 351 elff_free_pc_address_info(elff_handle, &pc_info); 352 elff_close(elff_handle); 353 return 0; 354 } else { 355 fprintf(parser->out_handle, 356 ": Unable to locate routine information for address %x in module %s\n", 357 (uint32_t)address, sym_file); 358 elff_close(elff_handle); 359 return -1; 360 } 361 } 362