mmc: bcm2835: Fix DMA channel leak on probe error
[linux/fpc-iii.git] / tools / perf / scripts / python / call-graph-from-sql.py
blobb494a67a1c679ec2e6cd89105ce09702ee76146f
1 #!/usr/bin/python2
2 # call-graph-from-sql.py: create call-graph from sql database
3 # Copyright (c) 2014-2017, Intel Corporation.
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.
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.
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.
18 # Following on from the example in the export scripts, a
19 # call-graph can be displayed for the pt_example database like this:
21 # python tools/perf/scripts/python/call-graph-from-sql.py pt_example
23 # Note that for PostgreSQL, this script supports connecting to remote databases
24 # by setting hostname, port, username, password, and dbname e.g.
26 # python tools/perf/scripts/python/call-graph-from-sql.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
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:
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
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
57 import sys
58 from PySide.QtCore import *
59 from PySide.QtGui import *
60 from PySide.QtSql import *
61 from decimal import *
63 class TreeItem():
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()
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))
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))
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)
116 def getChildItem(self, row):
117 return self.child_items[row]
119 def getParentItem(self):
120 return self.parent_item
122 def getRow(self):
123 return self.row
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))
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))
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
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)
207 def childCount(self):
208 if not self.query_done:
209 self.selectCalls()
210 return self.child_count
212 def columnCount(self):
213 return 7
215 def columnHeader(self, column):
216 headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
217 return headers[column]
219 def getData(self, column):
220 return self.data[column]
222 class TreeModel(QAbstractItemModel):
224 def __init__(self, db, parent=None):
225 super(TreeModel, self).__init__(parent)
226 self.db = db
227 self.root = TreeItem(db, 0, None)
229 def columnCount(self, parent):
230 return self.root.columnCount()
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()
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)
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)
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)
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())
273 class MainWindow(QMainWindow):
275 def __init__(self, db, dbname, parent=None):
276 super(MainWindow, self).__init__(parent)
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);
286 self.model = TreeModel(db)
288 self.view = QTreeView()
289 self.view.setModel(self.model)
291 self.setCentralWidget(self.view)
293 if __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")
298 dbname = sys.argv[1]
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
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
330 db.setDatabaseName(dbname)
331 if not db.open():
332 raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
334 app = QApplication(sys.argv)
335 window = MainWindow(db, dbname)
336 window.show()
337 err = app.exec_()
338 db.close()
339 sys.exit(err)