aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/exported-sql-viewer.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/exported-sql-viewer.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/exported-sql-viewer.py')
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py2128
1 files changed, 2128 insertions, 0 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
new file mode 100755
index 000000000000..24cb0bd56afa
--- /dev/null
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -0,0 +1,2128 @@
1#!/usr/bin/python2
2# SPDX-License-Identifier: GPL-2.0
3# exported-sql-viewer.py: view data from sql database
4# Copyright (c) 2014-2018, Intel Corporation.
5
6# To use this script you will need to have exported data using either the
7# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those
8# scripts for details.
9#
10# Following on from the example in the export scripts, a
11# call-graph can be displayed for the pt_example database like this:
12#
13# python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14#
15# Note that for PostgreSQL, this script supports connecting to remote databases
16# by setting hostname, port, username, password, and dbname e.g.
17#
18# python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19#
20# The result is a GUI window with a tree representing a context-sensitive
21# call-graph. Expanding a couple of levels of the tree and adjusting column
22# widths to suit will display something like:
23#
24# Call Graph: pt_example
25# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%)
26# v- ls
27# v- 2638:2638
28# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0
29# |- unknown unknown 1 13198 0.1 1 0.0
30# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3
31# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3
32# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4
33# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1
34# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0
35# >- __libc_csu_init ls 1 10354 0.1 10 0.0
36# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0
37# v- main ls 1 8182043 99.6 180254 99.9
38#
39# Points to note:
40# The top level is a command name (comm)
41# The next level is a thread (pid:tid)
42# Subsequent levels are functions
43# 'Count' is the number of calls
44# 'Time' is the elapsed time until the function returns
45# Percentages are relative to the level above
46# 'Branch Count' is the total number of branches for that function and all
47# functions that it calls
48
49# There is also a "All branches" report, which displays branches and
50# possibly disassembly. However, presently, the only supported disassembler is
51# Intel XED, and additionally the object code must be present in perf build ID
52# cache. To use Intel XED, libxed.so must be present. To build and install
53# libxed.so:
54# git clone https://github.com/intelxed/mbuild.git mbuild
55# git clone https://github.com/intelxed/xed
56# cd xed
57# ./mfile.py --share
58# sudo ./mfile.py --prefix=/usr/local install
59# sudo ldconfig
60#
61# Example report:
62#
63# Time CPU Command PID TID Branch Type In Tx Branch
64# 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
65# 7fab593ea260 48 89 e7 mov %rsp, %rdi
66# 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
67# 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
68# 7fab593ea260 48 89 e7 mov %rsp, %rdi
69# 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930
70# 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
71# 7fab593ea930 55 pushq %rbp
72# 7fab593ea931 48 89 e5 mov %rsp, %rbp
73# 7fab593ea934 41 57 pushq %r15
74# 7fab593ea936 41 56 pushq %r14
75# 7fab593ea938 41 55 pushq %r13
76# 7fab593ea93a 41 54 pushq %r12
77# 7fab593ea93c 53 pushq %rbx
78# 7fab593ea93d 48 89 fb mov %rdi, %rbx
79# 7fab593ea940 48 83 ec 68 sub $0x68, %rsp
80# 7fab593ea944 0f 31 rdtsc
81# 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx
82# 7fab593ea94a 89 c0 mov %eax, %eax
83# 7fab593ea94c 48 09 c2 or %rax, %rdx
84# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax
85# 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
86# 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
87# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax
88# 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip)
89# 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
90
91import sys
92import weakref
93import threading
94import string
95import cPickle
96import re
97import os
98from PySide.QtCore import *
99from PySide.QtGui import *
100from PySide.QtSql import *
101from decimal import *
102from ctypes import *
103from multiprocessing import Process, Array, Value, Event
104
105# Data formatting helpers
106
107def tohex(ip):
108 if ip < 0:
109 ip += 1 << 64
110 return "%x" % ip
111
112def offstr(offset):
113 if offset:
114 return "+0x%x" % offset
115 return ""
116
117def dsoname(name):
118 if name == "[kernel.kallsyms]":
119 return "[kernel]"
120 return name
121
122# Percent to one decimal place
123
124def PercentToOneDP(n, d):
125 if not d:
126 return "0.0"
127 x = (n * Decimal(100)) / d
128 return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
129
130# Helper for queries that must not fail
131
132def QueryExec(query, stmt):
133 ret = query.exec_(stmt)
134 if not ret:
135 raise Exception("Query failed: " + query.lastError().text())
136
137# Background thread
138
139class Thread(QThread):
140
141 done = Signal(object)
142
143 def __init__(self, task, param=None, parent=None):
144 super(Thread, self).__init__(parent)
145 self.task = task
146 self.param = param
147
148 def run(self):
149 while True:
150 if self.param is None:
151 done, result = self.task()
152 else:
153 done, result = self.task(self.param)
154 self.done.emit(result)
155 if done:
156 break
157
158# Tree data model
159
160class TreeModel(QAbstractItemModel):
161
162 def __init__(self, root, parent=None):
163 super(TreeModel, self).__init__(parent)
164 self.root = root
165 self.last_row_read = 0
166
167 def Item(self, parent):
168 if parent.isValid():
169 return parent.internalPointer()
170 else:
171 return self.root
172
173 def rowCount(self, parent):
174 result = self.Item(parent).childCount()
175 if result < 0:
176 result = 0
177 self.dataChanged.emit(parent, parent)
178 return result
179
180 def hasChildren(self, parent):
181 return self.Item(parent).hasChildren()
182
183 def headerData(self, section, orientation, role):
184 if role == Qt.TextAlignmentRole:
185 return self.columnAlignment(section)
186 if role != Qt.DisplayRole:
187 return None
188 if orientation != Qt.Horizontal:
189 return None
190 return self.columnHeader(section)
191
192 def parent(self, child):
193 child_item = child.internalPointer()
194 if child_item is self.root:
195 return QModelIndex()
196 parent_item = child_item.getParentItem()
197 return self.createIndex(parent_item.getRow(), 0, parent_item)
198
199 def index(self, row, column, parent):
200 child_item = self.Item(parent).getChildItem(row)
201 return self.createIndex(row, column, child_item)
202
203 def DisplayData(self, item, index):
204 return item.getData(index.column())
205
206 def FetchIfNeeded(self, row):
207 if row > self.last_row_read:
208 self.last_row_read = row
209 if row + 10 >= self.root.child_count:
210 self.fetcher.Fetch(glb_chunk_sz)
211
212 def columnAlignment(self, column):
213 return Qt.AlignLeft
214
215 def columnFont(self, column):
216 return None
217
218 def data(self, index, role):
219 if role == Qt.TextAlignmentRole:
220 return self.columnAlignment(index.column())
221 if role == Qt.FontRole:
222 return self.columnFont(index.column())
223 if role != Qt.DisplayRole:
224 return None
225 item = index.internalPointer()
226 return self.DisplayData(item, index)
227
228# Table data model
229
230class TableModel(QAbstractTableModel):
231
232 def __init__(self, parent=None):
233 super(TableModel, self).__init__(parent)
234 self.child_count = 0
235 self.child_items = []
236 self.last_row_read = 0
237
238 def Item(self, parent):
239 if parent.isValid():
240 return parent.internalPointer()
241 else:
242 return self
243
244 def rowCount(self, parent):
245 return self.child_count
246
247 def headerData(self, section, orientation, role):
248 if role == Qt.TextAlignmentRole:
249 return self.columnAlignment(section)
250 if role != Qt.DisplayRole:
251 return None
252 if orientation != Qt.Horizontal:
253 return None
254 return self.columnHeader(section)
255
256 def index(self, row, column, parent):
257 return self.createIndex(row, column, self.child_items[row])
258
259 def DisplayData(self, item, index):
260 return item.getData(index.column())
261
262 def FetchIfNeeded(self, row):
263 if row > self.last_row_read:
264 self.last_row_read = row
265 if row + 10 >= self.child_count:
266 self.fetcher.Fetch(glb_chunk_sz)
267
268 def columnAlignment(self, column):
269 return Qt.AlignLeft
270
271 def columnFont(self, column):
272 return None
273
274 def data(self, index, role):
275 if role == Qt.TextAlignmentRole:
276 return self.columnAlignment(index.column())
277 if role == Qt.FontRole:
278 return self.columnFont(index.column())
279 if role != Qt.DisplayRole:
280 return None
281 item = index.internalPointer()
282 return self.DisplayData(item, index)
283
284# Model cache
285
286model_cache = weakref.WeakValueDictionary()
287model_cache_lock = threading.Lock()
288
289def LookupCreateModel(model_name, create_fn):
290 model_cache_lock.acquire()
291 try:
292 model = model_cache[model_name]
293 except:
294 model = None
295 if model is None:
296 model = create_fn()
297 model_cache[model_name] = model
298 model_cache_lock.release()
299 return model
300
301# Find bar
302
303class FindBar():
304
305 def __init__(self, parent, finder, is_reg_expr=False):
306 self.finder = finder
307 self.context = []
308 self.last_value = None
309 self.last_pattern = None
310
311 label = QLabel("Find:")
312 label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
313
314 self.textbox = QComboBox()
315 self.textbox.setEditable(True)
316 self.textbox.currentIndexChanged.connect(self.ValueChanged)
317
318 self.progress = QProgressBar()
319 self.progress.setRange(0, 0)
320 self.progress.hide()
321
322 if is_reg_expr:
323 self.pattern = QCheckBox("Regular Expression")
324 else:
325 self.pattern = QCheckBox("Pattern")
326 self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
327
328 self.next_button = QToolButton()
329 self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
330 self.next_button.released.connect(lambda: self.NextPrev(1))
331
332 self.prev_button = QToolButton()
333 self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
334 self.prev_button.released.connect(lambda: self.NextPrev(-1))
335
336 self.close_button = QToolButton()
337 self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
338 self.close_button.released.connect(self.Deactivate)
339
340 self.hbox = QHBoxLayout()
341 self.hbox.setContentsMargins(0, 0, 0, 0)
342
343 self.hbox.addWidget(label)
344 self.hbox.addWidget(self.textbox)
345 self.hbox.addWidget(self.progress)
346 self.hbox.addWidget(self.pattern)
347 self.hbox.addWidget(self.next_button)
348 self.hbox.addWidget(self.prev_button)
349 self.hbox.addWidget(self.close_button)
350
351 self.bar = QWidget()
352 self.bar.setLayout(self.hbox);
353 self.bar.hide()
354
355 def Widget(self):
356 return self.bar
357
358 def Activate(self):
359 self.bar.show()
360 self.textbox.setFocus()
361
362 def Deactivate(self):
363 self.bar.hide()
364
365 def Busy(self):
366 self.textbox.setEnabled(False)
367 self.pattern.hide()
368 self.next_button.hide()
369 self.prev_button.hide()
370 self.progress.show()
371
372 def Idle(self):
373 self.textbox.setEnabled(True)
374 self.progress.hide()
375 self.pattern.show()
376 self.next_button.show()
377 self.prev_button.show()
378
379 def Find(self, direction):
380 value = self.textbox.currentText()
381 pattern = self.pattern.isChecked()
382 self.last_value = value
383 self.last_pattern = pattern
384 self.finder.Find(value, direction, pattern, self.context)
385
386 def ValueChanged(self):
387 value = self.textbox.currentText()
388 pattern = self.pattern.isChecked()
389 index = self.textbox.currentIndex()
390 data = self.textbox.itemData(index)
391 # Store the pattern in the combo box to keep it with the text value
392 if data == None:
393 self.textbox.setItemData(index, pattern)
394 else:
395 self.pattern.setChecked(data)
396 self.Find(0)
397
398 def NextPrev(self, direction):
399 value = self.textbox.currentText()
400 pattern = self.pattern.isChecked()
401 if value != self.last_value:
402 index = self.textbox.findText(value)
403 # Allow for a button press before the value has been added to the combo box
404 if index < 0:
405 index = self.textbox.count()
406 self.textbox.addItem(value, pattern)
407 self.textbox.setCurrentIndex(index)
408 return
409 else:
410 self.textbox.setItemData(index, pattern)
411 elif pattern != self.last_pattern:
412 # Keep the pattern recorded in the combo box up to date
413 index = self.textbox.currentIndex()
414 self.textbox.setItemData(index, pattern)
415 self.Find(direction)
416
417 def NotFound(self):
418 QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
419
420# Context-sensitive call graph data model item base
421
422class CallGraphLevelItemBase(object):
423
424 def __init__(self, glb, row, parent_item):
425 self.glb = glb
426 self.row = row
427 self.parent_item = parent_item
428 self.query_done = False;
429 self.child_count = 0
430 self.child_items = []
431
432 def getChildItem(self, row):
433 return self.child_items[row]
434
435 def getParentItem(self):
436 return self.parent_item
437
438 def getRow(self):
439 return self.row
440
441 def childCount(self):
442 if not self.query_done:
443 self.Select()
444 if not self.child_count:
445 return -1
446 return self.child_count
447
448 def hasChildren(self):
449 if not self.query_done:
450 return True
451 return self.child_count > 0
452
453 def getData(self, column):
454 return self.data[column]
455
456# Context-sensitive call graph data model level 2+ item base
457
458class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
459
460 def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
461 super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
462 self.comm_id = comm_id
463 self.thread_id = thread_id
464 self.call_path_id = call_path_id
465 self.branch_count = branch_count
466 self.time = time
467
468 def Select(self):
469 self.query_done = True;
470 query = QSqlQuery(self.glb.db)
471 QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
472 " FROM calls"
473 " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
474 " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
475 " INNER JOIN dsos ON symbols.dso_id = dsos.id"
476 " WHERE parent_call_path_id = " + str(self.call_path_id) +
477 " AND comm_id = " + str(self.comm_id) +
478 " AND thread_id = " + str(self.thread_id) +
479 " GROUP BY call_path_id, name, short_name"
480 " ORDER BY call_path_id")
481 while query.next():
482 child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
483 self.child_items.append(child_item)
484 self.child_count += 1
485
486# Context-sensitive call graph data model level three item
487
488class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
489
490 def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
491 super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
492 dso = dsoname(dso)
493 self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
494 self.dbid = call_path_id
495
496# Context-sensitive call graph data model level two item
497
498class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
499
500 def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
501 super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
502 self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
503 self.dbid = thread_id
504
505 def Select(self):
506 super(CallGraphLevelTwoItem, self).Select()
507 for child_item in self.child_items:
508 self.time += child_item.time
509 self.branch_count += child_item.branch_count
510 for child_item in self.child_items:
511 child_item.data[4] = PercentToOneDP(child_item.time, self.time)
512 child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
513
514# Context-sensitive call graph data model level one item
515
516class CallGraphLevelOneItem(CallGraphLevelItemBase):
517
518 def __init__(self, glb, row, comm_id, comm, parent_item):
519 super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
520 self.data = [comm, "", "", "", "", "", ""]
521 self.dbid = comm_id
522
523 def Select(self):
524 self.query_done = True;
525 query = QSqlQuery(self.glb.db)
526 QueryExec(query, "SELECT thread_id, pid, tid"
527 " FROM comm_threads"
528 " INNER JOIN threads ON thread_id = threads.id"
529 " WHERE comm_id = " + str(self.dbid))
530 while query.next():
531 child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
532 self.child_items.append(child_item)
533 self.child_count += 1
534
535# Context-sensitive call graph data model root item
536
537class CallGraphRootItem(CallGraphLevelItemBase):
538
539 def __init__(self, glb):
540 super(CallGraphRootItem, self).__init__(glb, 0, None)
541 self.dbid = 0
542 self.query_done = True;
543 query = QSqlQuery(glb.db)
544 QueryExec(query, "SELECT id, comm FROM comms")
545 while query.next():
546 if not query.value(0):
547 continue
548 child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
549 self.child_items.append(child_item)
550 self.child_count += 1
551
552# Context-sensitive call graph data model
553
554class CallGraphModel(TreeModel):
555
556 def __init__(self, glb, parent=None):
557 super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
558 self.glb = glb
559
560 def columnCount(self, parent=None):
561 return 7
562
563 def columnHeader(self, column):
564 headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
565 return headers[column]
566
567 def columnAlignment(self, column):
568 alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
569 return alignment[column]
570
571 def FindSelect(self, value, pattern, query):
572 if pattern:
573 # postgresql and sqlite pattern patching differences:
574 # postgresql LIKE is case sensitive but sqlite LIKE is not
575 # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
576 # postgresql supports ILIKE which is case insensitive
577 # sqlite supports GLOB (text only) which uses * and ? and is case sensitive
578 if not self.glb.dbref.is_sqlite3:
579 # Escape % and _
580 s = value.replace("%", "\%")
581 s = s.replace("_", "\_")
582 # Translate * and ? into SQL LIKE pattern characters % and _
583 trans = string.maketrans("*?", "%_")
584 match = " LIKE '" + str(s).translate(trans) + "'"
585 else:
586 match = " GLOB '" + str(value) + "'"
587 else:
588 match = " = '" + str(value) + "'"
589 QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
590 " FROM calls"
591 " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
592 " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
593 " WHERE symbols.name" + match +
594 " GROUP BY comm_id, thread_id, call_path_id"
595 " ORDER BY comm_id, thread_id, call_path_id")
596
597 def FindPath(self, query):
598 # Turn the query result into a list of ids that the tree view can walk
599 # to open the tree at the right place.
600 ids = []
601 parent_id = query.value(0)
602 while parent_id:
603 ids.insert(0, parent_id)
604 q2 = QSqlQuery(self.glb.db)
605 QueryExec(q2, "SELECT parent_id"
606 " FROM call_paths"
607 " WHERE id = " + str(parent_id))
608 if not q2.next():
609 break
610 parent_id = q2.value(0)
611 # The call path root is not used
612 if ids[0] == 1:
613 del ids[0]
614 ids.insert(0, query.value(2))
615 ids.insert(0, query.value(1))
616 return ids
617
618 def Found(self, query, found):
619 if found:
620 return self.FindPath(query)
621 return []
622
623 def FindValue(self, value, pattern, query, last_value, last_pattern):
624 if last_value == value and pattern == last_pattern:
625 found = query.first()
626 else:
627 self.FindSelect(value, pattern, query)
628 found = query.next()
629 return self.Found(query, found)
630
631 def FindNext(self, query):
632 found = query.next()
633 if not found:
634 found = query.first()
635 return self.Found(query, found)
636
637 def FindPrev(self, query):
638 found = query.previous()
639 if not found:
640 found = query.last()
641 return self.Found(query, found)
642
643 def FindThread(self, c):
644 if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
645 ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
646 elif c.direction > 0:
647 ids = self.FindNext(c.query)
648 else:
649 ids = self.FindPrev(c.query)
650 return (True, ids)
651
652 def Find(self, value, direction, pattern, context, callback):
653 class Context():
654 def __init__(self, *x):
655 self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
656 def Update(self, *x):
657 self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
658 if len(context):
659 context[0].Update(value, direction, pattern)
660 else:
661 context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
662 # Use a thread so the UI is not blocked during the SELECT
663 thread = Thread(self.FindThread, context[0])
664 thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
665 thread.start()
666
667 def FindDone(self, thread, callback, ids):
668 callback(ids)
669
670# Vertical widget layout
671
672class VBox():
673
674 def __init__(self, w1, w2, w3=None):
675 self.vbox = QWidget()
676 self.vbox.setLayout(QVBoxLayout());
677
678 self.vbox.layout().setContentsMargins(0, 0, 0, 0)
679
680 self.vbox.layout().addWidget(w1)
681 self.vbox.layout().addWidget(w2)
682 if w3:
683 self.vbox.layout().addWidget(w3)
684
685 def Widget(self):
686 return self.vbox
687
688# Context-sensitive call graph window
689
690class CallGraphWindow(QMdiSubWindow):
691
692 def __init__(self, glb, parent=None):
693 super(CallGraphWindow, self).__init__(parent)
694
695 self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
696
697 self.view = QTreeView()
698 self.view.setModel(self.model)
699
700 for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
701 self.view.setColumnWidth(c, w)
702
703 self.find_bar = FindBar(self, self)
704
705 self.vbox = VBox(self.view, self.find_bar.Widget())
706
707 self.setWidget(self.vbox.Widget())
708
709 AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
710
711 def DisplayFound(self, ids):
712 if not len(ids):
713 return False
714 parent = QModelIndex()
715 for dbid in ids:
716 found = False
717 n = self.model.rowCount(parent)
718 for row in xrange(n):
719 child = self.model.index(row, 0, parent)
720 if child.internalPointer().dbid == dbid:
721 found = True
722 self.view.setCurrentIndex(child)
723 parent = child
724 break
725 if not found:
726 break
727 return found
728
729 def Find(self, value, direction, pattern, context):
730 self.view.setFocus()
731 self.find_bar.Busy()
732 self.model.Find(value, direction, pattern, context, self.FindDone)
733
734 def FindDone(self, ids):
735 found = True
736 if not self.DisplayFound(ids):
737 found = False
738 self.find_bar.Idle()
739 if not found:
740 self.find_bar.NotFound()
741
742# Child data item finder
743
744class ChildDataItemFinder():
745
746 def __init__(self, root):
747 self.root = root
748 self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
749 self.rows = []
750 self.pos = 0
751
752 def FindSelect(self):
753 self.rows = []
754 if self.pattern:
755 pattern = re.compile(self.value)
756 for child in self.root.child_items:
757 for column_data in child.data:
758 if re.search(pattern, str(column_data)) is not None:
759 self.rows.append(child.row)
760 break
761 else:
762 for child in self.root.child_items:
763 for column_data in child.data:
764 if self.value in str(column_data):
765 self.rows.append(child.row)
766 break
767
768 def FindValue(self):
769 self.pos = 0
770 if self.last_value != self.value or self.pattern != self.last_pattern:
771 self.FindSelect()
772 if not len(self.rows):
773 return -1
774 return self.rows[self.pos]
775
776 def FindThread(self):
777 if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
778 row = self.FindValue()
779 elif len(self.rows):
780 if self.direction > 0:
781 self.pos += 1
782 if self.pos >= len(self.rows):
783 self.pos = 0
784 else:
785 self.pos -= 1
786 if self.pos < 0:
787 self.pos = len(self.rows) - 1
788 row = self.rows[self.pos]
789 else:
790 row = -1
791 return (True, row)
792
793 def Find(self, value, direction, pattern, context, callback):
794 self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
795 # Use a thread so the UI is not blocked
796 thread = Thread(self.FindThread)
797 thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
798 thread.start()
799
800 def FindDone(self, thread, callback, row):
801 callback(row)
802
803# Number of database records to fetch in one go
804
805glb_chunk_sz = 10000
806
807# size of pickled integer big enough for record size
808
809glb_nsz = 8
810
811# Background process for SQL data fetcher
812
813class SQLFetcherProcess():
814
815 def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
816 # Need a unique connection name
817 conn_name = "SQLFetcher" + str(os.getpid())
818 self.db, dbname = dbref.Open(conn_name)
819 self.sql = sql
820 self.buffer = buffer
821 self.head = head
822 self.tail = tail
823 self.fetch_count = fetch_count
824 self.fetching_done = fetching_done
825 self.process_target = process_target
826 self.wait_event = wait_event
827 self.fetched_event = fetched_event
828 self.prep = prep
829 self.query = QSqlQuery(self.db)
830 self.query_limit = 0 if "$$last_id$$" in sql else 2
831 self.last_id = -1
832 self.fetched = 0
833 self.more = True
834 self.local_head = self.head.value
835 self.local_tail = self.tail.value
836
837 def Select(self):
838 if self.query_limit:
839 if self.query_limit == 1:
840 return
841 self.query_limit -= 1
842 stmt = self.sql.replace("$$last_id$$", str(self.last_id))
843 QueryExec(self.query, stmt)
844
845 def Next(self):
846 if not self.query.next():
847 self.Select()
848 if not self.query.next():
849 return None
850 self.last_id = self.query.value(0)
851 return self.prep(self.query)
852
853 def WaitForTarget(self):
854 while True:
855 self.wait_event.clear()
856 target = self.process_target.value
857 if target > self.fetched or target < 0:
858 break
859 self.wait_event.wait()
860 return target
861
862 def HasSpace(self, sz):
863 if self.local_tail <= self.local_head:
864 space = len(self.buffer) - self.local_head
865 if space > sz:
866 return True
867 if space >= glb_nsz:
868 # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
869 nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
870 self.buffer[self.local_head : self.local_head + len(nd)] = nd
871 self.local_head = 0
872 if self.local_tail - self.local_head > sz:
873 return True
874 return False
875
876 def WaitForSpace(self, sz):
877 if self.HasSpace(sz):
878 return
879 while True:
880 self.wait_event.clear()
881 self.local_tail = self.tail.value
882 if self.HasSpace(sz):
883 return
884 self.wait_event.wait()
885
886 def AddToBuffer(self, obj):
887 d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
888 n = len(d)
889 nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
890 sz = n + glb_nsz
891 self.WaitForSpace(sz)
892 pos = self.local_head
893 self.buffer[pos : pos + len(nd)] = nd
894 self.buffer[pos + glb_nsz : pos + sz] = d
895 self.local_head += sz
896
897 def FetchBatch(self, batch_size):
898 fetched = 0
899 while batch_size > fetched:
900 obj = self.Next()
901 if obj is None:
902 self.more = False
903 break
904 self.AddToBuffer(obj)
905 fetched += 1
906 if fetched:
907 self.fetched += fetched
908 with self.fetch_count.get_lock():
909 self.fetch_count.value += fetched
910 self.head.value = self.local_head
911 self.fetched_event.set()
912
913 def Run(self):
914 while self.more:
915 target = self.WaitForTarget()
916 if target < 0:
917 break
918 batch_size = min(glb_chunk_sz, target - self.fetched)
919 self.FetchBatch(batch_size)
920 self.fetching_done.value = True
921 self.fetched_event.set()
922
923def SQLFetcherFn(*x):
924 process = SQLFetcherProcess(*x)
925 process.Run()
926
927# SQL data fetcher
928
929class SQLFetcher(QObject):
930
931 done = Signal(object)
932
933 def __init__(self, glb, sql, prep, process_data, parent=None):
934 super(SQLFetcher, self).__init__(parent)
935 self.process_data = process_data
936 self.more = True
937 self.target = 0
938 self.last_target = 0
939 self.fetched = 0
940 self.buffer_size = 16 * 1024 * 1024
941 self.buffer = Array(c_char, self.buffer_size, lock=False)
942 self.head = Value(c_longlong)
943 self.tail = Value(c_longlong)
944 self.local_tail = 0
945 self.fetch_count = Value(c_longlong)
946 self.fetching_done = Value(c_bool)
947 self.last_count = 0
948 self.process_target = Value(c_longlong)
949 self.wait_event = Event()
950 self.fetched_event = Event()
951 glb.AddInstanceToShutdownOnExit(self)
952 self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep))
953 self.process.start()
954 self.thread = Thread(self.Thread)
955 self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
956 self.thread.start()
957
958 def Shutdown(self):
959 # Tell the thread and process to exit
960 self.process_target.value = -1
961 self.wait_event.set()
962 self.more = False
963 self.fetching_done.value = True
964 self.fetched_event.set()
965
966 def Thread(self):
967 if not self.more:
968 return True, 0
969 while True:
970 self.fetched_event.clear()
971 fetch_count = self.fetch_count.value
972 if fetch_count != self.last_count:
973 break
974 if self.fetching_done.value:
975 self.more = False
976 return True, 0
977 self.fetched_event.wait()
978 count = fetch_count - self.last_count
979 self.last_count = fetch_count
980 self.fetched += count
981 return False, count
982
983 def Fetch(self, nr):
984 if not self.more:
985 # -1 inidcates there are no more
986 return -1
987 result = self.fetched
988 extra = result + nr - self.target
989 if extra > 0:
990 self.target += extra
991 # process_target < 0 indicates shutting down
992 if self.process_target.value >= 0:
993 self.process_target.value = self.target
994 self.wait_event.set()
995 return result
996
997 def RemoveFromBuffer(self):
998 pos = self.local_tail
999 if len(self.buffer) - pos < glb_nsz:
1000 pos = 0
1001 n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
1002 if n == 0:
1003 pos = 0
1004 n = cPickle.loads(self.buffer[0 : glb_nsz])
1005 pos += glb_nsz
1006 obj = cPickle.loads(self.buffer[pos : pos + n])
1007 self.local_tail = pos + n
1008 return obj
1009
1010 def ProcessData(self, count):
1011 for i in xrange(count):
1012 obj = self.RemoveFromBuffer()
1013 self.process_data(obj)
1014 self.tail.value = self.local_tail
1015 self.wait_event.set()
1016 self.done.emit(count)
1017
1018# Fetch more records bar
1019
1020class FetchMoreRecordsBar():
1021
1022 def __init__(self, model, parent):
1023 self.model = model
1024
1025 self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
1026 self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1027
1028 self.fetch_count = QSpinBox()
1029 self.fetch_count.setRange(1, 1000000)
1030 self.fetch_count.setValue(10)
1031 self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1032
1033 self.fetch = QPushButton("Go!")
1034 self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1035 self.fetch.released.connect(self.FetchMoreRecords)
1036
1037 self.progress = QProgressBar()
1038 self.progress.setRange(0, 100)
1039 self.progress.hide()
1040
1041 self.done_label = QLabel("All records fetched")
1042 self.done_label.hide()
1043
1044 self.spacer = QLabel("")
1045
1046 self.close_button = QToolButton()
1047 self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
1048 self.close_button.released.connect(self.Deactivate)
1049
1050 self.hbox = QHBoxLayout()
1051 self.hbox.setContentsMargins(0, 0, 0, 0)
1052
1053 self.hbox.addWidget(self.label)
1054 self.hbox.addWidget(self.fetch_count)
1055 self.hbox.addWidget(self.fetch)
1056 self.hbox.addWidget(self.spacer)
1057 self.hbox.addWidget(self.progress)
1058 self.hbox.addWidget(self.done_label)
1059 self.hbox.addWidget(self.close_button)
1060
1061 self.bar = QWidget()
1062 self.bar.setLayout(self.hbox);
1063 self.bar.show()
1064
1065 self.in_progress = False
1066 self.model.progress.connect(self.Progress)
1067
1068 self.done = False
1069
1070 if not model.HasMoreRecords():
1071 self.Done()
1072
1073 def Widget(self):
1074 return self.bar
1075
1076 def Activate(self):
1077 self.bar.show()
1078 self.fetch.setFocus()
1079
1080 def Deactivate(self):
1081 self.bar.hide()
1082
1083 def Enable(self, enable):
1084 self.fetch.setEnabled(enable)
1085 self.fetch_count.setEnabled(enable)
1086
1087 def Busy(self):
1088 self.Enable(False)
1089 self.fetch.hide()
1090 self.spacer.hide()
1091 self.progress.show()
1092
1093 def Idle(self):
1094 self.in_progress = False
1095 self.Enable(True)
1096 self.progress.hide()
1097 self.fetch.show()
1098 self.spacer.show()
1099
1100 def Target(self):
1101 return self.fetch_count.value() * glb_chunk_sz
1102
1103 def Done(self):
1104 self.done = True
1105 self.Idle()
1106 self.label.hide()
1107 self.fetch_count.hide()
1108 self.fetch.hide()
1109 self.spacer.hide()
1110 self.done_label.show()
1111
1112 def Progress(self, count):
1113 if self.in_progress:
1114 if count:
1115 percent = ((count - self.start) * 100) / self.Target()
1116 if percent >= 100:
1117 self.Idle()
1118 else:
1119 self.progress.setValue(percent)
1120 if not count:
1121 # Count value of zero means no more records
1122 self.Done()
1123
1124 def FetchMoreRecords(self):
1125 if self.done:
1126 return
1127 self.progress.setValue(0)
1128 self.Busy()
1129 self.in_progress = True
1130 self.start = self.model.FetchMoreRecords(self.Target())
1131
1132# Brance data model level two item
1133
1134class BranchLevelTwoItem():
1135
1136 def __init__(self, row, text, parent_item):
1137 self.row = row
1138 self.parent_item = parent_item
1139 self.data = [""] * 8
1140 self.data[7] = text
1141 self.level = 2
1142
1143 def getParentItem(self):
1144 return self.parent_item
1145
1146 def getRow(self):
1147 return self.row
1148
1149 def childCount(self):
1150 return 0
1151
1152 def hasChildren(self):
1153 return False
1154
1155 def getData(self, column):
1156 return self.data[column]
1157
1158# Brance data model level one item
1159
1160class BranchLevelOneItem():
1161
1162 def __init__(self, glb, row, data, parent_item):
1163 self.glb = glb
1164 self.row = row
1165 self.parent_item = parent_item
1166 self.child_count = 0
1167 self.child_items = []
1168 self.data = data[1:]
1169 self.dbid = data[0]
1170 self.level = 1
1171 self.query_done = False
1172
1173 def getChildItem(self, row):
1174 return self.child_items[row]
1175
1176 def getParentItem(self):
1177 return self.parent_item
1178
1179 def getRow(self):
1180 return self.row
1181
1182 def Select(self):
1183 self.query_done = True
1184
1185 if not self.glb.have_disassembler:
1186 return
1187
1188 query = QSqlQuery(self.glb.db)
1189
1190 QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
1191 " FROM samples"
1192 " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
1193 " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
1194 " WHERE samples.id = " + str(self.dbid))
1195 if not query.next():
1196 return
1197 cpu = query.value(0)
1198 dso = query.value(1)
1199 sym = query.value(2)
1200 if dso == 0 or sym == 0:
1201 return
1202 off = query.value(3)
1203 short_name = query.value(4)
1204 long_name = query.value(5)
1205 build_id = query.value(6)
1206 sym_start = query.value(7)
1207 ip = query.value(8)
1208
1209 QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
1210 " FROM samples"
1211 " INNER JOIN symbols ON samples.symbol_id = symbols.id"
1212 " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
1213 " ORDER BY samples.id"
1214 " LIMIT 1")
1215 if not query.next():
1216 return
1217 if query.value(0) != dso:
1218 # Cannot disassemble from one dso to another
1219 return
1220 bsym = query.value(1)
1221 boff = query.value(2)
1222 bsym_start = query.value(3)
1223 if bsym == 0:
1224 return
1225 tot = bsym_start + boff + 1 - sym_start - off
1226 if tot <= 0 or tot > 16384:
1227 return
1228
1229 inst = self.glb.disassembler.Instruction()
1230 f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
1231 if not f:
1232 return
1233 mode = 0 if Is64Bit(f) else 1
1234 self.glb.disassembler.SetMode(inst, mode)
1235
1236 buf_sz = tot + 16
1237 buf = create_string_buffer(tot + 16)
1238 f.seek(sym_start + off)
1239 buf.value = f.read(buf_sz)
1240 buf_ptr = addressof(buf)
1241 i = 0
1242 while tot > 0:
1243 cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
1244 if cnt:
1245 byte_str = tohex(ip).rjust(16)
1246 for k in xrange(cnt):
1247 byte_str += " %02x" % ord(buf[i])
1248 i += 1
1249 while k < 15:
1250 byte_str += " "
1251 k += 1
1252 self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
1253 self.child_count += 1
1254 else:
1255 return
1256 buf_ptr += cnt
1257 tot -= cnt
1258 buf_sz -= cnt
1259 ip += cnt
1260
1261 def childCount(self):
1262 if not self.query_done:
1263 self.Select()
1264 if not self.child_count:
1265 return -1
1266 return self.child_count
1267
1268 def hasChildren(self):
1269 if not self.query_done:
1270 return True
1271 return self.child_count > 0
1272
1273 def getData(self, column):
1274 return self.data[column]
1275
1276# Brance data model root item
1277
1278class BranchRootItem():
1279
1280 def __init__(self):
1281 self.child_count = 0
1282 self.child_items = []
1283 self.level = 0
1284
1285 def getChildItem(self, row):
1286 return self.child_items[row]
1287
1288 def getParentItem(self):
1289 return None
1290
1291 def getRow(self):
1292 return 0
1293
1294 def childCount(self):
1295 return self.child_count
1296
1297 def hasChildren(self):
1298 return self.child_count > 0
1299
1300 def getData(self, column):
1301 return ""
1302
1303# Branch data preparation
1304
1305def BranchDataPrep(query):
1306 data = []
1307 for i in xrange(0, 8):
1308 data.append(query.value(i))
1309 data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
1310 " (" + dsoname(query.value(11)) + ")" + " -> " +
1311 tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
1312 " (" + dsoname(query.value(15)) + ")")
1313 return data
1314
1315# Branch data model
1316
1317class BranchModel(TreeModel):
1318
1319 progress = Signal(object)
1320
1321 def __init__(self, glb, event_id, where_clause, parent=None):
1322 super(BranchModel, self).__init__(BranchRootItem(), parent)
1323 self.glb = glb
1324 self.event_id = event_id
1325 self.more = True
1326 self.populated = 0
1327 sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
1328 " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
1329 " ip, symbols.name, sym_offset, dsos.short_name,"
1330 " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
1331 " FROM samples"
1332 " INNER JOIN comms ON comm_id = comms.id"
1333 " INNER JOIN threads ON thread_id = threads.id"
1334 " INNER JOIN branch_types ON branch_type = branch_types.id"
1335 " INNER JOIN symbols ON symbol_id = symbols.id"
1336 " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
1337 " INNER JOIN dsos ON samples.dso_id = dsos.id"
1338 " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
1339 " WHERE samples.id > $$last_id$$" + where_clause +
1340 " AND evsel_id = " + str(self.event_id) +
1341 " ORDER BY samples.id"
1342 " LIMIT " + str(glb_chunk_sz))
1343 self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
1344 self.fetcher.done.connect(self.Update)
1345 self.fetcher.Fetch(glb_chunk_sz)
1346
1347 def columnCount(self, parent=None):
1348 return 8
1349
1350 def columnHeader(self, column):
1351 return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
1352
1353 def columnFont(self, column):
1354 if column != 7:
1355 return None
1356 return QFont("Monospace")
1357
1358 def DisplayData(self, item, index):
1359 if item.level == 1:
1360 self.FetchIfNeeded(item.row)
1361 return item.getData(index.column())
1362
1363 def AddSample(self, data):
1364 child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
1365 self.root.child_items.append(child)
1366 self.populated += 1
1367
1368 def Update(self, fetched):
1369 if not fetched:
1370 self.more = False
1371 self.progress.emit(0)
1372 child_count = self.root.child_count
1373 count = self.populated - child_count
1374 if count > 0:
1375 parent = QModelIndex()
1376 self.beginInsertRows(parent, child_count, child_count + count - 1)
1377 self.insertRows(child_count, count, parent)
1378 self.root.child_count += count
1379 self.endInsertRows()
1380 self.progress.emit(self.root.child_count)
1381
1382 def FetchMoreRecords(self, count):
1383 current = self.root.child_count
1384 if self.more:
1385 self.fetcher.Fetch(count)
1386 else:
1387 self.progress.emit(0)
1388 return current
1389
1390 def HasMoreRecords(self):
1391 return self.more
1392
1393# Branch window
1394
1395class BranchWindow(QMdiSubWindow):
1396
1397 def __init__(self, glb, event_id, name, where_clause, parent=None):
1398 super(BranchWindow, self).__init__(parent)
1399
1400 model_name = "Branch Events " + str(event_id)
1401 if len(where_clause):
1402 model_name = where_clause + " " + model_name
1403
1404 self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause))
1405
1406 self.view = QTreeView()
1407 self.view.setUniformRowHeights(True)
1408 self.view.setModel(self.model)
1409
1410 self.ResizeColumnsToContents()
1411
1412 self.find_bar = FindBar(self, self, True)
1413
1414 self.finder = ChildDataItemFinder(self.model.root)
1415
1416 self.fetch_bar = FetchMoreRecordsBar(self.model, self)
1417
1418 self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
1419
1420 self.setWidget(self.vbox.Widget())
1421
1422 AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events")
1423
1424 def ResizeColumnToContents(self, column, n):
1425 # Using the view's resizeColumnToContents() here is extrememly slow
1426 # so implement a crude alternative
1427 mm = "MM" if column else "MMMM"
1428 font = self.view.font()
1429 metrics = QFontMetrics(font)
1430 max = 0
1431 for row in xrange(n):
1432 val = self.model.root.child_items[row].data[column]
1433 len = metrics.width(str(val) + mm)
1434 max = len if len > max else max
1435 val = self.model.columnHeader(column)
1436 len = metrics.width(str(val) + mm)
1437 max = len if len > max else max
1438 self.view.setColumnWidth(column, max)
1439
1440 def ResizeColumnsToContents(self):
1441 n = min(self.model.root.child_count, 100)
1442 if n < 1:
1443 # No data yet, so connect a signal to notify when there is
1444 self.model.rowsInserted.connect(self.UpdateColumnWidths)
1445 return
1446 columns = self.model.columnCount()
1447 for i in xrange(columns):
1448 self.ResizeColumnToContents(i, n)
1449
1450 def UpdateColumnWidths(self, *x):
1451 # This only needs to be done once, so disconnect the signal now
1452 self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
1453 self.ResizeColumnsToContents()
1454
1455 def Find(self, value, direction, pattern, context):
1456 self.view.setFocus()
1457 self.find_bar.Busy()
1458 self.finder.Find(value, direction, pattern, context, self.FindDone)
1459
1460 def FindDone(self, row):
1461 self.find_bar.Idle()
1462 if row >= 0:
1463 self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
1464 else:
1465 self.find_bar.NotFound()
1466
1467# Event list
1468
1469def GetEventList(db):
1470 events = []
1471 query = QSqlQuery(db)
1472 QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
1473 while query.next():
1474 events.append(query.value(0))
1475 return events
1476
1477# SQL data preparation
1478
1479def SQLTableDataPrep(query, count):
1480 data = []
1481 for i in xrange(count):
1482 data.append(query.value(i))
1483 return data
1484
1485# SQL table data model item
1486
1487class SQLTableItem():
1488
1489 def __init__(self, row, data):
1490 self.row = row
1491 self.data = data
1492
1493 def getData(self, column):
1494 return self.data[column]
1495
1496# SQL table data model
1497
1498class SQLTableModel(TableModel):
1499
1500 progress = Signal(object)
1501
1502 def __init__(self, glb, sql, column_count, parent=None):
1503 super(SQLTableModel, self).__init__(parent)
1504 self.glb = glb
1505 self.more = True
1506 self.populated = 0
1507 self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample)
1508 self.fetcher.done.connect(self.Update)
1509 self.fetcher.Fetch(glb_chunk_sz)
1510
1511 def DisplayData(self, item, index):
1512 self.FetchIfNeeded(item.row)
1513 return item.getData(index.column())
1514
1515 def AddSample(self, data):
1516 child = SQLTableItem(self.populated, data)
1517 self.child_items.append(child)
1518 self.populated += 1
1519
1520 def Update(self, fetched):
1521 if not fetched:
1522 self.more = False
1523 self.progress.emit(0)
1524 child_count = self.child_count
1525 count = self.populated - child_count
1526 if count > 0:
1527 parent = QModelIndex()
1528 self.beginInsertRows(parent, child_count, child_count + count - 1)
1529 self.insertRows(child_count, count, parent)
1530 self.child_count += count
1531 self.endInsertRows()
1532 self.progress.emit(self.child_count)
1533
1534 def FetchMoreRecords(self, count):
1535 current = self.child_count
1536 if self.more:
1537 self.fetcher.Fetch(count)
1538 else:
1539 self.progress.emit(0)
1540 return current
1541
1542 def HasMoreRecords(self):
1543 return self.more
1544
1545# SQL automatic table data model
1546
1547class SQLAutoTableModel(SQLTableModel):
1548
1549 def __init__(self, glb, table_name, parent=None):
1550 sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
1551 if table_name == "comm_threads_view":
1552 # For now, comm_threads_view has no id column
1553 sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
1554 self.column_headers = []
1555 query = QSqlQuery(glb.db)
1556 if glb.dbref.is_sqlite3:
1557 QueryExec(query, "PRAGMA table_info(" + table_name + ")")
1558 while query.next():
1559 self.column_headers.append(query.value(1))
1560 if table_name == "sqlite_master":
1561 sql = "SELECT * FROM " + table_name
1562 else:
1563 if table_name[:19] == "information_schema.":
1564 sql = "SELECT * FROM " + table_name
1565 select_table_name = table_name[19:]
1566 schema = "information_schema"
1567 else:
1568 select_table_name = table_name
1569 schema = "public"
1570 QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
1571 while query.next():
1572 self.column_headers.append(query.value(0))
1573 super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent)
1574
1575 def columnCount(self, parent=None):
1576 return len(self.column_headers)
1577
1578 def columnHeader(self, column):
1579 return self.column_headers[column]
1580
1581# Base class for custom ResizeColumnsToContents
1582
1583class ResizeColumnsToContentsBase(QObject):
1584
1585 def __init__(self, parent=None):
1586 super(ResizeColumnsToContentsBase, self).__init__(parent)
1587
1588 def ResizeColumnToContents(self, column, n):
1589 # Using the view's resizeColumnToContents() here is extrememly slow
1590 # so implement a crude alternative
1591 font = self.view.font()
1592 metrics = QFontMetrics(font)
1593 max = 0
1594 for row in xrange(n):
1595 val = self.data_model.child_items[row].data[column]
1596 len = metrics.width(str(val) + "MM")
1597 max = len if len > max else max
1598 val = self.data_model.columnHeader(column)
1599 len = metrics.width(str(val) + "MM")
1600 max = len if len > max else max
1601 self.view.setColumnWidth(column, max)
1602
1603 def ResizeColumnsToContents(self):
1604 n = min(self.data_model.child_count, 100)
1605 if n < 1:
1606 # No data yet, so connect a signal to notify when there is
1607 self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
1608 return
1609 columns = self.data_model.columnCount()
1610 for i in xrange(columns):
1611 self.ResizeColumnToContents(i, n)
1612
1613 def UpdateColumnWidths(self, *x):
1614 # This only needs to be done once, so disconnect the signal now
1615 self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
1616 self.ResizeColumnsToContents()
1617
1618# Table window
1619
1620class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
1621
1622 def __init__(self, glb, table_name, parent=None):
1623 super(TableWindow, self).__init__(parent)
1624
1625 self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
1626
1627 self.model = QSortFilterProxyModel()
1628 self.model.setSourceModel(self.data_model)
1629
1630 self.view = QTableView()
1631 self.view.setModel(self.model)
1632 self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
1633 self.view.verticalHeader().setVisible(False)
1634 self.view.sortByColumn(-1, Qt.AscendingOrder)
1635 self.view.setSortingEnabled(True)
1636
1637 self.ResizeColumnsToContents()
1638
1639 self.find_bar = FindBar(self, self, True)
1640
1641 self.finder = ChildDataItemFinder(self.data_model)
1642
1643 self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
1644
1645 self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
1646
1647 self.setWidget(self.vbox.Widget())
1648
1649 AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
1650
1651 def Find(self, value, direction, pattern, context):
1652 self.view.setFocus()
1653 self.find_bar.Busy()
1654 self.finder.Find(value, direction, pattern, context, self.FindDone)
1655
1656 def FindDone(self, row):
1657 self.find_bar.Idle()
1658 if row >= 0:
1659 self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
1660 else:
1661 self.find_bar.NotFound()
1662
1663# Table list
1664
1665def GetTableList(glb):
1666 tables = []
1667 query = QSqlQuery(glb.db)
1668 if glb.dbref.is_sqlite3:
1669 QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
1670 else:
1671 QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name")
1672 while query.next():
1673 tables.append(query.value(0))
1674 if glb.dbref.is_sqlite3:
1675 tables.append("sqlite_master")
1676 else:
1677 tables.append("information_schema.tables")
1678 tables.append("information_schema.views")
1679 tables.append("information_schema.columns")
1680 return tables
1681
1682# Action Definition
1683
1684def CreateAction(label, tip, callback, parent=None, shortcut=None):
1685 action = QAction(label, parent)
1686 if shortcut != None:
1687 action.setShortcuts(shortcut)
1688 action.setStatusTip(tip)
1689 action.triggered.connect(callback)
1690 return action
1691
1692# Typical application actions
1693
1694def CreateExitAction(app, parent=None):
1695 return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
1696
1697# Typical MDI actions
1698
1699def CreateCloseActiveWindowAction(mdi_area):
1700 return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
1701
1702def CreateCloseAllWindowsAction(mdi_area):
1703 return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
1704
1705def CreateTileWindowsAction(mdi_area):
1706 return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
1707
1708def CreateCascadeWindowsAction(mdi_area):
1709 return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
1710
1711def CreateNextWindowAction(mdi_area):
1712 return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
1713
1714def CreatePreviousWindowAction(mdi_area):
1715 return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
1716
1717# Typical MDI window menu
1718
1719class WindowMenu():
1720
1721 def __init__(self, mdi_area, menu):
1722 self.mdi_area = mdi_area
1723 self.window_menu = menu.addMenu("&Windows")
1724 self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
1725 self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
1726 self.tile_windows = CreateTileWindowsAction(mdi_area)
1727 self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
1728 self.next_window = CreateNextWindowAction(mdi_area)
1729 self.previous_window = CreatePreviousWindowAction(mdi_area)
1730 self.window_menu.aboutToShow.connect(self.Update)
1731
1732 def Update(self):
1733 self.window_menu.clear()
1734 sub_window_count = len(self.mdi_area.subWindowList())
1735 have_sub_windows = sub_window_count != 0
1736 self.close_active_window.setEnabled(have_sub_windows)
1737 self.close_all_windows.setEnabled(have_sub_windows)
1738 self.tile_windows.setEnabled(have_sub_windows)
1739 self.cascade_windows.setEnabled(have_sub_windows)
1740 self.next_window.setEnabled(have_sub_windows)
1741 self.previous_window.setEnabled(have_sub_windows)
1742 self.window_menu.addAction(self.close_active_window)
1743 self.window_menu.addAction(self.close_all_windows)
1744 self.window_menu.addSeparator()
1745 self.window_menu.addAction(self.tile_windows)
1746 self.window_menu.addAction(self.cascade_windows)
1747 self.window_menu.addSeparator()
1748 self.window_menu.addAction(self.next_window)
1749 self.window_menu.addAction(self.previous_window)
1750 if sub_window_count == 0:
1751 return
1752 self.window_menu.addSeparator()
1753 nr = 1
1754 for sub_window in self.mdi_area.subWindowList():
1755 label = str(nr) + " " + sub_window.name
1756 if nr < 10:
1757 label = "&" + label
1758 action = self.window_menu.addAction(label)
1759 action.setCheckable(True)
1760 action.setChecked(sub_window == self.mdi_area.activeSubWindow())
1761 action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
1762 self.window_menu.addAction(action)
1763 nr += 1
1764
1765 def setActiveSubWindow(self, nr):
1766 self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
1767
1768# Font resize
1769
1770def ResizeFont(widget, diff):
1771 font = widget.font()
1772 sz = font.pointSize()
1773 font.setPointSize(sz + diff)
1774 widget.setFont(font)
1775
1776def ShrinkFont(widget):
1777 ResizeFont(widget, -1)
1778
1779def EnlargeFont(widget):
1780 ResizeFont(widget, 1)
1781
1782# Unique name for sub-windows
1783
1784def NumberedWindowName(name, nr):
1785 if nr > 1:
1786 name += " <" + str(nr) + ">"
1787 return name
1788
1789def UniqueSubWindowName(mdi_area, name):
1790 nr = 1
1791 while True:
1792 unique_name = NumberedWindowName(name, nr)
1793 ok = True
1794 for sub_window in mdi_area.subWindowList():
1795 if sub_window.name == unique_name:
1796 ok = False
1797 break
1798 if ok:
1799 return unique_name
1800 nr += 1
1801
1802# Add a sub-window
1803
1804def AddSubWindow(mdi_area, sub_window, name):
1805 unique_name = UniqueSubWindowName(mdi_area, name)
1806 sub_window.setMinimumSize(200, 100)
1807 sub_window.resize(800, 600)
1808 sub_window.setWindowTitle(unique_name)
1809 sub_window.setAttribute(Qt.WA_DeleteOnClose)
1810 sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
1811 sub_window.name = unique_name
1812 mdi_area.addSubWindow(sub_window)
1813 sub_window.show()
1814
1815# Main window
1816
1817class MainWindow(QMainWindow):
1818
1819 def __init__(self, glb, parent=None):
1820 super(MainWindow, self).__init__(parent)
1821
1822 self.glb = glb
1823
1824 self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
1825 self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
1826 self.setMinimumSize(200, 100)
1827
1828 self.mdi_area = QMdiArea()
1829 self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
1830 self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
1831
1832 self.setCentralWidget(self.mdi_area)
1833
1834 menu = self.menuBar()
1835
1836 file_menu = menu.addMenu("&File")
1837 file_menu.addAction(CreateExitAction(glb.app, self))
1838
1839 edit_menu = menu.addMenu("&Edit")
1840 edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
1841 edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
1842 edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
1843 edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
1844
1845 reports_menu = menu.addMenu("&Reports")
1846 reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
1847
1848 self.EventMenu(GetEventList(glb.db), reports_menu)
1849
1850 self.TableMenu(GetTableList(glb), menu)
1851
1852 self.window_menu = WindowMenu(self.mdi_area, menu)
1853
1854 def Find(self):
1855 win = self.mdi_area.activeSubWindow()
1856 if win:
1857 try:
1858 win.find_bar.Activate()
1859 except:
1860 pass
1861
1862 def FetchMoreRecords(self):
1863 win = self.mdi_area.activeSubWindow()
1864 if win:
1865 try:
1866 win.fetch_bar.Activate()
1867 except:
1868 pass
1869
1870 def ShrinkFont(self):
1871 win = self.mdi_area.activeSubWindow()
1872 ShrinkFont(win.view)
1873
1874 def EnlargeFont(self):
1875 win = self.mdi_area.activeSubWindow()
1876 EnlargeFont(win.view)
1877
1878 def EventMenu(self, events, reports_menu):
1879 branches_events = 0
1880 for event in events:
1881 event = event.split(":")[0]
1882 if event == "branches":
1883 branches_events += 1
1884 dbid = 0
1885 for event in events:
1886 dbid += 1
1887 event = event.split(":")[0]
1888 if event == "branches":
1889 label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
1890 reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
1891
1892 def TableMenu(self, tables, menu):
1893 table_menu = menu.addMenu("&Tables")
1894 for table in tables:
1895 table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
1896
1897 def NewCallGraph(self):
1898 CallGraphWindow(self.glb, self)
1899
1900 def NewBranchView(self, event_id):
1901 BranchWindow(self.glb, event_id, "", "", self)
1902
1903 def NewTableView(self, table_name):
1904 TableWindow(self.glb, table_name, self)
1905
1906# XED Disassembler
1907
1908class xed_state_t(Structure):
1909
1910 _fields_ = [
1911 ("mode", c_int),
1912 ("width", c_int)
1913 ]
1914
1915class XEDInstruction():
1916
1917 def __init__(self, libxed):
1918 # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
1919 xedd_t = c_byte * 512
1920 self.xedd = xedd_t()
1921 self.xedp = addressof(self.xedd)
1922 libxed.xed_decoded_inst_zero(self.xedp)
1923 self.state = xed_state_t()
1924 self.statep = addressof(self.state)
1925 # Buffer for disassembled instruction text
1926 self.buffer = create_string_buffer(256)
1927 self.bufferp = addressof(self.buffer)
1928
1929class LibXED():
1930
1931 def __init__(self):
1932 self.libxed = CDLL("libxed.so")
1933
1934 self.xed_tables_init = self.libxed.xed_tables_init
1935 self.xed_tables_init.restype = None
1936 self.xed_tables_init.argtypes = []
1937
1938 self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
1939 self.xed_decoded_inst_zero.restype = None
1940 self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
1941
1942 self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
1943 self.xed_operand_values_set_mode.restype = None
1944 self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
1945
1946 self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
1947 self.xed_decoded_inst_zero_keep_mode.restype = None
1948 self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
1949
1950 self.xed_decode = self.libxed.xed_decode
1951 self.xed_decode.restype = c_int
1952 self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
1953
1954 self.xed_format_context = self.libxed.xed_format_context
1955 self.xed_format_context.restype = c_uint
1956 self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
1957
1958 self.xed_tables_init()
1959
1960 def Instruction(self):
1961 return XEDInstruction(self)
1962
1963 def SetMode(self, inst, mode):
1964 if mode:
1965 inst.state.mode = 4 # 32-bit
1966 inst.state.width = 4 # 4 bytes
1967 else:
1968 inst.state.mode = 1 # 64-bit
1969 inst.state.width = 8 # 8 bytes
1970 self.xed_operand_values_set_mode(inst.xedp, inst.statep)
1971
1972 def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
1973 self.xed_decoded_inst_zero_keep_mode(inst.xedp)
1974 err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
1975 if err:
1976 return 0, ""
1977 # Use AT&T mode (2), alternative is Intel (3)
1978 ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
1979 if not ok:
1980 return 0, ""
1981 # Return instruction length and the disassembled instruction text
1982 # For now, assume the length is in byte 166
1983 return inst.xedd[166], inst.buffer.value
1984
1985def TryOpen(file_name):
1986 try:
1987 return open(file_name, "rb")
1988 except:
1989 return None
1990
1991def Is64Bit(f):
1992 result = sizeof(c_void_p)
1993 # ELF support only
1994 pos = f.tell()
1995 f.seek(0)
1996 header = f.read(7)
1997 f.seek(pos)
1998 magic = header[0:4]
1999 eclass = ord(header[4])
2000 encoding = ord(header[5])
2001 version = ord(header[6])
2002 if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
2003 result = True if eclass == 2 else False
2004 return result
2005
2006# Global data
2007
2008class Glb():
2009
2010 def __init__(self, dbref, db, dbname):
2011 self.dbref = dbref
2012 self.db = db
2013 self.dbname = dbname
2014 self.home_dir = os.path.expanduser("~")
2015 self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
2016 if self.buildid_dir:
2017 self.buildid_dir += "/.build-id/"
2018 else:
2019 self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2020 self.app = None
2021 self.mainwindow = None
2022 self.instances_to_shutdown_on_exit = weakref.WeakSet()
2023 try:
2024 self.disassembler = LibXED()
2025 self.have_disassembler = True
2026 except:
2027 self.have_disassembler = False
2028
2029 def FileFromBuildId(self, build_id):
2030 file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
2031 return TryOpen(file_name)
2032
2033 def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
2034 # Assume current machine i.e. no support for virtualization
2035 if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
2036 file_name = os.getenv("PERF_KCORE")
2037 f = TryOpen(file_name) if file_name else None
2038 if f:
2039 return f
2040 # For now, no special handling if long_name is /proc/kcore
2041 f = TryOpen(long_name)
2042 if f:
2043 return f
2044 f = self.FileFromBuildId(build_id)
2045 if f:
2046 return f
2047 return None
2048
2049 def AddInstanceToShutdownOnExit(self, instance):
2050 self.instances_to_shutdown_on_exit.add(instance)
2051
2052 # Shutdown any background processes or threads
2053 def ShutdownInstances(self):
2054 for x in self.instances_to_shutdown_on_exit:
2055 try:
2056 x.Shutdown()
2057 except:
2058 pass
2059
2060# Database reference
2061
2062class DBRef():
2063
2064 def __init__(self, is_sqlite3, dbname):
2065 self.is_sqlite3 = is_sqlite3
2066 self.dbname = dbname
2067
2068 def Open(self, connection_name):
2069 dbname = self.dbname
2070 if self.is_sqlite3:
2071 db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2072 else:
2073 db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2074 opts = dbname.split()
2075 for opt in opts:
2076 if "=" in opt:
2077 opt = opt.split("=")
2078 if opt[0] == "hostname":
2079 db.setHostName(opt[1])
2080 elif opt[0] == "port":
2081 db.setPort(int(opt[1]))
2082 elif opt[0] == "username":
2083 db.setUserName(opt[1])
2084 elif opt[0] == "password":
2085 db.setPassword(opt[1])
2086 elif opt[0] == "dbname":
2087 dbname = opt[1]
2088 else:
2089 dbname = opt
2090
2091 db.setDatabaseName(dbname)
2092 if not db.open():
2093 raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2094 return db, dbname
2095
2096# Main
2097
2098def Main():
2099 if (len(sys.argv) < 2):
2100 print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>"
2101 raise Exception("Too few arguments")
2102
2103 dbname = sys.argv[1]
2104
2105 is_sqlite3 = False
2106 try:
2107 f = open(dbname)
2108 if f.read(15) == "SQLite format 3":
2109 is_sqlite3 = True
2110 f.close()
2111 except:
2112 pass
2113
2114 dbref = DBRef(is_sqlite3, dbname)
2115 db, dbname = dbref.Open("main")
2116 glb = Glb(dbref, db, dbname)
2117 app = QApplication(sys.argv)
2118 glb.app = app
2119 mainwindow = MainWindow(glb)
2120 glb.mainwindow = mainwindow
2121 mainwindow.show()
2122 err = app.exec_()
2123 glb.ShutdownInstances()
2124 db.close()
2125 sys.exit(err)
2126
2127if __name__ == "__main__":
2128 Main()