1#!/usr/bin/env python
2
3# An oops analyser for Xen
4# Usage: xensymoops path-to-xen.s < oops-message
5
6# There's probably some more features that could go in here but this
7# is sufficient to analyse most errors in my code ;-)
8
9# by Mark Williamson (C) 2004 Intel Research Cambridge
10
11import re, sys
12
13def read_oops():
14    """Process an oops message on stdin and return (eip_addr, stack_addrs)
15
16    eip_addr is the location of EIP at the point of the crash.
17    stack_addrs is a dictionary mapping potential code addresses in the stack
18      to their order in the stack trace.
19    """
20    stackaddr_ptn = "\[([a-z,0-9]*)\]"
21    stackaddr_re  = re.compile(stackaddr_ptn)
22
23    eip_ptn = ".*EIP:.*<([a-z,0-9]*)>.*"
24    eip_re  = re.compile(eip_ptn)
25
26    matches = 0
27    stack_addresses = {}
28    eip_addr = "Not known"
29
30    while True:
31        line = sys.stdin.readline()
32        if not line: break
33
34        m = eip_re.match(line)
35        if m: eip_addr = m.group(1)
36
37        m = stackaddr_re.findall(line)
38
39        for i in m:
40            stack_addresses[i] = matches
41            matches += 1
42
43    return (eip_addr, stack_addresses)
44
45def usage():
46    print >> sys.stderr, """Usage: %s path-to-asm < oops-msg
47    The oops message should be fed to the standard input.  The
48    command-line argument specifies the path to the Xen assembly dump
49    produced by \"make debug\".  The location of EIP and the backtrace
50    will be output to standard output.
51    """ % sys.argv[0]
52    sys.exit()
53
54##### main
55
56if len(sys.argv) != 2:
57    usage()
58
59# get address of EIP and the potential code addresses from the stack
60(eip_addr, stk_addrs) = read_oops()
61
62# open Xen disassembly
63asm_file = open(sys.argv[1])
64
65# regexp to match addresses of code lines in the objdump
66addr_ptn = "([a-z,0-9]*):"
67addr_re  = re.compile(addr_ptn)
68
69# regexp to match the start of functions in the objdump
70func_ptn = "(.*<[\S]*>):"
71func_re  = re.compile(func_ptn)
72
73func = "<No function>" # holds the name of the current function being scanned
74
75eip_func = "<No function>" # name of the function EIP was in
76
77# list of (position in original backtrace, code address, function) tuples
78# describing all the potential code addresses we identified in the backtrace
79# whose addresses we also located in the objdump output
80backtrace = []
81
82while True:
83    line = asm_file.readline()
84    if not line: break
85
86    # if we've read the start of the function, record the name and address
87    fm = func_re.match(line)
88    if fm:
89        func = fm.group(1)
90        continue
91
92    # try match the address at the start of the line
93    m = addr_re.match(line)
94    if not m: continue
95
96    # we're on a code line...
97
98    address = m.group(1)
99
100    # if this address was seen as a potential code address in the backtrace then
101    # record it in the backtrace list
102    if stk_addrs.has_key(address):
103        backtrace.append((stk_addrs[address], address, func))
104
105    # if this was the address that EIP...
106    if address == eip_addr:
107        eip_func = func
108
109
110print "EIP %s in function %s" % (eip_addr, eip_func)
111print "Backtrace:"
112
113# sorting will order primarily by the first element of each tuple,
114# i.e. the order in the original oops
115backtrace.sort()
116
117for (i, a, f) in backtrace:
118    print "%s in function %s" % ( a, f )
119