aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/exported-sql-viewer.py
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2019-02-22 02:27:28 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2019-02-22 14:52:07 -0500
commitcd358012ba20d4193c225d89cd1d0c11bc54b1bc (patch)
treefb598c601d41d7b0db13966ea2f1e94ae6ec6901 /tools/perf/scripts/python/exported-sql-viewer.py
parentfc2c77aa8437855d2992d3f3c6a1dff681789a07 (diff)
perf scripts python: exported-sql-viewer.py: Add top calls report
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>
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py141
1 files 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):
1402 1402
1403class ReportVars(): 1403class ReportVars():
1404 1404
1405 def __init__(self, name = "", where_clause = ""): 1405 def __init__(self, name = "", where_clause = "", limit = ""):
1406 self.name = name 1406 self.name = name
1407 self.where_clause = where_clause 1407 self.where_clause = where_clause
1408 self.limit = limit
1408 1409
1409 def UniqueId(self): 1410 def UniqueId(self):
1410 return str(self.where_clause) 1411 return str(self.where_clause + ";" + self.limit)
1411 1412
1412# Branch window 1413# Branch window
1413 1414
@@ -1485,16 +1486,16 @@ class BranchWindow(QMdiSubWindow):
1485 1486
1486class LineEditDataItem(object): 1487class LineEditDataItem(object):
1487 1488
1488 def __init__(self, glb, label, placeholder_text, parent, id = ""): 1489 def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1489 self.glb = glb 1490 self.glb = glb
1490 self.label = label 1491 self.label = label
1491 self.placeholder_text = placeholder_text 1492 self.placeholder_text = placeholder_text
1492 self.parent = parent 1493 self.parent = parent
1493 self.id = id 1494 self.id = id
1494 1495
1495 self.value = "" 1496 self.value = default
1496 1497
1497 self.widget = QLineEdit() 1498 self.widget = QLineEdit(default)
1498 self.widget.editingFinished.connect(self.Validate) 1499 self.widget.editingFinished.connect(self.Validate)
1499 self.widget.textChanged.connect(self.Invalidate) 1500 self.widget.textChanged.connect(self.Invalidate)
1500 self.red = False 1501 self.red = False
@@ -1582,6 +1583,21 @@ class NonNegativeIntegerRangesDataItem(LineEditDataItem):
1582 ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 1583 ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
1583 self.value = " OR ".join(ranges) 1584 self.value = " OR ".join(ranges)
1584 1585
1586# Positive integer dialog data item
1587
1588class PositiveIntegerDataItem(LineEditDataItem):
1589
1590 def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1591 super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1592
1593 def DoValidate(self, input_string):
1594 if not self.IsNumber(input_string.strip()):
1595 return self.InvalidValue(input_string)
1596 value = int(input_string.strip())
1597 if value <= 0:
1598 return self.InvalidValue(input_string)
1599 self.value = str(value)
1600
1585# Dialog data item converted and validated using a SQL table 1601# Dialog data item converted and validated using a SQL table
1586 1602
1587class SQLTableDataItem(LineEditDataItem): 1603class SQLTableDataItem(LineEditDataItem):
@@ -1799,7 +1815,9 @@ class ReportDialogBase(QDialog):
1799 if not d.IsValid(): 1815 if not d.IsValid():
1800 return 1816 return
1801 for d in self.data_items[1:]: 1817 for d in self.data_items[1:]:
1802 if len(d.value): 1818 if d.id == "LIMIT":
1819 vars.limit = d.value
1820 elif len(d.value):
1803 if len(vars.where_clause): 1821 if len(vars.where_clause):
1804 vars.where_clause += " AND " 1822 vars.where_clause += " AND "
1805 vars.where_clause += d.value 1823 vars.where_clause += d.value
@@ -2059,6 +2077,103 @@ def GetTableList(glb):
2059 tables.append("information_schema.columns") 2077 tables.append("information_schema.columns")
2060 return tables 2078 return tables
2061 2079
2080# Top Calls data model
2081
2082class TopCallsModel(SQLTableModel):
2083
2084 def __init__(self, glb, report_vars, parent=None):
2085 text = ""
2086 if not glb.dbref.is_sqlite3:
2087 text = "::text"
2088 limit = ""
2089 if len(report_vars.limit):
2090 limit = " LIMIT " + report_vars.limit
2091 sql = ("SELECT comm, pid, tid, name,"
2092 " CASE"
2093 " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2094 " ELSE short_name"
2095 " END AS dso,"
2096 " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2097 " CASE"
2098 " WHEN (calls.flags = 1) THEN 'no call'" + text +
2099 " WHEN (calls.flags = 2) THEN 'no return'" + text +
2100 " WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2101 " ELSE ''" + text +
2102 " END AS flags"
2103 " FROM calls"
2104 " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2105 " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2106 " INNER JOIN dsos ON symbols.dso_id = dsos.id"
2107 " INNER JOIN comms ON calls.comm_id = comms.id"
2108 " INNER JOIN threads ON calls.thread_id = threads.id" +
2109 report_vars.where_clause +
2110 " ORDER BY elapsed_time DESC" +
2111 limit
2112 )
2113 column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2114 self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2115 super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2116
2117 def columnAlignment(self, column):
2118 return self.alignment[column]
2119
2120# Top Calls report creation dialog
2121
2122class TopCallsDialog(ReportDialogBase):
2123
2124 def __init__(self, glb, parent=None):
2125 title = "Top Calls by Elapsed Time"
2126 items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2127 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2128 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2129 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2130 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2131 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2132 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2133 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2134 super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2135
2136# Top Calls window
2137
2138class TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2139
2140 def __init__(self, glb, report_vars, parent=None):
2141 super(TopCallsWindow, self).__init__(parent)
2142
2143 self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2144 self.model = self.data_model
2145
2146 self.view = QTableView()
2147 self.view.setModel(self.model)
2148 self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2149 self.view.verticalHeader().setVisible(False)
2150
2151 self.ResizeColumnsToContents()
2152
2153 self.find_bar = FindBar(self, self, True)
2154
2155 self.finder = ChildDataItemFinder(self.model)
2156
2157 self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2158
2159 self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2160
2161 self.setWidget(self.vbox.Widget())
2162
2163 AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2164
2165 def Find(self, value, direction, pattern, context):
2166 self.view.setFocus()
2167 self.find_bar.Busy()
2168 self.finder.Find(value, direction, pattern, context, self.FindDone)
2169
2170 def FindDone(self, row):
2171 self.find_bar.Idle()
2172 if row >= 0:
2173 self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2174 else:
2175 self.find_bar.NotFound()
2176
2062# Action Definition 2177# Action Definition
2063 2178
2064def CreateAction(label, tip, callback, parent=None, shortcut=None): 2179def CreateAction(label, tip, callback, parent=None, shortcut=None):
@@ -2162,6 +2277,7 @@ p.c2 {
2162<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2277<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2163<p class=c2><a href=#allbranches>1.2 All branches</a></p> 2278<p class=c2><a href=#allbranches>1.2 All branches</a></p>
2164<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> 2279<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
2280<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p>
2165<p class=c1><a href=#tables>2. Tables</a></p> 2281<p class=c1><a href=#tables>2. Tables</a></p>
2166<h1 id=reports>1. Reports</h1> 2282<h1 id=reports>1. Reports</h1>
2167<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 2283<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:
2237 -10ms- The last 10ms 2353 -10ms- The last 10ms
2238</pre> 2354</pre>
2239N.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2355N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2356<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2>
2357The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
2358The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2359If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
2240<h1 id=tables>2. Tables</h1> 2360<h1 id=tables>2. Tables</h1>
2241The Tables menu shows all tables and views in the database. Most tables have an associated view 2361The Tables menu shows all tables and views in the database. Most tables have an associated view
2242which displays the information in a more friendly way. Not all data for large tables is fetched 2362which displays the information in a more friendly way. Not all data for large tables is fetched
@@ -2371,6 +2491,9 @@ class MainWindow(QMainWindow):
2371 2491
2372 self.EventMenu(GetEventList(glb.db), reports_menu) 2492 self.EventMenu(GetEventList(glb.db), reports_menu)
2373 2493
2494 if IsSelectable(glb.db, "calls"):
2495 reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
2496
2374 self.TableMenu(GetTableList(glb), menu) 2497 self.TableMenu(GetTableList(glb), menu)
2375 2498
2376 self.window_menu = WindowMenu(self.mdi_area, menu) 2499 self.window_menu = WindowMenu(self.mdi_area, menu)
@@ -2426,6 +2549,12 @@ class MainWindow(QMainWindow):
2426 def NewCallGraph(self): 2549 def NewCallGraph(self):
2427 CallGraphWindow(self.glb, self) 2550 CallGraphWindow(self.glb, self)
2428 2551
2552 def NewTopCalls(self):
2553 dialog = TopCallsDialog(self.glb, self)
2554 ret = dialog.exec_()
2555 if ret:
2556 TopCallsWindow(self.glb, dialog.report_vars, self)
2557
2429 def NewBranchView(self, event_id): 2558 def NewBranchView(self, event_id):
2430 BranchWindow(self.glb, event_id, ReportVars(), self) 2559 BranchWindow(self.glb, event_id, ReportVars(), self)
2431 2560