aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/call-graph-from-sql.py
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2018-10-26 03:22:45 -0400
committerIngo Molnar <mingo@kernel.org>2018-10-26 03:22:45 -0400
commitefe8eaf7b525f1be26fe20d723d2bfbfcd7455fd (patch)
tree79b4182adcaf4506780194347dbad3656f2a08ca /tools/perf/scripts/python/call-graph-from-sql.py
parent034bda1cd5abbe7b170ce76b618768d164030bbd (diff)
parentfe57120e18a1f9124ca758c89cc54f91333d1847 (diff)
Merge tag 'perf-core-for-mingo-4.20-20181025' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: - Introduce 'perf trace --max-events' for stopping 'perf trace' when that many syscalls (enter+exit), tracepoints or other events such as page faults take place. Support that as well on a per-event basis, e.g.: perf trace -e sched:*switch/nr=2/,block:*_plug/nr=4/,block:*_unplug/nr=1/,net:*dev_queue/nr=3,max-stack=16/ Will stop when 2 context switches, 4 block plugs, 1 block unplug and 3 net_dev_queue tracepoints take place. (Arnaldo Carvalho de Melo) - Poll for monitored tasks being alive in 'perf stat -p/-t', exiting when those tasks all terminate (Jiri Olsa) - Encode -k clockid frequency into perf.data to enable timestamps derived metrics conversion into wall clock time on reporting stage. (Alexey Budankov) - Improve Intel PT call graph from SQL database and GUI python scripts, including adopting the Qt MDI interface to allow for multiple subwindows for all the tables, helping in better visualizing the data in the SQL tables, also uses, when available, the Intel XED disassembler libraries to present the Intel PT data as x86 asm mnemonics. This last feature is not currently working in some cases, fix is being discussed (Adrian Hunter) - Implement a ftrace function_graph view in 'perf script' when processing hardware trace data such as Intel PT (Andi Kleen) - Better integration with the Intel XED disassembler, when available, in 'perf script' (Andi Kleen) - Some 'perf trace' drop refcount fixes (Arnaldo Carvalho de Melo) - Add Sparc support to 'perf annotate', jitdump (David Miller) - Fix PLT symbols entry/header sizes properly on Sparc (David Miller) - Fix generation of system call table failure with /tmp mounted with 'noexec' in arm64 (Hongxu Jia) - Allow extended console debug output in 'perf script' (Milian Wolff) - Flush output stream after events in 'perf script' verbose mode (Milian Wolff) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/scripts/python/call-graph-from-sql.py')
-rw-r--r--tools/perf/scripts/python/call-graph-from-sql.py339
1 files changed, 0 insertions, 339 deletions
diff --git a/tools/perf/scripts/python/call-graph-from-sql.py b/tools/perf/scripts/python/call-graph-from-sql.py
deleted file mode 100644
index b494a67a1c67..000000000000
--- a/tools/perf/scripts/python/call-graph-from-sql.py
+++ /dev/null
@@ -1,339 +0,0 @@
1#!/usr/bin/python2
2# call-graph-from-sql.py: create call-graph from sql database
3# Copyright (c) 2014-2017, Intel Corporation.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms and conditions of the GNU General Public License,
7# version 2, as published by the Free Software Foundation.
8#
9# This program is distributed in the hope it will be useful, but WITHOUT
10# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12# more details.
13
14# To use this script you will need to have exported data using either the
15# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those
16# scripts for details.
17#
18# Following on from the example in the export scripts, a
19# call-graph can be displayed for the pt_example database like this:
20#
21# python tools/perf/scripts/python/call-graph-from-sql.py pt_example
22#
23# Note that for PostgreSQL, this script supports connecting to remote databases
24# by setting hostname, port, username, password, and dbname e.g.
25#
26# python tools/perf/scripts/python/call-graph-from-sql.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
27#
28# The result is a GUI window with a tree representing a context-sensitive
29# call-graph. Expanding a couple of levels of the tree and adjusting column
30# widths to suit will display something like:
31#
32# Call Graph: pt_example
33# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%)
34# v- ls
35# v- 2638:2638
36# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0
37# |- unknown unknown 1 13198 0.1 1 0.0
38# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3
39# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3
40# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4
41# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1
42# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0
43# >- __libc_csu_init ls 1 10354 0.1 10 0.0
44# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0
45# v- main ls 1 8182043 99.6 180254 99.9
46#
47# Points to note:
48# The top level is a command name (comm)
49# The next level is a thread (pid:tid)
50# Subsequent levels are functions
51# 'Count' is the number of calls
52# 'Time' is the elapsed time until the function returns
53# Percentages are relative to the level above
54# 'Branch Count' is the total number of branches for that function and all
55# functions that it calls
56
57import sys
58from PySide.QtCore import *
59from PySide.QtGui import *
60from PySide.QtSql import *
61from decimal import *
62
63class TreeItem():
64
65 def __init__(self, db, row, parent_item):
66 self.db = db
67 self.row = row
68 self.parent_item = parent_item
69 self.query_done = False;
70 self.child_count = 0
71 self.child_items = []
72 self.data = ["", "", "", "", "", "", ""]
73 self.comm_id = 0
74 self.thread_id = 0
75 self.call_path_id = 1
76 self.branch_count = 0
77 self.time = 0
78 if not parent_item:
79 self.setUpRoot()
80
81 def setUpRoot(self):
82 self.query_done = True
83 query = QSqlQuery(self.db)
84 ret = query.exec_('SELECT id, comm FROM comms')
85 if not ret:
86 raise Exception("Query failed: " + query.lastError().text())
87 while query.next():
88 if not query.value(0):
89 continue
90 child_item = TreeItem(self.db, self.child_count, self)
91 self.child_items.append(child_item)
92 self.child_count += 1
93 child_item.setUpLevel1(query.value(0), query.value(1))
94
95 def setUpLevel1(self, comm_id, comm):
96 self.query_done = True;
97 self.comm_id = comm_id
98 self.data[0] = comm
99 self.child_items = []
100 self.child_count = 0
101 query = QSqlQuery(self.db)
102 ret = query.exec_('SELECT thread_id, ( SELECT pid FROM threads WHERE id = thread_id ), ( SELECT tid FROM threads WHERE id = thread_id ) FROM comm_threads WHERE comm_id = ' + str(comm_id))
103 if not ret:
104 raise Exception("Query failed: " + query.lastError().text())
105 while query.next():
106 child_item = TreeItem(self.db, self.child_count, self)
107 self.child_items.append(child_item)
108 self.child_count += 1
109 child_item.setUpLevel2(comm_id, query.value(0), query.value(1), query.value(2))
110
111 def setUpLevel2(self, comm_id, thread_id, pid, tid):
112 self.comm_id = comm_id
113 self.thread_id = thread_id
114 self.data[0] = str(pid) + ":" + str(tid)
115
116 def getChildItem(self, row):
117 return self.child_items[row]
118
119 def getParentItem(self):
120 return self.parent_item
121
122 def getRow(self):
123 return self.row
124
125 def timePercent(self, b):
126 if not self.time:
127 return "0.0"
128 x = (b * Decimal(100)) / self.time
129 return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP))
130
131 def branchPercent(self, b):
132 if not self.branch_count:
133 return "0.0"
134 x = (b * Decimal(100)) / self.branch_count
135 return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP))
136
137 def addChild(self, call_path_id, name, dso, count, time, branch_count):
138 child_item = TreeItem(self.db, self.child_count, self)
139 child_item.comm_id = self.comm_id
140 child_item.thread_id = self.thread_id
141 child_item.call_path_id = call_path_id
142 child_item.branch_count = branch_count
143 child_item.time = time
144 child_item.data[0] = name
145 if dso == "[kernel.kallsyms]":
146 dso = "[kernel]"
147 child_item.data[1] = dso
148 child_item.data[2] = str(count)
149 child_item.data[3] = str(time)
150 child_item.data[4] = self.timePercent(time)
151 child_item.data[5] = str(branch_count)
152 child_item.data[6] = self.branchPercent(branch_count)
153 self.child_items.append(child_item)
154 self.child_count += 1
155
156 def selectCalls(self):
157 self.query_done = True;
158 query = QSqlQuery(self.db)
159 ret = query.exec_('SELECT id, call_path_id, branch_count, call_time, return_time, '
160 '( SELECT name FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ), '
161 '( SELECT short_name FROM dsos WHERE id = ( SELECT dso_id FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ) ), '
162 '( SELECT ip FROM call_paths where id = call_path_id ) '
163 'FROM calls WHERE parent_call_path_id = ' + str(self.call_path_id) + ' AND comm_id = ' + str(self.comm_id) + ' AND thread_id = ' + str(self.thread_id) +
164 ' ORDER BY call_path_id')
165 if not ret:
166 raise Exception("Query failed: " + query.lastError().text())
167 last_call_path_id = 0
168 name = ""
169 dso = ""
170 count = 0
171 branch_count = 0
172 total_branch_count = 0
173 time = 0
174 total_time = 0
175 while query.next():
176 if query.value(1) == last_call_path_id:
177 count += 1
178 branch_count += query.value(2)
179 time += query.value(4) - query.value(3)
180 else:
181 if count:
182 self.addChild(last_call_path_id, name, dso, count, time, branch_count)
183 last_call_path_id = query.value(1)
184 name = query.value(5)
185 dso = query.value(6)
186 count = 1
187 total_branch_count += branch_count
188 total_time += time
189 branch_count = query.value(2)
190 time = query.value(4) - query.value(3)
191 if count:
192 self.addChild(last_call_path_id, name, dso, count, time, branch_count)
193 total_branch_count += branch_count
194 total_time += time
195 # Top level does not have time or branch count, so fix that here
196 if total_branch_count > self.branch_count:
197 self.branch_count = total_branch_count
198 if self.branch_count:
199 for child_item in self.child_items:
200 child_item.data[6] = self.branchPercent(child_item.branch_count)
201 if total_time > self.time:
202 self.time = total_time
203 if self.time:
204 for child_item in self.child_items:
205 child_item.data[4] = self.timePercent(child_item.time)
206
207 def childCount(self):
208 if not self.query_done:
209 self.selectCalls()
210 return self.child_count
211
212 def columnCount(self):
213 return 7
214
215 def columnHeader(self, column):
216 headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
217 return headers[column]
218
219 def getData(self, column):
220 return self.data[column]
221
222class TreeModel(QAbstractItemModel):
223
224 def __init__(self, db, parent=None):
225 super(TreeModel, self).__init__(parent)
226 self.db = db
227 self.root = TreeItem(db, 0, None)
228
229 def columnCount(self, parent):
230 return self.root.columnCount()
231
232 def rowCount(self, parent):
233 if parent.isValid():
234 parent_item = parent.internalPointer()
235 else:
236 parent_item = self.root
237 return parent_item.childCount()
238
239 def headerData(self, section, orientation, role):
240 if role == Qt.TextAlignmentRole:
241 if section > 1:
242 return Qt.AlignRight
243 if role != Qt.DisplayRole:
244 return None
245 if orientation != Qt.Horizontal:
246 return None
247 return self.root.columnHeader(section)
248
249 def parent(self, child):
250 child_item = child.internalPointer()
251 if child_item is self.root:
252 return QModelIndex()
253 parent_item = child_item.getParentItem()
254 return self.createIndex(parent_item.getRow(), 0, parent_item)
255
256 def index(self, row, column, parent):
257 if parent.isValid():
258 parent_item = parent.internalPointer()
259 else:
260 parent_item = self.root
261 child_item = parent_item.getChildItem(row)
262 return self.createIndex(row, column, child_item)
263
264 def data(self, index, role):
265 if role == Qt.TextAlignmentRole:
266 if index.column() > 1:
267 return Qt.AlignRight
268 if role != Qt.DisplayRole:
269 return None
270 index_item = index.internalPointer()
271 return index_item.getData(index.column())
272
273class MainWindow(QMainWindow):
274
275 def __init__(self, db, dbname, parent=None):
276 super(MainWindow, self).__init__(parent)
277
278 self.setObjectName("MainWindow")
279 self.setWindowTitle("Call Graph: " + dbname)
280 self.move(100, 100)
281 self.resize(800, 600)
282 style = self.style()
283 icon = style.standardIcon(QStyle.SP_MessageBoxInformation)
284 self.setWindowIcon(icon);
285
286 self.model = TreeModel(db)
287
288 self.view = QTreeView()
289 self.view.setModel(self.model)
290
291 self.setCentralWidget(self.view)
292
293if __name__ == '__main__':
294 if (len(sys.argv) < 2):
295 print >> sys.stderr, "Usage is: call-graph-from-sql.py <database name>"
296 raise Exception("Too few arguments")
297
298 dbname = sys.argv[1]
299
300 is_sqlite3 = False
301 try:
302 f = open(dbname)
303 if f.read(15) == "SQLite format 3":
304 is_sqlite3 = True
305 f.close()
306 except:
307 pass
308
309 if is_sqlite3:
310 db = QSqlDatabase.addDatabase('QSQLITE')
311 else:
312 db = QSqlDatabase.addDatabase('QPSQL')
313 opts = dbname.split()
314 for opt in opts:
315 if '=' in opt:
316 opt = opt.split('=')
317 if opt[0] == 'hostname':
318 db.setHostName(opt[1])
319 elif opt[0] == 'port':
320 db.setPort(int(opt[1]))
321 elif opt[0] == 'username':
322 db.setUserName(opt[1])
323 elif opt[0] == 'password':
324 db.setPassword(opt[1])
325 elif opt[0] == 'dbname':
326 dbname = opt[1]
327 else:
328 dbname = opt
329
330 db.setDatabaseName(dbname)
331 if not db.open():
332 raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
333
334 app = QApplication(sys.argv)
335 window = MainWindow(db, dbname)
336 window.show()
337 err = app.exec_()
338 db.close()
339 sys.exit(err)