]>
Commit | Line | Data |
---|---|---|
c5a58398 AK |
1 | #!/usr/bin/env python3 |
2 | ||
3 | # Print the top N most executed functions in QEMU using perf. | |
4 | # Syntax: | |
5 | # topN_perf.py [-h] [-n] <number of displayed top functions> -- \ | |
6 | # <qemu executable> [<qemu executable options>] \ | |
d30b5bc9 | 7 | # <target executable> [<target executable options>] |
c5a58398 AK |
8 | # |
9 | # [-h] - Print the script arguments help message. | |
10 | # [-n] - Specify the number of top functions to print. | |
11 | # - If this flag is not specified, the tool defaults to 25. | |
12 | # | |
13 | # Example of usage: | |
14 | # topN_perf.py -n 20 -- qemu-arm coulomb_double-arm | |
15 | # | |
16 | # This file is a part of the project "TCG Continuous Benchmarking". | |
17 | # | |
18 | # Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkaraman@gmail.com> | |
19 | # Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> | |
20 | # | |
21 | # This program is free software: you can redistribute it and/or modify | |
22 | # it under the terms of the GNU General Public License as published by | |
23 | # the Free Software Foundation, either version 2 of the License, or | |
24 | # (at your option) any later version. | |
25 | # | |
26 | # This program is distributed in the hope that it will be useful, | |
27 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
29 | # GNU General Public License for more details. | |
30 | # | |
31 | # You should have received a copy of the GNU General Public License | |
32 | # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
33 | ||
34 | import argparse | |
35 | import os | |
36 | import subprocess | |
37 | import sys | |
38 | ||
39 | ||
40 | # Parse the command line arguments | |
41 | parser = argparse.ArgumentParser( | |
42 | usage='topN_perf.py [-h] [-n] <number of displayed top functions > -- ' | |
43 | '<qemu executable> [<qemu executable options>] ' | |
44 | '<target executable> [<target executable options>]') | |
45 | ||
46 | parser.add_argument('-n', dest='top', type=int, default=25, | |
47 | help='Specify the number of top functions to print.') | |
48 | ||
49 | parser.add_argument('command', type=str, nargs='+', help=argparse.SUPPRESS) | |
50 | ||
51 | args = parser.parse_args() | |
52 | ||
53 | # Extract the needed variables from the args | |
54 | command = args.command | |
55 | top = args.top | |
56 | ||
57 | # Insure that perf is installed | |
58 | check_perf_presence = subprocess.run(["which", "perf"], | |
59 | stdout=subprocess.DEVNULL) | |
60 | if check_perf_presence.returncode: | |
61 | sys.exit("Please install perf before running the script!") | |
62 | ||
63 | # Insure user has previllage to run perf | |
64 | check_perf_executability = subprocess.run(["perf", "stat", "ls", "/"], | |
65 | stdout=subprocess.DEVNULL, | |
66 | stderr=subprocess.DEVNULL) | |
67 | if check_perf_executability.returncode: | |
68 | sys.exit( | |
69 | """ | |
70 | Error: | |
71 | You may not have permission to collect stats. | |
72 | ||
73 | Consider tweaking /proc/sys/kernel/perf_event_paranoid, | |
74 | which controls use of the performance events system by | |
75 | unprivileged users (without CAP_SYS_ADMIN). | |
76 | ||
77 | -1: Allow use of (almost) all events by all users | |
78 | Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK | |
79 | 0: Disallow ftrace function tracepoint by users without CAP_SYS_ADMIN | |
80 | Disallow raw tracepoint access by users without CAP_SYS_ADMIN | |
81 | 1: Disallow CPU event access by users without CAP_SYS_ADMIN | |
82 | 2: Disallow kernel profiling by users without CAP_SYS_ADMIN | |
83 | ||
84 | To make this setting permanent, edit /etc/sysctl.conf too, e.g.: | |
85 | kernel.perf_event_paranoid = -1 | |
86 | ||
87 | * Alternatively, you can run this script under sudo privileges. | |
88 | """ | |
89 | ) | |
90 | ||
91 | # Run perf record | |
92 | perf_record = subprocess.run((["perf", "record", "--output=/tmp/perf.data"] + | |
93 | command), | |
94 | stdout=subprocess.DEVNULL, | |
95 | stderr=subprocess.PIPE) | |
96 | if perf_record.returncode: | |
97 | os.unlink('/tmp/perf.data') | |
98 | sys.exit(perf_record.stderr.decode("utf-8")) | |
99 | ||
100 | # Save perf report output to /tmp/perf_report.out | |
101 | with open("/tmp/perf_report.out", "w") as output: | |
102 | perf_report = subprocess.run( | |
103 | ["perf", "report", "--input=/tmp/perf.data", "--stdio"], | |
104 | stdout=output, | |
105 | stderr=subprocess.PIPE) | |
106 | if perf_report.returncode: | |
107 | os.unlink('/tmp/perf.data') | |
108 | output.close() | |
109 | os.unlink('/tmp/perf_report.out') | |
110 | sys.exit(perf_report.stderr.decode("utf-8")) | |
111 | ||
112 | # Read the reported data to functions[] | |
113 | functions = [] | |
114 | with open("/tmp/perf_report.out", "r") as data: | |
115 | # Only read lines that are not comments (comments start with #) | |
116 | # Only read lines that are not empty | |
117 | functions = [line for line in data.readlines() if line and line[0] | |
118 | != '#' and line[0] != "\n"] | |
119 | ||
120 | # Limit the number of top functions to "top" | |
121 | number_of_top_functions = top if len(functions) > top else len(functions) | |
122 | ||
123 | # Store the data of the top functions in top_functions[] | |
124 | top_functions = functions[:number_of_top_functions] | |
125 | ||
126 | # Print table header | |
127 | print('{:>4} {:>10} {:<30} {}\n{} {} {} {}'.format('No.', | |
128 | 'Percentage', | |
129 | 'Name', | |
130 | 'Invoked by', | |
131 | '-' * 4, | |
132 | '-' * 10, | |
133 | '-' * 30, | |
134 | '-' * 25)) | |
135 | ||
136 | # Print top N functions | |
137 | for (index, function) in enumerate(top_functions, start=1): | |
138 | function_data = function.split() | |
139 | function_percentage = function_data[0] | |
140 | function_name = function_data[-1] | |
141 | function_invoker = ' '.join(function_data[2:-2]) | |
142 | print('{:>4} {:>10} {:<30} {}'.format(index, | |
143 | function_percentage, | |
144 | function_name, | |
145 | function_invoker)) | |
146 | ||
147 | # Remove intermediate files | |
148 | os.unlink('/tmp/perf.data') | |
149 | os.unlink('/tmp/perf_report.out') |