From c6aba1bf258ff1ce201f112dafe1bdde601573dd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Apr 2019 14:38:23 +0300 Subject: perf scripts python: exported-sql-viewer.py: Change python2 to python Now that there is also support for python3, there is no need to specify python2 explicitly. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190412113830.4126-2-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index affed7d149be..9ff92a130655 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # SPDX-License-Identifier: GPL-2.0 # exported-sql-viewer.py: view data from sql database # Copyright (c) 2014-2018, Intel Corporation. -- cgit v1.2.3 From 1ed7f47fd3af3c09d2cd64d1aff1c5b96d238111 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Apr 2019 14:38:24 +0300 Subject: perf scripts python: exported-sql-viewer.py: Use argparse module for argument parsing The argparse module makes it easier to add new arguments. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190412113830.4126-3-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index 9ff92a130655..498b79454012 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -91,6 +91,7 @@ from __future__ import print_function import sys +import argparse import weakref import threading import string @@ -3361,18 +3362,26 @@ class DBRef(): # Main def Main(): - if (len(sys.argv) < 2): - printerr("Usage is: exported-sql-viewer.py { | --help-only}"); - raise Exception("Too few arguments") - - dbname = sys.argv[1] - if dbname == "--help-only": + usage_str = "exported-sql-viewer.py [--pyside-version-1] \n" \ + " or: exported-sql-viewer.py --help-only" + ap = argparse.ArgumentParser(usage = usage_str, add_help = False) + ap.add_argument("dbname", nargs="?") + ap.add_argument("--help-only", action='store_true') + args = ap.parse_args() + + if args.help_only: app = QApplication(sys.argv) mainwindow = HelpOnlyWindow() mainwindow.show() err = app.exec_() sys.exit(err) + dbname = args.dbname + if dbname is None: + ap.print_usage() + print("Too few arguments") + sys.exit(1) + is_sqlite3 = False try: f = open(dbname, "rb") -- cgit v1.2.3 From df8ea22a8fd9e4e8502f4fa917622801e1b4d09e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Apr 2019 14:38:25 +0300 Subject: perf scripts python: exported-sql-viewer.py: Add support for pyside2 pyside2 is the future for pyside support. Note pyside use Qt4 whereas pyside2 uses Qt5. Committer testing: On a system with just: # rpm -qa| grep -i pyside python2-pyside-1.2.4-7.fc29.x86_64 # Running: $ python ~acme/libexec/perf-core/scripts/python/exported-sql-viewer.py ~/c/adrian.hunter/simple-retpoline.db & [1] 7438 Makes it use the pyside 1 files: $ grep -i pyside /proc/7438/maps | cut -d ' ' -f 6- | sort -u /usr/lib64/libpyside-python2.7.so.1.2.4 /usr/lib64/python2.7/site-packages/PySide/QtCore.so /usr/lib64/python2.7/site-packages/PySide/QtGui.so /usr/lib64/python2.7/site-packages/PySide/QtSql.so $ rpm -qf /usr/lib64/libpyside-python2.7.so.1.2.4 python2-pyside-1.2.4-7.fc29.x86_64 $ To get PySide2 I guess one needs to do: $ pip install PySide2 But thats a 142MiB download I can't do right now, perhaps before pushing upstream... Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190412113830.4126-4-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 28 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index 498b79454012..6fe553258ce5 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -105,10 +105,23 @@ except ImportError: glb_nsz = 16 import re import os -from PySide.QtCore import * -from PySide.QtGui import * -from PySide.QtSql import * + pyside_version_1 = True +if not "--pyside-version-1" in sys.argv: + try: + from PySide2.QtCore import * + from PySide2.QtGui import * + from PySide2.QtSql import * + from PySide2.QtWidgets import * + pyside_version_1 = False + except: + pass + +if pyside_version_1: + from PySide.QtCore import * + from PySide.QtGui import * + from PySide.QtSql import * + from decimal import * from ctypes import * from multiprocessing import Process, Array, Value, Event @@ -2755,7 +2768,7 @@ class WindowMenu(): action = self.window_menu.addAction(label) action.setCheckable(True) action.setChecked(sub_window == self.mdi_area.activeSubWindow()) - action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) + action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) self.window_menu.addAction(action) nr += 1 @@ -3115,14 +3128,14 @@ class MainWindow(QMainWindow): event = event.split(":")[0] if event == "branches": label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" - reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) + reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" - reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) + reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) def TableMenu(self, tables, menu): table_menu = menu.addMenu("&Tables") for table in tables: - table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) + table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) def NewCallGraph(self): CallGraphWindow(self.glb, self) @@ -3365,6 +3378,7 @@ def Main(): usage_str = "exported-sql-viewer.py [--pyside-version-1] \n" \ " or: exported-sql-viewer.py --help-only" ap = argparse.ArgumentParser(usage = usage_str, add_help = False) + ap.add_argument("--pyside-version-1", action='store_true') ap.add_argument("dbname", nargs="?") ap.add_argument("--help-only", action='store_true') args = ap.parse_args() -- cgit v1.2.3 From bfb3170e2481b76a4f8aae94176e45d681a37f3e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Apr 2019 14:38:26 +0300 Subject: perf scripts python: export-to-sqlite.py: Add support for pyside2 pyside2 is the future for pyside support. Note pyside use Qt4 whereas pyside2 uses Qt5. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190412113830.4126-5-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-sqlite.py | 44 +++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py index bf271fbc3a88..f617e518332f 100644 --- a/tools/perf/scripts/python/export-to-sqlite.py +++ b/tools/perf/scripts/python/export-to-sqlite.py @@ -21,6 +21,26 @@ import datetime # provides LGPL-licensed Python bindings for Qt. You will also need the package # libqt4-sql-sqlite for Qt sqlite3 support. # +# Examples of installing pyside: +# +# ubuntu: +# +# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql +# +# Alternately, to use Python3 and/or pyside 2, one of the following: +# +# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql +# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql +# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql +# fedora: +# +# $ sudo yum install python-pyside +# +# Alternately, to use Python3 and/or pyside 2, one of the following: +# $ sudo yum install python3-pyside +# $ pip install --user PySide2 +# $ pip3 install --user PySide2 +# # An example of using this script with Intel PT: # # $ perf record -e intel_pt//u ls @@ -49,7 +69,16 @@ import datetime # difference is the 'transaction' column of the 'samples' table which is # renamed 'transaction_' in sqlite because 'transaction' is a reserved word. -from PySide.QtSql import * +pyside_version_1 = True +if not "pyside-version-1" in sys.argv: + try: + from PySide2.QtSql import * + pyside_version_1 = False + except: + pass + +if pyside_version_1: + from PySide.QtSql import * sys.path.append(os.environ['PERF_EXEC_PATH'] + \ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') @@ -69,11 +98,12 @@ def printdate(*args, **kw_args): print(datetime.datetime.today(), *args, sep=' ', **kw_args) def usage(): - printerr("Usage is: export-to-sqlite.py [] [] []"); - printerr("where: columns 'all' or 'branches'"); - printerr(" calls 'calls' => create calls and call_paths table"); - printerr(" callchains 'callchains' => create call_paths table"); - raise Exception("Too few arguments") + printerr("Usage is: export-to-sqlite.py [] [] [] []"); + printerr("where: columns 'all' or 'branches'"); + printerr(" calls 'calls' => create calls and call_paths table"); + printerr(" callchains 'callchains' => create call_paths table"); + printerr(" pyside-version-1 'pyside-version-1' => use pyside version 1"); + raise Exception("Too few or bad arguments") if (len(sys.argv) < 2): usage() @@ -95,6 +125,8 @@ for i in range(3,len(sys.argv)): perf_db_export_calls = True elif (sys.argv[i] == "callchains"): perf_db_export_callchains = True + elif (sys.argv[i] == "pyside-version-1"): + pass else: usage() -- cgit v1.2.3 From 3cd3216dbb421244b96b992f193e778a3baa2220 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Apr 2019 14:38:27 +0300 Subject: perf scripts python: export-to-postgresql.py: Add support for pyside2 pyside2 is the future for pyside support. Note pyside use Qt4 whereas pyside2 uses Qt5. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190412113830.4126-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-postgresql.py | 43 ++++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index c3eae1d77d36..b2f481b0d28d 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py @@ -27,18 +27,31 @@ import datetime # # fedora: # -# $ sudo yum install postgresql postgresql-server python-pyside qt-postgresql +# $ sudo yum install postgresql postgresql-server qt-postgresql # $ sudo su - postgres -c initdb # $ sudo service postgresql start # $ sudo su - postgres -# $ createuser +# $ createuser -s # Older versions may not support -s, in which case answer the prompt below: # Shall the new role be a superuser? (y/n) y +# $ sudo yum install python-pyside +# +# Alternately, to use Python3 and/or pyside 2, one of the following: +# $ sudo yum install python3-pyside +# $ pip install --user PySide2 +# $ pip3 install --user PySide2 # # ubuntu: # -# $ sudo apt-get install postgresql python-pyside.qtsql libqt4-sql-psql +# $ sudo apt-get install postgresql # $ sudo su - postgres # $ createuser -s +# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql +# +# Alternately, to use Python3 and/or pyside 2, one of the following: +# +# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql +# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql +# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql # # An example of using this script with Intel PT: # @@ -199,7 +212,16 @@ import datetime # print "{0:>6} {1:>10} {2:>9} {3:<30} {4:>6} {5:<30}".format(query.value(0), query.value(1), query.value(2), query.value(3), query.value(4), query.value(5)) # call_path_id = query.value(6) -from PySide.QtSql import * +pyside_version_1 = True +if not "pyside-version-1" in sys.argv: + try: + from PySide2.QtSql import * + pyside_version_1 = False + except: + pass + +if pyside_version_1: + from PySide.QtSql import * if sys.version_info < (3, 0): def toserverstr(str): @@ -255,11 +277,12 @@ def printdate(*args, **kw_args): print(datetime.datetime.today(), *args, sep=' ', **kw_args) def usage(): - printerr("Usage is: export-to-postgresql.py [] [] []") - printerr("where: columns 'all' or 'branches'") - printerr(" calls 'calls' => create calls and call_paths table") - printerr(" callchains 'callchains' => create call_paths table") - raise Exception("Too few arguments") + printerr("Usage is: export-to-postgresql.py [] [] [] []"); + printerr("where: columns 'all' or 'branches'"); + printerr(" calls 'calls' => create calls and call_paths table"); + printerr(" callchains 'callchains' => create call_paths table"); + printerr(" pyside-version-1 'pyside-version-1' => use pyside version 1"); + raise Exception("Too few or bad arguments") if (len(sys.argv) < 2): usage() @@ -281,6 +304,8 @@ for i in range(3,len(sys.argv)): perf_db_export_calls = True elif (sys.argv[i] == "callchains"): perf_db_export_callchains = True + elif (sys.argv[i] == "pyside-version-1"): + pass else: usage() -- cgit v1.2.3 From 64adadb3f9dbaaae3d14ea75fa71a3b877cbe82e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:22 +0300 Subject: perf scripts python: export-to-sqlite.py: Export IPC information Export cycle and instruction counts on samples and calls tables. Committer testing: First runs some workload collecting intel_pt with the 'cyc' ter just for userspace: [root@quaco adrian.hunter]# perf record -o simple-retpoline.perf.data -e intel_pt/cyc/u ./simple-retpoline [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.035 MB simple-retpoline.perf.data ] [root@quaco adrian.hunter]# Then use the export-to-sqlite.py script to see if the changes in this cset don't make it to break and if the changes in the db schema are the ones expected: [root@quaco adrian.hunter]# perf script -i simple-retpoline.perf.data --itrace=be -s ~acme/libexec/perf-core/scripts/python/export-to-sqlite.py simple-retpoline.db branches calls 2019-05-31 11:50:46.942710 Creating database ... 2019-05-31 11:50:46.949663 Writing records... 2019-05-31 11:50:47.224033 Adding indexes 2019-05-31 11:50:47.231599 Done [root@quaco adrian.hunter]# Now lets use the db: [root@quaco adrian.hunter]# sqlite3 simple-retpoline.db SQLite version 3.26.0 2018-12-01 12:34:55 Enter ".help" for usage hints. sqlite> .schema samples CREATE TABLE samples (id integer NOT NULL PRIMARY KEY,evsel_id bigint,machine_id bigint,thread_id bigint,comm_id bigint,dso_id bigint,symbol_id bigint,sym_offset bigint,ip bigint,time bigint,cpuinteger,to_dso_id bigint,to_symbol_id bigint,to_sym_offset bigint,to_ip bigint,branch_type integer,in_tx boolean,call_path_id bigint,insn_count bigint,cyc_count bigint); sqlite> Cool, the 'insn_count' and 'cyc_count' are there, now lets see if we can use them in a query: sqlite> select insn_count,cyc_count from samples where cyc_count > 1500 and insn_count < 10; 6|1507 sqlite> select insn_count,cyc_count from samples where cyc_count > 1500; 118|2210 140|1516 3783|1861 132|1521 6|1507 sqlite> Seems to work :-) Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190520113728.14389-17-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-sqlite.py | 36 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py index f617e518332f..4542ce89034b 100644 --- a/tools/perf/scripts/python/export-to-sqlite.py +++ b/tools/perf/scripts/python/export-to-sqlite.py @@ -218,7 +218,9 @@ if branches: 'to_ip bigint,' 'branch_type integer,' 'in_tx boolean,' - 'call_path_id bigint)') + 'call_path_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') else: do_query(query, 'CREATE TABLE samples (' 'id integer NOT NULL PRIMARY KEY,' @@ -242,7 +244,9 @@ else: 'data_src bigint,' 'branch_type integer,' 'in_tx boolean,' - 'call_path_id bigint)') + 'call_path_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') if perf_db_export_calls or perf_db_export_callchains: do_query(query, 'CREATE TABLE call_paths (' @@ -263,7 +267,9 @@ if perf_db_export_calls: 'return_id bigint,' 'parent_call_path_id bigint,' 'flags integer,' - 'parent_id bigint)') + 'parent_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') # printf was added to sqlite in version 3.8.3 sqlite_has_printf = False @@ -359,6 +365,9 @@ if perf_db_export_calls: 'return_time,' 'return_time - call_time AS elapsed_time,' 'branch_count,' + 'insn_count,' + 'cyc_count,' + 'CASE WHEN cyc_count=0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_count AS FLOAT) / cyc_count, 2) END AS IPC,' 'call_id,' 'return_id,' 'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,' @@ -384,7 +393,10 @@ do_query(query, 'CREATE VIEW samples_view AS ' 'to_sym_offset,' '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' - 'in_tx' + 'in_tx,' + 'insn_count,' + 'cyc_count,' + 'CASE WHEN cyc_count=0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_count AS FLOAT) / cyc_count, 2) END AS IPC' ' FROM samples') do_query(query, 'END TRANSACTION') @@ -407,15 +419,15 @@ branch_type_query = QSqlQuery(db) branch_type_query.prepare("INSERT INTO branch_types VALUES (?, ?)") sample_query = QSqlQuery(db) if branches: - sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") else: - sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") if perf_db_export_calls or perf_db_export_callchains: call_path_query = QSqlQuery(db) call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)") if perf_db_export_calls: call_query = QSqlQuery(db) - call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") def trace_begin(): printdate("Writing records...") @@ -427,10 +439,10 @@ def trace_begin(): comm_table(0, "unknown") dso_table(0, 0, "unknown", "unknown", "") symbol_table(0, 0, 0, 0, 0, "unknown") - sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) if perf_db_export_calls or perf_db_export_callchains: call_path_table(0, 0, 0, 0) - call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) unhandled_count = 0 @@ -486,14 +498,14 @@ def sample_table(*x): if branches: for xx in x[0:15]: sample_query.addBindValue(str(xx)) - for xx in x[19:22]: + for xx in x[19:24]: sample_query.addBindValue(str(xx)) do_query_(sample_query) else: - bind_exec(sample_query, 22, x) + bind_exec(sample_query, 24, x) def call_path_table(*x): bind_exec(call_path_query, 4, x) def call_return_table(*x): - bind_exec(call_query, 12, x) + bind_exec(call_query, 14, x) -- cgit v1.2.3 From ec7f448e2b2e13d1629300c5881cb3b5e0a99c2f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:23 +0300 Subject: perf scripts python: export-to-postgresql.py: Export IPC information Export cycle and instruction counts on samples and calls tables. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190520113728.14389-18-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/export-to-postgresql.py | 36 +++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index b2f481b0d28d..93225c02117e 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py @@ -394,7 +394,9 @@ if branches: 'to_ip bigint,' 'branch_type integer,' 'in_tx boolean,' - 'call_path_id bigint)') + 'call_path_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') else: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,' @@ -418,7 +420,9 @@ else: 'data_src bigint,' 'branch_type integer,' 'in_tx boolean,' - 'call_path_id bigint)') + 'call_path_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') if perf_db_export_calls or perf_db_export_callchains: do_query(query, 'CREATE TABLE call_paths (' @@ -439,7 +443,9 @@ if perf_db_export_calls: 'return_id bigint,' 'parent_call_path_id bigint,' 'flags integer,' - 'parent_id bigint)') + 'parent_id bigint,' + 'insn_count bigint,' + 'cyc_count bigint)') do_query(query, 'CREATE VIEW machines_view AS ' 'SELECT ' @@ -521,6 +527,9 @@ if perf_db_export_calls: 'return_time,' 'return_time - call_time AS elapsed_time,' 'branch_count,' + 'insn_count,' + 'cyc_count,' + 'CASE WHEN cyc_count=0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST(insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC,' 'call_id,' 'return_id,' 'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,' @@ -546,7 +555,10 @@ do_query(query, 'CREATE VIEW samples_view AS ' 'to_sym_offset,' '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' - 'in_tx' + 'in_tx,' + 'insn_count,' + 'cyc_count,' + 'CASE WHEN cyc_count=0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST(insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC' ' FROM samples') @@ -618,10 +630,10 @@ def trace_begin(): comm_table(0, "unknown") dso_table(0, 0, "unknown", "unknown", "") symbol_table(0, 0, 0, 0, 0, "unknown") - sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) if perf_db_export_calls or perf_db_export_callchains: call_path_table(0, 0, 0, 0) - call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) unhandled_count = 0 @@ -772,11 +784,11 @@ def branch_type_table(branch_type, name, *x): value = struct.pack(fmt, 2, 4, branch_type, n, name) branch_type_file.write(value) -def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, call_path_id, *x): +def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, call_path_id, insn_cnt, cyc_cnt, *x): if branches: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiq", 18, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, call_path_id) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiqiqiq", 20, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, call_path_id, 8, insn_cnt, 8, cyc_cnt) else: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiq", 22, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiqiqiq", 24, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id, 8, insn_cnt, 8, cyc_cnt) sample_file.write(value) def call_path_table(cp_id, parent_id, symbol_id, ip, *x): @@ -784,7 +796,7 @@ def call_path_table(cp_id, parent_id, symbol_id, ip, *x): value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) call_path_file.write(value) -def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, parent_id, *x): - fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiq" - value = struct.pack(fmt, 12, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags, 8, parent_id) +def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, parent_id, insn_cnt, cyc_cnt, *x): + fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiqiqiq" + value = struct.pack(fmt, 14, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags, 8, parent_id, 8, insn_cnt, 8, cyc_cnt) call_file.write(value) -- cgit v1.2.3 From 530e22fd5c6d2c572b1bbdda23eafa01a005fce0 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:24 +0300 Subject: perf scripts python: exported-sql-viewer.py: Add IPC information to the Branch reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the "All branches" and "Selected branches" reports to display IPC information if it is available. Committer testing: So, testing this I noticed that it all starts with the left arrow in every line, that should mean there is some tree there, i.e. look at all those ▶ symbols: Reports -> All Branches: Time CPU Command PID TID Branch Type In Tx Insn Cnt Cyc Cnt IPC Branch ▶ 187836112195670 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4f110 +_start (ld-2.28.so) ▶ 187836112195987 7 simple-retpolin 23003 23003 trace end No 0 883 0 7f6f33d4f110 _start (ld-2.28.so) -> 0 unknown +(unknown) ▶ 187836112199189 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4f110 +_start (ld-2.28.so) ▶ 187836112199189 7 simple-retpolin 23003 23003 call No 0 0 0 7f6f33d4f113 _start+0x3 (ld-2.28.so) -> 7f6f33d4ff50 +_dl_start (ld-2.28.so) ▶ 187836112199544 7 simple-retpolin 23003 23003 trace end No 17 996 0.02 7f6f33d4ff73 _dl_start+0x23 (ld-2.28.so) -> 0 +unknown (unknown) ▶ 187836112200939 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4ff73 +_dl_start+0x23 (ld-2.28.so) ▶ 187836112201229 7 simple-retpolin 23003 23003 trace end No 1 816 0.00 7f6f33d4ff7a _dl_start+0x2a (ld-2.28.so) -> 0 +unknown (unknown) ▶ 187836112203500 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4ff7a +_dl_start+0x2a (ld-2.28.so) But if you click on it, that ▶ disappears and a new click doesn't make it reappear, looks buggy, minor oddity, reported to Adrian. Reports -> Selected Branches, then ask for branches in the ld-2.28.so DSO: Time CPU Command PID TID Branch Type In Tx Insn Cnt Cyc Cnt IPC Branch ▶ 187836112195987 7 simple-retpolin 23003 23003 trace end No 0 883 0 7f6f33d4f110 _start (ld-2.28.so) -> 0 unknown (unknown) ▶ 187836112199189 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4f110 _start (ld-2.28.so) ▶ 187836112199189 7 simple-retpolin 23003 23003 call No 0 0 0 7f6f33d4f113 _start+0x3 (ld-2.28.so) -> 7f6f33d4ff50 _dl_start (ld-2.28.so) ▶ 187836112199544 7 simple-retpolin 23003 23003 trace end No 17 996 0.02 7f6f33d4ff73 _dl_start+0x23 (ld-2.28.so) -> 0 unknown (unknown) ▶ 187836112200939 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4ff73 _dl_start+0x23 (ld-2.28.so) ▶ 187836112201229 7 simple-retpolin 23003 23003 trace end No 1 816 0.00 7f6f33d4ff7a _dl_start+0x2a (ld-2.28.so) -> 0 unknown (unknown) ▶ 187836112203500 7 simple-retpolin 23003 23003 trace begin No 0 0 0 0 unknown (unknown) -> 7f6f33d4ff7a _dl_start+0x2a (ld-2.28.so) ▶ 187836112203528 7 simple-retpolin 23003 23003 unconditional jump No 0 0 0 7f6f33d4ffe7 _dl_start+0x97 (ld-2.28.so) -> 7f6f33d5000b _dl_start+0xbb (ld-2.28.so) ▶ 187836112203528 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d5000f _dl_start+0xbf (ld-2.28.so) -> 7f6f33d4fffb _dl_start+0xab (ld-2.28.so) ▶ 187836112203528 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d5000f _dl_start+0xbf (ld-2.28.so) -> 7f6f33d4fffb _dl_start+0xab (ld-2.28.so) ▶ 187836112203539 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d50025 _dl_start+0xd5 (ld-2.28.so) -> 7f6f33d50210 _dl_start+0x2c0 (ld-2.28.so) ▶ 187836112203539 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d5021a _dl_start+0x2ca (ld-2.28.so) -> 7f6f33d50360 _dl_start+0x410 (ld-2.28.so) ▶ 187836112203539 7 simple-retpolin 23003 23003 unconditional jump No 0 0 0 7f6f33d50377 _dl_start+0x427 (ld-2.28.so) -> 7f6f33d4ffff _dl_start+0xaf (ld-2.28.so) ▶ 187836112203539 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d5000f _dl_start+0xbf (ld-2.28.so) -> 7f6f33d4fffb _dl_start+0xab (ld-2.28.so) ▶ 187836112203562 7 simple-retpolin 23003 23003 conditional jump No 0 0 0 7f6f33d5000f _dl_start+0xbf (ld-2.28.so) -> 7f6f33d4fffb _dl_start+0xab (ld-2.28.so) Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190520113728.14389-19-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 102 ++++++++++++++++++----- 1 file changed, 83 insertions(+), 19 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index 6fe553258ce5..a607235c8cd9 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -1369,11 +1369,11 @@ class FetchMoreRecordsBar(): class BranchLevelTwoItem(): - def __init__(self, row, text, parent_item): + def __init__(self, row, col, text, parent_item): self.row = row self.parent_item = parent_item - self.data = [""] * 8 - self.data[7] = text + self.data = [""] * (col + 1) + self.data[col] = text self.level = 2 def getParentItem(self): @@ -1405,6 +1405,7 @@ class BranchLevelOneItem(): self.dbid = data[0] self.level = 1 self.query_done = False + self.br_col = len(self.data) - 1 def getChildItem(self, row): return self.child_items[row] @@ -1485,7 +1486,7 @@ class BranchLevelOneItem(): while k < 15: byte_str += " " k += 1 - self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) + self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) self.child_count += 1 else: return @@ -1536,16 +1537,37 @@ class BranchRootItem(): def getData(self, column): return "" +# Calculate instructions per cycle + +def CalcIPC(cyc_cnt, insn_cnt): + if cyc_cnt and insn_cnt: + ipc = Decimal(float(insn_cnt) / cyc_cnt) + ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) + else: + ipc = "0" + return ipc + # Branch data preparation -def BranchDataPrep(query): - data = [] - for i in xrange(0, 8): - data.append(query.value(i)) +def BranchDataPrepBr(query, data): data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + " (" + dsoname(query.value(11)) + ")" + " -> " + tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + " (" + dsoname(query.value(15)) + ")") + +def BranchDataPrepIPC(query, data): + insn_cnt = query.value(16) + cyc_cnt = query.value(17) + ipc = CalcIPC(cyc_cnt, insn_cnt) + data.append(insn_cnt) + data.append(cyc_cnt) + data.append(ipc) + +def BranchDataPrep(query): + data = [] + for i in xrange(0, 8): + data.append(query.value(i)) + BranchDataPrepBr(query, data) return data def BranchDataPrepWA(query): @@ -1555,10 +1577,26 @@ def BranchDataPrepWA(query): data.append("{:>19}".format(query.value(1))) for i in xrange(2, 8): data.append(query.value(i)) - data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + - " (" + dsoname(query.value(11)) + ")" + " -> " + - tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + - " (" + dsoname(query.value(15)) + ")") + BranchDataPrepBr(query, data) + return data + +def BranchDataWithIPCPrep(query): + data = [] + for i in xrange(0, 8): + data.append(query.value(i)) + BranchDataPrepIPC(query, data) + BranchDataPrepBr(query, data) + return data + +def BranchDataWithIPCPrepWA(query): + data = [] + data.append(query.value(0)) + # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string + data.append("{:>19}".format(query.value(1))) + for i in xrange(2, 8): + data.append(query.value(i)) + BranchDataPrepIPC(query, data) + BranchDataPrepBr(query, data) return data # Branch data model @@ -1572,10 +1610,20 @@ class BranchModel(TreeModel): self.event_id = event_id self.more = True self.populated = 0 + self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") + if self.have_ipc: + select_ipc = ", insn_count, cyc_count" + prep_fn = BranchDataWithIPCPrep + prep_wa_fn = BranchDataWithIPCPrepWA + else: + select_ipc = "" + prep_fn = BranchDataPrep + prep_wa_fn = BranchDataPrepWA sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," " ip, symbols.name, sym_offset, dsos.short_name," " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" + + select_ipc + " FROM samples" " INNER JOIN comms ON comm_id = comms.id" " INNER JOIN threads ON thread_id = threads.id" @@ -1589,9 +1637,9 @@ class BranchModel(TreeModel): " ORDER BY samples.id" " LIMIT " + str(glb_chunk_sz)) if pyside_version_1 and sys.version_info[0] == 3: - prep = BranchDataPrepWA + prep = prep_fn else: - prep = BranchDataPrep + prep = prep_wa_fn self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) self.fetcher.done.connect(self.Update) self.fetcher.Fetch(glb_chunk_sz) @@ -1600,13 +1648,23 @@ class BranchModel(TreeModel): return BranchRootItem() def columnCount(self, parent=None): - return 8 + if self.have_ipc: + return 11 + else: + return 8 def columnHeader(self, column): - return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] + if self.have_ipc: + return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] + else: + return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] def columnFont(self, column): - if column != 7: + if self.have_ipc: + br_col = 10 + else: + br_col = 7 + if column != br_col: return None return QFont("Monospace") @@ -2114,10 +2172,10 @@ def GetEventList(db): # Is a table selectable -def IsSelectable(db, table, sql = ""): +def IsSelectable(db, table, sql = "", columns = "*"): query = QSqlQuery(db) try: - QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") + QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") except: return False return True @@ -2854,6 +2912,12 @@ cd xed sudo ./mfile.py --prefix=/usr/local install sudo ldconfig +

Instructions per Cycle (IPC)

+If available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. +

Intel PT note: The information applies to the blocks of code ending with, and including, that branch. +Due to the granularity of timing information, the number of cycles for some code blocks will not be known. +In that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period +since the previous displayed 'IPC'.

Find

Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. Refer to Python documentation for the regular expression syntax. -- cgit v1.2.3 From 4a0979d4b4feee67a7f9a5605b5bfae3b0a2b6a9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:25 +0300 Subject: perf scripts python: exported-sql-viewer.py: Add CallGraphModelParams Add a parameter to call graph and call tree, to determine whether IPC information is available. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190520113728.14389-20-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 73 +++++++++++++----------- 1 file changed, 41 insertions(+), 32 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index a607235c8cd9..b3508bd4eb00 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -200,9 +200,10 @@ class Thread(QThread): class TreeModel(QAbstractItemModel): - def __init__(self, glb, parent=None): + def __init__(self, glb, params, parent=None): super(TreeModel, self).__init__(parent) self.glb = glb + self.params = params self.root = self.GetRoot() self.last_row_read = 0 @@ -463,8 +464,9 @@ class FindBar(): class CallGraphLevelItemBase(object): - def __init__(self, glb, row, parent_item): + def __init__(self, glb, params, row, parent_item): self.glb = glb + self.params = params self.row = row self.parent_item = parent_item self.query_done = False; @@ -503,8 +505,8 @@ class CallGraphLevelItemBase(object): class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): - def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): - super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): + super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) self.comm_id = comm_id self.thread_id = thread_id self.call_path_id = call_path_id @@ -525,7 +527,7 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): " GROUP BY call_path_id, name, short_name" " ORDER BY call_path_id") while query.next(): - child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) + child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) self.child_items.append(child_item) self.child_count += 1 @@ -533,8 +535,8 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): - def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): - super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): + super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) dso = dsoname(dso) self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] self.dbid = call_path_id @@ -543,8 +545,8 @@ class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): - def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): - super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): + super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, parent_item) self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] self.dbid = thread_id @@ -561,8 +563,8 @@ class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): class CallGraphLevelOneItem(CallGraphLevelItemBase): - def __init__(self, glb, row, comm_id, comm, parent_item): - super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) + def __init__(self, glb, params, row, comm_id, comm, parent_item): + super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) self.data = [comm, "", "", "", "", "", ""] self.dbid = comm_id @@ -574,7 +576,7 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase): " INNER JOIN threads ON thread_id = threads.id" " WHERE comm_id = " + str(self.dbid)) while query.next(): - child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) + child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) self.child_items.append(child_item) self.child_count += 1 @@ -582,8 +584,8 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase): class CallGraphRootItem(CallGraphLevelItemBase): - def __init__(self, glb): - super(CallGraphRootItem, self).__init__(glb, 0, None) + def __init__(self, glb, params): + super(CallGraphRootItem, self).__init__(glb, params, 0, None) self.dbid = 0 self.query_done = True; query = QSqlQuery(glb.db) @@ -591,16 +593,23 @@ class CallGraphRootItem(CallGraphLevelItemBase): while query.next(): if not query.value(0): continue - child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) + child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) self.child_items.append(child_item) self.child_count += 1 +# Call graph model parameters + +class CallGraphModelParams(): + + def __init__(self, glb, parent=None): + self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") + # Context-sensitive call graph data model base class CallGraphModelBase(TreeModel): def __init__(self, glb, parent=None): - super(CallGraphModelBase, self).__init__(glb, parent) + super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) def FindSelect(self, value, pattern, query): if pattern: @@ -682,7 +691,7 @@ class CallGraphModel(CallGraphModelBase): super(CallGraphModel, self).__init__(glb, parent) def GetRoot(self): - return CallGraphRootItem(self.glb) + return CallGraphRootItem(self.glb, self.params) def columnCount(self, parent=None): return 7 @@ -729,8 +738,8 @@ class CallGraphModel(CallGraphModelBase): class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): - def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): - super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): + super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) self.comm_id = comm_id self.thread_id = thread_id self.calls_id = calls_id @@ -752,7 +761,7 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + " ORDER BY call_time, calls.id") while query.next(): - child_item = CallTreeLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) + child_item = CallTreeLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) self.child_items.append(child_item) self.child_count += 1 @@ -760,8 +769,8 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): - def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): - super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): + super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) dso = dsoname(dso) self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] self.dbid = calls_id @@ -770,8 +779,8 @@ class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): - def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): - super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): + super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, parent_item) self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] self.dbid = thread_id @@ -788,8 +797,8 @@ class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): class CallTreeLevelOneItem(CallGraphLevelItemBase): - def __init__(self, glb, row, comm_id, comm, parent_item): - super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) + def __init__(self, glb, params, row, comm_id, comm, parent_item): + super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) self.data = [comm, "", "", "", "", "", ""] self.dbid = comm_id @@ -801,7 +810,7 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase): " INNER JOIN threads ON thread_id = threads.id" " WHERE comm_id = " + str(self.dbid)) while query.next(): - child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) + child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) self.child_items.append(child_item) self.child_count += 1 @@ -809,8 +818,8 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase): class CallTreeRootItem(CallGraphLevelItemBase): - def __init__(self, glb): - super(CallTreeRootItem, self).__init__(glb, 0, None) + def __init__(self, glb, params): + super(CallTreeRootItem, self).__init__(glb, params, 0, None) self.dbid = 0 self.query_done = True; query = QSqlQuery(glb.db) @@ -818,7 +827,7 @@ class CallTreeRootItem(CallGraphLevelItemBase): while query.next(): if not query.value(0): continue - child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) + child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) self.child_items.append(child_item) self.child_count += 1 @@ -830,7 +839,7 @@ class CallTreeModel(CallGraphModelBase): super(CallTreeModel, self).__init__(glb, parent) def GetRoot(self): - return CallTreeRootItem(self.glb) + return CallTreeRootItem(self.glb, self.params) def columnCount(self, parent=None): return 7 @@ -1606,7 +1615,7 @@ class BranchModel(TreeModel): progress = Signal(object) def __init__(self, glb, event_id, where_clause, parent=None): - super(BranchModel, self).__init__(glb, parent) + super(BranchModel, self).__init__(glb, None, parent) self.event_id = event_id self.more = True self.populated = 0 -- cgit v1.2.3 From 38a846d47f3d2fe6783e2df7bc5c2415239e6a63 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:26 +0300 Subject: perf scripts python: exported-sql-viewer.py: Add IPC information to Call Graph Graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the call graph to display IPC information if it is available. Committer testing: [acme@quaco adrian.hunter]$ python ~acme/libexec/perf-core/scripts/python/exported-sql-viewer.py ~/c/adrian.hunter/simple-retpoline.db Reports -> Context Sensitive Callgraph, then expand a few trees, then select with the mouse and press control+C: Call Path Object Count Time(ns) Time(%) Insn Insn Cyc Cyc IPC Branch Branch ▼ simple-retpolin Cnt Cnt(%) Cnt Cnt(%) Cnt Cnt(%) ▼ 23003:23003 ▼ _start ld-2.28.so 1 218295 100.0 127746 100.0 207320 100.0 0.62 13046 100.0 ▶ unknown unknown 1 3202 1.5 0 0.0 0 0.0 0 1 0.0 ▶ _dl_start ld-2.28.so 1 188471 86.3 123394 96.6 180007 86.8 0.69 12529 96.0 ▶ _dl_init ld-2.28.so 1 13406 6.1 3207 2.5 14868 7.2 0.22 327 2.5 ▼ _start simple-retpoline 1 12899 5.9 1142 0.9 11561 5.6 0.10 184 1.4 ▶ unknown unknown 1 846 6.6 0 0.0 0 0.0 0 1 0.5 ▼ __libc_start_main libc-2.28.so 1 11621 90.1 1129 98.9 10350 89.5 0.11 181 98.4 ▶ __cxa_atexit libc-2.28.so 1 2302 19.8 101 8.9 1817 17.6 0.06 13 7.2 ▶ __libc_csu_init simple-retpoline 1 121 1.0 43 3.8 340 3.3 0.13 8 4.4 ▼ _setjmp libc-2.28.so 1 74 0.6 46 4.1 206 2.0 0.22 4 2.2 ▼ __sigsetjmp libc-2.28.so 1 74 100.0 46 100.0 206 100.0 0.22 3 75.0 ▶ __sigjmp_save libc-2.28.so 1 0 0.0 0 0.0 0 0.0 0 1 33.3 ▼ main simple-retpoline 1 44 0.4 23 2.0 126 1.2 0.18 12 6.6 ▼ foo simple-retpoline 2 44 100.0 23 100.0 126 100.0 0.18 10 83.3 bar simple-retpoline 2 22 50.0 6 26.1 61 48.4 0.10 2 20.0 ▶ exit libc-2.28.so 1 9029 77.7 878 77.8 7765 75.0 0.11 139 76.8 Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Link: http://lkml.kernel.org/r/20190520113728.14389-21-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/exported-sql-viewer.py | 69 +++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) (limited to 'tools/perf/scripts/python') diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index b3508bd4eb00..f5b1b63995b0 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -505,18 +505,24 @@ class CallGraphLevelItemBase(object): class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): - def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): + def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) self.comm_id = comm_id self.thread_id = thread_id self.call_path_id = call_path_id + self.insn_cnt = insn_cnt + self.cyc_cnt = cyc_cnt self.branch_count = branch_count self.time = time def Select(self): self.query_done = True; query = QSqlQuery(self.glb.db) - QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" + if self.params.have_ipc: + ipc_str = ", SUM(insn_count), SUM(cyc_count)" + else: + ipc_str = "" + QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)" " FROM calls" " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" @@ -527,7 +533,15 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): " GROUP BY call_path_id, name, short_name" " ORDER BY call_path_id") while query.next(): - child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) + if self.params.have_ipc: + insn_cnt = int(query.value(5)) + cyc_cnt = int(query.value(6)) + branch_count = int(query.value(7)) + else: + insn_cnt = 0 + cyc_cnt = 0 + branch_count = int(query.value(5)) + child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self) self.child_items.append(child_item) self.child_count += 1 @@ -535,10 +549,17 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): - def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): - super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) + def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item): + super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) dso = dsoname(dso) - self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] + if self.params.have_ipc: + insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) + cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) + br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) + ipc = CalcIPC(cyc_cnt, insn_cnt) + self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ] + else: + self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] self.dbid = call_path_id # Context-sensitive call graph data model level two item @@ -546,18 +567,28 @@ class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): - super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, parent_item) - self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] + super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) + if self.params.have_ipc: + self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] + else: + self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] self.dbid = thread_id def Select(self): super(CallGraphLevelTwoItem, self).Select() for child_item in self.child_items: self.time += child_item.time + self.insn_cnt += child_item.insn_cnt + self.cyc_cnt += child_item.cyc_cnt self.branch_count += child_item.branch_count for child_item in self.child_items: child_item.data[4] = PercentToOneDP(child_item.time, self.time) - child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) + if self.params.have_ipc: + child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) + child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) + child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) + else: + child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) # Context-sensitive call graph data model level one item @@ -565,7 +596,10 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase): def __init__(self, glb, params, row, comm_id, comm, parent_item): super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) - self.data = [comm, "", "", "", "", "", ""] + if self.params.have_ipc: + self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] + else: + self.data = [comm, "", "", "", "", "", ""] self.dbid = comm_id def Select(self): @@ -694,14 +728,23 @@ class CallGraphModel(CallGraphModelBase): return CallGraphRootItem(self.glb, self.params) def columnCount(self, parent=None): - return 7 + if self.params.have_ipc: + return 12 + else: + return 7 def columnHeader(self, column): - headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] + if self.params.have_ipc: + headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] + else: + headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] return headers[column] def columnAlignment(self, column): - alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] + if self.params.have_ipc: + alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] + else: + alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] return alignment[column] def DoFindSelect(self, query, match): -- cgit v1.2.3 From b3b660792e049c7ef4a40c4caa7008efd4777b3c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 20 May 2019 14:37:27 +0300 Subject: perf scripts python: exported-sql-viewer.py: Add IPC information to Call Tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the call tree to display IPC information if it is available. Committer testing: [acme@quaco adrian.hunter]$ python ~acme/libexec/perf-core/scripts/python/exported-sql-viewer.py ~/c/adrian.hunter/simple-retpoline.db Reports -> Call Tree, then expand a few trees, then select with the mouse and press control+C (copy): Call Path Object Call Time Time Time(%) Insn Insn Cyc Cyc IPC Branch Branch ▼ simple-retpolin (ns) Cnt Cnt(%) Cnt Cnt(%) Count Count(%) ▼ 23003:23003 ▼ _start ld-2.28.so 112195670 218295 100.0 127746 100.0 207320 100.0 0.62 13046 100.0 ▶ unknown unknown 112195987 3202 1.5 0 0.0 0 0.0 0 1 0.0 ▶ _dl_start ld-2.28.so 112199189 188471 86.3 123394 96.6 180007 86.8 0.69 1