These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / tools / perf / scripts / python / call-graph-from-postgresql.py
1 #!/usr/bin/python2
2 # call-graph-from-postgresql.py: create call-graph from postgresql database
3 # Copyright (c) 2014, 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 the
15 # export-to-postgresql.py script.  Refer to that script for details.
16 #
17 # Following on from the example in the export-to-postgresql.py script, a
18 # call-graph can be displayed for the pt_example database like this:
19 #
20 #       python tools/perf/scripts/python/call-graph-from-postgresql.py pt_example
21 #
22 # Note this script supports connecting to remote databases by setting hostname,
23 # port, username, password, and dbname e.g.
24 #
25 #       python tools/perf/scripts/python/call-graph-from-postgresql.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
26 #
27 # The result is a GUI window with a tree representing a context-sensitive
28 # call-graph.  Expanding a couple of levels of the tree and adjusting column
29 # widths to suit will display something like:
30 #
31 #                                         Call Graph: pt_example
32 # Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
33 # v- ls
34 #     v- 2638:2638
35 #         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
36 #           |- unknown               unknown       1        13198     0.1              1              0.0
37 #           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
38 #           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
39 #           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
40 #              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
41 #              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
42 #              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
43 #              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
44 #              v- main               ls            1      8182043    99.6         180254             99.9
45 #
46 # Points to note:
47 #       The top level is a command name (comm)
48 #       The next level is a thread (pid:tid)
49 #       Subsequent levels are functions
50 #       'Count' is the number of calls
51 #       'Time' is the elapsed time until the function returns
52 #       Percentages are relative to the level above
53 #       'Branch Count' is the total number of branches for that function and all
54 #       functions that it calls
55
56 import sys
57 from PySide.QtCore import *
58 from PySide.QtGui import *
59 from PySide.QtSql import *
60 from decimal import *
61
62 class TreeItem():
63
64         def __init__(self, db, row, parent_item):
65                 self.db = db
66                 self.row = row
67                 self.parent_item = parent_item
68                 self.query_done = False;
69                 self.child_count = 0
70                 self.child_items = []
71                 self.data = ["", "", "", "", "", "", ""]
72                 self.comm_id = 0
73                 self.thread_id = 0
74                 self.call_path_id = 1
75                 self.branch_count = 0
76                 self.time = 0
77                 if not parent_item:
78                         self.setUpRoot()
79
80         def setUpRoot(self):
81                 self.query_done = True
82                 query = QSqlQuery(self.db)
83                 ret = query.exec_('SELECT id, comm FROM comms')
84                 if not ret:
85                         raise Exception("Query failed: " + query.lastError().text())
86                 while query.next():
87                         if not query.value(0):
88                                 continue
89                         child_item = TreeItem(self.db, self.child_count, self)
90                         self.child_items.append(child_item)
91                         self.child_count += 1
92                         child_item.setUpLevel1(query.value(0), query.value(1))
93
94         def setUpLevel1(self, comm_id, comm):
95                 self.query_done = True;
96                 self.comm_id = comm_id
97                 self.data[0] = comm
98                 self.child_items = []
99                 self.child_count = 0
100                 query = QSqlQuery(self.db)
101                 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))
102                 if not ret:
103                         raise Exception("Query failed: " + query.lastError().text())
104                 while query.next():
105                         child_item = TreeItem(self.db, self.child_count, self)
106                         self.child_items.append(child_item)
107                         self.child_count += 1
108                         child_item.setUpLevel2(comm_id, query.value(0), query.value(1), query.value(2))
109
110         def setUpLevel2(self, comm_id, thread_id, pid, tid):
111                 self.comm_id = comm_id
112                 self.thread_id = thread_id
113                 self.data[0] = str(pid) + ":" + str(tid)
114
115         def getChildItem(self, row):
116                 return self.child_items[row]
117
118         def getParentItem(self):
119                 return self.parent_item
120
121         def getRow(self):
122                 return self.row
123
124         def timePercent(self, b):
125                 if not self.time:
126                         return "0.0"
127                 x = (b * Decimal(100)) / self.time
128                 return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP))
129
130         def branchPercent(self, b):
131                 if not self.branch_count:
132                         return "0.0"
133                 x = (b * Decimal(100)) / self.branch_count
134                 return str(x.quantize(Decimal('.1'), rounding=ROUND_HALF_UP))
135
136         def addChild(self, call_path_id, name, dso, count, time, branch_count):
137                 child_item = TreeItem(self.db, self.child_count, self)
138                 child_item.comm_id = self.comm_id
139                 child_item.thread_id = self.thread_id
140                 child_item.call_path_id = call_path_id
141                 child_item.branch_count = branch_count
142                 child_item.time = time
143                 child_item.data[0] = name
144                 if dso == "[kernel.kallsyms]":
145                         dso = "[kernel]"
146                 child_item.data[1] = dso
147                 child_item.data[2] = str(count)
148                 child_item.data[3] = str(time)
149                 child_item.data[4] = self.timePercent(time)
150                 child_item.data[5] = str(branch_count)
151                 child_item.data[6] = self.branchPercent(branch_count)
152                 self.child_items.append(child_item)
153                 self.child_count += 1
154
155         def selectCalls(self):
156                 self.query_done = True;
157                 query = QSqlQuery(self.db)
158                 ret = query.exec_('SELECT id, call_path_id, branch_count, call_time, return_time, '
159                                   '( SELECT name FROM symbols WHERE id = ( SELECT symbol_id FROM call_paths WHERE id = call_path_id ) ), '
160                                   '( 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 ) ) ), '
161                                   '( SELECT ip FROM call_paths where id = call_path_id ) '
162                                   '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) +
163                                   'ORDER BY call_path_id')
164                 if not ret:
165                         raise Exception("Query failed: " + query.lastError().text())
166                 last_call_path_id = 0
167                 name = ""
168                 dso = ""
169                 count = 0
170                 branch_count = 0
171                 total_branch_count = 0
172                 time = 0
173                 total_time = 0
174                 while query.next():
175                         if query.value(1) == last_call_path_id:
176                                 count += 1
177                                 branch_count += query.value(2)
178                                 time += query.value(4) - query.value(3)
179                         else:
180                                 if count:
181                                         self.addChild(last_call_path_id, name, dso, count, time, branch_count)
182                                 last_call_path_id = query.value(1)
183                                 name = query.value(5)
184                                 dso = query.value(6)
185                                 count = 1
186                                 total_branch_count += branch_count
187                                 total_time += time
188                                 branch_count = query.value(2)
189                                 time = query.value(4) - query.value(3)
190                 if count:
191                         self.addChild(last_call_path_id, name, dso, count, time, branch_count)
192                 total_branch_count += branch_count
193                 total_time += time
194                 # Top level does not have time or branch count, so fix that here
195                 if total_branch_count > self.branch_count:
196                         self.branch_count = total_branch_count
197                         if self.branch_count:
198                                 for child_item in self.child_items:
199                                         child_item.data[6] = self.branchPercent(child_item.branch_count)
200                 if total_time > self.time:
201                         self.time = total_time
202                         if self.time:
203                                 for child_item in self.child_items:
204                                         child_item.data[4] = self.timePercent(child_item.time)
205
206         def childCount(self):
207                 if not self.query_done:
208                         self.selectCalls()
209                 return self.child_count
210
211         def columnCount(self):
212                 return 7
213
214         def columnHeader(self, column):
215                 headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
216                 return headers[column]
217
218         def getData(self, column):
219                 return self.data[column]
220
221 class TreeModel(QAbstractItemModel):
222
223         def __init__(self, db, parent=None):
224                 super(TreeModel, self).__init__(parent)
225                 self.db = db
226                 self.root = TreeItem(db, 0, None)
227
228         def columnCount(self, parent):
229                 return self.root.columnCount()
230
231         def rowCount(self, parent):
232                 if parent.isValid():
233                         parent_item = parent.internalPointer()
234                 else:
235                         parent_item = self.root
236                 return parent_item.childCount()
237
238         def headerData(self, section, orientation, role):
239                 if role == Qt.TextAlignmentRole:
240                         if section > 1:
241                                 return Qt.AlignRight
242                 if role != Qt.DisplayRole:
243                         return None
244                 if orientation != Qt.Horizontal:
245                         return None
246                 return self.root.columnHeader(section)
247
248         def parent(self, child):
249                 child_item = child.internalPointer()
250                 if child_item is self.root:
251                         return QModelIndex()
252                 parent_item = child_item.getParentItem()
253                 return self.createIndex(parent_item.getRow(), 0, parent_item)
254
255         def index(self, row, column, parent):
256                 if parent.isValid():
257                         parent_item = parent.internalPointer()
258                 else:
259                         parent_item = self.root
260                 child_item = parent_item.getChildItem(row)
261                 return self.createIndex(row, column, child_item)
262
263         def data(self, index, role):
264                 if role == Qt.TextAlignmentRole:
265                         if index.column() > 1:
266                                 return Qt.AlignRight
267                 if role != Qt.DisplayRole:
268                         return None
269                 index_item = index.internalPointer()
270                 return index_item.getData(index.column())
271
272 class MainWindow(QMainWindow):
273
274         def __init__(self, db, dbname, parent=None):
275                 super(MainWindow, self).__init__(parent)
276
277                 self.setObjectName("MainWindow")
278                 self.setWindowTitle("Call Graph: " + dbname)
279                 self.move(100, 100)
280                 self.resize(800, 600)
281                 style = self.style()
282                 icon = style.standardIcon(QStyle.SP_MessageBoxInformation)
283                 self.setWindowIcon(icon);
284
285                 self.model = TreeModel(db)
286
287                 self.view = QTreeView()
288                 self.view.setModel(self.model)
289
290                 self.setCentralWidget(self.view)
291
292 if __name__ == '__main__':
293         if (len(sys.argv) < 2):
294                 print >> sys.stderr, "Usage is: call-graph-from-postgresql.py <database name>"
295                 raise Exception("Too few arguments")
296
297         dbname = sys.argv[1]
298
299         db = QSqlDatabase.addDatabase('QPSQL')
300
301         opts = dbname.split()
302         for opt in opts:
303                 if '=' in opt:
304                         opt = opt.split('=')
305                         if opt[0] == 'hostname':
306                                 db.setHostName(opt[1])
307                         elif opt[0] == 'port':
308                                 db.setPort(int(opt[1]))
309                         elif opt[0] == 'username':
310                                 db.setUserName(opt[1])
311                         elif opt[0] == 'password':
312                                 db.setPassword(opt[1])
313                         elif opt[0] == 'dbname':
314                                 dbname = opt[1]
315                 else:
316                         dbname = opt
317
318         db.setDatabaseName(dbname)
319         if not db.open():
320                 raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
321
322         app = QApplication(sys.argv)
323         window = MainWindow(db, dbname)
324         window.show()
325         err = app.exec_()
326         db.close()
327         sys.exit(err)