| 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 | |
|---|
| 11 | import re, sys |
|---|
| 12 | |
|---|
| 13 | def 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 | |
|---|
| 45 | def 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 | |
|---|
| 56 | if 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 |
|---|
| 63 | asm_file = open(sys.argv[1]) |
|---|
| 64 | |
|---|
| 65 | # regexp to match addresses of code lines in the objdump |
|---|
| 66 | addr_ptn = "([a-z,0-9]*):" |
|---|
| 67 | addr_re = re.compile(addr_ptn) |
|---|
| 68 | |
|---|
| 69 | # regexp to match the start of functions in the objdump |
|---|
| 70 | func_ptn = "(.*<[\S]*>):" |
|---|
| 71 | func_re = re.compile(func_ptn) |
|---|
| 72 | |
|---|
| 73 | func = "<No function>" # holds the name of the current function being scanned |
|---|
| 74 | |
|---|
| 75 | eip_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 |
|---|
| 80 | backtrace = [] |
|---|
| 81 | |
|---|
| 82 | while 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 | |
|---|
| 110 | print "EIP %s in function %s" % (eip_addr, eip_func) |
|---|
| 111 | print "Backtrace:" |
|---|
| 112 | |
|---|
| 113 | # sorting will order primarily by the first element of each tuple, |
|---|
| 114 | # i.e. the order in the original oops |
|---|
| 115 | backtrace.sort() |
|---|
| 116 | |
|---|
| 117 | for (i, a, f) in backtrace: |
|---|
| 118 | print "%s in function %s" % ( a, f ) |
|---|