* [PATCH 1/2] scripts/performance: Add list_fn_callees.py script
2020-07-14 16:41 [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts Ahmed Karaman
@ 2020-07-14 16:41 ` Ahmed Karaman
2020-07-14 16:41 ` [PATCH 2/2] scripts/performance: Add list_helpers.py script Ahmed Karaman
2020-07-14 17:25 ` [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts no-reply
2 siblings, 0 replies; 4+ messages in thread
From: Ahmed Karaman @ 2020-07-14 16:41 UTC (permalink / raw)
To: qemu-devel, aleksandar.qemu.devel, philmd, alex.bennee, eblake,
ldoktor, rth, ehabkost, crosa
Cc: Ahmed Karaman
Python script that prints the callees of a given list of QEMU
functions.
Syntax:
list_fn_callees.py [-h] -f FUNCTION [FUNCTION ...] -- \
<qemu executable> [<qemu executable options>] \
<target executable> [<target executable options>]
[-h] - Print the script arguments help message.
-f FUNCTION [FUNCTION ...] - List of function names
Example of usage:
list_fn_callees.py -f helper_float_sub_d helper_float_mul_d -- \
qemu-mips coulomb_double-mips -n10
Example output:
Total number of instructions: 108,952,851
Callees of helper_float_sub_d:
No. Instructions Percentage Calls Ins/Call Function Name Source File
--- ------------ ---------- ------ -------- ------------- ---------------
1 153,160 0.141% 1,305 117 float64_sub <qemu>/fpu/softfloat.c
Callees of helper_float_mul_d:
No. Instructions Percentage Calls Ins/Call Function Name Source File
--- ------------ ---------- ------ -------- ------------- ---------------
1 131,137 0.120% 1,014 129 float64_mul <qemu>/fpu/softfloat.c
Signed-off-by: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
---
scripts/performance/list_fn_callees.py | 228 +++++++++++++++++++++++++
1 file changed, 228 insertions(+)
create mode 100755 scripts/performance/list_fn_callees.py
diff --git a/scripts/performance/list_fn_callees.py b/scripts/performance/list_fn_callees.py
new file mode 100755
index 0000000000..f0ec5c8e81
--- /dev/null
+++ b/scripts/performance/list_fn_callees.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python3
+
+# Print the callees of a given list of QEMU functions.
+#
+# Syntax:
+# list_fn_callees.py [-h] -f FUNCTION [FUNCTION ...] -- \
+# <qemu executable> [<qemu executable options>] \
+# <target executable> [<target executable options>]
+#
+# [-h] - Print the script arguments help message.
+# -f FUNCTION [FUNCTION ...] - List of function names
+#
+# Example of usage:
+# list_fn_callees.py -f helper_float_sub_d helper_float_mul_d -- \
+# qemu-mips coulomb_double-mips
+#
+# This file is a part of the project "TCG Continuous Benchmarking".
+#
+# Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
+# Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@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
+
+
+def find_function_lines(function_name, callgrind_data):
+ """
+ Search for the line with the function name in the
+ callgrind_annotate output when ran using --tre=calling.
+ All the function callees should be listed after that line.
+
+ Parameters:
+ function_name (string): The desired function name to print its callees
+ callgrind_data (list): callgrind_annotate output
+
+ Returns:
+ (list): List of function line numbers
+ """
+ lines = []
+ for i in range(len(callgrind_data)):
+ split_line = callgrind_data[i].split()
+ if len(split_line) > 2 and \
+ split_line[1] == "*" and \
+ split_line[2].split(":")[-1] == function_name:
+ # Function might be in the callgrind_annotate output more than
+ # once, so don't break after finding an instance
+ if callgrind_data[i + 1] != "\n":
+ # Only append the line number if the found instance has
+ # callees
+ lines.append(i)
+ return lines
+
+
+def get_function_calles(function_lines, callgrind_data):
+ """
+ Get all callees data for a function given its list of line numbers in
+ callgrind_annotate output.
+
+ Parameters:
+ function_lines (list): Line numbers of the function to get its callees
+ callgrind_data (list): callgrind_annotate output
+
+ Returns:
+ (list):[[number_of_instructions(int), callee_name(str),
+ number_of_calls(int), source_file(str)]]
+ """
+ callees = []
+ for function_line in function_lines:
+ next_callee = function_line + 1
+ while (callgrind_data[next_callee] != "\n"):
+ split_line = callgrind_data[next_callee].split()
+ number_of_instructions = int(split_line[0].replace(",", ""))
+ source_file = split_line[2].split(":")[0]
+ callee_name = split_line[2].split(":")[1]
+ number_of_calls = int(split_line[3][1:-2])
+ callees.append([number_of_instructions, callee_name,
+ number_of_calls, source_file])
+ next_callee += 1
+ return sorted(callees, reverse=True)
+
+
+def main():
+ # Parse the command line arguments
+ parser = argparse.ArgumentParser(
+ usage="list_fn_callees.py [-h] -f FUNCTION [FUNCTION ...] -- "
+ "<qemu executable> [<qemu executable options>] "
+ "<target executable> [<target executable options>]")
+
+ parser.add_argument("-f", dest="function", type=str,
+ nargs="+", required=True,
+ help="list of function names to print their callees")
+
+ parser.add_argument("command", type=str, nargs="+", help=argparse.SUPPRESS)
+
+ args = parser.parse_args()
+
+ # Extract the needed variables from the args
+ command = args.command
+ function_names = args.function
+
+ # Insure that valgrind is installed
+ check_valgrind = subprocess.run(
+ ["which", "valgrind"], stdout=subprocess.DEVNULL)
+ if check_valgrind.returncode:
+ sys.exit("Please install valgrind before running the script.")
+
+ # Save all intermediate files in a temporary directory
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ # callgrind output file path
+ data_path = os.path.join(tmpdirname, "callgrind.data")
+ # callgrind_annotate output file path
+ annotate_out_path = os.path.join(tmpdirname, "callgrind_annotate.out")
+
+ # Run callgrind
+ callgrind = subprocess.run((["valgrind",
+ "--tool=callgrind",
+ "--callgrind-out-file=" + data_path]
+ + command),
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE)
+ if callgrind.returncode:
+ sys.exit(callgrind.stderr.decode("utf-8"))
+
+ # Save callgrind_annotate output
+ with open(annotate_out_path, "w") as output:
+ callgrind_annotate = subprocess.run(
+ ["callgrind_annotate", data_path,
+ "--threshold=100", "--tree=calling"],
+ stdout=output,
+ stderr=subprocess.PIPE)
+ 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
+ # Get the total number of instructions
+ total_instructions_line_data = \
+ callgrind_data[total_instructions_line_number]
+ total_instructions = total_instructions_line_data.split()[0]
+
+ print("Total number of instructions: {}\n".format(total_instructions))
+
+ # Remove commas and convert to int
+ total_instructions = int(total_instructions.replace(",", ""))
+
+ for function_name in function_names:
+ # Line numbers with the desired function
+ function_lines = find_function_lines(function_name, callgrind_data)
+
+ if len(function_lines) == 0:
+ print("Couldn't locate function: {}.\n".format(
+ function_name))
+ continue
+
+ # Get function callees
+ function_callees = get_function_calles(
+ function_lines, callgrind_data)
+
+ print("Callees of {}:\n".format(function_name))
+
+ # Print table header
+ print("{:>4} {:>15} {:>10} {:>15} {:>10} {:<25} {}".
+ format(
+ "No.",
+ "Instructions",
+ "Percentage",
+ "Calls",
+ "Ins/Call",
+ "Function Name",
+ "Source File")
+ )
+
+ print("{:>4} {:>15} {:>10} {:>15} {:>10} {:<25} {}".
+ format(
+ "-" * 4,
+ "-" * 15,
+ "-" * 10,
+ "-" * 15,
+ "-" * 10,
+ "-" * 25,
+ "-" * 30)
+ )
+
+ for (index, callee) in enumerate(function_callees, start=1):
+ instructions = callee[0]
+ percentage = (callee[0] / total_instructions) * 100
+ calls = callee[2]
+ instruction_per_call = int(callee[0] / callee[2])
+ function_name = callee[1]
+ source_file = callee[3]
+ # Print extracted data
+ print("{:>4} {:>15} {:>9.3f}% {:>15} {:>10} {:<25} {}".
+ format(
+ index,
+ format(instructions, ","),
+ round(percentage, 3),
+ format(calls, ","),
+ format(instruction_per_call, ","),
+ function_name,
+ source_file)
+ )
+
+ print("\n")
+
+
+if __name__ == "__main__":
+ main()
--
2.17.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] scripts/performance: Add list_helpers.py script
2020-07-14 16:41 [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts Ahmed Karaman
2020-07-14 16:41 ` [PATCH 1/2] scripts/performance: Add list_fn_callees.py script Ahmed Karaman
@ 2020-07-14 16:41 ` Ahmed Karaman
2020-07-14 17:25 ` [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts no-reply
2 siblings, 0 replies; 4+ messages in thread
From: Ahmed Karaman @ 2020-07-14 16:41 UTC (permalink / raw)
To: qemu-devel, aleksandar.qemu.devel, philmd, alex.bennee, eblake,
ldoktor, rth, ehabkost, crosa
Cc: Ahmed Karaman
Python script that prints executed helpers of a QEMU invocation.
Syntax:
list_helpers.py [-h] -- \
<qemu executable> [<qemu executable options>] \
<target executable> [<target executable options>]
[-h] - Print the script arguments help message.
Example of usage:
list_helpers.py -- qemu-mips coulomb_double-mips -n10
Example output:
Total number of instructions: 108,933,695
Executed QEMU Helpers:
No. Ins Percent Calls Ins/Call Helper Name Source File
--- ------- ------- ------ -------- -------------------- ---------------
1 183,021 0.168% 1,305 140 helper_float_sub_d <qemu>/target/mips/fpu_helper.c
2 177,111 0.163% 770 230 helper_float_madd_d <qemu>/target/mips/fpu_helper.c
3 171,537 0.157% 1,014 169 helper_float_mul_d <qemu>/target/mips/fpu_helper.c
4 157,298 0.144% 2,443 64 helper_lookup_tb_ptr <qemu>/accel/tcg/tcg-runtime.c
5 138,123 0.127% 897 153 helper_float_add_d <qemu>/target/mips/fpu_helper.c
6 47,083 0.043% 207 227 helper_float_msub_d <qemu>/target/mips/fpu_helper.c
7 24,062 0.022% 487 49 helper_cmp_d_lt <qemu>/target/mips/fpu_helper.c
8 22,910 0.021% 150 152 helper_float_div_d <qemu>/target/mips/fpu_helper.c
9 15,497 0.014% 321 48 helper_cmp_d_eq <qemu>/target/mips/fpu_helper.c
10 9,100 0.008% 52 175 helper_float_trunc_w_d <qemu>/target/mips/fpu_helper.c
11 7,059 0.006% 10 705 helper_float_sqrt_d <qemu>/target/mips/fpu_helper.c
12 3,000 0.003% 40 75 helper_cmp_d_ule <qemu>/target/mips/fpu_helper.c
13 2,720 0.002% 20 136 helper_float_cvtd_w <qemu>/target/mips/fpu_helper.c
14 2,477 0.002% 27 91 helper_swl <qemu>/target/mips/op_helper.c
15 2,000 0.002% 40 50 helper_cmp_d_le <qemu>/target/mips/fpu_helper.c
16 1,800 0.002% 40 45 helper_cmp_d_un <qemu>/target/mips/fpu_helper.c
17 1,164 0.001% 12 97 helper_raise_exception_ <qemu>/target/mips/op_helper.c
18 720 0.001% 10 72 helper_cmp_d_ult <qemu>/target/mips/fpu_helper.c
19 560 0.001% 140 4 helper_cfc1 <qemu>/target/mips/fpu_helper.c
Signed-off-by: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
---
scripts/performance/list_helpers.py | 207 ++++++++++++++++++++++++++++
1 file changed, 207 insertions(+)
create mode 100755 scripts/performance/list_helpers.py
diff --git a/scripts/performance/list_helpers.py b/scripts/performance/list_helpers.py
new file mode 100755
index 0000000000..a97c7ed4fe
--- /dev/null
+++ b/scripts/performance/list_helpers.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+
+# Print the executed helpers of a QEMU invocation.
+#
+# Syntax:
+# list_helpers.py [-h] -- \
+# <qemu executable> [<qemu executable options>] \
+# <target executable> [<target executable options>]
+#
+# [-h] - Print the script arguments help message.
+#
+# Example of usage:
+# list_helpers.py -- qemu-mips coulomb_double-mips
+#
+# This file is a part of the project "TCG Continuous Benchmarking".
+#
+# Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
+# Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@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
+
+
+def find_JIT_line(callgrind_data):
+ """
+ Search for the line with the JIT call in the callgrind_annotate
+ output when ran using --tre=calling.
+ All the helpers should be listed after that line.
+
+ Parameters:
+ callgrind_data (list): callgrind_annotate output
+
+ Returns:
+ (int): Line number of JIT call
+ """
+ line = -1
+ for i in range(len(callgrind_data)):
+ split_line = callgrind_data[i].split()
+ if len(split_line) > 2 and \
+ split_line[1] == "*" and \
+ split_line[-1] == "[???]":
+ line = i
+ break
+ return line
+
+
+def get_helpers(JIT_line, callgrind_data):
+ """
+ Get all helpers data given the line number of the JIT call.
+
+ Parameters:
+ JIT_line (int): Line number of the JIT call
+ callgrind_data (list): callgrind_annotate output
+
+ Returns:
+ (list):[[number_of_instructions(int), helper_name(str),
+ number_of_calls(int), source_file(str)]]
+ """
+ helpers = []
+ next_helper = JIT_line + 1
+ while (callgrind_data[next_helper] != "\n"):
+ split_line = callgrind_data[next_helper].split()
+ number_of_instructions = int(split_line[0].replace(",", ""))
+ source_file = split_line[2].split(":")[0]
+ callee_name = split_line[2].split(":")[1]
+ number_of_calls = int(split_line[3][1:-2])
+ helpers.append([number_of_instructions, callee_name,
+ number_of_calls, source_file])
+ next_helper += 1
+ return sorted(helpers, reverse=True)
+
+
+def main():
+ # Parse the command line arguments
+ parser = argparse.ArgumentParser(
+ usage="list_helpers.py [-h] -- "
+ "<qemu executable> [<qemu executable options>] "
+ "<target executable> [<target executable options>]")
+
+ parser.add_argument("command", type=str, nargs="+", help=argparse.SUPPRESS)
+
+ args = parser.parse_args()
+
+ # Extract the needed variables from the args
+ command = args.command
+
+ # Insure that valgrind is installed
+ check_valgrind = subprocess.run(
+ ["which", "valgrind"], stdout=subprocess.DEVNULL)
+ if check_valgrind.returncode:
+ sys.exit("Please install valgrind before running the script.")
+
+ # Save all intermediate files in a temporary directory
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ # callgrind output file path
+ data_path = os.path.join(tmpdirname, "callgrind.data")
+ # callgrind_annotate output file path
+ annotate_out_path = os.path.join(tmpdirname, "callgrind_annotate.out")
+
+ # Run callgrind
+ callgrind = subprocess.run((["valgrind",
+ "--tool=callgrind",
+ "--callgrind-out-file=" + data_path]
+ + command),
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE)
+ if callgrind.returncode:
+ sys.exit(callgrind.stderr.decode("utf-8"))
+
+ # Save callgrind_annotate output
+ with open(annotate_out_path, "w") as output:
+ callgrind_annotate = subprocess.run(
+ ["callgrind_annotate", data_path,
+ "--threshold=100", "--tree=calling"],
+ stdout=output,
+ stderr=subprocess.PIPE)
+ 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
+ # Get the total number of instructions
+ total_instructions_line_data = \
+ callgrind_data[total_instructions_line_number]
+ total_instructions = total_instructions_line_data.split()[0]
+
+ print("Total number of instructions: {}\n".format(total_instructions))
+
+ # Remove commas and convert to int
+ total_instructions = int(total_instructions.replace(",", ""))
+
+ # Line number with the JIT call
+ JIT_line = find_JIT_line(callgrind_data)
+ if JIT_line == -1:
+ sys.exit("Couldn't locate the JIT call ... Exiting")
+
+ # Get helpers
+ helpers = get_helpers(JIT_line, callgrind_data)
+
+ print("Executed QEMU Helpers:\n")
+
+ # Print table header
+ print("{:>4} {:>15} {:>10} {:>15} {:>10} {:<25} {}".
+ format(
+ "No.",
+ "Instructions",
+ "Percentage",
+ "Calls",
+ "Ins/Call",
+ "Helper Name",
+ "Source File")
+ )
+
+ print("{:>4} {:>15} {:>10} {:>15} {:>10} {:<25} {}".
+ format(
+ "-" * 4,
+ "-" * 15,
+ "-" * 10,
+ "-" * 15,
+ "-" * 10,
+ "-" * 25,
+ "-" * 30)
+ )
+
+ for (index, callee) in enumerate(helpers, start=1):
+ instructions = callee[0]
+ percentage = (callee[0] / total_instructions) * 100
+ calls = callee[2]
+ instruction_per_call = int(callee[0] / callee[2])
+ helper_name = callee[1]
+ source_file = callee[3]
+ # Print extracted data
+ print("{:>4} {:>15} {:>9.3f}% {:>15} {:>10} {:<25} {}".
+ format(
+ index,
+ format(instructions, ","),
+ round(percentage, 3),
+ format(calls, ","),
+ format(instruction_per_call, ","),
+ helper_name,
+ source_file)
+ )
+
+
+if __name__ == "__main__":
+ main()
--
2.17.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts
2020-07-14 16:41 [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts Ahmed Karaman
2020-07-14 16:41 ` [PATCH 1/2] scripts/performance: Add list_fn_callees.py script Ahmed Karaman
2020-07-14 16:41 ` [PATCH 2/2] scripts/performance: Add list_helpers.py script Ahmed Karaman
@ 2020-07-14 17:25 ` no-reply
2 siblings, 0 replies; 4+ messages in thread
From: no-reply @ 2020-07-14 17:25 UTC (permalink / raw)
To: ahmedkhaledkaraman
Cc: ldoktor, ehabkost, philmd, qemu-devel, ahmedkhaledkaraman,
aleksandar.qemu.devel, crosa, alex.bennee, rth
Patchew URL: https://patchew.org/QEMU/20200714164156.9353-1-ahmedkhaledkaraman@gmail.com/
Hi,
This series seems to have some coding style problems. See output below for
more information:
Type: series
Message-id: 20200714164156.9353-1-ahmedkhaledkaraman@gmail.com
Subject: [PATCH 0/2] Add list_fn_callees.py and list_helpers.py scripts
=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===
From https://github.com/patchew-project/qemu
1a53dfe..beff47a master -> master
Switched to a new branch 'test'
f9644e8 scripts/performance: Add list_helpers.py script
aa66eb0 scripts/performance: Add list_fn_callees.py script
=== OUTPUT BEGIN ===
1/2 Checking commit aa66eb05dbfb (scripts/performance: Add list_fn_callees.py script)
ERROR: Missing Signed-off-by: line(s)
total: 1 errors, 0 warnings, 228 lines checked
Patch 1/2 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
2/2 Checking commit f9644e83658f (scripts/performance: Add list_helpers.py script)
ERROR: Missing Signed-off-by: line(s)
total: 1 errors, 0 warnings, 207 lines checked
Patch 2/2 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
The full log is available at
http://patchew.org/logs/20200714164156.9353-1-ahmedkhaledkaraman@gmail.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
^ permalink raw reply [flat|nested] 4+ messages in thread