1b3a67546SAdrian Hunter#!/usr/bin/env python2
2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5031c2a00SAdrian Hunter
6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8031c2a00SAdrian Hunter# scripts for details.
9031c2a00SAdrian Hunter#
10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12031c2a00SAdrian Hunter#
13031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14031c2a00SAdrian Hunter#
15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17031c2a00SAdrian Hunter#
18031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19031c2a00SAdrian Hunter#
20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22031c2a00SAdrian Hunter# widths to suit will display something like:
23031c2a00SAdrian Hunter#
24031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26031c2a00SAdrian Hunter# v- ls
27031c2a00SAdrian Hunter#     v- 2638:2638
28031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38031c2a00SAdrian Hunter#
39031c2a00SAdrian Hunter# Points to note:
40031c2a00SAdrian Hunter#	The top level is a command name (comm)
41031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42031c2a00SAdrian Hunter#	Subsequent levels are functions
43031c2a00SAdrian Hunter#	'Count' is the number of calls
44031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45031c2a00SAdrian Hunter#	Percentages are relative to the level above
46031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47031c2a00SAdrian Hunter#       functions that it calls
48031c2a00SAdrian Hunter
4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
5076099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
5376099f98SAdrian Hunter# libxed.so:
5476099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
5576099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
5676099f98SAdrian Hunter#            cd xed
5776099f98SAdrian Hunter#            ./mfile.py --share
5876099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
5976099f98SAdrian Hunter#            sudo ldconfig
6076099f98SAdrian Hunter#
6176099f98SAdrian Hunter# Example report:
6276099f98SAdrian Hunter#
6376099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6476099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6576099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6676099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6776099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6876099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6976099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7076099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7176099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
7276099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7376099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
7476099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
7576099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
7676099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7776099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
7876099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7976099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8076099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
8176099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8276099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8376099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8476099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8576099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8676099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8776099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8876099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8976099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9076099f98SAdrian Hunter
91*beda0e72STony Jonesfrom __future__ import print_function
92*beda0e72STony Jones
93031c2a00SAdrian Hunterimport sys
941beb5c7bSAdrian Hunterimport weakref
951beb5c7bSAdrian Hunterimport threading
96ebd70c7dSAdrian Hunterimport string
97*beda0e72STony Jonestry:
98*beda0e72STony Jones	# Python2
99*beda0e72STony Jones	import cPickle as pickle
100*beda0e72STony Jones	# size of pickled integer big enough for record size
101*beda0e72STony Jones	glb_nsz = 8
102*beda0e72STony Jonesexcept ImportError:
103*beda0e72STony Jones	import pickle
104*beda0e72STony Jones	glb_nsz = 16
1058392b74bSAdrian Hunterimport re
1068392b74bSAdrian Hunterimport os
107031c2a00SAdrian Hunterfrom PySide.QtCore import *
108031c2a00SAdrian Hunterfrom PySide.QtGui import *
109031c2a00SAdrian Hunterfrom PySide.QtSql import *
110031c2a00SAdrian Hunterfrom decimal import *
1118392b74bSAdrian Hunterfrom ctypes import *
1128392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
113031c2a00SAdrian Hunter
114*beda0e72STony Jones# xrange is range in Python3
115*beda0e72STony Jonestry:
116*beda0e72STony Jones	xrange
117*beda0e72STony Jonesexcept NameError:
118*beda0e72STony Jones	xrange = range
119*beda0e72STony Jones
120*beda0e72STony Jonesdef printerr(*args, **keyword_args):
121*beda0e72STony Jones	print(*args, file=sys.stderr, **keyword_args)
122*beda0e72STony Jones
123031c2a00SAdrian Hunter# Data formatting helpers
124031c2a00SAdrian Hunter
12576099f98SAdrian Hunterdef tohex(ip):
12676099f98SAdrian Hunter	if ip < 0:
12776099f98SAdrian Hunter		ip += 1 << 64
12876099f98SAdrian Hunter	return "%x" % ip
12976099f98SAdrian Hunter
13076099f98SAdrian Hunterdef offstr(offset):
13176099f98SAdrian Hunter	if offset:
13276099f98SAdrian Hunter		return "+0x%x" % offset
13376099f98SAdrian Hunter	return ""
13476099f98SAdrian Hunter
135031c2a00SAdrian Hunterdef dsoname(name):
136031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
137031c2a00SAdrian Hunter		return "[kernel]"
138031c2a00SAdrian Hunter	return name
139031c2a00SAdrian Hunter
140210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
141210cf1f9SAdrian Hunter	pos = s.find(sub)
142210cf1f9SAdrian Hunter	if pos < 0:
143210cf1f9SAdrian Hunter		return pos
144210cf1f9SAdrian Hunter	if n <= 1:
145210cf1f9SAdrian Hunter		return offs + pos
146210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
147210cf1f9SAdrian Hunter
148031c2a00SAdrian Hunter# Percent to one decimal place
149031c2a00SAdrian Hunter
150031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
151031c2a00SAdrian Hunter	if not d:
152031c2a00SAdrian Hunter		return "0.0"
153031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
154031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
155031c2a00SAdrian Hunter
156031c2a00SAdrian Hunter# Helper for queries that must not fail
157031c2a00SAdrian Hunter
158031c2a00SAdrian Hunterdef QueryExec(query, stmt):
159031c2a00SAdrian Hunter	ret = query.exec_(stmt)
160031c2a00SAdrian Hunter	if not ret:
161031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
162031c2a00SAdrian Hunter
163ebd70c7dSAdrian Hunter# Background thread
164ebd70c7dSAdrian Hunter
165ebd70c7dSAdrian Hunterclass Thread(QThread):
166ebd70c7dSAdrian Hunter
167ebd70c7dSAdrian Hunter	done = Signal(object)
168ebd70c7dSAdrian Hunter
169ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
170ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
171ebd70c7dSAdrian Hunter		self.task = task
172ebd70c7dSAdrian Hunter		self.param = param
173ebd70c7dSAdrian Hunter
174ebd70c7dSAdrian Hunter	def run(self):
175ebd70c7dSAdrian Hunter		while True:
176ebd70c7dSAdrian Hunter			if self.param is None:
177ebd70c7dSAdrian Hunter				done, result = self.task()
178ebd70c7dSAdrian Hunter			else:
179ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
180ebd70c7dSAdrian Hunter			self.done.emit(result)
181ebd70c7dSAdrian Hunter			if done:
182ebd70c7dSAdrian Hunter				break
183ebd70c7dSAdrian Hunter
184031c2a00SAdrian Hunter# Tree data model
185031c2a00SAdrian Hunter
186031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
187031c2a00SAdrian Hunter
188a448ba23SAdrian Hunter	def __init__(self, glb, parent=None):
189031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
190a448ba23SAdrian Hunter		self.glb = glb
191a448ba23SAdrian Hunter		self.root = self.GetRoot()
192031c2a00SAdrian Hunter		self.last_row_read = 0
193031c2a00SAdrian Hunter
194031c2a00SAdrian Hunter	def Item(self, parent):
195031c2a00SAdrian Hunter		if parent.isValid():
196031c2a00SAdrian Hunter			return parent.internalPointer()
197031c2a00SAdrian Hunter		else:
198031c2a00SAdrian Hunter			return self.root
199031c2a00SAdrian Hunter
200031c2a00SAdrian Hunter	def rowCount(self, parent):
201031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
202031c2a00SAdrian Hunter		if result < 0:
203031c2a00SAdrian Hunter			result = 0
204031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
205031c2a00SAdrian Hunter		return result
206031c2a00SAdrian Hunter
207031c2a00SAdrian Hunter	def hasChildren(self, parent):
208031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
209031c2a00SAdrian Hunter
210031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
211031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
212031c2a00SAdrian Hunter			return self.columnAlignment(section)
213031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
214031c2a00SAdrian Hunter			return None
215031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
216031c2a00SAdrian Hunter			return None
217031c2a00SAdrian Hunter		return self.columnHeader(section)
218031c2a00SAdrian Hunter
219031c2a00SAdrian Hunter	def parent(self, child):
220031c2a00SAdrian Hunter		child_item = child.internalPointer()
221031c2a00SAdrian Hunter		if child_item is self.root:
222031c2a00SAdrian Hunter			return QModelIndex()
223031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
224031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
225031c2a00SAdrian Hunter
226031c2a00SAdrian Hunter	def index(self, row, column, parent):
227031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
228031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
229031c2a00SAdrian Hunter
230031c2a00SAdrian Hunter	def DisplayData(self, item, index):
231031c2a00SAdrian Hunter		return item.getData(index.column())
232031c2a00SAdrian Hunter
2338392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2348392b74bSAdrian Hunter		if row > self.last_row_read:
2358392b74bSAdrian Hunter			self.last_row_read = row
2368392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2378392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2388392b74bSAdrian Hunter
2398392b74bSAdrian Hunter	def columnAlignment(self, column):
2408392b74bSAdrian Hunter		return Qt.AlignLeft
2418392b74bSAdrian Hunter
2428392b74bSAdrian Hunter	def columnFont(self, column):
2438392b74bSAdrian Hunter		return None
2448392b74bSAdrian Hunter
2458392b74bSAdrian Hunter	def data(self, index, role):
2468392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2478392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2488392b74bSAdrian Hunter		if role == Qt.FontRole:
2498392b74bSAdrian Hunter			return self.columnFont(index.column())
2508392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2518392b74bSAdrian Hunter			return None
2528392b74bSAdrian Hunter		item = index.internalPointer()
2538392b74bSAdrian Hunter		return self.DisplayData(item, index)
2548392b74bSAdrian Hunter
2558392b74bSAdrian Hunter# Table data model
2568392b74bSAdrian Hunter
2578392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2588392b74bSAdrian Hunter
2598392b74bSAdrian Hunter	def __init__(self, parent=None):
2608392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2618392b74bSAdrian Hunter		self.child_count = 0
2628392b74bSAdrian Hunter		self.child_items = []
2638392b74bSAdrian Hunter		self.last_row_read = 0
2648392b74bSAdrian Hunter
2658392b74bSAdrian Hunter	def Item(self, parent):
2668392b74bSAdrian Hunter		if parent.isValid():
2678392b74bSAdrian Hunter			return parent.internalPointer()
2688392b74bSAdrian Hunter		else:
2698392b74bSAdrian Hunter			return self
2708392b74bSAdrian Hunter
2718392b74bSAdrian Hunter	def rowCount(self, parent):
2728392b74bSAdrian Hunter		return self.child_count
2738392b74bSAdrian Hunter
2748392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2758392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2768392b74bSAdrian Hunter			return self.columnAlignment(section)
2778392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2788392b74bSAdrian Hunter			return None
2798392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2808392b74bSAdrian Hunter			return None
2818392b74bSAdrian Hunter		return self.columnHeader(section)
2828392b74bSAdrian Hunter
2838392b74bSAdrian Hunter	def index(self, row, column, parent):
2848392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2858392b74bSAdrian Hunter
2868392b74bSAdrian Hunter	def DisplayData(self, item, index):
2878392b74bSAdrian Hunter		return item.getData(index.column())
2888392b74bSAdrian Hunter
2898392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2908392b74bSAdrian Hunter		if row > self.last_row_read:
2918392b74bSAdrian Hunter			self.last_row_read = row
2928392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2938392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2948392b74bSAdrian Hunter
295031c2a00SAdrian Hunter	def columnAlignment(self, column):
296031c2a00SAdrian Hunter		return Qt.AlignLeft
297031c2a00SAdrian Hunter
298031c2a00SAdrian Hunter	def columnFont(self, column):
299031c2a00SAdrian Hunter		return None
300031c2a00SAdrian Hunter
301031c2a00SAdrian Hunter	def data(self, index, role):
302031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
303031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
304031c2a00SAdrian Hunter		if role == Qt.FontRole:
305031c2a00SAdrian Hunter			return self.columnFont(index.column())
306031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
307031c2a00SAdrian Hunter			return None
308031c2a00SAdrian Hunter		item = index.internalPointer()
309031c2a00SAdrian Hunter		return self.DisplayData(item, index)
310031c2a00SAdrian Hunter
3111beb5c7bSAdrian Hunter# Model cache
3121beb5c7bSAdrian Hunter
3131beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
3141beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
3151beb5c7bSAdrian Hunter
3161beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
3171beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3181beb5c7bSAdrian Hunter	try:
3191beb5c7bSAdrian Hunter		model = model_cache[model_name]
3201beb5c7bSAdrian Hunter	except:
3211beb5c7bSAdrian Hunter		model = None
3221beb5c7bSAdrian Hunter	if model is None:
3231beb5c7bSAdrian Hunter		model = create_fn()
3241beb5c7bSAdrian Hunter		model_cache[model_name] = model
3251beb5c7bSAdrian Hunter	model_cache_lock.release()
3261beb5c7bSAdrian Hunter	return model
3271beb5c7bSAdrian Hunter
328ebd70c7dSAdrian Hunter# Find bar
329ebd70c7dSAdrian Hunter
330ebd70c7dSAdrian Hunterclass FindBar():
331ebd70c7dSAdrian Hunter
332ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
333ebd70c7dSAdrian Hunter		self.finder = finder
334ebd70c7dSAdrian Hunter		self.context = []
335ebd70c7dSAdrian Hunter		self.last_value = None
336ebd70c7dSAdrian Hunter		self.last_pattern = None
337ebd70c7dSAdrian Hunter
338ebd70c7dSAdrian Hunter		label = QLabel("Find:")
339ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
340ebd70c7dSAdrian Hunter
341ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
342ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
343ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
344ebd70c7dSAdrian Hunter
345ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
346ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
347ebd70c7dSAdrian Hunter		self.progress.hide()
348ebd70c7dSAdrian Hunter
349ebd70c7dSAdrian Hunter		if is_reg_expr:
350ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
351ebd70c7dSAdrian Hunter		else:
352ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
353ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
354ebd70c7dSAdrian Hunter
355ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
356ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
357ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
358ebd70c7dSAdrian Hunter
359ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
360ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
361ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
362ebd70c7dSAdrian Hunter
363ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
364ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
365ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
366ebd70c7dSAdrian Hunter
367ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
368ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
371ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
372ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
373ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
374ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
375ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
376ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
377ebd70c7dSAdrian Hunter
378ebd70c7dSAdrian Hunter		self.bar = QWidget()
379ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
380ebd70c7dSAdrian Hunter		self.bar.hide()
381ebd70c7dSAdrian Hunter
382ebd70c7dSAdrian Hunter	def Widget(self):
383ebd70c7dSAdrian Hunter		return self.bar
384ebd70c7dSAdrian Hunter
385ebd70c7dSAdrian Hunter	def Activate(self):
386ebd70c7dSAdrian Hunter		self.bar.show()
387ebd70c7dSAdrian Hunter		self.textbox.setFocus()
388ebd70c7dSAdrian Hunter
389ebd70c7dSAdrian Hunter	def Deactivate(self):
390ebd70c7dSAdrian Hunter		self.bar.hide()
391ebd70c7dSAdrian Hunter
392ebd70c7dSAdrian Hunter	def Busy(self):
393ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
394ebd70c7dSAdrian Hunter		self.pattern.hide()
395ebd70c7dSAdrian Hunter		self.next_button.hide()
396ebd70c7dSAdrian Hunter		self.prev_button.hide()
397ebd70c7dSAdrian Hunter		self.progress.show()
398ebd70c7dSAdrian Hunter
399ebd70c7dSAdrian Hunter	def Idle(self):
400ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
401ebd70c7dSAdrian Hunter		self.progress.hide()
402ebd70c7dSAdrian Hunter		self.pattern.show()
403ebd70c7dSAdrian Hunter		self.next_button.show()
404ebd70c7dSAdrian Hunter		self.prev_button.show()
405ebd70c7dSAdrian Hunter
406ebd70c7dSAdrian Hunter	def Find(self, direction):
407ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
408ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
409ebd70c7dSAdrian Hunter		self.last_value = value
410ebd70c7dSAdrian Hunter		self.last_pattern = pattern
411ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
412ebd70c7dSAdrian Hunter
413ebd70c7dSAdrian Hunter	def ValueChanged(self):
414ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
415ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
416ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
417ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
418ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
419ebd70c7dSAdrian Hunter		if data == None:
420ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
421ebd70c7dSAdrian Hunter		else:
422ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
423ebd70c7dSAdrian Hunter		self.Find(0)
424ebd70c7dSAdrian Hunter
425ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
426ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
427ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
428ebd70c7dSAdrian Hunter		if value != self.last_value:
429ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
430ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
431ebd70c7dSAdrian Hunter			if index < 0:
432ebd70c7dSAdrian Hunter				index = self.textbox.count()
433ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
434ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
435ebd70c7dSAdrian Hunter				return
436ebd70c7dSAdrian Hunter			else:
437ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
438ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
439ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
440ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
441ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
442ebd70c7dSAdrian Hunter		self.Find(direction)
443ebd70c7dSAdrian Hunter
444ebd70c7dSAdrian Hunter	def NotFound(self):
445ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
446ebd70c7dSAdrian Hunter
447031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
448031c2a00SAdrian Hunter
449031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
450031c2a00SAdrian Hunter
451031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
452031c2a00SAdrian Hunter		self.glb = glb
453031c2a00SAdrian Hunter		self.row = row
454031c2a00SAdrian Hunter		self.parent_item = parent_item
455031c2a00SAdrian Hunter		self.query_done = False;
456031c2a00SAdrian Hunter		self.child_count = 0
457031c2a00SAdrian Hunter		self.child_items = []
458031c2a00SAdrian Hunter
459031c2a00SAdrian Hunter	def getChildItem(self, row):
460031c2a00SAdrian Hunter		return self.child_items[row]
461031c2a00SAdrian Hunter
462031c2a00SAdrian Hunter	def getParentItem(self):
463031c2a00SAdrian Hunter		return self.parent_item
464031c2a00SAdrian Hunter
465031c2a00SAdrian Hunter	def getRow(self):
466031c2a00SAdrian Hunter		return self.row
467031c2a00SAdrian Hunter
468031c2a00SAdrian Hunter	def childCount(self):
469031c2a00SAdrian Hunter		if not self.query_done:
470031c2a00SAdrian Hunter			self.Select()
471031c2a00SAdrian Hunter			if not self.child_count:
472031c2a00SAdrian Hunter				return -1
473031c2a00SAdrian Hunter		return self.child_count
474031c2a00SAdrian Hunter
475031c2a00SAdrian Hunter	def hasChildren(self):
476031c2a00SAdrian Hunter		if not self.query_done:
477031c2a00SAdrian Hunter			return True
478031c2a00SAdrian Hunter		return self.child_count > 0
479031c2a00SAdrian Hunter
480031c2a00SAdrian Hunter	def getData(self, column):
481031c2a00SAdrian Hunter		return self.data[column]
482031c2a00SAdrian Hunter
483031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
484031c2a00SAdrian Hunter
485031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
486031c2a00SAdrian Hunter
487031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
488031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
489031c2a00SAdrian Hunter		self.comm_id = comm_id
490031c2a00SAdrian Hunter		self.thread_id = thread_id
491031c2a00SAdrian Hunter		self.call_path_id = call_path_id
492031c2a00SAdrian Hunter		self.branch_count = branch_count
493031c2a00SAdrian Hunter		self.time = time
494031c2a00SAdrian Hunter
495031c2a00SAdrian Hunter	def Select(self):
496031c2a00SAdrian Hunter		self.query_done = True;
497031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
498031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
499031c2a00SAdrian Hunter					" FROM calls"
500031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
501031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
502031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
503031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
504031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
505031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
506031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
507031c2a00SAdrian Hunter					" ORDER BY call_path_id")
508031c2a00SAdrian Hunter		while query.next():
509031c2a00SAdrian Hunter			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)
510031c2a00SAdrian Hunter			self.child_items.append(child_item)
511031c2a00SAdrian Hunter			self.child_count += 1
512031c2a00SAdrian Hunter
513031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
514031c2a00SAdrian Hunter
515031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
516031c2a00SAdrian Hunter
517031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
518031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
519031c2a00SAdrian Hunter		dso = dsoname(dso)
520031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
521031c2a00SAdrian Hunter		self.dbid = call_path_id
522031c2a00SAdrian Hunter
523031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
524031c2a00SAdrian Hunter
525031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
526031c2a00SAdrian Hunter
527031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
528031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
529031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
530031c2a00SAdrian Hunter		self.dbid = thread_id
531031c2a00SAdrian Hunter
532031c2a00SAdrian Hunter	def Select(self):
533031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
534031c2a00SAdrian Hunter		for child_item in self.child_items:
535031c2a00SAdrian Hunter			self.time += child_item.time
536031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
537031c2a00SAdrian Hunter		for child_item in self.child_items:
538031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
539031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
540031c2a00SAdrian Hunter
541031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
542031c2a00SAdrian Hunter
543031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
544031c2a00SAdrian Hunter
545031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
546031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
547031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
548031c2a00SAdrian Hunter		self.dbid = comm_id
549031c2a00SAdrian Hunter
550031c2a00SAdrian Hunter	def Select(self):
551031c2a00SAdrian Hunter		self.query_done = True;
552031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
553031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
554031c2a00SAdrian Hunter					" FROM comm_threads"
555031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
556031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
557031c2a00SAdrian Hunter		while query.next():
558031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
559031c2a00SAdrian Hunter			self.child_items.append(child_item)
560031c2a00SAdrian Hunter			self.child_count += 1
561031c2a00SAdrian Hunter
562031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
563031c2a00SAdrian Hunter
564031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
565031c2a00SAdrian Hunter
566031c2a00SAdrian Hunter	def __init__(self, glb):
567031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
568031c2a00SAdrian Hunter		self.dbid = 0
569031c2a00SAdrian Hunter		self.query_done = True;
570031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
571031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
572031c2a00SAdrian Hunter		while query.next():
573031c2a00SAdrian Hunter			if not query.value(0):
574031c2a00SAdrian Hunter				continue
575031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
576031c2a00SAdrian Hunter			self.child_items.append(child_item)
577031c2a00SAdrian Hunter			self.child_count += 1
578031c2a00SAdrian Hunter
579254c0d82SAdrian Hunter# Context-sensitive call graph data model base
580031c2a00SAdrian Hunter
581254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
582031c2a00SAdrian Hunter
583031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
584254c0d82SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, parent)
585031c2a00SAdrian Hunter
586ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
587ebd70c7dSAdrian Hunter		if pattern:
588ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
589ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
590ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
591ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
592ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
593ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
594ebd70c7dSAdrian Hunter				# Escape % and _
595ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
596ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
597ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
598ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
599ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
600ebd70c7dSAdrian Hunter			else:
601ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
602ebd70c7dSAdrian Hunter		else:
603ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
604254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
605ebd70c7dSAdrian Hunter
606ebd70c7dSAdrian Hunter	def Found(self, query, found):
607ebd70c7dSAdrian Hunter		if found:
608ebd70c7dSAdrian Hunter			return self.FindPath(query)
609ebd70c7dSAdrian Hunter		return []
610ebd70c7dSAdrian Hunter
611ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
612ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
613ebd70c7dSAdrian Hunter			found = query.first()
614ebd70c7dSAdrian Hunter		else:
615ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
616ebd70c7dSAdrian Hunter			found = query.next()
617ebd70c7dSAdrian Hunter		return self.Found(query, found)
618ebd70c7dSAdrian Hunter
619ebd70c7dSAdrian Hunter	def FindNext(self, query):
620ebd70c7dSAdrian Hunter		found = query.next()
621ebd70c7dSAdrian Hunter		if not found:
622ebd70c7dSAdrian Hunter			found = query.first()
623ebd70c7dSAdrian Hunter		return self.Found(query, found)
624ebd70c7dSAdrian Hunter
625ebd70c7dSAdrian Hunter	def FindPrev(self, query):
626ebd70c7dSAdrian Hunter		found = query.previous()
627ebd70c7dSAdrian Hunter		if not found:
628ebd70c7dSAdrian Hunter			found = query.last()
629ebd70c7dSAdrian Hunter		return self.Found(query, found)
630ebd70c7dSAdrian Hunter
631ebd70c7dSAdrian Hunter	def FindThread(self, c):
632ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
633ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
634ebd70c7dSAdrian Hunter		elif c.direction > 0:
635ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
636ebd70c7dSAdrian Hunter		else:
637ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
638ebd70c7dSAdrian Hunter		return (True, ids)
639ebd70c7dSAdrian Hunter
640ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
641ebd70c7dSAdrian Hunter		class Context():
642ebd70c7dSAdrian Hunter			def __init__(self, *x):
643ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
644ebd70c7dSAdrian Hunter			def Update(self, *x):
645ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
646ebd70c7dSAdrian Hunter		if len(context):
647ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
648ebd70c7dSAdrian Hunter		else:
649ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
650ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
651ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
652ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
653ebd70c7dSAdrian Hunter		thread.start()
654ebd70c7dSAdrian Hunter
655ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
656ebd70c7dSAdrian Hunter		callback(ids)
657ebd70c7dSAdrian Hunter
658254c0d82SAdrian Hunter# Context-sensitive call graph data model
659254c0d82SAdrian Hunter
660254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
661254c0d82SAdrian Hunter
662254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
663254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
664254c0d82SAdrian Hunter
665254c0d82SAdrian Hunter	def GetRoot(self):
666254c0d82SAdrian Hunter		return CallGraphRootItem(self.glb)
667254c0d82SAdrian Hunter
668254c0d82SAdrian Hunter	def columnCount(self, parent=None):
669254c0d82SAdrian Hunter		return 7
670254c0d82SAdrian Hunter
671254c0d82SAdrian Hunter	def columnHeader(self, column):
672254c0d82SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
673254c0d82SAdrian Hunter		return headers[column]
674254c0d82SAdrian Hunter
675254c0d82SAdrian Hunter	def columnAlignment(self, column):
676254c0d82SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
677254c0d82SAdrian Hunter		return alignment[column]
678254c0d82SAdrian Hunter
679254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
680254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
681254c0d82SAdrian Hunter						" FROM calls"
682254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
683254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
684254c0d82SAdrian Hunter						" WHERE symbols.name" + match +
685254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
686254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
687254c0d82SAdrian Hunter
688254c0d82SAdrian Hunter	def FindPath(self, query):
689254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
690254c0d82SAdrian Hunter		# to open the tree at the right place.
691254c0d82SAdrian Hunter		ids = []
692254c0d82SAdrian Hunter		parent_id = query.value(0)
693254c0d82SAdrian Hunter		while parent_id:
694254c0d82SAdrian Hunter			ids.insert(0, parent_id)
695254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
696254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
697254c0d82SAdrian Hunter					" FROM call_paths"
698254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
699254c0d82SAdrian Hunter			if not q2.next():
700254c0d82SAdrian Hunter				break
701254c0d82SAdrian Hunter			parent_id = q2.value(0)
702254c0d82SAdrian Hunter		# The call path root is not used
703254c0d82SAdrian Hunter		if ids[0] == 1:
704254c0d82SAdrian Hunter			del ids[0]
705254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
706254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
707254c0d82SAdrian Hunter		return ids
708254c0d82SAdrian Hunter
709ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
710ae8b887cSAdrian Hunter
711ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
712ae8b887cSAdrian Hunter
713ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item):
714ae8b887cSAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
715ae8b887cSAdrian Hunter		self.comm_id = comm_id
716ae8b887cSAdrian Hunter		self.thread_id = thread_id
717ae8b887cSAdrian Hunter		self.calls_id = calls_id
718ae8b887cSAdrian Hunter		self.branch_count = branch_count
719ae8b887cSAdrian Hunter		self.time = time
720ae8b887cSAdrian Hunter
721ae8b887cSAdrian Hunter	def Select(self):
722ae8b887cSAdrian Hunter		self.query_done = True;
723ae8b887cSAdrian Hunter		if self.calls_id == 0:
724ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
725ae8b887cSAdrian Hunter		else:
726ae8b887cSAdrian Hunter			comm_thread = ""
727ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
728ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count"
729ae8b887cSAdrian Hunter					" FROM calls"
730ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
731ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
732ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
733ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
734ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
735ae8b887cSAdrian Hunter		while query.next():
736ae8b887cSAdrian Hunter			child_item = CallTreeLevelThreeItem(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)
737ae8b887cSAdrian Hunter			self.child_items.append(child_item)
738ae8b887cSAdrian Hunter			self.child_count += 1
739ae8b887cSAdrian Hunter
740ae8b887cSAdrian Hunter# Call tree data model level three item
741ae8b887cSAdrian Hunter
742ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
743ae8b887cSAdrian Hunter
744ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item):
745ae8b887cSAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item)
746ae8b887cSAdrian Hunter		dso = dsoname(dso)
747ae8b887cSAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
748ae8b887cSAdrian Hunter		self.dbid = calls_id
749ae8b887cSAdrian Hunter
750ae8b887cSAdrian Hunter# Call tree data model level two item
751ae8b887cSAdrian Hunter
752ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
753ae8b887cSAdrian Hunter
754ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
755ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item)
756ae8b887cSAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
757ae8b887cSAdrian Hunter		self.dbid = thread_id
758ae8b887cSAdrian Hunter
759ae8b887cSAdrian Hunter	def Select(self):
760ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
761ae8b887cSAdrian Hunter		for child_item in self.child_items:
762ae8b887cSAdrian Hunter			self.time += child_item.time
763ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
764ae8b887cSAdrian Hunter		for child_item in self.child_items:
765ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
766ae8b887cSAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
767ae8b887cSAdrian Hunter
768ae8b887cSAdrian Hunter# Call tree data model level one item
769ae8b887cSAdrian Hunter
770ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
771ae8b887cSAdrian Hunter
772ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
773ae8b887cSAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item)
774ae8b887cSAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
775ae8b887cSAdrian Hunter		self.dbid = comm_id
776ae8b887cSAdrian Hunter
777ae8b887cSAdrian Hunter	def Select(self):
778ae8b887cSAdrian Hunter		self.query_done = True;
779ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
780ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
781ae8b887cSAdrian Hunter					" FROM comm_threads"
782ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
783ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
784ae8b887cSAdrian Hunter		while query.next():
785ae8b887cSAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
786ae8b887cSAdrian Hunter			self.child_items.append(child_item)
787ae8b887cSAdrian Hunter			self.child_count += 1
788ae8b887cSAdrian Hunter
789ae8b887cSAdrian Hunter# Call tree data model root item
790ae8b887cSAdrian Hunter
791ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
792ae8b887cSAdrian Hunter
793ae8b887cSAdrian Hunter	def __init__(self, glb):
794ae8b887cSAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, 0, None)
795ae8b887cSAdrian Hunter		self.dbid = 0
796ae8b887cSAdrian Hunter		self.query_done = True;
797ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
798ae8b887cSAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
799ae8b887cSAdrian Hunter		while query.next():
800ae8b887cSAdrian Hunter			if not query.value(0):
801ae8b887cSAdrian Hunter				continue
802ae8b887cSAdrian Hunter			child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
803ae8b887cSAdrian Hunter			self.child_items.append(child_item)
804ae8b887cSAdrian Hunter			self.child_count += 1
805ae8b887cSAdrian Hunter
806ae8b887cSAdrian Hunter# Call Tree data model
807ae8b887cSAdrian Hunter
808ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
809ae8b887cSAdrian Hunter
810ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
811ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
812ae8b887cSAdrian Hunter
813ae8b887cSAdrian Hunter	def GetRoot(self):
814ae8b887cSAdrian Hunter		return CallTreeRootItem(self.glb)
815ae8b887cSAdrian Hunter
816ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
817ae8b887cSAdrian Hunter		return 7
818ae8b887cSAdrian Hunter
819ae8b887cSAdrian Hunter	def columnHeader(self, column):
820ae8b887cSAdrian Hunter		headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
821ae8b887cSAdrian Hunter		return headers[column]
822ae8b887cSAdrian Hunter
823ae8b887cSAdrian Hunter	def columnAlignment(self, column):
824ae8b887cSAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
825ae8b887cSAdrian Hunter		return alignment[column]
826ae8b887cSAdrian Hunter
827ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
828ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
829ae8b887cSAdrian Hunter						" FROM calls"
830ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
831ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
832ae8b887cSAdrian Hunter						" WHERE symbols.name" + match +
833ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
834ae8b887cSAdrian Hunter
835ae8b887cSAdrian Hunter	def FindPath(self, query):
836ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
837ae8b887cSAdrian Hunter		# to open the tree at the right place.
838ae8b887cSAdrian Hunter		ids = []
839ae8b887cSAdrian Hunter		parent_id = query.value(0)
840ae8b887cSAdrian Hunter		while parent_id:
841ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
842ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
843ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
844ae8b887cSAdrian Hunter					" FROM calls"
845ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
846ae8b887cSAdrian Hunter			if not q2.next():
847ae8b887cSAdrian Hunter				break
848ae8b887cSAdrian Hunter			parent_id = q2.value(0)
849ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
850ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
851ae8b887cSAdrian Hunter		return ids
852ae8b887cSAdrian Hunter
853ebd70c7dSAdrian Hunter# Vertical widget layout
854ebd70c7dSAdrian Hunter
855ebd70c7dSAdrian Hunterclass VBox():
856ebd70c7dSAdrian Hunter
857ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
858ebd70c7dSAdrian Hunter		self.vbox = QWidget()
859ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
860ebd70c7dSAdrian Hunter
861ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
862ebd70c7dSAdrian Hunter
863ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
864ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
865ebd70c7dSAdrian Hunter		if w3:
866ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
867ebd70c7dSAdrian Hunter
868ebd70c7dSAdrian Hunter	def Widget(self):
869ebd70c7dSAdrian Hunter		return self.vbox
870ebd70c7dSAdrian Hunter
871a731cc4cSAdrian Hunter# Tree window base
8721beb5c7bSAdrian Hunter
873a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
8741beb5c7bSAdrian Hunter
875a731cc4cSAdrian Hunter	def __init__(self, parent=None):
876a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
8771beb5c7bSAdrian Hunter
878a731cc4cSAdrian Hunter		self.model = None
879a731cc4cSAdrian Hunter		self.view = None
880a731cc4cSAdrian Hunter		self.find_bar = None
8811beb5c7bSAdrian Hunter
882ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
883ebd70c7dSAdrian Hunter		if not len(ids):
884ebd70c7dSAdrian Hunter			return False
885ebd70c7dSAdrian Hunter		parent = QModelIndex()
886ebd70c7dSAdrian Hunter		for dbid in ids:
887ebd70c7dSAdrian Hunter			found = False
888ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
889ebd70c7dSAdrian Hunter			for row in xrange(n):
890ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
891ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
892ebd70c7dSAdrian Hunter					found = True
893ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
894ebd70c7dSAdrian Hunter					parent = child
895ebd70c7dSAdrian Hunter					break
896ebd70c7dSAdrian Hunter			if not found:
897ebd70c7dSAdrian Hunter				break
898ebd70c7dSAdrian Hunter		return found
899ebd70c7dSAdrian Hunter
900ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
901ebd70c7dSAdrian Hunter		self.view.setFocus()
902ebd70c7dSAdrian Hunter		self.find_bar.Busy()
903ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
904ebd70c7dSAdrian Hunter
905ebd70c7dSAdrian Hunter	def FindDone(self, ids):
906ebd70c7dSAdrian Hunter		found = True
907ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
908ebd70c7dSAdrian Hunter			found = False
909ebd70c7dSAdrian Hunter		self.find_bar.Idle()
910ebd70c7dSAdrian Hunter		if not found:
911ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
912ebd70c7dSAdrian Hunter
913a731cc4cSAdrian Hunter
914a731cc4cSAdrian Hunter# Context-sensitive call graph window
915a731cc4cSAdrian Hunter
916a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
917a731cc4cSAdrian Hunter
918a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
919a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
920a731cc4cSAdrian Hunter
921a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
922a731cc4cSAdrian Hunter
923a731cc4cSAdrian Hunter		self.view = QTreeView()
924a731cc4cSAdrian Hunter		self.view.setModel(self.model)
925a731cc4cSAdrian Hunter
926a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
927a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
928a731cc4cSAdrian Hunter
929a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
930a731cc4cSAdrian Hunter
931a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
932a731cc4cSAdrian Hunter
933a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
934a731cc4cSAdrian Hunter
935a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
936a731cc4cSAdrian Hunter
937ae8b887cSAdrian Hunter# Call tree window
938ae8b887cSAdrian Hunter
939ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
940ae8b887cSAdrian Hunter
941ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
942ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
943ae8b887cSAdrian Hunter
944ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
945ae8b887cSAdrian Hunter
946ae8b887cSAdrian Hunter		self.view = QTreeView()
947ae8b887cSAdrian Hunter		self.view.setModel(self.model)
948ae8b887cSAdrian Hunter
949ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
950ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
951ae8b887cSAdrian Hunter
952ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
953ae8b887cSAdrian Hunter
954ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
955ae8b887cSAdrian Hunter
956ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
957ae8b887cSAdrian Hunter
958ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
959ae8b887cSAdrian Hunter
9608392b74bSAdrian Hunter# Child data item  finder
9618392b74bSAdrian Hunter
9628392b74bSAdrian Hunterclass ChildDataItemFinder():
9638392b74bSAdrian Hunter
9648392b74bSAdrian Hunter	def __init__(self, root):
9658392b74bSAdrian Hunter		self.root = root
9668392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
9678392b74bSAdrian Hunter		self.rows = []
9688392b74bSAdrian Hunter		self.pos = 0
9698392b74bSAdrian Hunter
9708392b74bSAdrian Hunter	def FindSelect(self):
9718392b74bSAdrian Hunter		self.rows = []
9728392b74bSAdrian Hunter		if self.pattern:
9738392b74bSAdrian Hunter			pattern = re.compile(self.value)
9748392b74bSAdrian Hunter			for child in self.root.child_items:
9758392b74bSAdrian Hunter				for column_data in child.data:
9768392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
9778392b74bSAdrian Hunter						self.rows.append(child.row)
9788392b74bSAdrian Hunter						break
9798392b74bSAdrian Hunter		else:
9808392b74bSAdrian Hunter			for child in self.root.child_items:
9818392b74bSAdrian Hunter				for column_data in child.data:
9828392b74bSAdrian Hunter					if self.value in str(column_data):
9838392b74bSAdrian Hunter						self.rows.append(child.row)
9848392b74bSAdrian Hunter						break
9858392b74bSAdrian Hunter
9868392b74bSAdrian Hunter	def FindValue(self):
9878392b74bSAdrian Hunter		self.pos = 0
9888392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
9898392b74bSAdrian Hunter			self.FindSelect()
9908392b74bSAdrian Hunter		if not len(self.rows):
9918392b74bSAdrian Hunter			return -1
9928392b74bSAdrian Hunter		return self.rows[self.pos]
9938392b74bSAdrian Hunter
9948392b74bSAdrian Hunter	def FindThread(self):
9958392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
9968392b74bSAdrian Hunter			row = self.FindValue()
9978392b74bSAdrian Hunter		elif len(self.rows):
9988392b74bSAdrian Hunter			if self.direction > 0:
9998392b74bSAdrian Hunter				self.pos += 1
10008392b74bSAdrian Hunter				if self.pos >= len(self.rows):
10018392b74bSAdrian Hunter					self.pos = 0
10028392b74bSAdrian Hunter			else:
10038392b74bSAdrian Hunter				self.pos -= 1
10048392b74bSAdrian Hunter				if self.pos < 0:
10058392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
10068392b74bSAdrian Hunter			row = self.rows[self.pos]
10078392b74bSAdrian Hunter		else:
10088392b74bSAdrian Hunter			row = -1
10098392b74bSAdrian Hunter		return (True, row)
10108392b74bSAdrian Hunter
10118392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
10128392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
10138392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
10148392b74bSAdrian Hunter		thread = Thread(self.FindThread)
10158392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
10168392b74bSAdrian Hunter		thread.start()
10178392b74bSAdrian Hunter
10188392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
10198392b74bSAdrian Hunter		callback(row)
10208392b74bSAdrian Hunter
10218392b74bSAdrian Hunter# Number of database records to fetch in one go
10228392b74bSAdrian Hunter
10238392b74bSAdrian Hunterglb_chunk_sz = 10000
10248392b74bSAdrian Hunter
10258392b74bSAdrian Hunter# Background process for SQL data fetcher
10268392b74bSAdrian Hunter
10278392b74bSAdrian Hunterclass SQLFetcherProcess():
10288392b74bSAdrian Hunter
10298392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
10308392b74bSAdrian Hunter		# Need a unique connection name
10318392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
10328392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
10338392b74bSAdrian Hunter		self.sql = sql
10348392b74bSAdrian Hunter		self.buffer = buffer
10358392b74bSAdrian Hunter		self.head = head
10368392b74bSAdrian Hunter		self.tail = tail
10378392b74bSAdrian Hunter		self.fetch_count = fetch_count
10388392b74bSAdrian Hunter		self.fetching_done = fetching_done
10398392b74bSAdrian Hunter		self.process_target = process_target
10408392b74bSAdrian Hunter		self.wait_event = wait_event
10418392b74bSAdrian Hunter		self.fetched_event = fetched_event
10428392b74bSAdrian Hunter		self.prep = prep
10438392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
10448392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
10458392b74bSAdrian Hunter		self.last_id = -1
10468392b74bSAdrian Hunter		self.fetched = 0
10478392b74bSAdrian Hunter		self.more = True
10488392b74bSAdrian Hunter		self.local_head = self.head.value
10498392b74bSAdrian Hunter		self.local_tail = self.tail.value
10508392b74bSAdrian Hunter
10518392b74bSAdrian Hunter	def Select(self):
10528392b74bSAdrian Hunter		if self.query_limit:
10538392b74bSAdrian Hunter			if self.query_limit == 1:
10548392b74bSAdrian Hunter				return
10558392b74bSAdrian Hunter			self.query_limit -= 1
10568392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
10578392b74bSAdrian Hunter		QueryExec(self.query, stmt)
10588392b74bSAdrian Hunter
10598392b74bSAdrian Hunter	def Next(self):
10608392b74bSAdrian Hunter		if not self.query.next():
10618392b74bSAdrian Hunter			self.Select()
10628392b74bSAdrian Hunter			if not self.query.next():
10638392b74bSAdrian Hunter				return None
10648392b74bSAdrian Hunter		self.last_id = self.query.value(0)
10658392b74bSAdrian Hunter		return self.prep(self.query)
10668392b74bSAdrian Hunter
10678392b74bSAdrian Hunter	def WaitForTarget(self):
10688392b74bSAdrian Hunter		while True:
10698392b74bSAdrian Hunter			self.wait_event.clear()
10708392b74bSAdrian Hunter			target = self.process_target.value
10718392b74bSAdrian Hunter			if target > self.fetched or target < 0:
10728392b74bSAdrian Hunter				break
10738392b74bSAdrian Hunter			self.wait_event.wait()
10748392b74bSAdrian Hunter		return target
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter	def HasSpace(self, sz):
10778392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
10788392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
10798392b74bSAdrian Hunter			if space > sz:
10808392b74bSAdrian Hunter				return True
10818392b74bSAdrian Hunter			if space >= glb_nsz:
10828392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
1083*beda0e72STony Jones				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
10848392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
10858392b74bSAdrian Hunter			self.local_head = 0
10868392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
10878392b74bSAdrian Hunter			return True
10888392b74bSAdrian Hunter		return False
10898392b74bSAdrian Hunter
10908392b74bSAdrian Hunter	def WaitForSpace(self, sz):
10918392b74bSAdrian Hunter		if self.HasSpace(sz):
10928392b74bSAdrian Hunter			return
10938392b74bSAdrian Hunter		while True:
10948392b74bSAdrian Hunter			self.wait_event.clear()
10958392b74bSAdrian Hunter			self.local_tail = self.tail.value
10968392b74bSAdrian Hunter			if self.HasSpace(sz):
10978392b74bSAdrian Hunter				return
10988392b74bSAdrian Hunter			self.wait_event.wait()
10998392b74bSAdrian Hunter
11008392b74bSAdrian Hunter	def AddToBuffer(self, obj):
1101*beda0e72STony Jones		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
11028392b74bSAdrian Hunter		n = len(d)
1103*beda0e72STony Jones		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
11048392b74bSAdrian Hunter		sz = n + glb_nsz
11058392b74bSAdrian Hunter		self.WaitForSpace(sz)
11068392b74bSAdrian Hunter		pos = self.local_head
11078392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
11088392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
11098392b74bSAdrian Hunter		self.local_head += sz
11108392b74bSAdrian Hunter
11118392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
11128392b74bSAdrian Hunter		fetched = 0
11138392b74bSAdrian Hunter		while batch_size > fetched:
11148392b74bSAdrian Hunter			obj = self.Next()
11158392b74bSAdrian Hunter			if obj is None:
11168392b74bSAdrian Hunter				self.more = False
11178392b74bSAdrian Hunter				break
11188392b74bSAdrian Hunter			self.AddToBuffer(obj)
11198392b74bSAdrian Hunter			fetched += 1
11208392b74bSAdrian Hunter		if fetched:
11218392b74bSAdrian Hunter			self.fetched += fetched
11228392b74bSAdrian Hunter			with self.fetch_count.get_lock():
11238392b74bSAdrian Hunter				self.fetch_count.value += fetched
11248392b74bSAdrian Hunter			self.head.value = self.local_head
11258392b74bSAdrian Hunter			self.fetched_event.set()
11268392b74bSAdrian Hunter
11278392b74bSAdrian Hunter	def Run(self):
11288392b74bSAdrian Hunter		while self.more:
11298392b74bSAdrian Hunter			target = self.WaitForTarget()
11308392b74bSAdrian Hunter			if target < 0:
11318392b74bSAdrian Hunter				break
11328392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
11338392b74bSAdrian Hunter			self.FetchBatch(batch_size)
11348392b74bSAdrian Hunter		self.fetching_done.value = True
11358392b74bSAdrian Hunter		self.fetched_event.set()
11368392b74bSAdrian Hunter
11378392b74bSAdrian Hunterdef SQLFetcherFn(*x):
11388392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
11398392b74bSAdrian Hunter	process.Run()
11408392b74bSAdrian Hunter
11418392b74bSAdrian Hunter# SQL data fetcher
11428392b74bSAdrian Hunter
11438392b74bSAdrian Hunterclass SQLFetcher(QObject):
11448392b74bSAdrian Hunter
11458392b74bSAdrian Hunter	done = Signal(object)
11468392b74bSAdrian Hunter
11478392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
11488392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
11498392b74bSAdrian Hunter		self.process_data = process_data
11508392b74bSAdrian Hunter		self.more = True
11518392b74bSAdrian Hunter		self.target = 0
11528392b74bSAdrian Hunter		self.last_target = 0
11538392b74bSAdrian Hunter		self.fetched = 0
11548392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
11558392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
11568392b74bSAdrian Hunter		self.head = Value(c_longlong)
11578392b74bSAdrian Hunter		self.tail = Value(c_longlong)
11588392b74bSAdrian Hunter		self.local_tail = 0
11598392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
11608392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
11618392b74bSAdrian Hunter		self.last_count = 0
11628392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
11638392b74bSAdrian Hunter		self.wait_event = Event()
11648392b74bSAdrian Hunter		self.fetched_event = Event()
11658392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
11668392b74bSAdrian Hunter		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))
11678392b74bSAdrian Hunter		self.process.start()
11688392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
11698392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
11708392b74bSAdrian Hunter		self.thread.start()
11718392b74bSAdrian Hunter
11728392b74bSAdrian Hunter	def Shutdown(self):
11738392b74bSAdrian Hunter		# Tell the thread and process to exit
11748392b74bSAdrian Hunter		self.process_target.value = -1
11758392b74bSAdrian Hunter		self.wait_event.set()
11768392b74bSAdrian Hunter		self.more = False
11778392b74bSAdrian Hunter		self.fetching_done.value = True
11788392b74bSAdrian Hunter		self.fetched_event.set()
11798392b74bSAdrian Hunter
11808392b74bSAdrian Hunter	def Thread(self):
11818392b74bSAdrian Hunter		if not self.more:
11828392b74bSAdrian Hunter			return True, 0
11838392b74bSAdrian Hunter		while True:
11848392b74bSAdrian Hunter			self.fetched_event.clear()
11858392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
11868392b74bSAdrian Hunter			if fetch_count != self.last_count:
11878392b74bSAdrian Hunter				break
11888392b74bSAdrian Hunter			if self.fetching_done.value:
11898392b74bSAdrian Hunter				self.more = False
11908392b74bSAdrian Hunter				return True, 0
11918392b74bSAdrian Hunter			self.fetched_event.wait()
11928392b74bSAdrian Hunter		count = fetch_count - self.last_count
11938392b74bSAdrian Hunter		self.last_count = fetch_count
11948392b74bSAdrian Hunter		self.fetched += count
11958392b74bSAdrian Hunter		return False, count
11968392b74bSAdrian Hunter
11978392b74bSAdrian Hunter	def Fetch(self, nr):
11988392b74bSAdrian Hunter		if not self.more:
11998392b74bSAdrian Hunter			# -1 inidcates there are no more
12008392b74bSAdrian Hunter			return -1
12018392b74bSAdrian Hunter		result = self.fetched
12028392b74bSAdrian Hunter		extra = result + nr - self.target
12038392b74bSAdrian Hunter		if extra > 0:
12048392b74bSAdrian Hunter			self.target += extra
12058392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
12068392b74bSAdrian Hunter			if self.process_target.value >= 0:
12078392b74bSAdrian Hunter				self.process_target.value = self.target
12088392b74bSAdrian Hunter			self.wait_event.set()
12098392b74bSAdrian Hunter		return result
12108392b74bSAdrian Hunter
12118392b74bSAdrian Hunter	def RemoveFromBuffer(self):
12128392b74bSAdrian Hunter		pos = self.local_tail
12138392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
12148392b74bSAdrian Hunter			pos = 0
1215*beda0e72STony Jones		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
12168392b74bSAdrian Hunter		if n == 0:
12178392b74bSAdrian Hunter			pos = 0
1218*beda0e72STony Jones			n = pickle.loads(self.buffer[0 : glb_nsz])
12198392b74bSAdrian Hunter		pos += glb_nsz
1220*beda0e72STony Jones		obj = pickle.loads(self.buffer[pos : pos + n])
12218392b74bSAdrian Hunter		self.local_tail = pos + n
12228392b74bSAdrian Hunter		return obj
12238392b74bSAdrian Hunter
12248392b74bSAdrian Hunter	def ProcessData(self, count):
12258392b74bSAdrian Hunter		for i in xrange(count):
12268392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
12278392b74bSAdrian Hunter			self.process_data(obj)
12288392b74bSAdrian Hunter		self.tail.value = self.local_tail
12298392b74bSAdrian Hunter		self.wait_event.set()
12308392b74bSAdrian Hunter		self.done.emit(count)
12318392b74bSAdrian Hunter
12328392b74bSAdrian Hunter# Fetch more records bar
12338392b74bSAdrian Hunter
12348392b74bSAdrian Hunterclass FetchMoreRecordsBar():
12358392b74bSAdrian Hunter
12368392b74bSAdrian Hunter	def __init__(self, model, parent):
12378392b74bSAdrian Hunter		self.model = model
12388392b74bSAdrian Hunter
12398392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
12408392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12418392b74bSAdrian Hunter
12428392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
12438392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
12448392b74bSAdrian Hunter		self.fetch_count.setValue(10)
12458392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12468392b74bSAdrian Hunter
12478392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
12488392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12498392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
12508392b74bSAdrian Hunter
12518392b74bSAdrian Hunter		self.progress = QProgressBar()
12528392b74bSAdrian Hunter		self.progress.setRange(0, 100)
12538392b74bSAdrian Hunter		self.progress.hide()
12548392b74bSAdrian Hunter
12558392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
12568392b74bSAdrian Hunter		self.done_label.hide()
12578392b74bSAdrian Hunter
12588392b74bSAdrian Hunter		self.spacer = QLabel("")
12598392b74bSAdrian Hunter
12608392b74bSAdrian Hunter		self.close_button = QToolButton()
12618392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
12628392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
12638392b74bSAdrian Hunter
12648392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
12658392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
12668392b74bSAdrian Hunter
12678392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
12688392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
12698392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
12708392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
12718392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
12728392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
12738392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
12748392b74bSAdrian Hunter
12758392b74bSAdrian Hunter		self.bar = QWidget()
12768392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
12778392b74bSAdrian Hunter		self.bar.show()
12788392b74bSAdrian Hunter
12798392b74bSAdrian Hunter		self.in_progress = False
12808392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
12818392b74bSAdrian Hunter
12828392b74bSAdrian Hunter		self.done = False
12838392b74bSAdrian Hunter
12848392b74bSAdrian Hunter		if not model.HasMoreRecords():
12858392b74bSAdrian Hunter			self.Done()
12868392b74bSAdrian Hunter
12878392b74bSAdrian Hunter	def Widget(self):
12888392b74bSAdrian Hunter		return self.bar
12898392b74bSAdrian Hunter
12908392b74bSAdrian Hunter	def Activate(self):
12918392b74bSAdrian Hunter		self.bar.show()
12928392b74bSAdrian Hunter		self.fetch.setFocus()
12938392b74bSAdrian Hunter
12948392b74bSAdrian Hunter	def Deactivate(self):
12958392b74bSAdrian Hunter		self.bar.hide()
12968392b74bSAdrian Hunter
12978392b74bSAdrian Hunter	def Enable(self, enable):
12988392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
12998392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
13008392b74bSAdrian Hunter
13018392b74bSAdrian Hunter	def Busy(self):
13028392b74bSAdrian Hunter		self.Enable(False)
13038392b74bSAdrian Hunter		self.fetch.hide()
13048392b74bSAdrian Hunter		self.spacer.hide()
13058392b74bSAdrian Hunter		self.progress.show()
13068392b74bSAdrian Hunter
13078392b74bSAdrian Hunter	def Idle(self):
13088392b74bSAdrian Hunter		self.in_progress = False
13098392b74bSAdrian Hunter		self.Enable(True)
13108392b74bSAdrian Hunter		self.progress.hide()
13118392b74bSAdrian Hunter		self.fetch.show()
13128392b74bSAdrian Hunter		self.spacer.show()
13138392b74bSAdrian Hunter
13148392b74bSAdrian Hunter	def Target(self):
13158392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
13168392b74bSAdrian Hunter
13178392b74bSAdrian Hunter	def Done(self):
13188392b74bSAdrian Hunter		self.done = True
13198392b74bSAdrian Hunter		self.Idle()
13208392b74bSAdrian Hunter		self.label.hide()
13218392b74bSAdrian Hunter		self.fetch_count.hide()
13228392b74bSAdrian Hunter		self.fetch.hide()
13238392b74bSAdrian Hunter		self.spacer.hide()
13248392b74bSAdrian Hunter		self.done_label.show()
13258392b74bSAdrian Hunter
13268392b74bSAdrian Hunter	def Progress(self, count):
13278392b74bSAdrian Hunter		if self.in_progress:
13288392b74bSAdrian Hunter			if count:
13298392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
13308392b74bSAdrian Hunter				if percent >= 100:
13318392b74bSAdrian Hunter					self.Idle()
13328392b74bSAdrian Hunter				else:
13338392b74bSAdrian Hunter					self.progress.setValue(percent)
13348392b74bSAdrian Hunter		if not count:
13358392b74bSAdrian Hunter			# Count value of zero means no more records
13368392b74bSAdrian Hunter			self.Done()
13378392b74bSAdrian Hunter
13388392b74bSAdrian Hunter	def FetchMoreRecords(self):
13398392b74bSAdrian Hunter		if self.done:
13408392b74bSAdrian Hunter			return
13418392b74bSAdrian Hunter		self.progress.setValue(0)
13428392b74bSAdrian Hunter		self.Busy()
13438392b74bSAdrian Hunter		self.in_progress = True
13448392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
13458392b74bSAdrian Hunter
134676099f98SAdrian Hunter# Brance data model level two item
134776099f98SAdrian Hunter
134876099f98SAdrian Hunterclass BranchLevelTwoItem():
134976099f98SAdrian Hunter
135076099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
135176099f98SAdrian Hunter		self.row = row
135276099f98SAdrian Hunter		self.parent_item = parent_item
135376099f98SAdrian Hunter		self.data = [""] * 8
135476099f98SAdrian Hunter		self.data[7] = text
135576099f98SAdrian Hunter		self.level = 2
135676099f98SAdrian Hunter
135776099f98SAdrian Hunter	def getParentItem(self):
135876099f98SAdrian Hunter		return self.parent_item
135976099f98SAdrian Hunter
136076099f98SAdrian Hunter	def getRow(self):
136176099f98SAdrian Hunter		return self.row
136276099f98SAdrian Hunter
136376099f98SAdrian Hunter	def childCount(self):
136476099f98SAdrian Hunter		return 0
136576099f98SAdrian Hunter
136676099f98SAdrian Hunter	def hasChildren(self):
136776099f98SAdrian Hunter		return False
136876099f98SAdrian Hunter
136976099f98SAdrian Hunter	def getData(self, column):
137076099f98SAdrian Hunter		return self.data[column]
137176099f98SAdrian Hunter
137276099f98SAdrian Hunter# Brance data model level one item
137376099f98SAdrian Hunter
137476099f98SAdrian Hunterclass BranchLevelOneItem():
137576099f98SAdrian Hunter
137676099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
137776099f98SAdrian Hunter		self.glb = glb
137876099f98SAdrian Hunter		self.row = row
137976099f98SAdrian Hunter		self.parent_item = parent_item
138076099f98SAdrian Hunter		self.child_count = 0
138176099f98SAdrian Hunter		self.child_items = []
138276099f98SAdrian Hunter		self.data = data[1:]
138376099f98SAdrian Hunter		self.dbid = data[0]
138476099f98SAdrian Hunter		self.level = 1
138576099f98SAdrian Hunter		self.query_done = False
138676099f98SAdrian Hunter
138776099f98SAdrian Hunter	def getChildItem(self, row):
138876099f98SAdrian Hunter		return self.child_items[row]
138976099f98SAdrian Hunter
139076099f98SAdrian Hunter	def getParentItem(self):
139176099f98SAdrian Hunter		return self.parent_item
139276099f98SAdrian Hunter
139376099f98SAdrian Hunter	def getRow(self):
139476099f98SAdrian Hunter		return self.row
139576099f98SAdrian Hunter
139676099f98SAdrian Hunter	def Select(self):
139776099f98SAdrian Hunter		self.query_done = True
139876099f98SAdrian Hunter
139976099f98SAdrian Hunter		if not self.glb.have_disassembler:
140076099f98SAdrian Hunter			return
140176099f98SAdrian Hunter
140276099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
140376099f98SAdrian Hunter
140476099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
140576099f98SAdrian Hunter				  " FROM samples"
140676099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
140776099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
140876099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
140976099f98SAdrian Hunter		if not query.next():
141076099f98SAdrian Hunter			return
141176099f98SAdrian Hunter		cpu = query.value(0)
141276099f98SAdrian Hunter		dso = query.value(1)
141376099f98SAdrian Hunter		sym = query.value(2)
141476099f98SAdrian Hunter		if dso == 0 or sym == 0:
141576099f98SAdrian Hunter			return
141676099f98SAdrian Hunter		off = query.value(3)
141776099f98SAdrian Hunter		short_name = query.value(4)
141876099f98SAdrian Hunter		long_name = query.value(5)
141976099f98SAdrian Hunter		build_id = query.value(6)
142076099f98SAdrian Hunter		sym_start = query.value(7)
142176099f98SAdrian Hunter		ip = query.value(8)
142276099f98SAdrian Hunter
142376099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
142476099f98SAdrian Hunter				  " FROM samples"
142576099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
142676099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
142776099f98SAdrian Hunter				  " ORDER BY samples.id"
142876099f98SAdrian Hunter				  " LIMIT 1")
142976099f98SAdrian Hunter		if not query.next():
143076099f98SAdrian Hunter			return
143176099f98SAdrian Hunter		if query.value(0) != dso:
143276099f98SAdrian Hunter			# Cannot disassemble from one dso to another
143376099f98SAdrian Hunter			return
143476099f98SAdrian Hunter		bsym = query.value(1)
143576099f98SAdrian Hunter		boff = query.value(2)
143676099f98SAdrian Hunter		bsym_start = query.value(3)
143776099f98SAdrian Hunter		if bsym == 0:
143876099f98SAdrian Hunter			return
143976099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
144076099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
144176099f98SAdrian Hunter			return
144276099f98SAdrian Hunter
144376099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
144476099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
144576099f98SAdrian Hunter		if not f:
144676099f98SAdrian Hunter			return
144776099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
144876099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
144976099f98SAdrian Hunter
145076099f98SAdrian Hunter		buf_sz = tot + 16
145176099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
145276099f98SAdrian Hunter		f.seek(sym_start + off)
145376099f98SAdrian Hunter		buf.value = f.read(buf_sz)
145476099f98SAdrian Hunter		buf_ptr = addressof(buf)
145576099f98SAdrian Hunter		i = 0
145676099f98SAdrian Hunter		while tot > 0:
145776099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
145876099f98SAdrian Hunter			if cnt:
145976099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
146076099f98SAdrian Hunter				for k in xrange(cnt):
146176099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
146276099f98SAdrian Hunter					i += 1
146376099f98SAdrian Hunter				while k < 15:
146476099f98SAdrian Hunter					byte_str += "   "
146576099f98SAdrian Hunter					k += 1
146676099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
146776099f98SAdrian Hunter				self.child_count += 1
146876099f98SAdrian Hunter			else:
146976099f98SAdrian Hunter				return
147076099f98SAdrian Hunter			buf_ptr += cnt
147176099f98SAdrian Hunter			tot -= cnt
147276099f98SAdrian Hunter			buf_sz -= cnt
147376099f98SAdrian Hunter			ip += cnt
147476099f98SAdrian Hunter
147576099f98SAdrian Hunter	def childCount(self):
147676099f98SAdrian Hunter		if not self.query_done:
147776099f98SAdrian Hunter			self.Select()
147876099f98SAdrian Hunter			if not self.child_count:
147976099f98SAdrian Hunter				return -1
148076099f98SAdrian Hunter		return self.child_count
148176099f98SAdrian Hunter
148276099f98SAdrian Hunter	def hasChildren(self):
148376099f98SAdrian Hunter		if not self.query_done:
148476099f98SAdrian Hunter			return True
148576099f98SAdrian Hunter		return self.child_count > 0
148676099f98SAdrian Hunter
148776099f98SAdrian Hunter	def getData(self, column):
148876099f98SAdrian Hunter		return self.data[column]
148976099f98SAdrian Hunter
149076099f98SAdrian Hunter# Brance data model root item
149176099f98SAdrian Hunter
149276099f98SAdrian Hunterclass BranchRootItem():
149376099f98SAdrian Hunter
149476099f98SAdrian Hunter	def __init__(self):
149576099f98SAdrian Hunter		self.child_count = 0
149676099f98SAdrian Hunter		self.child_items = []
149776099f98SAdrian Hunter		self.level = 0
149876099f98SAdrian Hunter
149976099f98SAdrian Hunter	def getChildItem(self, row):
150076099f98SAdrian Hunter		return self.child_items[row]
150176099f98SAdrian Hunter
150276099f98SAdrian Hunter	def getParentItem(self):
150376099f98SAdrian Hunter		return None
150476099f98SAdrian Hunter
150576099f98SAdrian Hunter	def getRow(self):
150676099f98SAdrian Hunter		return 0
150776099f98SAdrian Hunter
150876099f98SAdrian Hunter	def childCount(self):
150976099f98SAdrian Hunter		return self.child_count
151076099f98SAdrian Hunter
151176099f98SAdrian Hunter	def hasChildren(self):
151276099f98SAdrian Hunter		return self.child_count > 0
151376099f98SAdrian Hunter
151476099f98SAdrian Hunter	def getData(self, column):
151576099f98SAdrian Hunter		return ""
151676099f98SAdrian Hunter
151776099f98SAdrian Hunter# Branch data preparation
151876099f98SAdrian Hunter
151976099f98SAdrian Hunterdef BranchDataPrep(query):
152076099f98SAdrian Hunter	data = []
152176099f98SAdrian Hunter	for i in xrange(0, 8):
152276099f98SAdrian Hunter		data.append(query.value(i))
152376099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
152476099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
152576099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
152676099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
152776099f98SAdrian Hunter	return data
152876099f98SAdrian Hunter
152976099f98SAdrian Hunter# Branch data model
153076099f98SAdrian Hunter
153176099f98SAdrian Hunterclass BranchModel(TreeModel):
153276099f98SAdrian Hunter
153376099f98SAdrian Hunter	progress = Signal(object)
153476099f98SAdrian Hunter
153576099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
1536a448ba23SAdrian Hunter		super(BranchModel, self).__init__(glb, parent)
153776099f98SAdrian Hunter		self.event_id = event_id
153876099f98SAdrian Hunter		self.more = True
153976099f98SAdrian Hunter		self.populated = 0
154076099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
154176099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
154276099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
154376099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
154476099f98SAdrian Hunter			" FROM samples"
154576099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
154676099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
154776099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
154876099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
154976099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
155076099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
155176099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
155276099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
155376099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
155476099f98SAdrian Hunter			" ORDER BY samples.id"
155576099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
155676099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
155776099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
155876099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
155976099f98SAdrian Hunter
1560a448ba23SAdrian Hunter	def GetRoot(self):
1561a448ba23SAdrian Hunter		return BranchRootItem()
1562a448ba23SAdrian Hunter
156376099f98SAdrian Hunter	def columnCount(self, parent=None):
156476099f98SAdrian Hunter		return 8
156576099f98SAdrian Hunter
156676099f98SAdrian Hunter	def columnHeader(self, column):
156776099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
156876099f98SAdrian Hunter
156976099f98SAdrian Hunter	def columnFont(self, column):
157076099f98SAdrian Hunter		if column != 7:
157176099f98SAdrian Hunter			return None
157276099f98SAdrian Hunter		return QFont("Monospace")
157376099f98SAdrian Hunter
157476099f98SAdrian Hunter	def DisplayData(self, item, index):
157576099f98SAdrian Hunter		if item.level == 1:
157676099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
157776099f98SAdrian Hunter		return item.getData(index.column())
157876099f98SAdrian Hunter
157976099f98SAdrian Hunter	def AddSample(self, data):
158076099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
158176099f98SAdrian Hunter		self.root.child_items.append(child)
158276099f98SAdrian Hunter		self.populated += 1
158376099f98SAdrian Hunter
158476099f98SAdrian Hunter	def Update(self, fetched):
158576099f98SAdrian Hunter		if not fetched:
158676099f98SAdrian Hunter			self.more = False
158776099f98SAdrian Hunter			self.progress.emit(0)
158876099f98SAdrian Hunter		child_count = self.root.child_count
158976099f98SAdrian Hunter		count = self.populated - child_count
159076099f98SAdrian Hunter		if count > 0:
159176099f98SAdrian Hunter			parent = QModelIndex()
159276099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
159376099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
159476099f98SAdrian Hunter			self.root.child_count += count
159576099f98SAdrian Hunter			self.endInsertRows()
159676099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
159776099f98SAdrian Hunter
159876099f98SAdrian Hunter	def FetchMoreRecords(self, count):
159976099f98SAdrian Hunter		current = self.root.child_count
160076099f98SAdrian Hunter		if self.more:
160176099f98SAdrian Hunter			self.fetcher.Fetch(count)
160276099f98SAdrian Hunter		else:
160376099f98SAdrian Hunter			self.progress.emit(0)
160476099f98SAdrian Hunter		return current
160576099f98SAdrian Hunter
160676099f98SAdrian Hunter	def HasMoreRecords(self):
160776099f98SAdrian Hunter		return self.more
160876099f98SAdrian Hunter
16090bf0947aSAdrian Hunter# Report Variables
16100bf0947aSAdrian Hunter
16110bf0947aSAdrian Hunterclass ReportVars():
16120bf0947aSAdrian Hunter
1613cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1614947cc38dSAdrian Hunter		self.name = name
16150bf0947aSAdrian Hunter		self.where_clause = where_clause
1616cd358012SAdrian Hunter		self.limit = limit
16170bf0947aSAdrian Hunter
16180bf0947aSAdrian Hunter	def UniqueId(self):
1619cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
16200bf0947aSAdrian Hunter
162176099f98SAdrian Hunter# Branch window
162276099f98SAdrian Hunter
162376099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
162476099f98SAdrian Hunter
1625947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
162676099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
162776099f98SAdrian Hunter
16280bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
162976099f98SAdrian Hunter
16300bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
163176099f98SAdrian Hunter
163276099f98SAdrian Hunter		self.view = QTreeView()
163376099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
163476099f98SAdrian Hunter		self.view.setModel(self.model)
163576099f98SAdrian Hunter
163676099f98SAdrian Hunter		self.ResizeColumnsToContents()
163776099f98SAdrian Hunter
163876099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
163976099f98SAdrian Hunter
164076099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
164176099f98SAdrian Hunter
164276099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
164376099f98SAdrian Hunter
164476099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
164576099f98SAdrian Hunter
164676099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
164776099f98SAdrian Hunter
1648947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
164976099f98SAdrian Hunter
165076099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
165176099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
165276099f98SAdrian Hunter		# so implement a crude alternative
165376099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
165476099f98SAdrian Hunter		font = self.view.font()
165576099f98SAdrian Hunter		metrics = QFontMetrics(font)
165676099f98SAdrian Hunter		max = 0
165776099f98SAdrian Hunter		for row in xrange(n):
165876099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
165976099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
166076099f98SAdrian Hunter			max = len if len > max else max
166176099f98SAdrian Hunter		val = self.model.columnHeader(column)
166276099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
166376099f98SAdrian Hunter		max = len if len > max else max
166476099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
166576099f98SAdrian Hunter
166676099f98SAdrian Hunter	def ResizeColumnsToContents(self):
166776099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
166876099f98SAdrian Hunter		if n < 1:
166976099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
167076099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
167176099f98SAdrian Hunter			return
167276099f98SAdrian Hunter		columns = self.model.columnCount()
167376099f98SAdrian Hunter		for i in xrange(columns):
167476099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
167576099f98SAdrian Hunter
167676099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
167776099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
167876099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
167976099f98SAdrian Hunter		self.ResizeColumnsToContents()
168076099f98SAdrian Hunter
168176099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
168276099f98SAdrian Hunter		self.view.setFocus()
168376099f98SAdrian Hunter		self.find_bar.Busy()
168476099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
168576099f98SAdrian Hunter
168676099f98SAdrian Hunter	def FindDone(self, row):
168776099f98SAdrian Hunter		self.find_bar.Idle()
168876099f98SAdrian Hunter		if row >= 0:
168976099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
169076099f98SAdrian Hunter		else:
169176099f98SAdrian Hunter			self.find_bar.NotFound()
169276099f98SAdrian Hunter
16931c3ca1b3SAdrian Hunter# Line edit data item
16941c3ca1b3SAdrian Hunter
16951c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
16961c3ca1b3SAdrian Hunter
1697cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
16981c3ca1b3SAdrian Hunter		self.glb = glb
16991c3ca1b3SAdrian Hunter		self.label = label
17001c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
17011c3ca1b3SAdrian Hunter		self.parent = parent
17021c3ca1b3SAdrian Hunter		self.id = id
17031c3ca1b3SAdrian Hunter
1704cd358012SAdrian Hunter		self.value = default
17051c3ca1b3SAdrian Hunter
1706cd358012SAdrian Hunter		self.widget = QLineEdit(default)
17071c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
17081c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
17091c3ca1b3SAdrian Hunter		self.red = False
17101c3ca1b3SAdrian Hunter		self.error = ""
17111c3ca1b3SAdrian Hunter		self.validated = True
17121c3ca1b3SAdrian Hunter
17131c3ca1b3SAdrian Hunter		if placeholder_text:
17141c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
17151c3ca1b3SAdrian Hunter
17161c3ca1b3SAdrian Hunter	def TurnTextRed(self):
17171c3ca1b3SAdrian Hunter		if not self.red:
17181c3ca1b3SAdrian Hunter			palette = QPalette()
17191c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
17201c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
17211c3ca1b3SAdrian Hunter			self.red = True
17221c3ca1b3SAdrian Hunter
17231c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
17241c3ca1b3SAdrian Hunter		if self.red:
17251c3ca1b3SAdrian Hunter			palette = QPalette()
17261c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
17271c3ca1b3SAdrian Hunter			self.red = False
17281c3ca1b3SAdrian Hunter
17291c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
17301c3ca1b3SAdrian Hunter		self.value = ""
17311c3ca1b3SAdrian Hunter		self.TurnTextRed()
17321c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
17331c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
17341c3ca1b3SAdrian Hunter
17351c3ca1b3SAdrian Hunter	def Invalidate(self):
17361c3ca1b3SAdrian Hunter		self.validated = False
17371c3ca1b3SAdrian Hunter
17381c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
17391c3ca1b3SAdrian Hunter		self.value = input_string.strip()
17401c3ca1b3SAdrian Hunter
17411c3ca1b3SAdrian Hunter	def Validate(self):
17421c3ca1b3SAdrian Hunter		self.validated = True
17431c3ca1b3SAdrian Hunter		self.error = ""
17441c3ca1b3SAdrian Hunter		self.TurnTextNormal()
17451c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
17461c3ca1b3SAdrian Hunter		input_string = self.widget.text()
17471c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
17481c3ca1b3SAdrian Hunter			self.value = ""
17491c3ca1b3SAdrian Hunter			return
17501c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
17511c3ca1b3SAdrian Hunter
17521c3ca1b3SAdrian Hunter	def IsValid(self):
17531c3ca1b3SAdrian Hunter		if not self.validated:
17541c3ca1b3SAdrian Hunter			self.Validate()
17551c3ca1b3SAdrian Hunter		if len(self.error):
17561c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
17571c3ca1b3SAdrian Hunter			return False
17581c3ca1b3SAdrian Hunter		return True
17591c3ca1b3SAdrian Hunter
17601c3ca1b3SAdrian Hunter	def IsNumber(self, value):
17611c3ca1b3SAdrian Hunter		try:
17621c3ca1b3SAdrian Hunter			x = int(value)
17631c3ca1b3SAdrian Hunter		except:
17641c3ca1b3SAdrian Hunter			x = 0
17651c3ca1b3SAdrian Hunter		return str(x) == value
17661c3ca1b3SAdrian Hunter
17671c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
17681c3ca1b3SAdrian Hunter
17691c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
17701c3ca1b3SAdrian Hunter
17711c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
17721c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
17731c3ca1b3SAdrian Hunter
17741c3ca1b3SAdrian Hunter		self.column_name = column_name
17751c3ca1b3SAdrian Hunter
17761c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
17771c3ca1b3SAdrian Hunter		singles = []
17781c3ca1b3SAdrian Hunter		ranges = []
17791c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
17801c3ca1b3SAdrian Hunter			if "-" in value:
17811c3ca1b3SAdrian Hunter				vrange = value.split("-")
17821c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
17831c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
17841c3ca1b3SAdrian Hunter				ranges.append(vrange)
17851c3ca1b3SAdrian Hunter			else:
17861c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
17871c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
17881c3ca1b3SAdrian Hunter				singles.append(value)
17891c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
17901c3ca1b3SAdrian Hunter		if len(singles):
17911c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
17921c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
17931c3ca1b3SAdrian Hunter
1794cd358012SAdrian Hunter# Positive integer dialog data item
1795cd358012SAdrian Hunter
1796cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
1797cd358012SAdrian Hunter
1798cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1799cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1800cd358012SAdrian Hunter
1801cd358012SAdrian Hunter	def DoValidate(self, input_string):
1802cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
1803cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1804cd358012SAdrian Hunter		value = int(input_string.strip())
1805cd358012SAdrian Hunter		if value <= 0:
1806cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1807cd358012SAdrian Hunter		self.value = str(value)
1808cd358012SAdrian Hunter
18091c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
18101c3ca1b3SAdrian Hunter
18111c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
18121c3ca1b3SAdrian Hunter
18131c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
18141c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
18151c3ca1b3SAdrian Hunter
18161c3ca1b3SAdrian Hunter		self.table_name = table_name
18171c3ca1b3SAdrian Hunter		self.match_column = match_column
18181c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
18191c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
18201c3ca1b3SAdrian Hunter
18211c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
18221c3ca1b3SAdrian Hunter		ids = []
18231c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
18241c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
18251c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
18261c3ca1b3SAdrian Hunter		if ret:
18271c3ca1b3SAdrian Hunter			while query.next():
18281c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
18291c3ca1b3SAdrian Hunter		return ids
18301c3ca1b3SAdrian Hunter
18311c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
18321c3ca1b3SAdrian Hunter		all_ids = []
18331c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
18341c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
18351c3ca1b3SAdrian Hunter			if len(ids):
18361c3ca1b3SAdrian Hunter				all_ids.extend(ids)
18371c3ca1b3SAdrian Hunter			else:
18381c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
18391c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
18401c3ca1b3SAdrian Hunter		if self.column_name2:
18411c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
18421c3ca1b3SAdrian Hunter
18431c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
18441c3ca1b3SAdrian Hunter
18451c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
18461c3ca1b3SAdrian Hunter
18471c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
18481c3ca1b3SAdrian Hunter		self.column_name = column_name
18491c3ca1b3SAdrian Hunter
18501c3ca1b3SAdrian Hunter		self.last_id = 0
18511c3ca1b3SAdrian Hunter		self.first_time = 0
18521c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
18531c3ca1b3SAdrian Hunter
18541c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
18551c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
18561c3ca1b3SAdrian Hunter		if query.next():
18571c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
18581c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
18591c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
18601c3ca1b3SAdrian Hunter		if query.next():
18611c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
18621c3ca1b3SAdrian Hunter		if placeholder_text:
18631c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
18641c3ca1b3SAdrian Hunter
18651c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
18661c3ca1b3SAdrian Hunter
18671c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
18681c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
18691c3ca1b3SAdrian Hunter		if query.next():
18701c3ca1b3SAdrian Hunter			return True, int(query.value(0))
18711c3ca1b3SAdrian Hunter		else:
18721c3ca1b3SAdrian Hunter			return False, 0
18731c3ca1b3SAdrian Hunter
18741c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
18751c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
18761c3ca1b3SAdrian Hunter		while True:
18771c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
18781c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
18791c3ca1b3SAdrian Hunter			if not query.next():
18801c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
18811c3ca1b3SAdrian Hunter				if not ok:
18821c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
18831c3ca1b3SAdrian Hunter					if not ok:
18841c3ca1b3SAdrian Hunter						return str(higher_id)
18851c3ca1b3SAdrian Hunter				next_id = dbid
18861c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
18871c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
18881c3ca1b3SAdrian Hunter			if get_floor:
18891c3ca1b3SAdrian Hunter				if target_time > next_time:
18901c3ca1b3SAdrian Hunter					lower_id = next_id
18911c3ca1b3SAdrian Hunter				else:
18921c3ca1b3SAdrian Hunter					higher_id = next_id
18931c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
18941c3ca1b3SAdrian Hunter					return str(higher_id)
18951c3ca1b3SAdrian Hunter			else:
18961c3ca1b3SAdrian Hunter				if target_time >= next_time:
18971c3ca1b3SAdrian Hunter					lower_id = next_id
18981c3ca1b3SAdrian Hunter				else:
18991c3ca1b3SAdrian Hunter					higher_id = next_id
19001c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
19011c3ca1b3SAdrian Hunter					return str(lower_id)
19021c3ca1b3SAdrian Hunter
19031c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
19041c3ca1b3SAdrian Hunter		mult = 1
19051c3ca1b3SAdrian Hunter		suffix = val[-2:]
19061c3ca1b3SAdrian Hunter		if suffix == "ms":
19071c3ca1b3SAdrian Hunter			mult = 1000000
19081c3ca1b3SAdrian Hunter		elif suffix == "us":
19091c3ca1b3SAdrian Hunter			mult = 1000
19101c3ca1b3SAdrian Hunter		elif suffix == "ns":
19111c3ca1b3SAdrian Hunter			mult = 1
19121c3ca1b3SAdrian Hunter		else:
19131c3ca1b3SAdrian Hunter			return val
19141c3ca1b3SAdrian Hunter		val = val[:-2].strip()
19151c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
19161c3ca1b3SAdrian Hunter			return val
19171c3ca1b3SAdrian Hunter		val = int(val) * mult
19181c3ca1b3SAdrian Hunter		if val >= 0:
19191c3ca1b3SAdrian Hunter			val += self.first_time
19201c3ca1b3SAdrian Hunter		else:
19211c3ca1b3SAdrian Hunter			val += self.last_time
19221c3ca1b3SAdrian Hunter		return str(val)
19231c3ca1b3SAdrian Hunter
19241c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
19251c3ca1b3SAdrian Hunter		if vrange[0] == "":
19261c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
19271c3ca1b3SAdrian Hunter		if vrange[1] == "":
19281c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
19291c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
19301c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
19311c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
19321c3ca1b3SAdrian Hunter			return False
19331c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
19341c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
19351c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
19361c3ca1b3SAdrian Hunter			return False
19371c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
19381c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
19391c3ca1b3SAdrian Hunter		return True
19401c3ca1b3SAdrian Hunter
19411c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
19421c3ca1b3SAdrian Hunter		n = value.count("-")
19431c3ca1b3SAdrian Hunter		if n == 1:
19441c3ca1b3SAdrian Hunter			pass
19451c3ca1b3SAdrian Hunter		elif n == 2:
19461c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
19471c3ca1b3SAdrian Hunter				n = 1
19481c3ca1b3SAdrian Hunter		elif n == 3:
19491c3ca1b3SAdrian Hunter			n = 2
19501c3ca1b3SAdrian Hunter		else:
19511c3ca1b3SAdrian Hunter			return False
19521c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
19531c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
19541c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
19551c3ca1b3SAdrian Hunter			ranges.append(vrange)
19561c3ca1b3SAdrian Hunter			return True
19571c3ca1b3SAdrian Hunter		return False
19581c3ca1b3SAdrian Hunter
19591c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
19601c3ca1b3SAdrian Hunter		ranges = []
19611c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
19621c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
19631c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
19641c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
19651c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
19661c3ca1b3SAdrian Hunter
19670924cd68SAdrian Hunter# Report Dialog Base
1968210cf1f9SAdrian Hunter
19690924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
1970210cf1f9SAdrian Hunter
19710924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
19720924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
1973210cf1f9SAdrian Hunter
1974210cf1f9SAdrian Hunter		self.glb = glb
1975210cf1f9SAdrian Hunter
19760bf0947aSAdrian Hunter		self.report_vars = ReportVars()
1977210cf1f9SAdrian Hunter
19780924cd68SAdrian Hunter		self.setWindowTitle(title)
1979210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
1980210cf1f9SAdrian Hunter
19811c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
1982210cf1f9SAdrian Hunter
19830924cd68SAdrian Hunter		self.partial = partial
19840924cd68SAdrian Hunter
1985210cf1f9SAdrian Hunter		self.grid = QGridLayout()
1986210cf1f9SAdrian Hunter
1987210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
1988210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1989210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
1990210cf1f9SAdrian Hunter
1991210cf1f9SAdrian Hunter		self.status = QLabel()
1992210cf1f9SAdrian Hunter
1993210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
1994210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
1995210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
1996210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1997210cf1f9SAdrian Hunter
1998210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
1999210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
2000210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2001210cf1f9SAdrian Hunter
2002210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
2003210cf1f9SAdrian Hunter		#self.hbox.addStretch()
2004210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
2005210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
2006210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
2007210cf1f9SAdrian Hunter
2008210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
2009210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
2010210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
2011210cf1f9SAdrian Hunter
2012210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
2013210cf1f9SAdrian Hunter
2014210cf1f9SAdrian Hunter	def Ok(self):
20150bf0947aSAdrian Hunter		vars = self.report_vars
20161c3ca1b3SAdrian Hunter		for d in self.data_items:
20171c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
20181c3ca1b3SAdrian Hunter				vars.name = d.value
2019947cc38dSAdrian Hunter		if not vars.name:
2020210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2021210cf1f9SAdrian Hunter			return
2022210cf1f9SAdrian Hunter		for d in self.data_items:
2023210cf1f9SAdrian Hunter			if not d.IsValid():
2024210cf1f9SAdrian Hunter				return
2025210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2026cd358012SAdrian Hunter			if d.id == "LIMIT":
2027cd358012SAdrian Hunter				vars.limit = d.value
2028cd358012SAdrian Hunter			elif len(d.value):
20290bf0947aSAdrian Hunter				if len(vars.where_clause):
20300bf0947aSAdrian Hunter					vars.where_clause += " AND "
20310bf0947aSAdrian Hunter				vars.where_clause += d.value
20320bf0947aSAdrian Hunter		if len(vars.where_clause):
20330924cd68SAdrian Hunter			if self.partial:
20340bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2035210cf1f9SAdrian Hunter			else:
20360bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
2037210cf1f9SAdrian Hunter		self.accept()
2038210cf1f9SAdrian Hunter
2039210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2040210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2041210cf1f9SAdrian Hunter
2042210cf1f9SAdrian Hunter	def ClearMessage(self):
2043210cf1f9SAdrian Hunter		self.status.setText("")
2044210cf1f9SAdrian Hunter
20450924cd68SAdrian Hunter# Selected branch report creation dialog
20460924cd68SAdrian Hunter
20470924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
20480924cd68SAdrian Hunter
20490924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
20500924cd68SAdrian Hunter		title = "Selected Branches"
20511c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
20521c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
20531c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
20541c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
20551c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
20561c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
20571c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p),
20581c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
20591c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
20600924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
20610924cd68SAdrian Hunter
206276099f98SAdrian Hunter# Event list
206376099f98SAdrian Hunter
206476099f98SAdrian Hunterdef GetEventList(db):
206576099f98SAdrian Hunter	events = []
206676099f98SAdrian Hunter	query = QSqlQuery(db)
206776099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
206876099f98SAdrian Hunter	while query.next():
206976099f98SAdrian Hunter		events.append(query.value(0))
207076099f98SAdrian Hunter	return events
207176099f98SAdrian Hunter
2072655cb952SAdrian Hunter# Is a table selectable
2073655cb952SAdrian Hunter
2074ae8b887cSAdrian Hunterdef IsSelectable(db, table, sql = ""):
2075655cb952SAdrian Hunter	query = QSqlQuery(db)
2076655cb952SAdrian Hunter	try:
2077ae8b887cSAdrian Hunter		QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1")
2078655cb952SAdrian Hunter	except:
2079655cb952SAdrian Hunter		return False
2080655cb952SAdrian Hunter	return True
2081655cb952SAdrian Hunter
20828392b74bSAdrian Hunter# SQL data preparation
20838392b74bSAdrian Hunter
20848392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
20858392b74bSAdrian Hunter	data = []
20868392b74bSAdrian Hunter	for i in xrange(count):
20878392b74bSAdrian Hunter		data.append(query.value(i))
20888392b74bSAdrian Hunter	return data
20898392b74bSAdrian Hunter
20908392b74bSAdrian Hunter# SQL table data model item
20918392b74bSAdrian Hunter
20928392b74bSAdrian Hunterclass SQLTableItem():
20938392b74bSAdrian Hunter
20948392b74bSAdrian Hunter	def __init__(self, row, data):
20958392b74bSAdrian Hunter		self.row = row
20968392b74bSAdrian Hunter		self.data = data
20978392b74bSAdrian Hunter
20988392b74bSAdrian Hunter	def getData(self, column):
20998392b74bSAdrian Hunter		return self.data[column]
21008392b74bSAdrian Hunter
21018392b74bSAdrian Hunter# SQL table data model
21028392b74bSAdrian Hunter
21038392b74bSAdrian Hunterclass SQLTableModel(TableModel):
21048392b74bSAdrian Hunter
21058392b74bSAdrian Hunter	progress = Signal(object)
21068392b74bSAdrian Hunter
21078c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
21088392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
21098392b74bSAdrian Hunter		self.glb = glb
21108392b74bSAdrian Hunter		self.more = True
21118392b74bSAdrian Hunter		self.populated = 0
21128c90fef9SAdrian Hunter		self.column_headers = column_headers
21138c90fef9SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample)
21148392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
21158392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
21168392b74bSAdrian Hunter
21178392b74bSAdrian Hunter	def DisplayData(self, item, index):
21188392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
21198392b74bSAdrian Hunter		return item.getData(index.column())
21208392b74bSAdrian Hunter
21218392b74bSAdrian Hunter	def AddSample(self, data):
21228392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
21238392b74bSAdrian Hunter		self.child_items.append(child)
21248392b74bSAdrian Hunter		self.populated += 1
21258392b74bSAdrian Hunter
21268392b74bSAdrian Hunter	def Update(self, fetched):
21278392b74bSAdrian Hunter		if not fetched:
21288392b74bSAdrian Hunter			self.more = False
21298392b74bSAdrian Hunter			self.progress.emit(0)
21308392b74bSAdrian Hunter		child_count = self.child_count
21318392b74bSAdrian Hunter		count = self.populated - child_count
21328392b74bSAdrian Hunter		if count > 0:
21338392b74bSAdrian Hunter			parent = QModelIndex()
21348392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
21358392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
21368392b74bSAdrian Hunter			self.child_count += count
21378392b74bSAdrian Hunter			self.endInsertRows()
21388392b74bSAdrian Hunter			self.progress.emit(self.child_count)
21398392b74bSAdrian Hunter
21408392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
21418392b74bSAdrian Hunter		current = self.child_count
21428392b74bSAdrian Hunter		if self.more:
21438392b74bSAdrian Hunter			self.fetcher.Fetch(count)
21448392b74bSAdrian Hunter		else:
21458392b74bSAdrian Hunter			self.progress.emit(0)
21468392b74bSAdrian Hunter		return current
21478392b74bSAdrian Hunter
21488392b74bSAdrian Hunter	def HasMoreRecords(self):
21498392b74bSAdrian Hunter		return self.more
21508392b74bSAdrian Hunter
21518c90fef9SAdrian Hunter	def columnCount(self, parent=None):
21528c90fef9SAdrian Hunter		return len(self.column_headers)
21538c90fef9SAdrian Hunter
21548c90fef9SAdrian Hunter	def columnHeader(self, column):
21558c90fef9SAdrian Hunter		return self.column_headers[column]
21568c90fef9SAdrian Hunter
21578392b74bSAdrian Hunter# SQL automatic table data model
21588392b74bSAdrian Hunter
21598392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
21608392b74bSAdrian Hunter
21618392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
21628392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
21638392b74bSAdrian Hunter		if table_name == "comm_threads_view":
21648392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
21658392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
21668c90fef9SAdrian Hunter		column_headers = []
21678392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
21688392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
21698392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
21708392b74bSAdrian Hunter			while query.next():
21718c90fef9SAdrian Hunter				column_headers.append(query.value(1))
21728392b74bSAdrian Hunter			if table_name == "sqlite_master":
21738392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21748392b74bSAdrian Hunter		else:
21758392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
21768392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21778392b74bSAdrian Hunter				select_table_name = table_name[19:]
21788392b74bSAdrian Hunter				schema = "information_schema"
21798392b74bSAdrian Hunter			else:
21808392b74bSAdrian Hunter				select_table_name = table_name
21818392b74bSAdrian Hunter				schema = "public"
21828392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
21838392b74bSAdrian Hunter			while query.next():
21848c90fef9SAdrian Hunter				column_headers.append(query.value(0))
21858c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
21868392b74bSAdrian Hunter
21878392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
21888392b74bSAdrian Hunter
21898392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
21908392b74bSAdrian Hunter
21918392b74bSAdrian Hunter	def __init__(self, parent=None):
21928392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
21938392b74bSAdrian Hunter
21948392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
21958392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
21968392b74bSAdrian Hunter		# so implement a crude alternative
21978392b74bSAdrian Hunter		font = self.view.font()
21988392b74bSAdrian Hunter		metrics = QFontMetrics(font)
21998392b74bSAdrian Hunter		max = 0
22008392b74bSAdrian Hunter		for row in xrange(n):
22018392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
22028392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
22038392b74bSAdrian Hunter			max = len if len > max else max
22048392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
22058392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
22068392b74bSAdrian Hunter		max = len if len > max else max
22078392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
22088392b74bSAdrian Hunter
22098392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
22108392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
22118392b74bSAdrian Hunter		if n < 1:
22128392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
22138392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
22148392b74bSAdrian Hunter			return
22158392b74bSAdrian Hunter		columns = self.data_model.columnCount()
22168392b74bSAdrian Hunter		for i in xrange(columns):
22178392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
22188392b74bSAdrian Hunter
22198392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
22208392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
22218392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
22228392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22238392b74bSAdrian Hunter
22248392b74bSAdrian Hunter# Table window
22258392b74bSAdrian Hunter
22268392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
22278392b74bSAdrian Hunter
22288392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
22298392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
22308392b74bSAdrian Hunter
22318392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
22328392b74bSAdrian Hunter
22338392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
22348392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
22358392b74bSAdrian Hunter
22368392b74bSAdrian Hunter		self.view = QTableView()
22378392b74bSAdrian Hunter		self.view.setModel(self.model)
22388392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
22398392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
22408392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
22418392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
22428392b74bSAdrian Hunter
22438392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22448392b74bSAdrian Hunter
22458392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
22468392b74bSAdrian Hunter
22478392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
22488392b74bSAdrian Hunter
22498392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
22508392b74bSAdrian Hunter
22518392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
22528392b74bSAdrian Hunter
22538392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
22548392b74bSAdrian Hunter
22558392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
22568392b74bSAdrian Hunter
22578392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
22588392b74bSAdrian Hunter		self.view.setFocus()
22598392b74bSAdrian Hunter		self.find_bar.Busy()
22608392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
22618392b74bSAdrian Hunter
22628392b74bSAdrian Hunter	def FindDone(self, row):
22638392b74bSAdrian Hunter		self.find_bar.Idle()
22648392b74bSAdrian Hunter		if row >= 0:
226535fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
22668392b74bSAdrian Hunter		else:
22678392b74bSAdrian Hunter			self.find_bar.NotFound()
22688392b74bSAdrian Hunter
22698392b74bSAdrian Hunter# Table list
22708392b74bSAdrian Hunter
22718392b74bSAdrian Hunterdef GetTableList(glb):
22728392b74bSAdrian Hunter	tables = []
22738392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
22748392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22758392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
22768392b74bSAdrian Hunter	else:
22778392b74bSAdrian Hunter		QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name")
22788392b74bSAdrian Hunter	while query.next():
22798392b74bSAdrian Hunter		tables.append(query.value(0))
22808392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22818392b74bSAdrian Hunter		tables.append("sqlite_master")
22828392b74bSAdrian Hunter	else:
22838392b74bSAdrian Hunter		tables.append("information_schema.tables")
22848392b74bSAdrian Hunter		tables.append("information_schema.views")
22858392b74bSAdrian Hunter		tables.append("information_schema.columns")
22868392b74bSAdrian Hunter	return tables
22878392b74bSAdrian Hunter
2288cd358012SAdrian Hunter# Top Calls data model
2289cd358012SAdrian Hunter
2290cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2291cd358012SAdrian Hunter
2292cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2293cd358012SAdrian Hunter		text = ""
2294cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2295cd358012SAdrian Hunter			text = "::text"
2296cd358012SAdrian Hunter		limit = ""
2297cd358012SAdrian Hunter		if len(report_vars.limit):
2298cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2299cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2300cd358012SAdrian Hunter			" CASE"
2301cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2302cd358012SAdrian Hunter			" ELSE short_name"
2303cd358012SAdrian Hunter			" END AS dso,"
2304cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2305cd358012SAdrian Hunter			" CASE"
2306cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2307cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2308cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2309cd358012SAdrian Hunter			" ELSE ''" + text +
2310cd358012SAdrian Hunter			" END AS flags"
2311cd358012SAdrian Hunter			" FROM calls"
2312cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2313cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2314cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2315cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2316cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2317cd358012SAdrian Hunter			report_vars.where_clause +
2318cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2319cd358012SAdrian Hunter			limit
2320cd358012SAdrian Hunter			)
2321cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2322cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2323cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2324cd358012SAdrian Hunter
2325cd358012SAdrian Hunter	def columnAlignment(self, column):
2326cd358012SAdrian Hunter		return self.alignment[column]
2327cd358012SAdrian Hunter
2328cd358012SAdrian Hunter# Top Calls report creation dialog
2329cd358012SAdrian Hunter
2330cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2331cd358012SAdrian Hunter
2332cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2333cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2334cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2335cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2336cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2337cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2338cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2339cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2340cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2341cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2342cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2343cd358012SAdrian Hunter
2344cd358012SAdrian Hunter# Top Calls window
2345cd358012SAdrian Hunter
2346cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2347cd358012SAdrian Hunter
2348cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2349cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2350cd358012SAdrian Hunter
2351cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2352cd358012SAdrian Hunter		self.model = self.data_model
2353cd358012SAdrian Hunter
2354cd358012SAdrian Hunter		self.view = QTableView()
2355cd358012SAdrian Hunter		self.view.setModel(self.model)
2356cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2357cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
2358cd358012SAdrian Hunter
2359cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2360cd358012SAdrian Hunter
2361cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2362cd358012SAdrian Hunter
2363cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2364cd358012SAdrian Hunter
2365cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2366cd358012SAdrian Hunter
2367cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2368cd358012SAdrian Hunter
2369cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2370cd358012SAdrian Hunter
2371cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2372cd358012SAdrian Hunter
2373cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2374cd358012SAdrian Hunter		self.view.setFocus()
2375cd358012SAdrian Hunter		self.find_bar.Busy()
2376cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2377cd358012SAdrian Hunter
2378cd358012SAdrian Hunter	def FindDone(self, row):
2379cd358012SAdrian Hunter		self.find_bar.Idle()
2380cd358012SAdrian Hunter		if row >= 0:
2381cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2382cd358012SAdrian Hunter		else:
2383cd358012SAdrian Hunter			self.find_bar.NotFound()
2384cd358012SAdrian Hunter
23851beb5c7bSAdrian Hunter# Action Definition
23861beb5c7bSAdrian Hunter
23871beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
23881beb5c7bSAdrian Hunter	action = QAction(label, parent)
23891beb5c7bSAdrian Hunter	if shortcut != None:
23901beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
23911beb5c7bSAdrian Hunter	action.setStatusTip(tip)
23921beb5c7bSAdrian Hunter	action.triggered.connect(callback)
23931beb5c7bSAdrian Hunter	return action
23941beb5c7bSAdrian Hunter
23951beb5c7bSAdrian Hunter# Typical application actions
23961beb5c7bSAdrian Hunter
23971beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
23981beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
23991beb5c7bSAdrian Hunter
24001beb5c7bSAdrian Hunter# Typical MDI actions
24011beb5c7bSAdrian Hunter
24021beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
24031beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
24041beb5c7bSAdrian Hunter
24051beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
24061beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
24071beb5c7bSAdrian Hunter
24081beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
24091beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
24101beb5c7bSAdrian Hunter
24111beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
24121beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
24131beb5c7bSAdrian Hunter
24141beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
24151beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
24161beb5c7bSAdrian Hunter
24171beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
24181beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
24191beb5c7bSAdrian Hunter
24201beb5c7bSAdrian Hunter# Typical MDI window menu
24211beb5c7bSAdrian Hunter
24221beb5c7bSAdrian Hunterclass WindowMenu():
24231beb5c7bSAdrian Hunter
24241beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
24251beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
24261beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
24271beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
24281beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
24291beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
24301beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
24311beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
24321beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
24331beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
24341beb5c7bSAdrian Hunter
24351beb5c7bSAdrian Hunter	def Update(self):
24361beb5c7bSAdrian Hunter		self.window_menu.clear()
24371beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
24381beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
24391beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
24401beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
24411beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
24421beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
24431beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
24441beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
24451beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
24461beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
24471beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24481beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
24491beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
24501beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24511beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
24521beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
24531beb5c7bSAdrian Hunter		if sub_window_count == 0:
24541beb5c7bSAdrian Hunter			return
24551beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24561beb5c7bSAdrian Hunter		nr = 1
24571beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
24581beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
24591beb5c7bSAdrian Hunter			if nr < 10:
24601beb5c7bSAdrian Hunter				label = "&" + label
24611beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
24621beb5c7bSAdrian Hunter			action.setCheckable(True)
24631beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
24641beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
24651beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
24661beb5c7bSAdrian Hunter			nr += 1
24671beb5c7bSAdrian Hunter
24681beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
24691beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
24701beb5c7bSAdrian Hunter
247165b24292SAdrian Hunter# Help text
247265b24292SAdrian Hunter
247365b24292SAdrian Hunterglb_help_text = """
247465b24292SAdrian Hunter<h1>Contents</h1>
247565b24292SAdrian Hunter<style>
247665b24292SAdrian Hunterp.c1 {
247765b24292SAdrian Hunter    text-indent: 40px;
247865b24292SAdrian Hunter}
247965b24292SAdrian Hunterp.c2 {
248065b24292SAdrian Hunter    text-indent: 80px;
248165b24292SAdrian Hunter}
248265b24292SAdrian Hunter}
248365b24292SAdrian Hunter</style>
248465b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
248565b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2486ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
2487ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
2488ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
2489ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
249065b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
249165b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
249265b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
249365b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
249465b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
249565b24292SAdrian Hunterwidths to suit will display something like:
249665b24292SAdrian Hunter<pre>
249765b24292SAdrian Hunter                                         Call Graph: pt_example
249865b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
249965b24292SAdrian Hunterv- ls
250065b24292SAdrian Hunter    v- 2638:2638
250165b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
250265b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
250365b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
250465b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
250565b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
250665b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
250765b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
250865b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
250965b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
251065b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
251165b24292SAdrian Hunter</pre>
251265b24292SAdrian Hunter<h3>Points to note:</h3>
251365b24292SAdrian Hunter<ul>
251465b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
251565b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
251665b24292SAdrian Hunter<li>Subsequent levels are functions</li>
251765b24292SAdrian Hunter<li>'Count' is the number of calls</li>
251865b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
251965b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
252065b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
252165b24292SAdrian Hunter</ul>
252265b24292SAdrian Hunter<h3>Find</h3>
252365b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
252465b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
2525ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
2526ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
2527ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
2528ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
252965b24292SAdrian HunterThe All branches report displays all branches in chronological order.
253065b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
253165b24292SAdrian Hunter<h3>Disassembly</h3>
253265b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
253365b24292SAdrian Hunter<ol>
253465b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
253565b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
253665b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
253765b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
253865b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
253965b24292SAdrian Hunter</ol>
254065b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
254165b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
254265b24292SAdrian Hunter<pre>
254365b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
254465b24292SAdrian Huntergit clone https://github.com/intelxed/xed
254565b24292SAdrian Huntercd xed
254665b24292SAdrian Hunter./mfile.py --share
254765b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
254865b24292SAdrian Huntersudo ldconfig
254965b24292SAdrian Hunter</pre>
255065b24292SAdrian Hunter<h3>Find</h3>
255165b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
255265b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
255365b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
2554ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
255565b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
255665b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
2557ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
255865b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
255965b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
256065b24292SAdrian Hunter<pre>
256165b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
256265b24292SAdrian Hunter	100us-200us		From 100us to 200us
256365b24292SAdrian Hunter	10ms-			From 10ms to the end
256465b24292SAdrian Hunter	-100ns			The first 100ns
256565b24292SAdrian Hunter	-10ms-			The last 10ms
256665b24292SAdrian Hunter</pre>
256765b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2568ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
2569cd358012SAdrian HunterThe Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
2570cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2571cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
257265b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
257365b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
257465b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
257565b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
257665b24292SAdrian Hunterbut that can be slow for large tables.
257765b24292SAdrian Hunter<p>There are also tables of database meta-information.
257865b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
257965b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
258065b24292SAdrian Hunter<h3>Find</h3>
258165b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
258265b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
258365b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
258435fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
258535fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
258665b24292SAdrian Hunter"""
258765b24292SAdrian Hunter
258865b24292SAdrian Hunter# Help window
258965b24292SAdrian Hunter
259065b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
259165b24292SAdrian Hunter
259265b24292SAdrian Hunter	def __init__(self, glb, parent=None):
259365b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
259465b24292SAdrian Hunter
259565b24292SAdrian Hunter		self.text = QTextBrowser()
259665b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
259765b24292SAdrian Hunter		self.text.setReadOnly(True)
259865b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
259965b24292SAdrian Hunter
260065b24292SAdrian Hunter		self.setWidget(self.text)
260165b24292SAdrian Hunter
260265b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
260365b24292SAdrian Hunter
260465b24292SAdrian Hunter# Main window that only displays the help text
260565b24292SAdrian Hunter
260665b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
260765b24292SAdrian Hunter
260865b24292SAdrian Hunter	def __init__(self, parent=None):
260965b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
261065b24292SAdrian Hunter
261165b24292SAdrian Hunter		self.setMinimumSize(200, 100)
261265b24292SAdrian Hunter		self.resize(800, 600)
261365b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
261465b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
261565b24292SAdrian Hunter
261665b24292SAdrian Hunter		self.text = QTextBrowser()
261765b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
261865b24292SAdrian Hunter		self.text.setReadOnly(True)
261965b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
262065b24292SAdrian Hunter
262165b24292SAdrian Hunter		self.setCentralWidget(self.text)
262265b24292SAdrian Hunter
262382f68e28SAdrian Hunter# Font resize
262482f68e28SAdrian Hunter
262582f68e28SAdrian Hunterdef ResizeFont(widget, diff):
262682f68e28SAdrian Hunter	font = widget.font()
262782f68e28SAdrian Hunter	sz = font.pointSize()
262882f68e28SAdrian Hunter	font.setPointSize(sz + diff)
262982f68e28SAdrian Hunter	widget.setFont(font)
263082f68e28SAdrian Hunter
263182f68e28SAdrian Hunterdef ShrinkFont(widget):
263282f68e28SAdrian Hunter	ResizeFont(widget, -1)
263382f68e28SAdrian Hunter
263482f68e28SAdrian Hunterdef EnlargeFont(widget):
263582f68e28SAdrian Hunter	ResizeFont(widget, 1)
263682f68e28SAdrian Hunter
26371beb5c7bSAdrian Hunter# Unique name for sub-windows
26381beb5c7bSAdrian Hunter
26391beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
26401beb5c7bSAdrian Hunter	if nr > 1:
26411beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
26421beb5c7bSAdrian Hunter	return name
26431beb5c7bSAdrian Hunter
26441beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
26451beb5c7bSAdrian Hunter	nr = 1
26461beb5c7bSAdrian Hunter	while True:
26471beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
26481beb5c7bSAdrian Hunter		ok = True
26491beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
26501beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
26511beb5c7bSAdrian Hunter				ok = False
26521beb5c7bSAdrian Hunter				break
26531beb5c7bSAdrian Hunter		if ok:
26541beb5c7bSAdrian Hunter			return unique_name
26551beb5c7bSAdrian Hunter		nr += 1
26561beb5c7bSAdrian Hunter
26571beb5c7bSAdrian Hunter# Add a sub-window
26581beb5c7bSAdrian Hunter
26591beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
26601beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
26611beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
26621beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
26631beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
26641beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
26651beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
26661beb5c7bSAdrian Hunter	sub_window.name = unique_name
26671beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
26681beb5c7bSAdrian Hunter	sub_window.show()
26691beb5c7bSAdrian Hunter
2670031c2a00SAdrian Hunter# Main window
2671031c2a00SAdrian Hunter
2672031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
2673031c2a00SAdrian Hunter
2674031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
2675031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
2676031c2a00SAdrian Hunter
2677031c2a00SAdrian Hunter		self.glb = glb
2678031c2a00SAdrian Hunter
26791beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
2680031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
2681031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
2682031c2a00SAdrian Hunter
26831beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
26841beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
26851beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
2686031c2a00SAdrian Hunter
26871beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
2688031c2a00SAdrian Hunter
26891beb5c7bSAdrian Hunter		menu = self.menuBar()
2690031c2a00SAdrian Hunter
26911beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
26921beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
26931beb5c7bSAdrian Hunter
2694ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
2695ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
26968392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
269782f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
269882f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2699ebd70c7dSAdrian Hunter
27001beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
2701655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
27021beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
27031beb5c7bSAdrian Hunter
2704ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
2705ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
2706ae8b887cSAdrian Hunter
270776099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
270876099f98SAdrian Hunter
2709cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
2710cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
2711cd358012SAdrian Hunter
27128392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
27138392b74bSAdrian Hunter
27141beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
27151beb5c7bSAdrian Hunter
271665b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
271765b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
271865b24292SAdrian Hunter
2719ebd70c7dSAdrian Hunter	def Find(self):
2720ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
2721ebd70c7dSAdrian Hunter		if win:
2722ebd70c7dSAdrian Hunter			try:
2723ebd70c7dSAdrian Hunter				win.find_bar.Activate()
2724ebd70c7dSAdrian Hunter			except:
2725ebd70c7dSAdrian Hunter				pass
2726ebd70c7dSAdrian Hunter
27278392b74bSAdrian Hunter	def FetchMoreRecords(self):
27288392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
27298392b74bSAdrian Hunter		if win:
27308392b74bSAdrian Hunter			try:
27318392b74bSAdrian Hunter				win.fetch_bar.Activate()
27328392b74bSAdrian Hunter			except:
27338392b74bSAdrian Hunter				pass
27348392b74bSAdrian Hunter
273582f68e28SAdrian Hunter	def ShrinkFont(self):
273682f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
273782f68e28SAdrian Hunter		ShrinkFont(win.view)
273882f68e28SAdrian Hunter
273982f68e28SAdrian Hunter	def EnlargeFont(self):
274082f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
274182f68e28SAdrian Hunter		EnlargeFont(win.view)
274282f68e28SAdrian Hunter
274376099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
274476099f98SAdrian Hunter		branches_events = 0
274576099f98SAdrian Hunter		for event in events:
274676099f98SAdrian Hunter			event = event.split(":")[0]
274776099f98SAdrian Hunter			if event == "branches":
274876099f98SAdrian Hunter				branches_events += 1
274976099f98SAdrian Hunter		dbid = 0
275076099f98SAdrian Hunter		for event in events:
275176099f98SAdrian Hunter			dbid += 1
275276099f98SAdrian Hunter			event = event.split(":")[0]
275376099f98SAdrian Hunter			if event == "branches":
275476099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
275576099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2756210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2757210cf1f9SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
275876099f98SAdrian Hunter
27598392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
27608392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
27618392b74bSAdrian Hunter		for table in tables:
27628392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
27638392b74bSAdrian Hunter
27641beb5c7bSAdrian Hunter	def NewCallGraph(self):
27651beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
2766031c2a00SAdrian Hunter
2767ae8b887cSAdrian Hunter	def NewCallTree(self):
2768ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
2769ae8b887cSAdrian Hunter
2770cd358012SAdrian Hunter	def NewTopCalls(self):
2771cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
2772cd358012SAdrian Hunter		ret = dialog.exec_()
2773cd358012SAdrian Hunter		if ret:
2774cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
2775cd358012SAdrian Hunter
277676099f98SAdrian Hunter	def NewBranchView(self, event_id):
2777947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
277876099f98SAdrian Hunter
2779210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
2780210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
2781210cf1f9SAdrian Hunter		ret = dialog.exec_()
2782210cf1f9SAdrian Hunter		if ret:
2783947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
2784210cf1f9SAdrian Hunter
27858392b74bSAdrian Hunter	def NewTableView(self, table_name):
27868392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
27878392b74bSAdrian Hunter
278865b24292SAdrian Hunter	def Help(self):
278965b24292SAdrian Hunter		HelpWindow(self.glb, self)
279065b24292SAdrian Hunter
279176099f98SAdrian Hunter# XED Disassembler
279276099f98SAdrian Hunter
279376099f98SAdrian Hunterclass xed_state_t(Structure):
279476099f98SAdrian Hunter
279576099f98SAdrian Hunter	_fields_ = [
279676099f98SAdrian Hunter		("mode", c_int),
279776099f98SAdrian Hunter		("width", c_int)
279876099f98SAdrian Hunter	]
279976099f98SAdrian Hunter
280076099f98SAdrian Hunterclass XEDInstruction():
280176099f98SAdrian Hunter
280276099f98SAdrian Hunter	def __init__(self, libxed):
280376099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
280476099f98SAdrian Hunter		xedd_t = c_byte * 512
280576099f98SAdrian Hunter		self.xedd = xedd_t()
280676099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
280776099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
280876099f98SAdrian Hunter		self.state = xed_state_t()
280976099f98SAdrian Hunter		self.statep = addressof(self.state)
281076099f98SAdrian Hunter		# Buffer for disassembled instruction text
281176099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
281276099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
281376099f98SAdrian Hunter
281476099f98SAdrian Hunterclass LibXED():
281576099f98SAdrian Hunter
281676099f98SAdrian Hunter	def __init__(self):
28175ed4419dSAdrian Hunter		try:
281876099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
28195ed4419dSAdrian Hunter		except:
28205ed4419dSAdrian Hunter			self.libxed = None
28215ed4419dSAdrian Hunter		if not self.libxed:
28225ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
282376099f98SAdrian Hunter
282476099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
282576099f98SAdrian Hunter		self.xed_tables_init.restype = None
282676099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
282776099f98SAdrian Hunter
282876099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
282976099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
283076099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
283176099f98SAdrian Hunter
283276099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
283376099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
283476099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
283576099f98SAdrian Hunter
283676099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
283776099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
283876099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
283976099f98SAdrian Hunter
284076099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
284176099f98SAdrian Hunter		self.xed_decode.restype = c_int
284276099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
284376099f98SAdrian Hunter
284476099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
284576099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
284676099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
284776099f98SAdrian Hunter
284876099f98SAdrian Hunter		self.xed_tables_init()
284976099f98SAdrian Hunter
285076099f98SAdrian Hunter	def Instruction(self):
285176099f98SAdrian Hunter		return XEDInstruction(self)
285276099f98SAdrian Hunter
285376099f98SAdrian Hunter	def SetMode(self, inst, mode):
285476099f98SAdrian Hunter		if mode:
285576099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
285676099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
285776099f98SAdrian Hunter		else:
285876099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
285976099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
286076099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
286176099f98SAdrian Hunter
286276099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
286376099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
286476099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
286576099f98SAdrian Hunter		if err:
286676099f98SAdrian Hunter			return 0, ""
286776099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
286876099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
286976099f98SAdrian Hunter		if not ok:
287076099f98SAdrian Hunter			return 0, ""
287176099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
287276099f98SAdrian Hunter		# For now, assume the length is in byte 166
287376099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
287476099f98SAdrian Hunter
287576099f98SAdrian Hunterdef TryOpen(file_name):
287676099f98SAdrian Hunter	try:
287776099f98SAdrian Hunter		return open(file_name, "rb")
287876099f98SAdrian Hunter	except:
287976099f98SAdrian Hunter		return None
288076099f98SAdrian Hunter
288176099f98SAdrian Hunterdef Is64Bit(f):
288276099f98SAdrian Hunter	result = sizeof(c_void_p)
288376099f98SAdrian Hunter	# ELF support only
288476099f98SAdrian Hunter	pos = f.tell()
288576099f98SAdrian Hunter	f.seek(0)
288676099f98SAdrian Hunter	header = f.read(7)
288776099f98SAdrian Hunter	f.seek(pos)
288876099f98SAdrian Hunter	magic = header[0:4]
288976099f98SAdrian Hunter	eclass = ord(header[4])
289076099f98SAdrian Hunter	encoding = ord(header[5])
289176099f98SAdrian Hunter	version = ord(header[6])
289276099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
289376099f98SAdrian Hunter		result = True if eclass == 2 else False
289476099f98SAdrian Hunter	return result
289576099f98SAdrian Hunter
2896031c2a00SAdrian Hunter# Global data
2897031c2a00SAdrian Hunter
2898031c2a00SAdrian Hunterclass Glb():
2899031c2a00SAdrian Hunter
2900031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2901031c2a00SAdrian Hunter		self.dbref = dbref
2902031c2a00SAdrian Hunter		self.db = db
2903031c2a00SAdrian Hunter		self.dbname = dbname
290476099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
290576099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
290676099f98SAdrian Hunter		if self.buildid_dir:
290776099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
290876099f98SAdrian Hunter		else:
290976099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2910031c2a00SAdrian Hunter		self.app = None
2911031c2a00SAdrian Hunter		self.mainwindow = None
29128392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
291376099f98SAdrian Hunter		try:
291476099f98SAdrian Hunter			self.disassembler = LibXED()
291576099f98SAdrian Hunter			self.have_disassembler = True
291676099f98SAdrian Hunter		except:
291776099f98SAdrian Hunter			self.have_disassembler = False
291876099f98SAdrian Hunter
291976099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
292076099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
292176099f98SAdrian Hunter		return TryOpen(file_name)
292276099f98SAdrian Hunter
292376099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
292476099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
292576099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
292676099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
292776099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
292876099f98SAdrian Hunter			if f:
292976099f98SAdrian Hunter				return f
293076099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
293176099f98SAdrian Hunter			f = TryOpen(long_name)
293276099f98SAdrian Hunter			if f:
293376099f98SAdrian Hunter				return f
293476099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
293576099f98SAdrian Hunter		if f:
293676099f98SAdrian Hunter			return f
293776099f98SAdrian Hunter		return None
29388392b74bSAdrian Hunter
29398392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
29408392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
29418392b74bSAdrian Hunter
29428392b74bSAdrian Hunter	# Shutdown any background processes or threads
29438392b74bSAdrian Hunter	def ShutdownInstances(self):
29448392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
29458392b74bSAdrian Hunter			try:
29468392b74bSAdrian Hunter				x.Shutdown()
29478392b74bSAdrian Hunter			except:
29488392b74bSAdrian Hunter				pass
2949031c2a00SAdrian Hunter
2950031c2a00SAdrian Hunter# Database reference
2951031c2a00SAdrian Hunter
2952031c2a00SAdrian Hunterclass DBRef():
2953031c2a00SAdrian Hunter
2954031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2955031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2956031c2a00SAdrian Hunter		self.dbname = dbname
2957031c2a00SAdrian Hunter
2958031c2a00SAdrian Hunter	def Open(self, connection_name):
2959031c2a00SAdrian Hunter		dbname = self.dbname
2960031c2a00SAdrian Hunter		if self.is_sqlite3:
2961031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2962031c2a00SAdrian Hunter		else:
2963031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2964031c2a00SAdrian Hunter			opts = dbname.split()
2965031c2a00SAdrian Hunter			for opt in opts:
2966031c2a00SAdrian Hunter				if "=" in opt:
2967031c2a00SAdrian Hunter					opt = opt.split("=")
2968031c2a00SAdrian Hunter					if opt[0] == "hostname":
2969031c2a00SAdrian Hunter						db.setHostName(opt[1])
2970031c2a00SAdrian Hunter					elif opt[0] == "port":
2971031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2972031c2a00SAdrian Hunter					elif opt[0] == "username":
2973031c2a00SAdrian Hunter						db.setUserName(opt[1])
2974031c2a00SAdrian Hunter					elif opt[0] == "password":
2975031c2a00SAdrian Hunter						db.setPassword(opt[1])
2976031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2977031c2a00SAdrian Hunter						dbname = opt[1]
2978031c2a00SAdrian Hunter				else:
2979031c2a00SAdrian Hunter					dbname = opt
2980031c2a00SAdrian Hunter
2981031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2982031c2a00SAdrian Hunter		if not db.open():
2983031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2984031c2a00SAdrian Hunter		return db, dbname
2985031c2a00SAdrian Hunter
2986031c2a00SAdrian Hunter# Main
2987031c2a00SAdrian Hunter
2988031c2a00SAdrian Hunterdef Main():
2989031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
2990*beda0e72STony Jones		printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}");
2991031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2992031c2a00SAdrian Hunter
2993031c2a00SAdrian Hunter	dbname = sys.argv[1]
299465b24292SAdrian Hunter	if dbname == "--help-only":
299565b24292SAdrian Hunter		app = QApplication(sys.argv)
299665b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
299765b24292SAdrian Hunter		mainwindow.show()
299865b24292SAdrian Hunter		err = app.exec_()
299965b24292SAdrian Hunter		sys.exit(err)
3000031c2a00SAdrian Hunter
3001031c2a00SAdrian Hunter	is_sqlite3 = False
3002031c2a00SAdrian Hunter	try:
3003*beda0e72STony Jones		f = open(dbname, "rb")
3004*beda0e72STony Jones		if f.read(15) == b'SQLite format 3':
3005031c2a00SAdrian Hunter			is_sqlite3 = True
3006031c2a00SAdrian Hunter		f.close()
3007031c2a00SAdrian Hunter	except:
3008031c2a00SAdrian Hunter		pass
3009031c2a00SAdrian Hunter
3010031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
3011031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
3012031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
3013031c2a00SAdrian Hunter	app = QApplication(sys.argv)
3014031c2a00SAdrian Hunter	glb.app = app
3015031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
3016031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
3017031c2a00SAdrian Hunter	mainwindow.show()
3018031c2a00SAdrian Hunter	err = app.exec_()
30198392b74bSAdrian Hunter	glb.ShutdownInstances()
3020031c2a00SAdrian Hunter	db.close()
3021031c2a00SAdrian Hunter	sys.exit(err)
3022031c2a00SAdrian Hunter
3023031c2a00SAdrian Hunterif __name__ == "__main__":
3024031c2a00SAdrian Hunter	Main()
3025