#!/usr/bin/python import sys from fnmatch import fnmatch def is_whitespace (c): return c == ' ' or c == '\n' or c == '\t' delimiters = ",=(){}" def is_delimiter (c): return delimiters.find(c) != -1 class Parser: def __init__(self, data): self.data = data; self.i = 0 def skip_whitespace(self): ret = False while self.i < len (self.data) and is_whitespace (self.data[self.i]): ret = True self.i = self.i + 1; return ret def get_string(self): if self.data[self.i] != '"': return None start = self.i # Skip " self.i = self.i + 1; while self.i < len (self.data) and self.data[self.i] != '"': if self.data[self.i] == '\\' and self.data[self.i + 1] == '"': self.i = self.i + 2 else: self.i = self.i + 1 if self.i < len (self.data): self.i = self.i + 1 str = self.data[start:self.i] return str; def get_special_string(self): if self.data[self.i] != '<': return None start = self.i # Skip " self.i = self.i + 1; while self.i < len (self.data) and self.data[self.i] != '>': if self.data[self.i] == '\\' and self.data[self.i + 1] == '>': self.i = self.i + 2 else: self.i = self.i + 1 if self.i < len (self.data): self.i = self.i + 1 str = self.data[start:self.i] return str; def get_token(self, skip_ws = True): if self.skip_whitespace() and not skip_ws: return " " if self.i >= len(self.data): return None c = self.data[self.i] if c == '"': return self.get_string() if c == '<': return self.get_special_string() if is_delimiter(c): self.i = self.i + 1 return c start = self.i while self.i < len (self.data) and not is_whitespace (self.data[self.i]) and not is_delimiter(self.data[self.i]): self.i = self.i + 1 str = self.data[start:self.i] return str def peek_token(self, skip_ws = True): pos = self.i t = self.get_token(skip_ws) self.i = pos return t def get_char(self): if self.i >= len(self.data): return None c = self.data[self.i] self.i = self.i + 1 return c def get_rest(self): return self.data[self.i:] class Frame: def __init__(self, data): self.frame = -1 self.name = "" self.args = [] self.pos = None self.source = None self.binary = None self.locals = None p = Parser(data) c = p.get_char() if c != '#': self.frame = -1 self.name = "invalid frame"; return self.frame = long(p.get_token()) ip = p.get_token() if not ip.startswith("0x"): self.name = ip return self.pos = ip s = p.get_token() if s != "in": print "unexpected data" self.name = s return self.name = p.get_token() if not self.parse_args(p): return t = p.peek_token() if t == 'at': p.get_token() self.source = p.get_token() elif t == 'from': p.get_token() self.binary = p.get_token() self.locals = p.get_rest().strip().split("\n") for i in range(len(self.locals)): self.locals[i] = self.locals[i].strip() def parse_args(self, p): s = p.get_token() if s != "(": return False while True: arg = p.get_token() if arg == None: return False if arg == ')': return True s = p.get_token() if s != '=': return False p.skip_whitespace() val = "" while True: s = p.get_token(False) if s == None: return False if s != ',' and s != ')': val = val + s else: self.args.append( (arg, val) ) if s == ')': return True break def format_args(self): i = 0 s = "" while i < len (self.args): s = s + "%s=%s" % (self.args[i][0], self.args[i][1]) if i != len (self.args) - 1: s = s + ", " i = i + 1 return s def format(self): s = "#%d %s (%s)"%(self.frame, self.name, self.format_args()) if self.source: s = s + " at %s" % self.source if self.binary: s = s + " from %s" % self.binary return s def __repr__(self): return "#%d %s" % (self.frame, self.name) def find_start(lines): i = 0; while i < len(lines): if lines[i].startswith("#0"): return i i = i + 1 return -1; def parse_bt(lines): last_frame = 0 frames = [] i = find_start(lines); if i == -1: return None while i < len(lines): frame_lines = lines[i] i = i + 1 while i < len(lines) and not lines[i].startswith("#"): frame_lines = frame_lines + lines[i] i = i + 1; frame = Frame(frame_lines) if frame.frame == -1 or frame.frame < last_frame: break last_frame = frame.frame frames.append(frame) return frames f = open(sys.argv[1]) lines = f.readlines() bt = parse_bt(lines) f.close() if bt == None: print "Unable to find backtrace in input" sys.exit(1) # Hide IA__ prefix for frame in bt: if frame.name.startswith("IA__"): frame.name = frame.name[4:] # Skip initial internal symbols hide_initial = ["_start", "__libc_start_main", "??"] i = len(bt) - 1 while i >= 0: frame = bt[i] if hide_initial.count(frame.name) == 0: break i = i - 1 def find_arg (frames, name, arg): for i in range(len(frames)): frame = frames[i] if frame.name == name and arg < len (frame.args): return frame.args[arg][1] return None def gdk_event_filter(frames): event = find_arg (frames, "gtk_main_do_event", 0) if event == None: event = find_arg (frames, "gtk_propagate_event", 1) widget = find_arg (frames, "gtk_propagate_event", 0) return "#%d ... dispatching GdkEvent %s to widget %s" % (frames[0].frame, event, widget) def g_signal_emit_filter(frames): instance = find_arg (frames, "g_signal_emit_valist", 0) signal_id = find_arg (frames, "g_signal_emit_valist", 1) details = find_arg (frames, "g_signal_emit_valist", 2) widget = find_arg (frames, "gtk_propagate_event", 0) if details != None and details != "0": details = ":%s"%(details) else: details = "" return "#%d ... emitting signal %s%s on instance %s" % (frames[0].frame, signal_id, details, instance) def bug_buddy_filter(frames): return "#%d ... segfault caught by bug-buddy" % (frames[0].frame) filter_symbol = 0 filter_where = 1 filter_compress = 2 filter_callback = 3 filter_print_current = 4 filters = [ ( "gdk_event_dispatch", None, ["gtk_main_do_event", "gtk_propagate_event", "gtk_widget_event_internal", "g_signal_emit", "g_signal_emit_valist", "signal_emit_*", "g_closure_invoke", "g_type_class_meta_marshal", "_gtk_marshal*", "g_cclosure_marshal*"], gdk_event_filter, False ), ( "g_signal_emit", None, ["g_signal_emit*", "signal_emit_*", "g_closure_invoke", "g_type_class_meta_marshal", "_gtk_marshal*", "g_cclosure_marshal*"], g_signal_emit_filter, False ), ( "gtk_object_dispose", None, ["g_signal_emit", "g_signal_emit*", "signal_emit_*", "g_closure_invoke", "g_type_class_meta_marshal", "_gtk_marshal*", "g_cclosure_marshal*"], None, True ), ( "google_breakpad::ExceptionHandler::HandleException", None, ["*"], bug_buddy_filter, False ), ( "??", "*libgnomebreakpad*", ["*"], bug_buddy_filter, False ) ] def match_filter (frame): for f in range (len (filters)): filter = filters[f] if fnmatch(frame.name, filter[filter_symbol]): return filter if filter[filter_where] != None: if frame.binary != None and fnmatch(frame.binary, filter[filter_where]): return filter if frame.source != None and fnmatch(frame.source, filter[filter_where]): return filter return None def compress_filter (bt, first_frame, filter): to_match = filter[filter_compress] i = first_frame - 1 while i >= 0: match = False frame = bt[i] for j in range (len (to_match)): if fnmatch(frame.name, to_match[j]): match = True break if not match: break i = i - 1 return i res = [] while i >= 0: frame = bt[i] filter = match_filter(frame) if filter: end = compress_filter (bt, i, filter) match = bt[end+1:i+1] match.reverse() if filter[filter_callback]: f_res = filter[filter_callback](match) else: f_res = " ..." s = "" if f_res: s = s + f_res if filter[filter_print_current]: if s != "": s = s + "\n" s = s + frame.format() i = end else: s = frame.format() i = i - 1 res.append(s) res.reverse() for s in res: print s