All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>, Namhyung Kim <namhyung@kernel.org>,
	Clark Williams <williams@redhat.com>,
	linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
	Adrian Hunter <adrian.hunter@intel.com>,
	Jiri Olsa <jolsa@redhat.com>,
	Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH 14/37] perf scripts python: exported-sql-viewer.py: Add top calls report
Date: Mon, 25 Feb 2019 18:20:12 -0300	[thread overview]
Message-ID: <20190225212035.24781-15-acme@kernel.org> (raw)
In-Reply-To: <20190225212035.24781-1-acme@kernel.org>

From: Adrian Hunter <adrian.hunter@intel.com>

Add a new report to display top calls by elapsed time. It displays calls
in descending order of time elapsed between when the function was called
and when it returned.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 .../scripts/python/exported-sql-viewer.py     | 141 +++++++++++++++++-
 1 file changed, 135 insertions(+), 6 deletions(-)

diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index 728200e3a691..09ce73b07d35 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -1402,12 +1402,13 @@ class BranchModel(TreeModel):
 
 class ReportVars():
 
-	def __init__(self, name = "", where_clause = ""):
+	def __init__(self, name = "", where_clause = "", limit = ""):
 		self.name = name
 		self.where_clause = where_clause
+		self.limit = limit
 
 	def UniqueId(self):
-		return str(self.where_clause)
+		return str(self.where_clause + ";" + self.limit)
 
 # Branch window
 
@@ -1485,16 +1486,16 @@ class BranchWindow(QMdiSubWindow):
 
 class LineEditDataItem(object):
 
-	def __init__(self, glb, label, placeholder_text, parent, id = ""):
+	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
 		self.glb = glb
 		self.label = label
 		self.placeholder_text = placeholder_text
 		self.parent = parent
 		self.id = id
 
-		self.value = ""
+		self.value = default
 
-		self.widget = QLineEdit()
+		self.widget = QLineEdit(default)
 		self.widget.editingFinished.connect(self.Validate)
 		self.widget.textChanged.connect(self.Invalidate)
 		self.red = False
@@ -1582,6 +1583,21 @@ class NonNegativeIntegerRangesDataItem(LineEditDataItem):
 			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
 		self.value = " OR ".join(ranges)
 
+# Positive integer dialog data item
+
+class PositiveIntegerDataItem(LineEditDataItem):
+
+	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
+		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
+
+	def DoValidate(self, input_string):
+		if not self.IsNumber(input_string.strip()):
+			return self.InvalidValue(input_string)
+		value = int(input_string.strip())
+		if value <= 0:
+			return self.InvalidValue(input_string)
+		self.value = str(value)
+
 # Dialog data item converted and validated using a SQL table
 
 class SQLTableDataItem(LineEditDataItem):
@@ -1799,7 +1815,9 @@ class ReportDialogBase(QDialog):
 			if not d.IsValid():
 				return
 		for d in self.data_items[1:]:
-			if len(d.value):
+			if d.id == "LIMIT":
+				vars.limit = d.value
+			elif len(d.value):
 				if len(vars.where_clause):
 					vars.where_clause += " AND "
 				vars.where_clause += d.value
@@ -2059,6 +2077,103 @@ def GetTableList(glb):
 		tables.append("information_schema.columns")
 	return tables
 
+# Top Calls data model
+
+class TopCallsModel(SQLTableModel):
+
+	def __init__(self, glb, report_vars, parent=None):
+		text = ""
+		if not glb.dbref.is_sqlite3:
+			text = "::text"
+		limit = ""
+		if len(report_vars.limit):
+			limit = " LIMIT " + report_vars.limit
+		sql = ("SELECT comm, pid, tid, name,"
+			" CASE"
+			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
+			" ELSE short_name"
+			" END AS dso,"
+			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
+			" CASE"
+			" WHEN (calls.flags = 1) THEN 'no call'" + text +
+			" WHEN (calls.flags = 2) THEN 'no return'" + text +
+			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
+			" ELSE ''" + text +
+			" END AS flags"
+			" FROM calls"
+			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
+			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
+			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
+			" INNER JOIN comms ON calls.comm_id = comms.id"
+			" INNER JOIN threads ON calls.thread_id = threads.id" +
+			report_vars.where_clause +
+			" ORDER BY elapsed_time DESC" +
+			limit
+			)
+		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
+		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
+		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
+
+	def columnAlignment(self, column):
+		return self.alignment[column]
+
+# Top Calls report creation dialog
+
+class TopCallsDialog(ReportDialogBase):
+
+	def __init__(self, glb, parent=None):
+		title = "Top Calls by Elapsed Time"
+		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
+			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
+			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
+			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
+			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
+			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
+			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
+			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
+		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
+
+# Top Calls window
+
+class TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
+
+	def __init__(self, glb, report_vars, parent=None):
+		super(TopCallsWindow, self).__init__(parent)
+
+		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
+		self.model = self.data_model
+
+		self.view = QTableView()
+		self.view.setModel(self.model)
+		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
+		self.view.verticalHeader().setVisible(False)
+
+		self.ResizeColumnsToContents()
+
+		self.find_bar = FindBar(self, self, True)
+
+		self.finder = ChildDataItemFinder(self.model)
+
+		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
+
+		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
+
+		self.setWidget(self.vbox.Widget())
+
+		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
+
+	def Find(self, value, direction, pattern, context):
+		self.view.setFocus()
+		self.find_bar.Busy()
+		self.finder.Find(value, direction, pattern, context, self.FindDone)
+
+	def FindDone(self, row):
+		self.find_bar.Idle()
+		if row >= 0:
+			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
+		else:
+			self.find_bar.NotFound()
+
 # Action Definition
 
 def CreateAction(label, tip, callback, parent=None, shortcut=None):
@@ -2162,6 +2277,7 @@ p.c2 {
 <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
 <p class=c2><a href=#allbranches>1.2 All branches</a></p>
 <p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
+<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p>
 <p class=c1><a href=#tables>2. Tables</a></p>
 <h1 id=reports>1. Reports</h1>
 <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
@@ -2237,6 +2353,10 @@ ms, us or ns. Also, negative values are relative to the end of trace.  Examples:
 	-10ms-			The last 10ms
 </pre>
 N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
+<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2>
+The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
+The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
+If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
 <h1 id=tables>2. Tables</h1>
 The Tables menu shows all tables and views in the database. Most tables have an associated view
 which displays the information in a more friendly way. Not all data for large tables is fetched
@@ -2371,6 +2491,9 @@ class MainWindow(QMainWindow):
 
 		self.EventMenu(GetEventList(glb.db), reports_menu)
 
+		if IsSelectable(glb.db, "calls"):
+			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
+
 		self.TableMenu(GetTableList(glb), menu)
 
 		self.window_menu = WindowMenu(self.mdi_area, menu)
@@ -2426,6 +2549,12 @@ class MainWindow(QMainWindow):
 	def NewCallGraph(self):
 		CallGraphWindow(self.glb, self)
 
+	def NewTopCalls(self):
+		dialog = TopCallsDialog(self.glb, self)
+		ret = dialog.exec_()
+		if ret:
+			TopCallsWindow(self.glb, dialog.report_vars, self)
+
 	def NewBranchView(self, event_id):
 		BranchWindow(self.glb, event_id, ReportVars(), self)
 
-- 
2.20.1

  parent reply	other threads:[~2019-02-25 21:20 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-25 21:19 [GIT PULL] perf/core improvements and fixes Arnaldo Carvalho de Melo
2019-02-25 21:19 ` [PATCH 01/37] perf annotate: Fix getting source line failure Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 02/37] perf thread-stack: Improve thread_stack__no_call_return() Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 03/37] perf thread-stack: Hide x86 retpolines Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 04/37] perf scripts python: exported-sql-viewer.py: Fix missing shebang Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 05/37] perf scripts python: exported-sql-viewer.py: Remove leftover debugging prints Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 06/37] perf scripts python: exported-sql-viewer.py: Hide Call Graph option if no calls table Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 07/37] perf scripts python: exported-sql-viewer.py: Move column headers Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 08/37] perf scripts python: exported-sql-viewer.py: Factor out ReportDialogBase Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 09/37] perf scripts python: exported-sql-viewer.py: Factor out ReportVars Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 10/37] perf scripts python: exported-sql-viewer.py: Move report name into ReportVars Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 11/37] perf scripts python: exported-sql-viewer.py: Create new dialog data item classes Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 12/37] perf scripts python: exported-sql-viewer.py: Remove SQLTableDialogDataItem Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 13/37] perf scripts python: exported-sql-viewer.py: Remove no selection error Arnaldo Carvalho de Melo
2019-02-25 21:20 ` Arnaldo Carvalho de Melo [this message]
2019-02-25 21:20 ` [PATCH 15/37] perf: Copy parent's address filter offsets on clone Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 16/37] perf, pt, coresight: Fix address filters for vmas with non-zero offset Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 17/37] perf data: Move size to struct perf_data_file Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 18/37] perf data: Add global path holder Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 19/37] perf tools: Add depth checking to rm_rf Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 20/37] perf tools: Add pattern name " Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 21/37] perf tools: Add rm_rf_perf_data function Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 22/37] perf data: Make check_backup work over directories Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 23/37] perf data: Fail check_backup in case of error Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 24/37] perf data: Add perf_data__(create_dir|close_dir) functions Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 25/37] perf data: Add perf_data__open_dir_data function Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 26/37] perf script: Handle missing fields with -F + Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 27/37] perf tools: Add perf_exe() helper to find perf binary Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 28/37] perf script python: Add Python3 support to netdev-times.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 29/37] perf script python: Add Python3 support to failed-syscalls-by-pid.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 30/37] perf script python: Add Python3 support to mem-phys-addr.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 31/37] perf script python: Add Python3 support to net_dropmonitor.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 32/37] perf script python: Add Python3 support to powerpc-hcalls.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 33/37] perf script python: Add Python3 support to sctop.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 34/37] perf script python: Add Python3 support to stackcollapse.py Arnaldo Carvalho de Melo
2019-02-26 11:49   ` Paolo Bonzini
2019-02-25 21:20 ` [PATCH 35/37] perf script python: Add Python3 support to stat-cpi.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 36/37] perf script python: Add Python3 support to syscall-counts.py Arnaldo Carvalho de Melo
2019-02-25 21:20 ` [PATCH 37/37] perf script python: Add Python3 support to syscall-counts-by-pid.py Arnaldo Carvalho de Melo
2019-02-28  7:31 ` [GIT PULL] perf/core improvements and fixes Ingo Molnar

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20190225212035.24781-15-acme@kernel.org \
    --to=acme@kernel.org \
    --cc=acme@redhat.com \
    --cc=adrian.hunter@intel.com \
    --cc=jolsa@kernel.org \
    --cc=jolsa@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=namhyung@kernel.org \
    --cc=williams@redhat.com \
    /path/to/YOUR_REPLY

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

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