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