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*
CreateNdkCrashParser(FILE * out_handle,const char * sym_root)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
DestroyNdkCrashParser(NdkCrashParser * parser)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
ParseLine(NdkCrashParser * parser,const char * line)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
MatchRegex(const char * line,const regex_t * regex,regmatch_t * match)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*
next_separator(const char * str)249 next_separator(const char* str)
250 {
251 return str + strcspn(str, " \t");
252 }
253
254 static const char*
next_token(const char * str)255 next_token(const char* str)
256 {
257 str = next_separator(str);
258 return str + strspn(str, " \t");
259 }
260
261 static const char*
get_next_token(const char * str,char * token,size_t size)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
ParseFrame(NdkCrashParser * parser,const char * frame)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