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