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