diff mbox series

[1/9] scripts/performance: Refactor topN_perf.py

Message ID 20200828104102.4490-2-ahmedkhaledkaraman@gmail.com
State New
Headers show
Series [1/9] scripts/performance: Refactor topN_perf.py | expand

Commit Message

Ahmed Karaman Aug. 28, 2020, 10:40 a.m. UTC
- 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_perf.py | 174 +++++++++++++++----------------
 1 file changed, 87 insertions(+), 87 deletions(-)

Comments

Aleksandar Markovic Sept. 7, 2020, 8:52 p.m. UTC | #1
On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@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.devel@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 <ahmedkhaledkaraman@gmail.com>
> ---
>  scripts/performance/topN_perf.py | 174 +++++++++++++++----------------
>  1 file changed, 87 insertions(+), 87 deletions(-)
>
> diff --git a/scripts/performance/topN_perf.py b/scripts/performance/topN_
> perf.py
> index 07be195fc8..56b100da87 100755
> --- a/scripts/performance/topN_perf.py
> +++ b/scripts/performance/topN_perf.py
> @@ -1,72 +1,77 @@
>  #!/usr/bin/env python3
>
> -#  Print the top N most executed functions in QEMU using perf.
> -#  Syntax:
> -#  topN_perf.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_perf.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 perf.
> +
> +Syntax:
> +topN_perf.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_perf.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_perf.py [-h] [-n] <number of displayed top functions >
> -- '
> +PARSER = argparse.ArgumentParser(
> +    usage='topN_perf.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 perf is installed
> -check_perf_presence = subprocess.run(["which", "perf"],
> -                                     stdout=subprocess.DEVNULL)
> -if check_perf_presence.returncode:
> +CHECK_PERF_PRESENCE = subprocess.run(["which", "perf"],
> +                                     stdout=subprocess.DEVNULL,
> +                                     check=False)
> +if CHECK_PERF_PRESENCE.returncode:
>      sys.exit("Please install perf before running the script!")
>
>  # Insure user has previllage to run perf
> -check_perf_executability = subprocess.run(["perf", "stat", "ls", "/"],
> +CHECK_PERF_EXECUTABILITY = subprocess.run(["perf", "stat", "ls", "/"],
>                                            stdout=subprocess.DEVNULL,
> -                                          stderr=subprocess.DEVNULL)
> -if check_perf_executability.returncode:
> -    sys.exit(
> -"""
> +                                          stderr=subprocess.DEVNULL,
> +                                          check=False)
> +if CHECK_PERF_EXECUTABILITY.returncode:
> +    sys.exit("""
>  Error:
>  You may not have permission to collect stats.
>
> @@ -85,43 +90,42 @@ To make this setting permanent, edit /etc/sysctl.conf
> too, e.g.:
>     kernel.perf_event_paranoid = -1
>
>  * Alternatively, you can run this script under sudo privileges.
> -"""
> -)
> -
> -# Run perf record
> -perf_record = subprocess.run((["perf", "record",
> "--output=/tmp/perf.data"] +
> -                              command),
> -                             stdout=subprocess.DEVNULL,
> -                             stderr=subprocess.PIPE)
> -if perf_record.returncode:
> -    os.unlink('/tmp/perf.data')
> -    sys.exit(perf_record.stderr.decode("utf-8"))
> -
> -# Save perf report output to /tmp/perf_report.out
> -with open("/tmp/perf_report.out", "w") as output:
> -    perf_report = subprocess.run(
> -        ["perf", "report", "--input=/tmp/perf.data", "--stdio"],
> -        stdout=output,
> -        stderr=subprocess.PIPE)
> -    if perf_report.returncode:
> -        os.unlink('/tmp/perf.data')
> -        output.close()
> -        os.unlink('/tmp/perf_report.out')
> -        sys.exit(perf_report.stderr.decode("utf-8"))
> -
> -# Read the reported data to functions[]
> -functions = []
> -with open("/tmp/perf_report.out", "r") as data:
> -    # Only read lines that are not comments (comments start with #)
> -    # Only read lines that are not empty
> -    functions = [line for line in data.readlines() if line and line[0]
> -                 != '#' and line[0] != "\n"]
> -
> -# Limit the number of top functions to "top"
> -number_of_top_functions = top if len(functions) > top else len(functions)
> -
> -# Store the data of the top functions in top_functions[]
> -top_functions = functions[:number_of_top_functions]
> +""")
> +
> +# Run perf and save all intermediate files in a temporary directory
> +with tempfile.TemporaryDirectory() as tmpdir:
> +    RECORD_PATH = os.path.join(tmpdir, "record.data")
> +    REPORT_PATH = os.path.join(tmpdir, "report.txt")
> +
> +    PERF_RECORD = subprocess.run((["perf", "record",
> "--output="+RECORD_PATH] +
> +                                  COMMAND),
> +                                 stdout=subprocess.DEVNULL,
> +                                 stderr=subprocess.PIPE,
> +                                 check=False)
> +    if PERF_RECORD.returncode:
> +        sys.exit(PERF_RECORD.stderr.decode("utf-8"))
> +
> +    with open(REPORT_PATH, "w") as output:
> +        PERF_REPORT = subprocess.run(
> +            ["perf", "report", "--input="+RECORD_PATH, "--stdio"],
> +            stdout=output,
> +            stderr=subprocess.PIPE,
> +            check=False)
> +        if PERF_REPORT.returncode:
> +            sys.exit(PERF_REPORT.stderr.decode("utf-8"))
> +
> +    # Save the reported data to FUNCTIONS[]
> +    with open(REPORT_PATH, "r") as data:
> +        # Only read lines that are not comments (comments start with #)
> +        # Only read lines that are not empty
> +        FUNCTIONS = [line for line in data.readlines() if line and
> +                     line[0] != '#' and line[0] != "\n"]
> +
> +# Limit the number of top functions to "TOP"
> +NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) > TOP else len(FUNCTIONS)
> +
> +# Store the data of the top functions in TOP_FUNCTIONS[]
> +TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]
>
>  # Print table header
>  print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',
> @@ -134,7 +138,7 @@ print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}
> {}'.format('No.',
>                                                           '-' * 25))
>
>  # 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()
>      function_percentage = function_data[0]
>      function_name = function_data[-1]
> @@ -143,7 +147,3 @@ for (index, function) in enumerate(top_functions,
> start=1):
>                                               function_percentage,
>                                               function_name,
>                                               function_invoker))
> -
> -# Remove intermediate files
> -os.unlink('/tmp/perf.data')
> -os.unlink('/tmp/perf_report.out')
> --
> 2.17.1
>
>
<br><br>On Friday, August 28, 2020, Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">- Apply pylint and flake8 formatting rules to the script.<br>
- Use &#39;tempfile&#39; instead of &#39;/tmp&#39; for creating temporary files.<br>
<br></blockquote><div><br></div><div><div id="cv_" class=" Xg  " style="width:320px;height:auto"><div class="Zg" style="width:320px;overflow-y:auto"><div class="Rg"><div class="Li"><div class="yj"><div class="Ph"><div id="cvcmsg_1744e012f27314c1" class="yh" style="border-top-left-radius:0px;border-top-right-radius:0px;margin-bottom:11px"><div class="Vh" id="cvcfullmsg_1744e012f27314c1"><div id="cvcmsgbod_1744e012f27314c1" class="aj"><div class="Ni"><div class="ni pi " dir="ltr"><div>Reviewed-by: Aleksandar Markovic &lt;<a href="mailto:aleksandar.qemu.devel@gmail.com" target="_blank">aleksandar.qemu.devel@gmail.c<wbr>om</a>&gt;<br></div><div style="clear:both"></div></div><div style="clear:both"></div><div><div class="M j T b hc Aj S" tabindex="0"><div class="V j hf"></div></div></div><div style="clear:both"></div></div></div></div></div></div><div class="Pj"></div><div id="cvcfb" class="Cj"><div class="Dj" style="width:300px"><div class="Ej"><div class="M j T b hc Fj Mi S" tabindex="0"><div class="V j vd Hj"></div><div class="V j Y Lj">Reply</div></div></div><div class="Gj" style="background-size:initial"></div><div id="cvcfra" class="Ej"><div class="M j T b hc Fj Mi S" tabindex="0"><div class="V j ud Hj"></div><div class="V j Y Lj">Reply all</div></div></div><div id="cvcfrab" class="Gj" style="background-size:initial"></div><div class="Ej"><div class="M j T b hc Fj Mi S" tabindex="0"><div class="V j sd Hj"></div><div class="V j Y Lj">Forward</div></div></div></div></div></div></div><div class="Og"><div class="dh"><div class="Nh"><div class="Nh">View Gmail in: <b>Mobile</b> | <a href="https://mail.google.com/mail/mu/mp/966/#">Older version</a> | <a href="https://mail.google.com/mail/mu/mp/966/#">Desktop</a></div><div class="Nh Oh"><span dir="ltr">© 2020 Google</span></div></div></div></div></div></div><div class="ah" style="width:320px"></div></div><div class="Ls" style="display:block"><div class="As"><div class="Bs"><div class="Gs  " style="font-size:13px"><div class="Is Hs"></div></div></div></div></div></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Signed-off-by: Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>
---<br>
 scripts/performance/topN_perf.<wbr>py | 174 +++++++++++++++---------------<wbr>-<br>
 1 file changed, 87 insertions(+), 87 deletions(-)<br>
<br>
diff --git a/scripts/performance/topN_<wbr>perf.py b/scripts/performance/topN_<wbr>perf.py<br>
index 07be195fc8..56b100da87 100755<br>
--- a/scripts/performance/topN_<wbr>perf.py<br>
+++ b/scripts/performance/topN_<wbr>perf.py<br>
@@ -1,72 +1,77 @@<br>
 #!/usr/bin/env python3<br>
<br>
-#  Print the top N most executed functions in QEMU using perf.<br>
-#  Syntax:<br>
-#  topN_perf.py [-h] [-n] &lt;number of displayed top functions&gt;  -- \<br>
-#           &lt;qemu executable&gt; [&lt;qemu executable options&gt;] \<br>
-#           &lt;target executable&gt; [&lt;target execurable options&gt;]<br>
-#<br>
-#  [-h] - Print the script arguments help message.<br>
-#  [-n] - Specify the number of top functions to print.<br>
-#       - If this flag is not specified, the tool defaults to 25.<br>
-#<br>
-#  Example of usage:<br>
-#  topN_perf.py -n 20 -- qemu-arm coulomb_double-arm<br>
-#<br>
-#  This file is a part of the project &quot;TCG Continuous Benchmarking&quot;.<br>
-#<br>
-#  Copyright (C) 2020  Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>
-#  Copyright (C) 2020  Aleksandar Markovic &lt;<a href="mailto:aleksandar.qemu.devel@gmail.com">aleksandar.qemu.devel@gmail.<wbr>com</a>&gt;<br>
-#<br>
-#  This program is free software: you can redistribute it and/or modify<br>
-#  it under the terms of the GNU General Public License as published by<br>
-#  the Free Software Foundation, either version 2 of the License, or<br>
-#  (at your option) any later version.<br>
-#<br>
-#  This program is distributed in the hope that it will be useful,<br>
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
-#  GNU General Public License for more details.<br>
-#<br>
-#  You should have received a copy of the GNU General Public License<br>
-#  along with this program. If not, see &lt;<a href="https://www.gnu.org/licenses/" target="_blank">https://www.gnu.org/licenses/</a><wbr>&gt;.<br>
+&quot;&quot;&quot;<br>
+Print the top N most executed functions in QEMU using perf.<br>
+<br>
+Syntax:<br>
+topN_perf.py [-h] [-n &lt;number of displayed top functions&gt;] -- \<br>
+         &lt;qemu executable&gt; [&lt;qemu executable options&gt;] \<br>
+         &lt;target executable&gt; [&lt;target execurable options&gt;]<br>
+<br>
+[-h] - Print the script arguments help message.<br>
+[-n] - Specify the number of top functions to print.<br>
+     - If this flag is not specified, the tool defaults to 25.<br>
+<br>
+Example of usage:<br>
+topN_perf.py -n 20 -- qemu-arm coulomb_double-arm<br>
+<br>
+This file is a part of the project &quot;TCG Continuous Benchmarking&quot;.<br>
+<br>
+Copyright (C) 2020  Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>
+Copyright (C) 2020  Aleksandar Markovic &lt;<a href="mailto:aleksandar.qemu.devel@gmail.com">aleksandar.qemu.devel@gmail.<wbr>com</a>&gt;<br>
+<br>
+This program is free software: you can redistribute it and/or modify<br>
+it under the terms of the GNU General Public License as published by<br>
+the Free Software Foundation, either version 2 of the License, or<br>
+(at your option) any later version.<br>
+<br>
+This program is distributed in the hope that it will be useful,<br>
+but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+GNU General Public License for more details.<br>
+<br>
+You should have received a copy of the GNU General Public License<br>
+along with this program. If not, see &lt;<a href="https://www.gnu.org/licenses/" target="_blank">https://www.gnu.org/licenses/</a><wbr>&gt;.<br>
+&quot;&quot;&quot;<br>
<br>
 import argparse<br>
 import os<br>
 import subprocess<br>
 import sys<br>
+import tempfile<br>
<br>
<br>
 # Parse the command line arguments<br>
-parser = argparse.ArgumentParser(<br>
-    usage=&#39;topN_perf.py [-h] [-n] &lt;number of displayed top functions &gt;  -- &#39;<br>
+PARSER = argparse.ArgumentParser(<br>
+    usage=&#39;topN_perf.py [-h] [-n &lt;number of displayed top functions&gt;] -- &#39;<br>
           &#39;&lt;qemu executable&gt; [&lt;qemu executable options&gt;] &#39;<br>
           &#39;&lt;target executable&gt; [&lt;target executable options&gt;]&#39;)<br>
<br>
-parser.add_argument(&#39;-n&#39;, dest=&#39;top&#39;, type=int, default=25,<br>
+PARSER.add_argument(&#39;-n&#39;, dest=&#39;top&#39;, type=int, default=25,<br>
                     help=&#39;Specify the number of top functions to print.&#39;)<br>
<br>
-parser.add_argument(&#39;command&#39;<wbr>, type=str, nargs=&#39;+&#39;, help=argparse.SUPPRESS)<br>
+PARSER.add_argument(&#39;command&#39;<wbr>, type=str, nargs=&#39;+&#39;, help=argparse.SUPPRESS)<br>
<br>
-args = parser.parse_args()<br>
+ARGS = PARSER.parse_args()<br>
<br>
 # Extract the needed variables from the args<br>
-command = args.command<br>
-top = args.top<br>
+COMMAND = ARGS.command<br>
+TOP = ARGS.top<br>
<br>
 # Insure that perf is installed<br>
-check_perf_presence = subprocess.run([&quot;which&quot;, &quot;perf&quot;],<br>
-                                     stdout=subprocess.DEVNULL)<br>
-if check_perf_presence.<wbr>returncode:<br>
+CHECK_PERF_PRESENCE = subprocess.run([&quot;which&quot;, &quot;perf&quot;],<br>
+                                     stdout=subprocess.DEVNULL,<br>
+                                     check=False)<br>
+if CHECK_PERF_PRESENCE.<wbr>returncode:<br>
     sys.exit(&quot;Please install perf before running the script!&quot;)<br>
<br>
 # Insure user has previllage to run perf<br>
-check_perf_executability = subprocess.run([&quot;perf&quot;, &quot;stat&quot;, &quot;ls&quot;, &quot;/&quot;],<br>
+CHECK_PERF_EXECUTABILITY = subprocess.run([&quot;perf&quot;, &quot;stat&quot;, &quot;ls&quot;, &quot;/&quot;],<br>
                                           stdout=subprocess.DEVNULL,<br>
-                                          stderr=subprocess.DEVNULL)<br>
-if check_perf_executability.<wbr>returncode:<br>
-    sys.exit(<br>
-&quot;&quot;&quot;<br>
+                                          stderr=subprocess.DEVNULL,<br>
+                                          check=False)<br>
+if CHECK_PERF_EXECUTABILITY.<wbr>returncode:<br>
+    sys.exit(&quot;&quot;&quot;<br>
 Error:<br>
 You may not have permission to collect stats.<br>
<br>
@@ -85,43 +90,42 @@ To make this setting permanent, edit /etc/sysctl.conf too, e.g.:<br>
    kernel.perf_event_paranoid = -1<br>
<br>
 * Alternatively, you can run this script under sudo privileges.<br>
-&quot;&quot;&quot;<br>
-)<br>
-<br>
-# Run perf record<br>
-perf_record = subprocess.run(([&quot;perf&quot;, &quot;record&quot;, &quot;--output=/tmp/perf.data&quot;] +<br>
-                              command),<br>
-                             stdout=subprocess.DEVNULL,<br>
-                             stderr=subprocess.PIPE)<br>
-if perf_record.returncode:<br>
-    os.unlink(&#39;/tmp/perf.data&#39;)<br>
-    sys.exit(perf_record.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
-<br>
-# Save perf report output to /tmp/perf_report.out<br>
-with open(&quot;/tmp/perf_report.out&quot;, &quot;w&quot;) as output:<br>
-    perf_report = subprocess.run(<br>
-        [&quot;perf&quot;, &quot;report&quot;, &quot;--input=/tmp/perf.data&quot;, &quot;--stdio&quot;],<br>
-        stdout=output,<br>
-        stderr=subprocess.PIPE)<br>
-    if perf_report.returncode:<br>
-        os.unlink(&#39;/tmp/perf.data&#39;)<br>
-        output.close()<br>
-        os.unlink(&#39;/tmp/perf_report.<wbr>out&#39;)<br>
-        sys.exit(perf_report.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
-<br>
-# Read the reported data to functions[]<br>
-functions = []<br>
-with open(&quot;/tmp/perf_report.out&quot;, &quot;r&quot;) as data:<br>
-    # Only read lines that are not comments (comments start with #)<br>
-    # Only read lines that are not empty<br>
-    functions = [line for line in data.readlines() if line and line[0]<br>
-                 != &#39;#&#39; and line[0] != &quot;\n&quot;]<br>
-<br>
-# Limit the number of top functions to &quot;top&quot;<br>
-number_of_top_functions = top if len(functions) &gt; top else len(functions)<br>
-<br>
-# Store the data of the top functions in top_functions[]<br>
-top_functions = functions[:number_of_top_<wbr>functions]<br>
+&quot;&quot;&quot;)<br>
+<br>
+# Run perf and save all intermediate files in a temporary directory<br>
+with tempfile.TemporaryDirectory() as tmpdir:<br>
+    RECORD_PATH = os.path.join(tmpdir, &quot;record.data&quot;)<br>
+    REPORT_PATH = os.path.join(tmpdir, &quot;report.txt&quot;)<br>
+<br>
+    PERF_RECORD = subprocess.run(([&quot;perf&quot;, &quot;record&quot;, &quot;--output=&quot;+RECORD_PATH] +<br>
+                                  COMMAND),<br>
+                                 stdout=subprocess.DEVNULL,<br>
+                                 stderr=subprocess.PIPE,<br>
+                                 check=False)<br>
+    if PERF_RECORD.returncode:<br>
+        sys.exit(PERF_RECORD.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
+<br>
+    with open(REPORT_PATH, &quot;w&quot;) as output:<br>
+        PERF_REPORT = subprocess.run(<br>
+            [&quot;perf&quot;, &quot;report&quot;, &quot;--input=&quot;+RECORD_PATH, &quot;--stdio&quot;],<br>
+            stdout=output,<br>
+            stderr=subprocess.PIPE,<br>
+            check=False)<br>
+        if PERF_REPORT.returncode:<br>
+            sys.exit(PERF_REPORT.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
+<br>
+    # Save the reported data to FUNCTIONS[]<br>
+    with open(REPORT_PATH, &quot;r&quot;) as data:<br>
+        # Only read lines that are not comments (comments start with #)<br>
+        # Only read lines that are not empty<br>
+        FUNCTIONS = [line for line in data.readlines() if line and<br>
+                     line[0] != &#39;#&#39; and line[0] != &quot;\n&quot;]<br>
+<br>
+# Limit the number of top functions to &quot;TOP&quot;<br>
+NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) &gt; TOP else len(FUNCTIONS)<br>
+<br>
+# Store the data of the top functions in TOP_FUNCTIONS[]<br>
+TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]<br>
<br>
 # Print table header<br>
 print(&#39;{:&gt;4}  {:&gt;10}  {:&lt;30}  {}\n{}  {}  {}  {}&#39;.format(&#39;No.&#39;,<br>
@@ -134,7 +138,7 @@ print(&#39;{:&gt;4}  {:&gt;10}  {:&lt;30}  {}\n{}  {}  {}  {}&#39;.format(&#39;No.&#39;,<br>
                                                          &#39;-&#39; * 25))<br>
<br>
 # Print top N functions<br>
-for (index, function) in enumerate(top_functions, start=1):<br>
+for (index, function) in enumerate(TOP_FUNCTIONS, start=1):<br>
     function_data = function.split()<br>
     function_percentage = function_data[0]<br>
     function_name = function_data[-1]<br>
@@ -143,7 +147,3 @@ for (index, function) in enumerate(top_functions, start=1):<br>
                                              function_percentage,<br>
                                              function_name,<br>
                                              function_invoker))<br>
-<br>
-# Remove intermediate files<br>
-os.unlink(&#39;/tmp/perf.data&#39;)<br>
-os.unlink(&#39;/tmp/perf_report.<wbr>out&#39;)<br>
-- <br>
2.17.1<br>
<br>
</blockquote>
Aleksandar Markovic Sept. 18, 2020, 8:33 p.m. UTC | #2
On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
wrote:

> - 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>

> ---



Hello, folks.

This series seems forgotten. Can some of you perhaps take a look, review,
and possibly integrate some patches in a pull request?

Ahmed invested quite of time to improve the functionality and quality of
scripts, and they are truly useful for developers interested in performance
measurement.

Thanks,
Aleksandar




>  scripts/performance/topN_perf.py | 174 +++++++++++++++----------------

>  1 file changed, 87 insertions(+), 87 deletions(-)

>

> diff --git a/scripts/performance/topN_perf.py b/scripts/performance/topN_

> perf.py

> index 07be195fc8..56b100da87 100755

> --- a/scripts/performance/topN_perf.py

> +++ b/scripts/performance/topN_perf.py

> @@ -1,72 +1,77 @@

>  #!/usr/bin/env python3

>

> -#  Print the top N most executed functions in QEMU using perf.

> -#  Syntax:

> -#  topN_perf.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_perf.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 perf.

> +

> +Syntax:

> +topN_perf.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_perf.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_perf.py [-h] [-n] <number of displayed top functions >

> -- '

> +PARSER = argparse.ArgumentParser(

> +    usage='topN_perf.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 perf is installed

> -check_perf_presence = subprocess.run(["which", "perf"],

> -                                     stdout=subprocess.DEVNULL)

> -if check_perf_presence.returncode:

> +CHECK_PERF_PRESENCE = subprocess.run(["which", "perf"],

> +                                     stdout=subprocess.DEVNULL,

> +                                     check=False)

> +if CHECK_PERF_PRESENCE.returncode:

>      sys.exit("Please install perf before running the script!")

>

>  # Insure user has previllage to run perf

> -check_perf_executability = subprocess.run(["perf", "stat", "ls", "/"],

> +CHECK_PERF_EXECUTABILITY = subprocess.run(["perf", "stat", "ls", "/"],

>                                            stdout=subprocess.DEVNULL,

> -                                          stderr=subprocess.DEVNULL)

> -if check_perf_executability.returncode:

> -    sys.exit(

> -"""

> +                                          stderr=subprocess.DEVNULL,

> +                                          check=False)

> +if CHECK_PERF_EXECUTABILITY.returncode:

> +    sys.exit("""

>  Error:

>  You may not have permission to collect stats.

>

> @@ -85,43 +90,42 @@ To make this setting permanent, edit /etc/sysctl.conf

> too, e.g.:

>     kernel.perf_event_paranoid = -1

>

>  * Alternatively, you can run this script under sudo privileges.

> -"""

> -)

> -

> -# Run perf record

> -perf_record = subprocess.run((["perf", "record",

> "--output=/tmp/perf.data"] +

> -                              command),

> -                             stdout=subprocess.DEVNULL,

> -                             stderr=subprocess.PIPE)

> -if perf_record.returncode:

> -    os.unlink('/tmp/perf.data')

> -    sys.exit(perf_record.stderr.decode("utf-8"))

> -

> -# Save perf report output to /tmp/perf_report.out

> -with open("/tmp/perf_report.out", "w") as output:

> -    perf_report = subprocess.run(

> -        ["perf", "report", "--input=/tmp/perf.data", "--stdio"],

> -        stdout=output,

> -        stderr=subprocess.PIPE)

> -    if perf_report.returncode:

> -        os.unlink('/tmp/perf.data')

> -        output.close()

> -        os.unlink('/tmp/perf_report.out')

> -        sys.exit(perf_report.stderr.decode("utf-8"))

> -

> -# Read the reported data to functions[]

> -functions = []

> -with open("/tmp/perf_report.out", "r") as data:

> -    # Only read lines that are not comments (comments start with #)

> -    # Only read lines that are not empty

> -    functions = [line for line in data.readlines() if line and line[0]

> -                 != '#' and line[0] != "\n"]

> -

> -# Limit the number of top functions to "top"

> -number_of_top_functions = top if len(functions) > top else len(functions)

> -

> -# Store the data of the top functions in top_functions[]

> -top_functions = functions[:number_of_top_functions]

> +""")

> +

> +# Run perf and save all intermediate files in a temporary directory

> +with tempfile.TemporaryDirectory() as tmpdir:

> +    RECORD_PATH = os.path.join(tmpdir, "record.data")

> +    REPORT_PATH = os.path.join(tmpdir, "report.txt")

> +

> +    PERF_RECORD = subprocess.run((["perf", "record",

> "--output="+RECORD_PATH] +

> +                                  COMMAND),

> +                                 stdout=subprocess.DEVNULL,

> +                                 stderr=subprocess.PIPE,

> +                                 check=False)

> +    if PERF_RECORD.returncode:

> +        sys.exit(PERF_RECORD.stderr.decode("utf-8"))

> +

> +    with open(REPORT_PATH, "w") as output:

> +        PERF_REPORT = subprocess.run(

> +            ["perf", "report", "--input="+RECORD_PATH, "--stdio"],

> +            stdout=output,

> +            stderr=subprocess.PIPE,

> +            check=False)

> +        if PERF_REPORT.returncode:

> +            sys.exit(PERF_REPORT.stderr.decode("utf-8"))

> +

> +    # Save the reported data to FUNCTIONS[]

> +    with open(REPORT_PATH, "r") as data:

> +        # Only read lines that are not comments (comments start with #)

> +        # Only read lines that are not empty

> +        FUNCTIONS = [line for line in data.readlines() if line and

> +                     line[0] != '#' and line[0] != "\n"]

> +

> +# Limit the number of top functions to "TOP"

> +NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) > TOP else len(FUNCTIONS)

> +

> +# Store the data of the top functions in TOP_FUNCTIONS[]

> +TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]

>

>  # Print table header

>  print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',

> @@ -134,7 +138,7 @@ print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}

> {}'.format('No.',

>                                                           '-' * 25))

>

>  # 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()

>      function_percentage = function_data[0]

>      function_name = function_data[-1]

> @@ -143,7 +147,3 @@ for (index, function) in enumerate(top_functions,

> start=1):

>                                               function_percentage,

>                                               function_name,

>                                               function_invoker))

> -

> -# Remove intermediate files

> -os.unlink('/tmp/perf.data')

> -os.unlink('/tmp/perf_report.out')

> --

> 2.17.1

>

>

>
<br><br>On Friday, August 28, 2020, Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">- Apply pylint and flake8 formatting rules to the script.<br>
- Use &#39;tempfile&#39; instead of &#39;/tmp&#39; for creating temporary files.<br>
<br>
Signed-off-by: Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>

---</blockquote><div><br></div><div>Hello, folks.</div><div><br></div><div>This series seems forgotten. Can some of you perhaps take a look, review, and possibly integrate some patches in a pull request?</div><div><br></div><div>Ahmed invested quite of time to improve the functionality and quality of scripts, and they are truly useful for developers interested in performance measurement.</div><div><br></div><div>Thanks,</div><div>Aleksandar</div><div><br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 scripts/performance/topN_perf.<wbr>py | 174 +++++++++++++++---------------<wbr>-<br>
 1 file changed, 87 insertions(+), 87 deletions(-)<br>
<br>
diff --git a/scripts/performance/topN_<wbr>perf.py b/scripts/performance/topN_<wbr>perf.py<br>
index 07be195fc8..56b100da87 100755<br>
--- a/scripts/performance/topN_<wbr>perf.py<br>
+++ b/scripts/performance/topN_<wbr>perf.py<br>
@@ -1,72 +1,77 @@<br>
 #!/usr/bin/env python3<br>
<br>
-#  Print the top N most executed functions in QEMU using perf.<br>
-#  Syntax:<br>
-#  topN_perf.py [-h] [-n] &lt;number of displayed top functions&gt;  -- \<br>
-#           &lt;qemu executable&gt; [&lt;qemu executable options&gt;] \<br>
-#           &lt;target executable&gt; [&lt;target execurable options&gt;]<br>
-#<br>
-#  [-h] - Print the script arguments help message.<br>
-#  [-n] - Specify the number of top functions to print.<br>
-#       - If this flag is not specified, the tool defaults to 25.<br>
-#<br>
-#  Example of usage:<br>
-#  topN_perf.py -n 20 -- qemu-arm coulomb_double-arm<br>
-#<br>
-#  This file is a part of the project &quot;TCG Continuous Benchmarking&quot;.<br>
-#<br>
-#  Copyright (C) 2020  Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>
-#  Copyright (C) 2020  Aleksandar Markovic &lt;<a href="mailto:aleksandar.qemu.devel@gmail.com">aleksandar.qemu.devel@gmail.<wbr>com</a>&gt;<br>
-#<br>
-#  This program is free software: you can redistribute it and/or modify<br>
-#  it under the terms of the GNU General Public License as published by<br>
-#  the Free Software Foundation, either version 2 of the License, or<br>
-#  (at your option) any later version.<br>
-#<br>
-#  This program is distributed in the hope that it will be useful,<br>
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
-#  GNU General Public License for more details.<br>
-#<br>
-#  You should have received a copy of the GNU General Public License<br>
-#  along with this program. If not, see &lt;<a href="https://www.gnu.org/licenses/" target="_blank">https://www.gnu.org/licenses/</a><wbr>&gt;.<br>
+&quot;&quot;&quot;<br>
+Print the top N most executed functions in QEMU using perf.<br>
+<br>
+Syntax:<br>
+topN_perf.py [-h] [-n &lt;number of displayed top functions&gt;] -- \<br>
+         &lt;qemu executable&gt; [&lt;qemu executable options&gt;] \<br>
+         &lt;target executable&gt; [&lt;target execurable options&gt;]<br>
+<br>
+[-h] - Print the script arguments help message.<br>
+[-n] - Specify the number of top functions to print.<br>
+     - If this flag is not specified, the tool defaults to 25.<br>
+<br>
+Example of usage:<br>
+topN_perf.py -n 20 -- qemu-arm coulomb_double-arm<br>
+<br>
+This file is a part of the project &quot;TCG Continuous Benchmarking&quot;.<br>
+<br>
+Copyright (C) 2020  Ahmed Karaman &lt;<a href="mailto:ahmedkhaledkaraman@gmail.com">ahmedkhaledkaraman@gmail.com</a>&gt;<br>
+Copyright (C) 2020  Aleksandar Markovic &lt;<a href="mailto:aleksandar.qemu.devel@gmail.com">aleksandar.qemu.devel@gmail.<wbr>com</a>&gt;<br>
+<br>
+This program is free software: you can redistribute it and/or modify<br>
+it under the terms of the GNU General Public License as published by<br>
+the Free Software Foundation, either version 2 of the License, or<br>
+(at your option) any later version.<br>
+<br>
+This program is distributed in the hope that it will be useful,<br>
+but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
+GNU General Public License for more details.<br>
+<br>
+You should have received a copy of the GNU General Public License<br>
+along with this program. If not, see &lt;<a href="https://www.gnu.org/licenses/" target="_blank">https://www.gnu.org/licenses/</a><wbr>&gt;.<br>
+&quot;&quot;&quot;<br>
<br>
 import argparse<br>
 import os<br>
 import subprocess<br>
 import sys<br>
+import tempfile<br>
<br>
<br>
 # Parse the command line arguments<br>
-parser = argparse.ArgumentParser(<br>
-    usage=&#39;topN_perf.py [-h] [-n] &lt;number of displayed top functions &gt;  -- &#39;<br>
+PARSER = argparse.ArgumentParser(<br>
+    usage=&#39;topN_perf.py [-h] [-n &lt;number of displayed top functions&gt;] -- &#39;<br>
           &#39;&lt;qemu executable&gt; [&lt;qemu executable options&gt;] &#39;<br>
           &#39;&lt;target executable&gt; [&lt;target executable options&gt;]&#39;)<br>
<br>
-parser.add_argument(&#39;-n&#39;, dest=&#39;top&#39;, type=int, default=25,<br>
+PARSER.add_argument(&#39;-n&#39;, dest=&#39;top&#39;, type=int, default=25,<br>
                     help=&#39;Specify the number of top functions to print.&#39;)<br>
<br>
-parser.add_argument(&#39;command&#39;<wbr>, type=str, nargs=&#39;+&#39;, help=argparse.SUPPRESS)<br>
+PARSER.add_argument(&#39;command&#39;<wbr>, type=str, nargs=&#39;+&#39;, help=argparse.SUPPRESS)<br>
<br>
-args = parser.parse_args()<br>
+ARGS = PARSER.parse_args()<br>
<br>
 # Extract the needed variables from the args<br>
-command = args.command<br>
-top = args.top<br>
+COMMAND = ARGS.command<br>
+TOP = ARGS.top<br>
<br>
 # Insure that perf is installed<br>
-check_perf_presence = subprocess.run([&quot;which&quot;, &quot;perf&quot;],<br>
-                                     stdout=subprocess.DEVNULL)<br>
-if check_perf_presence.<wbr>returncode:<br>
+CHECK_PERF_PRESENCE = subprocess.run([&quot;which&quot;, &quot;perf&quot;],<br>
+                                     stdout=subprocess.DEVNULL,<br>
+                                     check=False)<br>
+if CHECK_PERF_PRESENCE.<wbr>returncode:<br>
     sys.exit(&quot;Please install perf before running the script!&quot;)<br>
<br>
 # Insure user has previllage to run perf<br>
-check_perf_executability = subprocess.run([&quot;perf&quot;, &quot;stat&quot;, &quot;ls&quot;, &quot;/&quot;],<br>
+CHECK_PERF_EXECUTABILITY = subprocess.run([&quot;perf&quot;, &quot;stat&quot;, &quot;ls&quot;, &quot;/&quot;],<br>
                                           stdout=subprocess.DEVNULL,<br>
-                                          stderr=subprocess.DEVNULL)<br>
-if check_perf_executability.<wbr>returncode:<br>
-    sys.exit(<br>
-&quot;&quot;&quot;<br>
+                                          stderr=subprocess.DEVNULL,<br>
+                                          check=False)<br>
+if CHECK_PERF_EXECUTABILITY.<wbr>returncode:<br>
+    sys.exit(&quot;&quot;&quot;<br>
 Error:<br>
 You may not have permission to collect stats.<br>
<br>
@@ -85,43 +90,42 @@ To make this setting permanent, edit /etc/sysctl.conf too, e.g.:<br>
    kernel.perf_event_paranoid = -1<br>
<br>
 * Alternatively, you can run this script under sudo privileges.<br>
-&quot;&quot;&quot;<br>
-)<br>
-<br>
-# Run perf record<br>
-perf_record = subprocess.run(([&quot;perf&quot;, &quot;record&quot;, &quot;--output=/tmp/perf.data&quot;] +<br>
-                              command),<br>
-                             stdout=subprocess.DEVNULL,<br>
-                             stderr=subprocess.PIPE)<br>
-if perf_record.returncode:<br>
-    os.unlink(&#39;/tmp/perf.data&#39;)<br>
-    sys.exit(perf_record.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
-<br>
-# Save perf report output to /tmp/perf_report.out<br>
-with open(&quot;/tmp/perf_report.out&quot;, &quot;w&quot;) as output:<br>
-    perf_report = subprocess.run(<br>
-        [&quot;perf&quot;, &quot;report&quot;, &quot;--input=/tmp/perf.data&quot;, &quot;--stdio&quot;],<br>
-        stdout=output,<br>
-        stderr=subprocess.PIPE)<br>
-    if perf_report.returncode:<br>
-        os.unlink(&#39;/tmp/perf.data&#39;)<br>
-        output.close()<br>
-        os.unlink(&#39;/tmp/perf_report.<wbr>out&#39;)<br>
-        sys.exit(perf_report.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
-<br>
-# Read the reported data to functions[]<br>
-functions = []<br>
-with open(&quot;/tmp/perf_report.out&quot;, &quot;r&quot;) as data:<br>
-    # Only read lines that are not comments (comments start with #)<br>
-    # Only read lines that are not empty<br>
-    functions = [line for line in data.readlines() if line and line[0]<br>
-                 != &#39;#&#39; and line[0] != &quot;\n&quot;]<br>
-<br>
-# Limit the number of top functions to &quot;top&quot;<br>
-number_of_top_functions = top if len(functions) &gt; top else len(functions)<br>
-<br>
-# Store the data of the top functions in top_functions[]<br>
-top_functions = functions[:number_of_top_<wbr>functions]<br>
+&quot;&quot;&quot;)<br>
+<br>
+# Run perf and save all intermediate files in a temporary directory<br>
+with tempfile.TemporaryDirectory() as tmpdir:<br>
+    RECORD_PATH = os.path.join(tmpdir, &quot;record.data&quot;)<br>
+    REPORT_PATH = os.path.join(tmpdir, &quot;report.txt&quot;)<br>
+<br>
+    PERF_RECORD = subprocess.run(([&quot;perf&quot;, &quot;record&quot;, &quot;--output=&quot;+RECORD_PATH] +<br>
+                                  COMMAND),<br>
+                                 stdout=subprocess.DEVNULL,<br>
+                                 stderr=subprocess.PIPE,<br>
+                                 check=False)<br>
+    if PERF_RECORD.returncode:<br>
+        sys.exit(PERF_RECORD.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
+<br>
+    with open(REPORT_PATH, &quot;w&quot;) as output:<br>
+        PERF_REPORT = subprocess.run(<br>
+            [&quot;perf&quot;, &quot;report&quot;, &quot;--input=&quot;+RECORD_PATH, &quot;--stdio&quot;],<br>
+            stdout=output,<br>
+            stderr=subprocess.PIPE,<br>
+            check=False)<br>
+        if PERF_REPORT.returncode:<br>
+            sys.exit(PERF_REPORT.stderr.<wbr>decode(&quot;utf-8&quot;))<br>
+<br>
+    # Save the reported data to FUNCTIONS[]<br>
+    with open(REPORT_PATH, &quot;r&quot;) as data:<br>
+        # Only read lines that are not comments (comments start with #)<br>
+        # Only read lines that are not empty<br>
+        FUNCTIONS = [line for line in data.readlines() if line and<br>
+                     line[0] != &#39;#&#39; and line[0] != &quot;\n&quot;]<br>
+<br>
+# Limit the number of top functions to &quot;TOP&quot;<br>
+NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) &gt; TOP else len(FUNCTIONS)<br>
+<br>
+# Store the data of the top functions in TOP_FUNCTIONS[]<br>
+TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]<br>
<br>
 # Print table header<br>
 print(&#39;{:&gt;4}  {:&gt;10}  {:&lt;30}  {}\n{}  {}  {}  {}&#39;.format(&#39;No.&#39;,<br>
@@ -134,7 +138,7 @@ print(&#39;{:&gt;4}  {:&gt;10}  {:&lt;30}  {}\n{}  {}  {}  {}&#39;.format(&#39;No.&#39;,<br>
                                                          &#39;-&#39; * 25))<br>
<br>
 # Print top N functions<br>
-for (index, function) in enumerate(top_functions, start=1):<br>
+for (index, function) in enumerate(TOP_FUNCTIONS, start=1):<br>
     function_data = function.split()<br>
     function_percentage = function_data[0]<br>
     function_name = function_data[-1]<br>
@@ -143,7 +147,3 @@ for (index, function) in enumerate(top_functions, start=1):<br>
                                              function_percentage,<br>
                                              function_name,<br>
                                              function_invoker))<br>
-<br>
-# Remove intermediate files<br>
-os.unlink(&#39;/tmp/perf.data&#39;)<br>
-os.unlink(&#39;/tmp/perf_report.<wbr>out&#39;)<br>
-- <br>
2.17.1<br>
<br>
<br>
</blockquote>
Philippe Mathieu-Daudé Sept. 19, 2020, 11:17 a.m. UTC | #3
Hi Aleksandar,

(extending the Cc list to broader audience)

On 9/18/20 10:33 PM, Aleksandar Markovic wrote:
> 
> On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@gmail.com
> <mailto:ahmedkhaledkaraman@gmail.com>> wrote:
> 
>     - Apply pylint and flake8 formatting rules to the script.
>     - Use 'tempfile' instead of '/tmp' for creating temporary files.
> 
>     ---
> 
> Hello, folks.
> 
> This series seems forgotten. Can some of you perhaps take a look,
> review, and possibly integrate some patches in a pull request?
> 
> Ahmed invested quite of time to improve the functionality and quality of
> scripts, and they are truly useful for developers interested in
> performance measurement.

The Python patches are indeed being merged slowly, but are not
forgotten :) Eduardo sent just a pull request *yesterday* for
patches he had queued *before* QEMU 5.1, that is more than 1 year
ago!
https://www.mail-archive.com/qemu-devel@nongnu.org/msg742228.html

I hope he will be able to process the other Python patches sent
the last 12 months. He raised the problem few month ago saying he
was overwhelmed and was looking for help from the community.
Cleber helped a pair of times, I helped once, but then nobody
popped up volunteering.

I agree this is a community problem, nobody wants to become the
de-facto Python maintainer. Current maintainers are already busy
solving problem with low-level languages such C.
As a project, QEMU is evolving, using more and more Python, switched
to Meson, we might have Rust code too. Learning that for current
maintainers takes time. I guess we lack new contributors/maintainers
with interest in Python & QEMU.

This is my simple/rough analysis, as John had the same problem
2/3 months ago, his patches were on the list unreviewed for various
weeks. Same problem with Avocado patches, Lukas sent a series a bit
before Ahmed and it is still unreviewed:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg737272.html

Alex Bennée recently said:

 "review time is the currency of qemu-devel ;-)"

We might be failing at explaining new contributors the importance
of peer-review, as this helps a lot maintainers. It is described
in the wiki but maybe new contributors don't read it, we don't
remember them to:
https://wiki.qemu.org/Contribute/SubmitAPatch#Participating_in_Code_Review
and:
https://wiki.qemu.org/Contribute/SubmitAPatch#Return_the_favor

My 2 cents...

Regards,

Phil.

> 
> Thanks,
> Aleksandar
John Snow Sept. 21, 2020, 2:49 p.m. UTC | #4
On 9/19/20 7:17 AM, Philippe Mathieu-Daudé wrote:
> Hi Aleksandar,

> 

> (extending the Cc list to broader audience)

> 

> On 9/18/20 10:33 PM, Aleksandar Markovic wrote:

>>

>> On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@gmail.com

>> <mailto:ahmedkhaledkaraman@gmail.com>> wrote:

>>

>>      - Apply pylint and flake8 formatting rules to the script.

>>      - Use 'tempfile' instead of '/tmp' for creating temporary files.

>>

>>      ---

>>

>> Hello, folks.

>>

>> This series seems forgotten. Can some of you perhaps take a look,

>> review, and possibly integrate some patches in a pull request?

>>

>> Ahmed invested quite of time to improve the functionality and quality of

>> scripts, and they are truly useful for developers interested in

>> performance measurement.

> 


Hi, I will add it to my queue. I intended to take over Python 
maintenance but I have been busy refactoring the QAPI python code and 
haven't been reading my mail as regularly.

> The Python patches are indeed being merged slowly, but are not

> forgotten :) Eduardo sent just a pull request *yesterday* for

> patches he had queued *before* QEMU 5.1, that is more than 1 year

> ago!

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg742228.html

> 

> I hope he will be able to process the other Python patches sent

> the last 12 months. He raised the problem few month ago saying he

> was overwhelmed and was looking for help from the community.

> Cleber helped a pair of times, I helped once, but then nobody

> popped up volunteering.

> 

> I agree this is a community problem, nobody wants to become the

> de-facto Python maintainer. Current maintainers are already busy

> solving problem with low-level languages such C.

> As a project, QEMU is evolving, using more and more Python, switched

> to Meson, we might have Rust code too. Learning that for current

> maintainers takes time. I guess we lack new contributors/maintainers

> with interest in Python & QEMU.

> 


I'm volunteering, since I am doing so much work in Python. I could use a 
dedicated reviewer to help me, however. I prefer a maintainer policy 
where all patches get at least ONE review by someone other than the 
primary author.

In the case that I am writing so much Python, I still need a 
co-maintainer to help review *my* patches.

> This is my simple/rough analysis, as John had the same problem

> 2/3 months ago, his patches were on the list unreviewed for various

> weeks. Same problem with Avocado patches, Lukas sent a series a bit

> before Ahmed and it is still unreviewed:

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg737272.html

> 

> Alex Bennée recently said:

> 

>   "review time is the currency of qemu-devel ;-)"

> 

> We might be failing at explaining new contributors the importance

> of peer-review, as this helps a lot maintainers. It is described

> in the wiki but maybe new contributors don't read it, we don't

> remember them to:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Participating_in_Code_Review

> and:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Return_the_favor

> 

> My 2 cents...

> 

> Regards,

> 

> Phil.

> 

>>

>> Thanks,

>> Aleksandar

>
Eduardo Habkost Sept. 21, 2020, 3:54 p.m. UTC | #5
On Sat, Sep 19, 2020 at 01:17:06PM +0200, Philippe Mathieu-Daudé wrote:
> Hi Aleksandar,

> 

> (extending the Cc list to broader audience)

> 

> On 9/18/20 10:33 PM, Aleksandar Markovic wrote:

> > 

> > On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@gmail.com

> > <mailto:ahmedkhaledkaraman@gmail.com>> wrote:

> > 

> >     - Apply pylint and flake8 formatting rules to the script.

> >     - Use 'tempfile' instead of '/tmp' for creating temporary files.

> > 

> >     ---

> > 

> > Hello, folks.

> > 

> > This series seems forgotten. Can some of you perhaps take a look,

> > review, and possibly integrate some patches in a pull request?

> > 

> > Ahmed invested quite of time to improve the functionality and quality of

> > scripts, and they are truly useful for developers interested in

> > performance measurement.

> 

> The Python patches are indeed being merged slowly, but are not

> forgotten :) Eduardo sent just a pull request *yesterday* for

> patches he had queued *before* QEMU 5.1, that is more than 1 year

> ago!

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg742228.html

> 

> I hope he will be able to process the other Python patches sent

> the last 12 months. He raised the problem few month ago saying he

> was overwhelmed and was looking for help from the community.

> Cleber helped a pair of times, I helped once, but then nobody

> popped up volunteering.


The patches were from July this year.  But, yeah, I'm not being
able to keep up with the maintainer role for Python code, and
plan to change my M: line to R: in the "Python scripts" section.

> 

> I agree this is a community problem, nobody wants to become the

> de-facto Python maintainer. Current maintainers are already busy

> solving problem with low-level languages such C.

> As a project, QEMU is evolving, using more and more Python, switched

> to Meson, we might have Rust code too. Learning that for current

> maintainers takes time. I guess we lack new contributors/maintainers

> with interest in Python & QEMU.


I believe the solution here is to not have a single owner for all
Python code, but owners for specific areas.  We already have 36
F: entries in MAINTAINERS for files inside `scripts/`, and each
of those maintainers can send pull requests containing Python
code in the areas they maintain.  Including Ahmed, who is already
listed as the maintainer of `scripts/performance/`.

> 

> This is my simple/rough analysis, as John had the same problem

> 2/3 months ago, his patches were on the list unreviewed for various

> weeks. Same problem with Avocado patches, Lukas sent a series a bit

> before Ahmed and it is still unreviewed:

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg737272.html

> 

> Alex Bennée recently said:

> 

>  "review time is the currency of qemu-devel ;-)"

> 

> We might be failing at explaining new contributors the importance

> of peer-review, as this helps a lot maintainers. It is described

> in the wiki but maybe new contributors don't read it, we don't

> remember them to:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Participating_in_Code_Review

> and:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Return_the_favor

> 

> My 2 cents...

> 

> Regards,

> 

> Phil.

> 

> > 

> > Thanks,

> > Aleksandar

> 


-- 
Eduardo
Cleber Rosa Sept. 21, 2020, 5:57 p.m. UTC | #6
On Sat, Sep 19, 2020 at 01:17:06PM +0200, Philippe Mathieu-Daudé wrote:
> Hi Aleksandar,

> 

> (extending the Cc list to broader audience)

> 

> On 9/18/20 10:33 PM, Aleksandar Markovic wrote:

> > 

> > On Friday, August 28, 2020, Ahmed Karaman <ahmedkhaledkaraman@gmail.com

> > <mailto:ahmedkhaledkaraman@gmail.com>> wrote:

> > 

> >     - Apply pylint and flake8 formatting rules to the script.

> >     - Use 'tempfile' instead of '/tmp' for creating temporary files.

> > 

> >     ---

> > 

> > Hello, folks.

> > 

> > This series seems forgotten. Can some of you perhaps take a look,

> > review, and possibly integrate some patches in a pull request?

> > 

> > Ahmed invested quite of time to improve the functionality and quality of

> > scripts, and they are truly useful for developers interested in

> > performance measurement.

> 

> The Python patches are indeed being merged slowly, but are not

> forgotten :) Eduardo sent just a pull request *yesterday* for

> patches he had queued *before* QEMU 5.1, that is more than 1 year

> ago!

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg742228.html

> 

> I hope he will be able to process the other Python patches sent

> the last 12 months. He raised the problem few month ago saying he

> was overwhelmed and was looking for help from the community.

> Cleber helped a pair of times, I helped once, but then nobody

> popped up volunteering.

>


Yes, Python patches are not forgotten... they have been haunting me
every day and night for the last few months.  I have a *lot* of blame
to take here, so my sincere apologies.

One word of hope is that my resources (which were exhausted during the
last months towards the Avocado 82.0 LTS release, released last week)
are now freed.  And *a lot* of the Avocado features were eyeing QEMU,
so it's just natural that I will (because I want and have to) get
back to being more active and responsive.

> I agree this is a community problem, nobody wants to become the

> de-facto Python maintainer. Current maintainers are already busy

> solving problem with low-level languages such C.

> As a project, QEMU is evolving, using more and more Python, switched

> to Meson, we might have Rust code too. Learning that for current

> maintainers takes time. I guess we lack new contributors/maintainers

> with interest in Python & QEMU.

> 

> This is my simple/rough analysis, as John had the same problem

> 2/3 months ago, his patches were on the list unreviewed for various

> weeks. Same problem with Avocado patches, Lukas sent a series a bit

> before Ahmed and it is still unreviewed:

> https://www.mail-archive.com/qemu-devel@nongnu.org/msg737272.html

>


ACK.  This is a good real example of my previous abstract explanation
above.

So, besides me getting back with more resources to review and maintain
the Python pathes, I have also requested Willian Rampazzo (thanks!) to
keep an eye on those patches and help with reviews.

> Alex Bennée recently said:

> 

>  "review time is the currency of qemu-devel ;-)"

> 

> We might be failing at explaining new contributors the importance

> of peer-review, as this helps a lot maintainers. It is described

> in the wiki but maybe new contributors don't read it, we don't

> remember them to:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Participating_in_Code_Review

> and:

> https://wiki.qemu.org/Contribute/SubmitAPatch#Return_the_favor

> 

> My 2 cents...

> 

> Regards,

> 

> Phil.

>


So while more help is always welcome, I do sincerely hope with myself
and Willian back into the loop, the Python patches won't lag behind
anymore.

And thanks for raising the awareness of the issue Phil!

- Cleber.

> > 

> > Thanks,

> > Aleksandar

>
John Snow Oct. 1, 2020, 8:41 p.m. UTC | #7
I realize this review comes well after you are no longer being paid to 
work on this, so I am offering my time to help polish your patches if 
you would like.

On 8/28/20 6:40 AM, Ahmed Karaman wrote:
> - Apply pylint and flake8 formatting rules to the script.
> - Use 'tempfile' instead of '/tmp' for creating temporary files.
> 

I had meant to maybe consider using some helper functions so that you 
didn't need to rename the globals, for instance:

> Signed-off-by: Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
> ---
>   scripts/performance/topN_perf.py | 174 +++++++++++++++----------------
>   1 file changed, 87 insertions(+), 87 deletions(-)
> 
> diff --git a/scripts/performance/topN_perf.py b/scripts/performance/topN_perf.py
> index 07be195fc8..56b100da87 100755
> --- a/scripts/performance/topN_perf.py
> +++ b/scripts/performance/topN_perf.py
> @@ -1,72 +1,77 @@
>   #!/usr/bin/env python3
>   
> -#  Print the top N most executed functions in QEMU using perf.
> -#  Syntax:
> -#  topN_perf.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_perf.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 perf.
> +
> +Syntax:
> +topN_perf.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_perf.py -n 20 -- qemu-arm coulomb_double-arm
> +

Based on discussion we've had upstream since you sent this, I think we 
will be keeping license and authorship information out of the 
docstrings, so this part can stay a comment.

> +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_perf.py [-h] [-n] <number of displayed top functions >  -- '
> +PARSER = argparse.ArgumentParser(
> +    usage='topN_perf.py [-h] [-n <number of displayed top functions>] -- '
>             '<qemu executable> [<qemu executable options>] '
>             '<target executable> [<target executable options>]')
>   

This is a little odd; generally we can avoid having such globals by 
making a main() function that defines a parser as a local instead.

e.g.,

def main():
     parser = ...
     parser.add_argument(...)

     args = parser.parse_args()

     ...

     return 0


if __name__ == '__main__':
     sys.exit(main())


> -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 perf is installed
> -check_perf_presence = subprocess.run(["which", "perf"],
> -                                     stdout=subprocess.DEVNULL)
> -if check_perf_presence.returncode:
> +CHECK_PERF_PRESENCE = subprocess.run(["which", "perf"],
> +                                     stdout=subprocess.DEVNULL,
> +                                     check=False)
> +if CHECK_PERF_PRESENCE.returncode:
>       sys.exit("Please install perf before running the script!")
>   
>   # Insure user has previllage to run perf
> -check_perf_executability = subprocess.run(["perf", "stat", "ls", "/"],
> +CHECK_PERF_EXECUTABILITY = subprocess.run(["perf", "stat", "ls", "/"],
>                                             stdout=subprocess.DEVNULL,
> -                                          stderr=subprocess.DEVNULL)
> -if check_perf_executability.returncode:
> -    sys.exit(
> -"""
> +                                          stderr=subprocess.DEVNULL,
> +                                          check=False)
> +if CHECK_PERF_EXECUTABILITY.returncode:
> +    sys.exit("""
>   Error:
>   You may not have permission to collect stats.
>   
> @@ -85,43 +90,42 @@ To make this setting permanent, edit /etc/sysctl.conf too, e.g.:
>      kernel.perf_event_paranoid = -1
>   
>   * Alternatively, you can run this script under sudo privileges.
> -"""
> -)
> -
> -# Run perf record
> -perf_record = subprocess.run((["perf", "record", "--output=/tmp/perf.data"] +
> -                              command),
> -                             stdout=subprocess.DEVNULL,
> -                             stderr=subprocess.PIPE)
> -if perf_record.returncode:
> -    os.unlink('/tmp/perf.data')
> -    sys.exit(perf_record.stderr.decode("utf-8"))
> -
> -# Save perf report output to /tmp/perf_report.out
> -with open("/tmp/perf_report.out", "w") as output:
> -    perf_report = subprocess.run(
> -        ["perf", "report", "--input=/tmp/perf.data", "--stdio"],
> -        stdout=output,
> -        stderr=subprocess.PIPE)
> -    if perf_report.returncode:
> -        os.unlink('/tmp/perf.data')
> -        output.close()
> -        os.unlink('/tmp/perf_report.out')
> -        sys.exit(perf_report.stderr.decode("utf-8"))
> -
> -# Read the reported data to functions[]
> -functions = []
> -with open("/tmp/perf_report.out", "r") as data:
> -    # Only read lines that are not comments (comments start with #)
> -    # Only read lines that are not empty
> -    functions = [line for line in data.readlines() if line and line[0]
> -                 != '#' and line[0] != "\n"]
> -
> -# Limit the number of top functions to "top"
> -number_of_top_functions = top if len(functions) > top else len(functions)
> -
> -# Store the data of the top functions in top_functions[]
> -top_functions = functions[:number_of_top_functions]
> +""")
> +
> +# Run perf and save all intermediate files in a temporary directory
> +with tempfile.TemporaryDirectory() as tmpdir:
> +    RECORD_PATH = os.path.join(tmpdir, "record.data")
> +    REPORT_PATH = os.path.join(tmpdir, "report.txt")
> +
> +    PERF_RECORD = subprocess.run((["perf", "record", "--output="+RECORD_PATH] +
> +                                  COMMAND),
> +                                 stdout=subprocess.DEVNULL,
> +                                 stderr=subprocess.PIPE,
> +                                 check=False)
> +    if PERF_RECORD.returncode:
> +        sys.exit(PERF_RECORD.stderr.decode("utf-8"))
> +
> +    with open(REPORT_PATH, "w") as output:
> +        PERF_REPORT = subprocess.run(
> +            ["perf", "report", "--input="+RECORD_PATH, "--stdio"],
> +            stdout=output,
> +            stderr=subprocess.PIPE,
> +            check=False)
> +        if PERF_REPORT.returncode:
> +            sys.exit(PERF_REPORT.stderr.decode("utf-8"))
> +
> +    # Save the reported data to FUNCTIONS[]
> +    with open(REPORT_PATH, "r") as data:
> +        # Only read lines that are not comments (comments start with #)
> +        # Only read lines that are not empty
> +        FUNCTIONS = [line for line in data.readlines() if line and
> +                     line[0] != '#' and line[0] != "\n"]
> +
> +# Limit the number of top functions to "TOP"
> +NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) > TOP else len(FUNCTIONS)
> +
> +# Store the data of the top functions in TOP_FUNCTIONS[]
> +TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]
>   
>   # Print table header
>   print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',
> @@ -134,7 +138,7 @@ print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',
>                                                            '-' * 25))
>   
>   # 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()
>       function_percentage = function_data[0]
>       function_name = function_data[-1]
> @@ -143,7 +147,3 @@ for (index, function) in enumerate(top_functions, start=1):
>                                                function_percentage,
>                                                function_name,
>                                                function_invoker))
> -
> -# Remove intermediate files
> -os.unlink('/tmp/perf.data')
> -os.unlink('/tmp/perf_report.out')
>
John Snow Oct. 1, 2020, 9:59 p.m. UTC | #8
On 10/1/20 4:41 PM, John Snow wrote:
> I realize this review comes well after you are no longer being paid to 

> work on this, so I am offering my time to help polish your patches if 

> you would like.


Actually, I see now that you are adding your name to the MAINTAINERS 
file here, so I suspect you probably rather want to be more involved 
than not.

I cleaned up patch 1/9 provisionally with my own style preferences, but 
it's all just style stuff, and it's mostly things I wouldn't actually 
require you to do (...I went way overboard.)

https://gitlab.com/jsnow/qemu/-/commit/c66a4a6ca8ccc3d406b92796935f92057bf1e48d


What I'd recommend for your cleanup is actually *much* simpler;

Use pylint 2.6.0 and flake8 3.8.3:

 > pip3 install --user pylint==2.6.0 flake8==3.8.3


flake8's default settings should be pretty good, but pylint has a lot of 
warnings you can ignore.

In particular, it's OK to use script-style python (Scripts with a 
#!/usr/bin/env python3, and where you do not use python functions to 
avoid side-effects that occur on 'import'.) In this case, IGNORE any of 
pylint's warnings telling you that you have too many lines, that you 
need to UPPERCASE variable names, etc. It just hurts readability here.

So I'd actually ask that you revise these patches to remove all of the 
UPPERCASE variable names, and then check your code with these:

flake8 topN_perf.py
pylint --disable=invalid-name topN_perf.py

Use your best judgment -- If something seems like it looks worse, it 
probably is. If in doubt, please reach out and ask.

--js
diff mbox series

Patch

diff --git a/scripts/performance/topN_perf.py b/scripts/performance/topN_perf.py
index 07be195fc8..56b100da87 100755
--- a/scripts/performance/topN_perf.py
+++ b/scripts/performance/topN_perf.py
@@ -1,72 +1,77 @@ 
 #!/usr/bin/env python3
 
-#  Print the top N most executed functions in QEMU using perf.
-#  Syntax:
-#  topN_perf.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_perf.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 perf.
+
+Syntax:
+topN_perf.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_perf.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_perf.py [-h] [-n] <number of displayed top functions >  -- '
+PARSER = argparse.ArgumentParser(
+    usage='topN_perf.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 perf is installed
-check_perf_presence = subprocess.run(["which", "perf"],
-                                     stdout=subprocess.DEVNULL)
-if check_perf_presence.returncode:
+CHECK_PERF_PRESENCE = subprocess.run(["which", "perf"],
+                                     stdout=subprocess.DEVNULL,
+                                     check=False)
+if CHECK_PERF_PRESENCE.returncode:
     sys.exit("Please install perf before running the script!")
 
 # Insure user has previllage to run perf
-check_perf_executability = subprocess.run(["perf", "stat", "ls", "/"],
+CHECK_PERF_EXECUTABILITY = subprocess.run(["perf", "stat", "ls", "/"],
                                           stdout=subprocess.DEVNULL,
-                                          stderr=subprocess.DEVNULL)
-if check_perf_executability.returncode:
-    sys.exit(
-"""
+                                          stderr=subprocess.DEVNULL,
+                                          check=False)
+if CHECK_PERF_EXECUTABILITY.returncode:
+    sys.exit("""
 Error:
 You may not have permission to collect stats.
 
@@ -85,43 +90,42 @@  To make this setting permanent, edit /etc/sysctl.conf too, e.g.:
    kernel.perf_event_paranoid = -1
 
 * Alternatively, you can run this script under sudo privileges.
-"""
-)
-
-# Run perf record
-perf_record = subprocess.run((["perf", "record", "--output=/tmp/perf.data"] +
-                              command),
-                             stdout=subprocess.DEVNULL,
-                             stderr=subprocess.PIPE)
-if perf_record.returncode:
-    os.unlink('/tmp/perf.data')
-    sys.exit(perf_record.stderr.decode("utf-8"))
-
-# Save perf report output to /tmp/perf_report.out
-with open("/tmp/perf_report.out", "w") as output:
-    perf_report = subprocess.run(
-        ["perf", "report", "--input=/tmp/perf.data", "--stdio"],
-        stdout=output,
-        stderr=subprocess.PIPE)
-    if perf_report.returncode:
-        os.unlink('/tmp/perf.data')
-        output.close()
-        os.unlink('/tmp/perf_report.out')
-        sys.exit(perf_report.stderr.decode("utf-8"))
-
-# Read the reported data to functions[]
-functions = []
-with open("/tmp/perf_report.out", "r") as data:
-    # Only read lines that are not comments (comments start with #)
-    # Only read lines that are not empty
-    functions = [line for line in data.readlines() if line and line[0]
-                 != '#' and line[0] != "\n"]
-
-# Limit the number of top functions to "top"
-number_of_top_functions = top if len(functions) > top else len(functions)
-
-# Store the data of the top functions in top_functions[]
-top_functions = functions[:number_of_top_functions]
+""")
+
+# Run perf and save all intermediate files in a temporary directory
+with tempfile.TemporaryDirectory() as tmpdir:
+    RECORD_PATH = os.path.join(tmpdir, "record.data")
+    REPORT_PATH = os.path.join(tmpdir, "report.txt")
+
+    PERF_RECORD = subprocess.run((["perf", "record", "--output="+RECORD_PATH] +
+                                  COMMAND),
+                                 stdout=subprocess.DEVNULL,
+                                 stderr=subprocess.PIPE,
+                                 check=False)
+    if PERF_RECORD.returncode:
+        sys.exit(PERF_RECORD.stderr.decode("utf-8"))
+
+    with open(REPORT_PATH, "w") as output:
+        PERF_REPORT = subprocess.run(
+            ["perf", "report", "--input="+RECORD_PATH, "--stdio"],
+            stdout=output,
+            stderr=subprocess.PIPE,
+            check=False)
+        if PERF_REPORT.returncode:
+            sys.exit(PERF_REPORT.stderr.decode("utf-8"))
+
+    # Save the reported data to FUNCTIONS[]
+    with open(REPORT_PATH, "r") as data:
+        # Only read lines that are not comments (comments start with #)
+        # Only read lines that are not empty
+        FUNCTIONS = [line for line in data.readlines() if line and
+                     line[0] != '#' and line[0] != "\n"]
+
+# Limit the number of top functions to "TOP"
+NO_TOP_FUNCTIONS = TOP if len(FUNCTIONS) > TOP else len(FUNCTIONS)
+
+# Store the data of the top functions in TOP_FUNCTIONS[]
+TOP_FUNCTIONS = FUNCTIONS[:NO_TOP_FUNCTIONS]
 
 # Print table header
 print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',
@@ -134,7 +138,7 @@  print('{:>4}  {:>10}  {:<30}  {}\n{}  {}  {}  {}'.format('No.',
                                                          '-' * 25))
 
 # 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()
     function_percentage = function_data[0]
     function_name = function_data[-1]
@@ -143,7 +147,3 @@  for (index, function) in enumerate(top_functions, start=1):
                                              function_percentage,
                                              function_name,
                                              function_invoker))
-
-# Remove intermediate files
-os.unlink('/tmp/perf.data')
-os.unlink('/tmp/perf_report.out')