1#!/usr/bin/env python3 2 3import re 4import subprocess 5import sys 6import traceback 7import os 8 9config = {} 10 11# Subvert output buffering. 12def puts(string): 13 sys.stdout.write(string) 14 sys.stdout.flush() 15 16def pretty_print_stack(binary, line): 17 addrs = line.split()[1:] 18 # Addresses are return addresses unless preceded by a '@'. We want the 19 # caller address so line numbers are more intuitive. Thus we subtract 1 20 # from the address to get the call code. 21 for i in range(len(addrs)): 22 addr = addrs[i] 23 if addr.startswith('@'): 24 addrs[i] = addr[1:] 25 else: 26 addrs[i] = '%lx' % max((int(addrs[i], 16) - 1), 0) 27 28 # Output like this: 29 # 0x004002be: start64 at path/to/kvm-unit-tests-repo-worktree/x86/cstart64.S:208 30 # (inlined by) test_ept_violation at path/to/kvm-unit-tests-repo-worktree/x86/vmx_tests.c:1719 (discriminator 1) 31 cmd = [config.get('ADDR2LINE', 'addr2line'), '-e', binary, '-i', '-f', '--pretty', '--address'] 32 cmd.extend(addrs) 33 34 p = subprocess.Popen(cmd, stdout=subprocess.PIPE) 35 out, err = p.communicate() 36 if p.returncode != 0: 37 puts(line) 38 return 39 40 for line in out.splitlines(): 41 m = re.match(rb'(.*) at (.*):(([0-9]+)|\?)([^:]*)', line) 42 if m is None: 43 puts('%s\n' % line) 44 return 45 46 head, path, maybeline, line, tail = m.groups() 47 path = os.path.relpath(os.path.realpath(path), start=os.path.realpath(os.getcwdb())) 48 puts('%s at %s:%s%s\n' % (head.decode(), path.decode(), maybeline.decode(), tail.decode())) 49 if line: 50 line = int(line) 51 try: 52 lines = open(path).readlines() 53 except IOError: 54 continue 55 if line > 1: 56 puts(' %s\n' % lines[line - 2].rstrip()) 57 puts(' > %s\n' % lines[line - 1].rstrip()) 58 if line < len(lines): 59 puts(' %s\n' % lines[line].rstrip()) 60 61def main(): 62 if len(sys.argv) != 2: 63 sys.stderr.write('usage: %s <kernel>\n' % sys.argv[0]) 64 sys.exit(1) 65 66 binary = sys.argv[1] 67 if binary.endswith('.flat'): 68 binary = binary.replace('.flat', '.elf') 69 elif binary.endswith('.efi'): 70 binary += '.debug' 71 72 with open("config.mak") as config_file: 73 for line in config_file: 74 name, val = line.partition("=")[::2] 75 config[name.strip()] = val.strip() 76 77 try: 78 while True: 79 # Subvert input buffering. 80 line = sys.stdin.readline() 81 if line == '': 82 break 83 84 puts(line) 85 86 if not line.strip().startswith('STACK:'): 87 continue 88 89 try: 90 pretty_print_stack(binary, line) 91 except Exception: 92 puts('Error pretty printing stack:\n') 93 puts(traceback.format_exc()) 94 puts('Continuing without pretty printing...\n') 95 while True: 96 puts(line) 97 line = sys.stdin.readline() 98 if line == '': 99 break 100 except: 101 sys.exit(1) 102 103if __name__ == '__main__': 104 main() 105