All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
To: qemu-devel@nongnu.org, aleksandar.qemu.devel@gmail.com,
	philmd@redhat.com, alex.bennee@linaro.org, eblake@redhat.com,
	ldoktor@redhat.com, jsnow@redhat.com, rth@twiddle.net,
	ehabkost@redhat.com, crosa@redhat.com
Cc: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
Subject: [PATCH 2/9] scripts/performance: Refactor topN_callgrind.py
Date: Fri, 28 Aug 2020 12:40:55 +0200	[thread overview]
Message-ID: <20200828104102.4490-3-ahmedkhaledkaraman@gmail.com> (raw)
In-Reply-To: <20200828104102.4490-1-ahmedkhaledkaraman@gmail.com>

    - Apply pylint and flake8 formatting rules to the script.
    - Use 'tempfile' instead of '/tmp' for creating temporary files.

    Signed-off-by: Ahmed Karaman <ahmedkhaledkaraman@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 <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/>.
+"""
+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 <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
 
 
 # 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/callgrind.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="+CALLGRIND_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_instructions_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



  parent reply	other threads:[~2020-08-28 10:44 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-28 10:40 [PATCH 0/9] GSoC 2020 - TCG Continuous Benchmarking scripts and tools Ahmed Karaman
2020-08-28 10:40 ` [PATCH 1/9] scripts/performance: Refactor topN_perf.py Ahmed Karaman
2020-09-07 20:52   ` Aleksandar Markovic
2020-09-18 20:33   ` Aleksandar Markovic
2020-09-19 11:17     ` Bottleneck problem to merge Python patches Philippe Mathieu-Daudé
2020-09-21 14:49       ` John Snow
2020-09-21 15:54       ` Eduardo Habkost
2020-09-21 17:57       ` Cleber Rosa
2020-10-01 20:41   ` [PATCH 1/9] scripts/performance: Refactor topN_perf.py John Snow
2020-10-01 21:59     ` John Snow
2020-08-28 10:40 ` Ahmed Karaman [this message]
2020-09-07 20:53   ` [PATCH 2/9] scripts/performance: Refactor topN_callgrind.py Aleksandar Markovic
2020-08-28 10:40 ` [PATCH 3/9] scripts/performance: Refactor dissect.py Ahmed Karaman
2020-09-02  8:48   ` Aleksandar Markovic
2020-08-28 10:40 ` [PATCH 4/9] scripts/performance: Add list_fn_callees.py script Ahmed Karaman
2020-08-28 10:40 ` [PATCH 5/9] scripts/performance: Add list_helpers.py script Ahmed Karaman
2020-08-28 10:40 ` [PATCH 6/9] scripts/performance: Add bisect.py script Ahmed Karaman
2020-08-28 10:41 ` [PATCH 7/9] tests/performance: Add nightly tests Ahmed Karaman
2020-09-02  8:36   ` Aleksandar Markovic
2020-09-02 13:26   ` Alex Bennée
2020-09-02 17:29     ` Ahmed Karaman
2020-09-15 16:39     ` Aleksandar Markovic
2020-09-16  8:31       ` Alex Bennée
2020-08-28 10:41 ` [PATCH 8/9] MAINTAINERS: Add 'tests/performance' to 'Performance Tools and Tests' subsection Ahmed Karaman
2020-09-02  8:37   ` Aleksandar Markovic
2020-08-28 10:41 ` [PATCH 9/9] scripts/performance: Add topN_system.py script Ahmed Karaman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200828104102.4490-3-ahmedkhaledkaraman@gmail.com \
    --to=ahmedkhaledkaraman@gmail.com \
    --cc=aleksandar.qemu.devel@gmail.com \
    --cc=alex.bennee@linaro.org \
    --cc=crosa@redhat.com \
    --cc=eblake@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=ldoktor@redhat.com \
    --cc=philmd@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.