On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkara...@gmail.com> wrote:
> - Apply pylint and flake8 formatting rules to the script. > - Use 'tempfile' instead of '/tmp' for creating temporary files. > > Reviewed-by: Aleksandar Markovic <aleksandar.qemu.de...@gmail.com> Reply Reply all Forward View Gmail in: *Mobile* | Older version <https://mail.google.com/mail/mu/mp/966/#> | Desktop <https://mail.google.com/mail/mu/mp/966/#> © 2020 Google > Signed-off-by: Ahmed Karaman <ahmedkhaledkara...@gmail.com> > --- > scripts/performance/topN_callgrind.py | 169 +++++++++++++------------- > 1 file changed, 87 insertions(+), 82 deletions(-) > > diff --git a/scripts/performance/topN_callgrind.py > b/scripts/performance/topN_callgrind.py > index 67c59197af..f8a554f393 100755 > --- a/scripts/performance/topN_callgrind.py > +++ b/scripts/performance/topN_callgrind.py > @@ -1,113 +1,122 @@ > #!/usr/bin/env python3 > > -# Print the top N most executed functions in QEMU using callgrind. > -# Syntax: > -# topN_callgrind.py [-h] [-n] <number of displayed top functions> -- \ > -# <qemu executable> [<qemu executable options>] \ > -# <target executable> [<target execurable options>] > -# > -# [-h] - Print the script arguments help message. > -# [-n] - Specify the number of top functions to print. > -# - If this flag is not specified, the tool defaults to 25. > -# > -# Example of usage: > -# topN_callgrind.py -n 20 -- qemu-arm coulomb_double-arm > -# > -# This file is a part of the project "TCG Continuous Benchmarking". > -# > -# Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkara...@gmail.com> > -# Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@gmail.c > om> > -# > -# This program is free software: you can redistribute it and/or modify > -# it under the terms of the GNU General Public License as published by > -# the Free Software Foundation, either version 2 of the License, or > -# (at your option) any later version. > -# > -# This program is distributed in the hope that it will be useful, > -# but WITHOUT ANY WARRANTY; without even the implied warranty of > -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > -# GNU General Public License for more details. > -# > -# You should have received a copy of the GNU General Public License > -# along with this program. If not, see <https://www.gnu.org/licenses/>. > +""" > +Print the top N most executed functions in QEMU using callgrind. > + > +Syntax: > +topN_callgrind.py [-h] [-n <number of displayed top functions>] -- \ > + <qemu executable> [<qemu executable options>] \ > + <target executable> [<target execurable options>] > + > +[-h] - Print the script arguments help message. > +[-n] - Specify the number of top functions to print. > + - If this flag is not specified, the tool defaults to 25. > + > +Example of usage: > +topN_callgrind.py -n 20 -- qemu-arm coulomb_double-arm > + > +This file is a part of the project "TCG Continuous Benchmarking". > + > +Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkara...@gmail.com> > +Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.de...@gmail.com> > + > +This program is free software: you can redistribute it and/or modify > +it under the terms of the GNU General Public License as published by > +the Free Software Foundation, either version 2 of the License, or > +(at your option) any later version. > + > +This program is distributed in the hope that it will be useful, > +but WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +GNU General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with this program. If not, see <https://www.gnu.org/licenses/>. > +""" > > import argparse > import os > import subprocess > import sys > +import tempfile > > > # Parse the command line arguments > -parser = argparse.ArgumentParser( > - usage='topN_callgrind.py [-h] [-n] <number of displayed top > functions> -- ' > +PARSER = argparse.ArgumentParser( > + usage='topN_callgrind.py [-h] [-n] <number of displayed top > functions> -- ' > '<qemu executable> [<qemu executable options>] ' > '<target executable> [<target executable options>]') > > -parser.add_argument('-n', dest='top', type=int, default=25, > +PARSER.add_argument('-n', dest='top', type=int, default=25, > help='Specify the number of top functions to print.') > > -parser.add_argument('command', type=str, nargs='+', > help=argparse.SUPPRESS) > +PARSER.add_argument('command', type=str, nargs='+', > help=argparse.SUPPRESS) > > -args = parser.parse_args() > +ARGS = PARSER.parse_args() > > # Extract the needed variables from the args > -command = args.command > -top = args.top > +COMMAND = ARGS.command > +TOP = ARGS.top > > # Insure that valgrind is installed > -check_valgrind_presence = subprocess.run(["which", "valgrind"], > - stdout=subprocess.DEVNULL) > -if check_valgrind_presence.returncode: > +CHECK_VALGRIND_PRESENCE = subprocess.run(["which", "valgrind"], > + stdout=subprocess.DEVNULL, > + check=False) > +if CHECK_VALGRIND_PRESENCE.returncode: > sys.exit("Please install valgrind before running the script!") > > -# Run callgrind > -callgrind = subprocess.run(( > - ["valgrind", "--tool=callgrind", "--callgrind-out-file=/tmp/cal > lgrind.data"] > - + command), > - stdout=subprocess.DEVNULL, > - stderr=subprocess.PIPE) > -if callgrind.returncode: > - sys.exit(callgrind.stderr.decode("utf-8")) > - > -# Save callgrind_annotate output to /tmp/callgrind_annotate.out > -with open("/tmp/callgrind_annotate.out", "w") as output: > - callgrind_annotate = subprocess.run(["callgrind_annotate", > - "/tmp/callgrind.data"], > - stdout=output, > - stderr=subprocess.PIPE) > - if callgrind_annotate.returncode: > - os.unlink('/tmp/callgrind.data') > - output.close() > - os.unlink('/tmp/callgrind_annotate.out') > - sys.exit(callgrind_annotate.stderr.decode("utf-8")) > - > -# Read the callgrind_annotate output to callgrind_data[] > -callgrind_data = [] > -with open('/tmp/callgrind_annotate.out', 'r') as data: > - callgrind_data = data.readlines() > +# Run callgrind and save all intermediate files in a temporary directory > +with tempfile.TemporaryDirectory() as tmpdir: > + CALLGRIND_DATA_PATH = os.path.join(tmpdir, "callgrind.data") > + ANNOTATE_OUT_PATH = os.path.join(tmpdir, "callgrind_annotate.out") > + > + # Run callgrind > + CALLGRIND = subprocess.run((["valgrind", > + "--tool=callgrind", > + "--callgrind-out-file="+CALLG > RIND_DATA_PATH] > + + COMMAND), > + stdout=subprocess.DEVNULL, > + stderr=subprocess.PIPE, > + check=False) > + if CALLGRIND.returncode: > + sys.exit(CALLGRIND.stderr.decode("utf-8")) > + > + with open(ANNOTATE_OUT_PATH, "w") as output: > + CALLGRIND_ANNOTATE = subprocess.run(["callgrind_annotate", > + CALLGRIND_DATA_PATH], > + stdout=output, > + stderr=subprocess.PIPE, > + check=False) > + if CALLGRIND_ANNOTATE.returncode: > + sys.exit(CALLGRIND_ANNOTATE.stderr.decode("utf-8")) > + > + # Read the callgrind_annotate output to CALLGRIND_DATA[] > + CALLGRIND_DATA = [] > + with open(ANNOTATE_OUT_PATH, 'r') as data: > + CALLGRIND_DATA = data.readlines() > > # Line number with the total number of instructions > -total_instructions_line_number = 20 > +TOTAL_INSTRUCTIONS_LINE_NO = 20 > > # Get the total number of instructions > -total_instructions_line_data = callgrind_data[total_instructi > ons_line_number] > -total_number_of_instructions = total_instructions_line_data.split(' ')[0] > -total_number_of_instructions = int( > - total_number_of_instructions.replace(',', '')) > +TOTAL_INSTRUCTIONS_LINE_DATA = CALLGRIND_DATA[TOTAL_INSTRUCTIONS_LINE_NO] > +TOTAL_NUMBER_OF_INSTRUCTIONS = TOTAL_INSTRUCTIONS_LINE_DATA.split(' ')[0] > +TOTAL_NUMBER_OF_INSTRUCTIONS = int( > + TOTAL_NUMBER_OF_INSTRUCTIONS.replace(',', '')) > > # Line number with the top function > -first_func_line = 25 > +FIRST_FUNC_LINE = 25 > > # Number of functions recorded by callgrind, last two lines are always > empty > -number_of_functions = len(callgrind_data) - first_func_line - 2 > +NUMBER_OF_FUNCTIONS = len(CALLGRIND_DATA) - FIRST_FUNC_LINE - 2 > > # Limit the number of top functions to "top" > -number_of_top_functions = (top if number_of_functions > > - top else number_of_functions) > +NUMBER_OF_TOP_FUNCTIONS = (TOP if NUMBER_OF_FUNCTIONS > > + TOP else NUMBER_OF_FUNCTIONS) > > # Store the data of the top functions in top_functions[] > -top_functions = callgrind_data[first_func_line: > - first_func_line + number_of_top_functions] > +TOP_FUNCTIONS = CALLGRIND_DATA[FIRST_FUNC_LINE: > + FIRST_FUNC_LINE + NUMBER_OF_TOP_FUNCTIONS] > > # Print table header > print('{:>4} {:>10} {:<30} {}\n{} {} {} {}'.format('No.', > @@ -121,12 +130,12 @@ print('{:>4} {:>10} {:<30} {}\n{} {} {} > {}'.format('No.', > )) > > # Print top N functions > -for (index, function) in enumerate(top_functions, start=1): > +for (index, function) in enumerate(TOP_FUNCTIONS, start=1): > function_data = function.split() > # Calculate function percentage > function_instructions = float(function_data[0].replace(',', '')) > function_percentage = (function_instructions / > - total_number_of_instructions)*100 > + TOTAL_NUMBER_OF_INSTRUCTIONS)*100 > # Get function name and source files path > function_source_file, function_name = function_data[1].split(':') > # Print extracted data > @@ -134,7 +143,3 @@ for (index, function) in enumerate(top_functions, > start=1): > > round(function_percentage, 3), > function_name, > function_source_file)) > - > -# Remove intermediate files > -os.unlink('/tmp/callgrind.data') > -os.unlink('/tmp/callgrind_annotate.out') > -- > 2.17.1 > >