1c6aba1bfSAdrian Hunter#!/usr/bin/env python
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
91beda0e72STony Jonesfrom __future__ import print_function
92beda0e72STony Jones
93031c2a00SAdrian Hunterimport sys
941ed7f47fSAdrian Hunterimport argparse
951beb5c7bSAdrian Hunterimport weakref
961beb5c7bSAdrian Hunterimport threading
97ebd70c7dSAdrian Hunterimport string
98beda0e72STony Jonestry:
99beda0e72STony Jones	# Python2
100beda0e72STony Jones	import cPickle as pickle
101beda0e72STony Jones	# size of pickled integer big enough for record size
102beda0e72STony Jones	glb_nsz = 8
103beda0e72STony Jonesexcept ImportError:
104beda0e72STony Jones	import pickle
105beda0e72STony Jones	glb_nsz = 16
1068392b74bSAdrian Hunterimport re
1078392b74bSAdrian Hunterimport os
108df8ea22aSAdrian Hunter
109df8ea22aSAdrian Hunterpyside_version_1 = True
110df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv:
111df8ea22aSAdrian Hunter	try:
112df8ea22aSAdrian Hunter		from PySide2.QtCore import *
113df8ea22aSAdrian Hunter		from PySide2.QtGui import *
114df8ea22aSAdrian Hunter		from PySide2.QtSql import *
115df8ea22aSAdrian Hunter		from PySide2.QtWidgets import *
116df8ea22aSAdrian Hunter		pyside_version_1 = False
117df8ea22aSAdrian Hunter	except:
118df8ea22aSAdrian Hunter		pass
119df8ea22aSAdrian Hunter
120df8ea22aSAdrian Hunterif pyside_version_1:
121031c2a00SAdrian Hunter	from PySide.QtCore import *
122031c2a00SAdrian Hunter	from PySide.QtGui import *
123031c2a00SAdrian Hunter	from PySide.QtSql import *
124df8ea22aSAdrian Hunter
125031c2a00SAdrian Hunterfrom decimal import *
1268392b74bSAdrian Hunterfrom ctypes import *
1278392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
128031c2a00SAdrian Hunter
129beda0e72STony Jones# xrange is range in Python3
130beda0e72STony Jonestry:
131beda0e72STony Jones	xrange
132beda0e72STony Jonesexcept NameError:
133beda0e72STony Jones	xrange = range
134beda0e72STony Jones
135beda0e72STony Jonesdef printerr(*args, **keyword_args):
136beda0e72STony Jones	print(*args, file=sys.stderr, **keyword_args)
137beda0e72STony Jones
138031c2a00SAdrian Hunter# Data formatting helpers
139031c2a00SAdrian Hunter
14076099f98SAdrian Hunterdef tohex(ip):
14176099f98SAdrian Hunter	if ip < 0:
14276099f98SAdrian Hunter		ip += 1 << 64
14376099f98SAdrian Hunter	return "%x" % ip
14476099f98SAdrian Hunter
14576099f98SAdrian Hunterdef offstr(offset):
14676099f98SAdrian Hunter	if offset:
14776099f98SAdrian Hunter		return "+0x%x" % offset
14876099f98SAdrian Hunter	return ""
14976099f98SAdrian Hunter
150031c2a00SAdrian Hunterdef dsoname(name):
151031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
152031c2a00SAdrian Hunter		return "[kernel]"
153031c2a00SAdrian Hunter	return name
154031c2a00SAdrian Hunter
155210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
156210cf1f9SAdrian Hunter	pos = s.find(sub)
157210cf1f9SAdrian Hunter	if pos < 0:
158210cf1f9SAdrian Hunter		return pos
159210cf1f9SAdrian Hunter	if n <= 1:
160210cf1f9SAdrian Hunter		return offs + pos
161210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
162210cf1f9SAdrian Hunter
163031c2a00SAdrian Hunter# Percent to one decimal place
164031c2a00SAdrian Hunter
165031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
166031c2a00SAdrian Hunter	if not d:
167031c2a00SAdrian Hunter		return "0.0"
168031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
169031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
170031c2a00SAdrian Hunter
171031c2a00SAdrian Hunter# Helper for queries that must not fail
172031c2a00SAdrian Hunter
173031c2a00SAdrian Hunterdef QueryExec(query, stmt):
174031c2a00SAdrian Hunter	ret = query.exec_(stmt)
175031c2a00SAdrian Hunter	if not ret:
176031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
177031c2a00SAdrian Hunter
178ebd70c7dSAdrian Hunter# Background thread
179ebd70c7dSAdrian Hunter
180ebd70c7dSAdrian Hunterclass Thread(QThread):
181ebd70c7dSAdrian Hunter
182ebd70c7dSAdrian Hunter	done = Signal(object)
183ebd70c7dSAdrian Hunter
184ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
185ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
186ebd70c7dSAdrian Hunter		self.task = task
187ebd70c7dSAdrian Hunter		self.param = param
188ebd70c7dSAdrian Hunter
189ebd70c7dSAdrian Hunter	def run(self):
190ebd70c7dSAdrian Hunter		while True:
191ebd70c7dSAdrian Hunter			if self.param is None:
192ebd70c7dSAdrian Hunter				done, result = self.task()
193ebd70c7dSAdrian Hunter			else:
194ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
195ebd70c7dSAdrian Hunter			self.done.emit(result)
196ebd70c7dSAdrian Hunter			if done:
197ebd70c7dSAdrian Hunter				break
198ebd70c7dSAdrian Hunter
199031c2a00SAdrian Hunter# Tree data model
200031c2a00SAdrian Hunter
201031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
202031c2a00SAdrian Hunter
2034a0979d4SAdrian Hunter	def __init__(self, glb, params, parent=None):
204031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
205a448ba23SAdrian Hunter		self.glb = glb
2064a0979d4SAdrian Hunter		self.params = params
207a448ba23SAdrian Hunter		self.root = self.GetRoot()
208031c2a00SAdrian Hunter		self.last_row_read = 0
209031c2a00SAdrian Hunter
210031c2a00SAdrian Hunter	def Item(self, parent):
211031c2a00SAdrian Hunter		if parent.isValid():
212031c2a00SAdrian Hunter			return parent.internalPointer()
213031c2a00SAdrian Hunter		else:
214031c2a00SAdrian Hunter			return self.root
215031c2a00SAdrian Hunter
216031c2a00SAdrian Hunter	def rowCount(self, parent):
217031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
218031c2a00SAdrian Hunter		if result < 0:
219031c2a00SAdrian Hunter			result = 0
220031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
221031c2a00SAdrian Hunter		return result
222031c2a00SAdrian Hunter
223031c2a00SAdrian Hunter	def hasChildren(self, parent):
224031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
225031c2a00SAdrian Hunter
226031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
227031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
228031c2a00SAdrian Hunter			return self.columnAlignment(section)
229031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
230031c2a00SAdrian Hunter			return None
231031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
232031c2a00SAdrian Hunter			return None
233031c2a00SAdrian Hunter		return self.columnHeader(section)
234031c2a00SAdrian Hunter
235031c2a00SAdrian Hunter	def parent(self, child):
236031c2a00SAdrian Hunter		child_item = child.internalPointer()
237031c2a00SAdrian Hunter		if child_item is self.root:
238031c2a00SAdrian Hunter			return QModelIndex()
239031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
240031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
241031c2a00SAdrian Hunter
242031c2a00SAdrian Hunter	def index(self, row, column, parent):
243031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
244031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
245031c2a00SAdrian Hunter
246031c2a00SAdrian Hunter	def DisplayData(self, item, index):
247031c2a00SAdrian Hunter		return item.getData(index.column())
248031c2a00SAdrian Hunter
2498392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2508392b74bSAdrian Hunter		if row > self.last_row_read:
2518392b74bSAdrian Hunter			self.last_row_read = row
2528392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2538392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2548392b74bSAdrian Hunter
2558392b74bSAdrian Hunter	def columnAlignment(self, column):
2568392b74bSAdrian Hunter		return Qt.AlignLeft
2578392b74bSAdrian Hunter
2588392b74bSAdrian Hunter	def columnFont(self, column):
2598392b74bSAdrian Hunter		return None
2608392b74bSAdrian Hunter
2618392b74bSAdrian Hunter	def data(self, index, role):
2628392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2638392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2648392b74bSAdrian Hunter		if role == Qt.FontRole:
2658392b74bSAdrian Hunter			return self.columnFont(index.column())
2668392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2678392b74bSAdrian Hunter			return None
2688392b74bSAdrian Hunter		item = index.internalPointer()
2698392b74bSAdrian Hunter		return self.DisplayData(item, index)
2708392b74bSAdrian Hunter
2718392b74bSAdrian Hunter# Table data model
2728392b74bSAdrian Hunter
2738392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2748392b74bSAdrian Hunter
2758392b74bSAdrian Hunter	def __init__(self, parent=None):
2768392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2778392b74bSAdrian Hunter		self.child_count = 0
2788392b74bSAdrian Hunter		self.child_items = []
2798392b74bSAdrian Hunter		self.last_row_read = 0
2808392b74bSAdrian Hunter
2818392b74bSAdrian Hunter	def Item(self, parent):
2828392b74bSAdrian Hunter		if parent.isValid():
2838392b74bSAdrian Hunter			return parent.internalPointer()
2848392b74bSAdrian Hunter		else:
2858392b74bSAdrian Hunter			return self
2868392b74bSAdrian Hunter
2878392b74bSAdrian Hunter	def rowCount(self, parent):
2888392b74bSAdrian Hunter		return self.child_count
2898392b74bSAdrian Hunter
2908392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2918392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2928392b74bSAdrian Hunter			return self.columnAlignment(section)
2938392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2948392b74bSAdrian Hunter			return None
2958392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2968392b74bSAdrian Hunter			return None
2978392b74bSAdrian Hunter		return self.columnHeader(section)
2988392b74bSAdrian Hunter
2998392b74bSAdrian Hunter	def index(self, row, column, parent):
3008392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
3018392b74bSAdrian Hunter
3028392b74bSAdrian Hunter	def DisplayData(self, item, index):
3038392b74bSAdrian Hunter		return item.getData(index.column())
3048392b74bSAdrian Hunter
3058392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
3068392b74bSAdrian Hunter		if row > self.last_row_read:
3078392b74bSAdrian Hunter			self.last_row_read = row
3088392b74bSAdrian Hunter			if row + 10 >= self.child_count:
3098392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
3108392b74bSAdrian Hunter
311031c2a00SAdrian Hunter	def columnAlignment(self, column):
312031c2a00SAdrian Hunter		return Qt.AlignLeft
313031c2a00SAdrian Hunter
314031c2a00SAdrian Hunter	def columnFont(self, column):
315031c2a00SAdrian Hunter		return None
316031c2a00SAdrian Hunter
317031c2a00SAdrian Hunter	def data(self, index, role):
318031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
319031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
320031c2a00SAdrian Hunter		if role == Qt.FontRole:
321031c2a00SAdrian Hunter			return self.columnFont(index.column())
322031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
323031c2a00SAdrian Hunter			return None
324031c2a00SAdrian Hunter		item = index.internalPointer()
325031c2a00SAdrian Hunter		return self.DisplayData(item, index)
326031c2a00SAdrian Hunter
3271beb5c7bSAdrian Hunter# Model cache
3281beb5c7bSAdrian Hunter
3291beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
3301beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
3311beb5c7bSAdrian Hunter
3321beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
3331beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3341beb5c7bSAdrian Hunter	try:
3351beb5c7bSAdrian Hunter		model = model_cache[model_name]
3361beb5c7bSAdrian Hunter	except:
3371beb5c7bSAdrian Hunter		model = None
3381beb5c7bSAdrian Hunter	if model is None:
3391beb5c7bSAdrian Hunter		model = create_fn()
3401beb5c7bSAdrian Hunter		model_cache[model_name] = model
3411beb5c7bSAdrian Hunter	model_cache_lock.release()
3421beb5c7bSAdrian Hunter	return model
3431beb5c7bSAdrian Hunter
344ebd70c7dSAdrian Hunter# Find bar
345ebd70c7dSAdrian Hunter
346ebd70c7dSAdrian Hunterclass FindBar():
347ebd70c7dSAdrian Hunter
348ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
349ebd70c7dSAdrian Hunter		self.finder = finder
350ebd70c7dSAdrian Hunter		self.context = []
351ebd70c7dSAdrian Hunter		self.last_value = None
352ebd70c7dSAdrian Hunter		self.last_pattern = None
353ebd70c7dSAdrian Hunter
354ebd70c7dSAdrian Hunter		label = QLabel("Find:")
355ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
356ebd70c7dSAdrian Hunter
357ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
358ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
359ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
360ebd70c7dSAdrian Hunter
361ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
362ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
363ebd70c7dSAdrian Hunter		self.progress.hide()
364ebd70c7dSAdrian Hunter
365ebd70c7dSAdrian Hunter		if is_reg_expr:
366ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
367ebd70c7dSAdrian Hunter		else:
368ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
369ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
370ebd70c7dSAdrian Hunter
371ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
372ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
373ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
374ebd70c7dSAdrian Hunter
375ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
376ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
377ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
378ebd70c7dSAdrian Hunter
379ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
380ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
381ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
382ebd70c7dSAdrian Hunter
383ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
384ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
385ebd70c7dSAdrian Hunter
386ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
387ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
388ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
389ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
390ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
391ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
392ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
393ebd70c7dSAdrian Hunter
394ebd70c7dSAdrian Hunter		self.bar = QWidget()
395ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
396ebd70c7dSAdrian Hunter		self.bar.hide()
397ebd70c7dSAdrian Hunter
398ebd70c7dSAdrian Hunter	def Widget(self):
399ebd70c7dSAdrian Hunter		return self.bar
400ebd70c7dSAdrian Hunter
401ebd70c7dSAdrian Hunter	def Activate(self):
402ebd70c7dSAdrian Hunter		self.bar.show()
403ebd70c7dSAdrian Hunter		self.textbox.setFocus()
404ebd70c7dSAdrian Hunter
405ebd70c7dSAdrian Hunter	def Deactivate(self):
406ebd70c7dSAdrian Hunter		self.bar.hide()
407ebd70c7dSAdrian Hunter
408ebd70c7dSAdrian Hunter	def Busy(self):
409ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
410ebd70c7dSAdrian Hunter		self.pattern.hide()
411ebd70c7dSAdrian Hunter		self.next_button.hide()
412ebd70c7dSAdrian Hunter		self.prev_button.hide()
413ebd70c7dSAdrian Hunter		self.progress.show()
414ebd70c7dSAdrian Hunter
415ebd70c7dSAdrian Hunter	def Idle(self):
416ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
417ebd70c7dSAdrian Hunter		self.progress.hide()
418ebd70c7dSAdrian Hunter		self.pattern.show()
419ebd70c7dSAdrian Hunter		self.next_button.show()
420ebd70c7dSAdrian Hunter		self.prev_button.show()
421ebd70c7dSAdrian Hunter
422ebd70c7dSAdrian Hunter	def Find(self, direction):
423ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
424ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
425ebd70c7dSAdrian Hunter		self.last_value = value
426ebd70c7dSAdrian Hunter		self.last_pattern = pattern
427ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
428ebd70c7dSAdrian Hunter
429ebd70c7dSAdrian Hunter	def ValueChanged(self):
430ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
431ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
432ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
433ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
434ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
435ebd70c7dSAdrian Hunter		if data == None:
436ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
437ebd70c7dSAdrian Hunter		else:
438ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
439ebd70c7dSAdrian Hunter		self.Find(0)
440ebd70c7dSAdrian Hunter
441ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
442ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
443ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
444ebd70c7dSAdrian Hunter		if value != self.last_value:
445ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
446ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
447ebd70c7dSAdrian Hunter			if index < 0:
448ebd70c7dSAdrian Hunter				index = self.textbox.count()
449ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
450ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
451ebd70c7dSAdrian Hunter				return
452ebd70c7dSAdrian Hunter			else:
453ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
454ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
455ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
456ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
457ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
458ebd70c7dSAdrian Hunter		self.Find(direction)
459ebd70c7dSAdrian Hunter
460ebd70c7dSAdrian Hunter	def NotFound(self):
461ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
462ebd70c7dSAdrian Hunter
463031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
464031c2a00SAdrian Hunter
465031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
466031c2a00SAdrian Hunter
4674a0979d4SAdrian Hunter	def __init__(self, glb, params, row, parent_item):
468031c2a00SAdrian Hunter		self.glb = glb
4694a0979d4SAdrian Hunter		self.params = params
470031c2a00SAdrian Hunter		self.row = row
471031c2a00SAdrian Hunter		self.parent_item = parent_item
472031c2a00SAdrian Hunter		self.query_done = False;
473031c2a00SAdrian Hunter		self.child_count = 0
474031c2a00SAdrian Hunter		self.child_items = []
4753ac641f4SAdrian Hunter		if parent_item:
4763ac641f4SAdrian Hunter			self.level = parent_item.level + 1
4773ac641f4SAdrian Hunter		else:
4783ac641f4SAdrian Hunter			self.level = 0
479031c2a00SAdrian Hunter
480031c2a00SAdrian Hunter	def getChildItem(self, row):
481031c2a00SAdrian Hunter		return self.child_items[row]
482031c2a00SAdrian Hunter
483031c2a00SAdrian Hunter	def getParentItem(self):
484031c2a00SAdrian Hunter		return self.parent_item
485031c2a00SAdrian Hunter
486031c2a00SAdrian Hunter	def getRow(self):
487031c2a00SAdrian Hunter		return self.row
488031c2a00SAdrian Hunter
489031c2a00SAdrian Hunter	def childCount(self):
490031c2a00SAdrian Hunter		if not self.query_done:
491031c2a00SAdrian Hunter			self.Select()
492031c2a00SAdrian Hunter			if not self.child_count:
493031c2a00SAdrian Hunter				return -1
494031c2a00SAdrian Hunter		return self.child_count
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunter	def hasChildren(self):
497031c2a00SAdrian Hunter		if not self.query_done:
498031c2a00SAdrian Hunter			return True
499031c2a00SAdrian Hunter		return self.child_count > 0
500031c2a00SAdrian Hunter
501031c2a00SAdrian Hunter	def getData(self, column):
502031c2a00SAdrian Hunter		return self.data[column]
503031c2a00SAdrian Hunter
504031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
505031c2a00SAdrian Hunter
506031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
507031c2a00SAdrian Hunter
50838a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
5094a0979d4SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
510031c2a00SAdrian Hunter		self.comm_id = comm_id
511031c2a00SAdrian Hunter		self.thread_id = thread_id
512031c2a00SAdrian Hunter		self.call_path_id = call_path_id
51338a846d4SAdrian Hunter		self.insn_cnt = insn_cnt
51438a846d4SAdrian Hunter		self.cyc_cnt = cyc_cnt
515031c2a00SAdrian Hunter		self.branch_count = branch_count
516031c2a00SAdrian Hunter		self.time = time
517031c2a00SAdrian Hunter
518031c2a00SAdrian Hunter	def Select(self):
519031c2a00SAdrian Hunter		self.query_done = True;
520031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
52138a846d4SAdrian Hunter		if self.params.have_ipc:
52238a846d4SAdrian Hunter			ipc_str = ", SUM(insn_count), SUM(cyc_count)"
52338a846d4SAdrian Hunter		else:
52438a846d4SAdrian Hunter			ipc_str = ""
52538a846d4SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
526031c2a00SAdrian Hunter					" FROM calls"
527031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
528031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
529031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
530031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
531031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
532031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
533031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
534031c2a00SAdrian Hunter					" ORDER BY call_path_id")
535031c2a00SAdrian Hunter		while query.next():
53638a846d4SAdrian Hunter			if self.params.have_ipc:
53738a846d4SAdrian Hunter				insn_cnt = int(query.value(5))
53838a846d4SAdrian Hunter				cyc_cnt = int(query.value(6))
53938a846d4SAdrian Hunter				branch_count = int(query.value(7))
54038a846d4SAdrian Hunter			else:
54138a846d4SAdrian Hunter				insn_cnt = 0
54238a846d4SAdrian Hunter				cyc_cnt = 0
54338a846d4SAdrian Hunter				branch_count = int(query.value(5))
54438a846d4SAdrian Hunter			child_item = CallGraphLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)
545031c2a00SAdrian Hunter			self.child_items.append(child_item)
546031c2a00SAdrian Hunter			self.child_count += 1
547031c2a00SAdrian Hunter
548031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
549031c2a00SAdrian Hunter
550031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
551031c2a00SAdrian Hunter
55238a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
55338a846d4SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
554031c2a00SAdrian Hunter		dso = dsoname(dso)
55538a846d4SAdrian Hunter		if self.params.have_ipc:
55638a846d4SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
55738a846d4SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
55838a846d4SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
55938a846d4SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
56038a846d4SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
56138a846d4SAdrian Hunter		else:
562031c2a00SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
563031c2a00SAdrian Hunter		self.dbid = call_path_id
564031c2a00SAdrian Hunter
565031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
566031c2a00SAdrian Hunter
567031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
568031c2a00SAdrian Hunter
5694a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
57038a846d4SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
57138a846d4SAdrian Hunter		if self.params.have_ipc:
57238a846d4SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
57338a846d4SAdrian Hunter		else:
574031c2a00SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
575031c2a00SAdrian Hunter		self.dbid = thread_id
576031c2a00SAdrian Hunter
577031c2a00SAdrian Hunter	def Select(self):
578031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
579031c2a00SAdrian Hunter		for child_item in self.child_items:
580031c2a00SAdrian Hunter			self.time += child_item.time
58138a846d4SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
58238a846d4SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
583031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
584031c2a00SAdrian Hunter		for child_item in self.child_items:
585031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
58638a846d4SAdrian Hunter			if self.params.have_ipc:
58738a846d4SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
58838a846d4SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
58938a846d4SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
59038a846d4SAdrian Hunter			else:
591031c2a00SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
592031c2a00SAdrian Hunter
593031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
594031c2a00SAdrian Hunter
595031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
596031c2a00SAdrian Hunter
5974a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
5984a0979d4SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
59938a846d4SAdrian Hunter		if self.params.have_ipc:
60038a846d4SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
60138a846d4SAdrian Hunter		else:
602031c2a00SAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
603031c2a00SAdrian Hunter		self.dbid = comm_id
604031c2a00SAdrian Hunter
605031c2a00SAdrian Hunter	def Select(self):
606031c2a00SAdrian Hunter		self.query_done = True;
607031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
608031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
609031c2a00SAdrian Hunter					" FROM comm_threads"
610031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
611031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
612031c2a00SAdrian Hunter		while query.next():
6134a0979d4SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
614031c2a00SAdrian Hunter			self.child_items.append(child_item)
615031c2a00SAdrian Hunter			self.child_count += 1
616031c2a00SAdrian Hunter
617031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
618031c2a00SAdrian Hunter
619031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
620031c2a00SAdrian Hunter
6214a0979d4SAdrian Hunter	def __init__(self, glb, params):
6224a0979d4SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, params, 0, None)
623031c2a00SAdrian Hunter		self.dbid = 0
624031c2a00SAdrian Hunter		self.query_done = True;
625031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
626031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
627031c2a00SAdrian Hunter		while query.next():
628031c2a00SAdrian Hunter			if not query.value(0):
629031c2a00SAdrian Hunter				continue
6304a0979d4SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
631031c2a00SAdrian Hunter			self.child_items.append(child_item)
632031c2a00SAdrian Hunter			self.child_count += 1
633031c2a00SAdrian Hunter
6344a0979d4SAdrian Hunter# Call graph model parameters
6354a0979d4SAdrian Hunter
6364a0979d4SAdrian Hunterclass CallGraphModelParams():
6374a0979d4SAdrian Hunter
6384a0979d4SAdrian Hunter	def __init__(self, glb, parent=None):
6394a0979d4SAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
6404a0979d4SAdrian Hunter
641254c0d82SAdrian Hunter# Context-sensitive call graph data model base
642031c2a00SAdrian Hunter
643254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
644031c2a00SAdrian Hunter
645031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
6464a0979d4SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
647031c2a00SAdrian Hunter
648ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
649ebd70c7dSAdrian Hunter		if pattern:
650ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
651ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
652ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
653ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
654ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
655ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
656ebd70c7dSAdrian Hunter				# Escape % and _
657ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
658ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
659ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
660ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
661ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
662ebd70c7dSAdrian Hunter			else:
663ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
664ebd70c7dSAdrian Hunter		else:
665ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
666254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
667ebd70c7dSAdrian Hunter
668ebd70c7dSAdrian Hunter	def Found(self, query, found):
669ebd70c7dSAdrian Hunter		if found:
670ebd70c7dSAdrian Hunter			return self.FindPath(query)
671ebd70c7dSAdrian Hunter		return []
672ebd70c7dSAdrian Hunter
673ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
674ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
675ebd70c7dSAdrian Hunter			found = query.first()
676ebd70c7dSAdrian Hunter		else:
677ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
678ebd70c7dSAdrian Hunter			found = query.next()
679ebd70c7dSAdrian Hunter		return self.Found(query, found)
680ebd70c7dSAdrian Hunter
681ebd70c7dSAdrian Hunter	def FindNext(self, query):
682ebd70c7dSAdrian Hunter		found = query.next()
683ebd70c7dSAdrian Hunter		if not found:
684ebd70c7dSAdrian Hunter			found = query.first()
685ebd70c7dSAdrian Hunter		return self.Found(query, found)
686ebd70c7dSAdrian Hunter
687ebd70c7dSAdrian Hunter	def FindPrev(self, query):
688ebd70c7dSAdrian Hunter		found = query.previous()
689ebd70c7dSAdrian Hunter		if not found:
690ebd70c7dSAdrian Hunter			found = query.last()
691ebd70c7dSAdrian Hunter		return self.Found(query, found)
692ebd70c7dSAdrian Hunter
693ebd70c7dSAdrian Hunter	def FindThread(self, c):
694ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
695ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
696ebd70c7dSAdrian Hunter		elif c.direction > 0:
697ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
698ebd70c7dSAdrian Hunter		else:
699ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
700ebd70c7dSAdrian Hunter		return (True, ids)
701ebd70c7dSAdrian Hunter
702ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
703ebd70c7dSAdrian Hunter		class Context():
704ebd70c7dSAdrian Hunter			def __init__(self, *x):
705ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
706ebd70c7dSAdrian Hunter			def Update(self, *x):
707ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
708ebd70c7dSAdrian Hunter		if len(context):
709ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
710ebd70c7dSAdrian Hunter		else:
711ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
712ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
713ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
714ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
715ebd70c7dSAdrian Hunter		thread.start()
716ebd70c7dSAdrian Hunter
717ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
718ebd70c7dSAdrian Hunter		callback(ids)
719ebd70c7dSAdrian Hunter
720254c0d82SAdrian Hunter# Context-sensitive call graph data model
721254c0d82SAdrian Hunter
722254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
723254c0d82SAdrian Hunter
724254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
725254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
726254c0d82SAdrian Hunter
727254c0d82SAdrian Hunter	def GetRoot(self):
7284a0979d4SAdrian Hunter		return CallGraphRootItem(self.glb, self.params)
729254c0d82SAdrian Hunter
730254c0d82SAdrian Hunter	def columnCount(self, parent=None):
73138a846d4SAdrian Hunter		if self.params.have_ipc:
73238a846d4SAdrian Hunter			return 12
73338a846d4SAdrian Hunter		else:
734254c0d82SAdrian Hunter			return 7
735254c0d82SAdrian Hunter
736254c0d82SAdrian Hunter	def columnHeader(self, column):
73738a846d4SAdrian Hunter		if self.params.have_ipc:
73838a846d4SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
73938a846d4SAdrian Hunter		else:
740254c0d82SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
741254c0d82SAdrian Hunter		return headers[column]
742254c0d82SAdrian Hunter
743254c0d82SAdrian Hunter	def columnAlignment(self, column):
74438a846d4SAdrian Hunter		if self.params.have_ipc:
74538a846d4SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
74638a846d4SAdrian Hunter		else:
747254c0d82SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
748254c0d82SAdrian Hunter		return alignment[column]
749254c0d82SAdrian Hunter
750254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
751254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
752254c0d82SAdrian Hunter						" FROM calls"
753254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
754254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
755254c0d82SAdrian Hunter						" WHERE symbols.name" + match +
756254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
757254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
758254c0d82SAdrian Hunter
759254c0d82SAdrian Hunter	def FindPath(self, query):
760254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
761254c0d82SAdrian Hunter		# to open the tree at the right place.
762254c0d82SAdrian Hunter		ids = []
763254c0d82SAdrian Hunter		parent_id = query.value(0)
764254c0d82SAdrian Hunter		while parent_id:
765254c0d82SAdrian Hunter			ids.insert(0, parent_id)
766254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
767254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
768254c0d82SAdrian Hunter					" FROM call_paths"
769254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
770254c0d82SAdrian Hunter			if not q2.next():
771254c0d82SAdrian Hunter				break
772254c0d82SAdrian Hunter			parent_id = q2.value(0)
773254c0d82SAdrian Hunter		# The call path root is not used
774254c0d82SAdrian Hunter		if ids[0] == 1:
775254c0d82SAdrian Hunter			del ids[0]
776254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
777254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
778254c0d82SAdrian Hunter		return ids
779254c0d82SAdrian Hunter
780ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
781ae8b887cSAdrian Hunter
782ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
783ae8b887cSAdrian Hunter
784*b3b66079SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
7854a0979d4SAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
786ae8b887cSAdrian Hunter		self.comm_id = comm_id
787ae8b887cSAdrian Hunter		self.thread_id = thread_id
788ae8b887cSAdrian Hunter		self.calls_id = calls_id
789*b3b66079SAdrian Hunter		self.insn_cnt = insn_cnt
790*b3b66079SAdrian Hunter		self.cyc_cnt = cyc_cnt
791ae8b887cSAdrian Hunter		self.branch_count = branch_count
792ae8b887cSAdrian Hunter		self.time = time
793ae8b887cSAdrian Hunter
794ae8b887cSAdrian Hunter	def Select(self):
795ae8b887cSAdrian Hunter		self.query_done = True;
796ae8b887cSAdrian Hunter		if self.calls_id == 0:
797ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
798ae8b887cSAdrian Hunter		else:
799ae8b887cSAdrian Hunter			comm_thread = ""
800*b3b66079SAdrian Hunter		if self.params.have_ipc:
801*b3b66079SAdrian Hunter			ipc_str = ", insn_count, cyc_count"
802*b3b66079SAdrian Hunter		else:
803*b3b66079SAdrian Hunter			ipc_str = ""
804ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
805*b3b66079SAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
806ae8b887cSAdrian Hunter					" FROM calls"
807ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
808ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
809ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
810ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
811ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
812ae8b887cSAdrian Hunter		while query.next():
813*b3b66079SAdrian Hunter			if self.params.have_ipc:
814*b3b66079SAdrian Hunter				insn_cnt = int(query.value(5))
815*b3b66079SAdrian Hunter				cyc_cnt = int(query.value(6))
816*b3b66079SAdrian Hunter				branch_count = int(query.value(7))
817*b3b66079SAdrian Hunter			else:
818*b3b66079SAdrian Hunter				insn_cnt = 0
819*b3b66079SAdrian Hunter				cyc_cnt = 0
820*b3b66079SAdrian Hunter				branch_count = int(query.value(5))
821*b3b66079SAdrian Hunter			child_item = CallTreeLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)
822ae8b887cSAdrian Hunter			self.child_items.append(child_item)
823ae8b887cSAdrian Hunter			self.child_count += 1
824ae8b887cSAdrian Hunter
825ae8b887cSAdrian Hunter# Call tree data model level three item
826ae8b887cSAdrian Hunter
827ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
828ae8b887cSAdrian Hunter
829*b3b66079SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
830*b3b66079SAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
831ae8b887cSAdrian Hunter		dso = dsoname(dso)
832*b3b66079SAdrian Hunter		if self.params.have_ipc:
833*b3b66079SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
834*b3b66079SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
835*b3b66079SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
836*b3b66079SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
837*b3b66079SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
838*b3b66079SAdrian Hunter		else:
839ae8b887cSAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
840ae8b887cSAdrian Hunter		self.dbid = calls_id
841ae8b887cSAdrian Hunter
842ae8b887cSAdrian Hunter# Call tree data model level two item
843ae8b887cSAdrian Hunter
844ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
845ae8b887cSAdrian Hunter
8464a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
847*b3b66079SAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item)
848*b3b66079SAdrian Hunter		if self.params.have_ipc:
849*b3b66079SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
850*b3b66079SAdrian Hunter		else:
851ae8b887cSAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
852ae8b887cSAdrian Hunter		self.dbid = thread_id
853ae8b887cSAdrian Hunter
854ae8b887cSAdrian Hunter	def Select(self):
855ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
856ae8b887cSAdrian Hunter		for child_item in self.child_items:
857ae8b887cSAdrian Hunter			self.time += child_item.time
858*b3b66079SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
859*b3b66079SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
860ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
861ae8b887cSAdrian Hunter		for child_item in self.child_items:
862ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
863*b3b66079SAdrian Hunter			if self.params.have_ipc:
864*b3b66079SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
865*b3b66079SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
866*b3b66079SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
867*b3b66079SAdrian Hunter			else:
868ae8b887cSAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
869ae8b887cSAdrian Hunter
870ae8b887cSAdrian Hunter# Call tree data model level one item
871ae8b887cSAdrian Hunter
872ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
873ae8b887cSAdrian Hunter
8744a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
8754a0979d4SAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
876*b3b66079SAdrian Hunter		if self.params.have_ipc:
877*b3b66079SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
878*b3b66079SAdrian Hunter		else:
879ae8b887cSAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
880ae8b887cSAdrian Hunter		self.dbid = comm_id
881ae8b887cSAdrian Hunter
882ae8b887cSAdrian Hunter	def Select(self):
883ae8b887cSAdrian Hunter		self.query_done = True;
884ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
885ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
886ae8b887cSAdrian Hunter					" FROM comm_threads"
887ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
888ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
889ae8b887cSAdrian Hunter		while query.next():
8904a0979d4SAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
891ae8b887cSAdrian Hunter			self.child_items.append(child_item)
892ae8b887cSAdrian Hunter			self.child_count += 1
893ae8b887cSAdrian Hunter
894ae8b887cSAdrian Hunter# Call tree data model root item
895ae8b887cSAdrian Hunter
896ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
897ae8b887cSAdrian Hunter
8984a0979d4SAdrian Hunter	def __init__(self, glb, params):
8994a0979d4SAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, params, 0, None)
900ae8b887cSAdrian Hunter		self.dbid = 0
901ae8b887cSAdrian Hunter		self.query_done = True;
902ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
903ae8b887cSAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
904ae8b887cSAdrian Hunter		while query.next():
905ae8b887cSAdrian Hunter			if not query.value(0):
906ae8b887cSAdrian Hunter				continue
9074a0979d4SAdrian Hunter			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
908ae8b887cSAdrian Hunter			self.child_items.append(child_item)
909ae8b887cSAdrian Hunter			self.child_count += 1
910ae8b887cSAdrian Hunter
911ae8b887cSAdrian Hunter# Call Tree data model
912ae8b887cSAdrian Hunter
913ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
914ae8b887cSAdrian Hunter
915ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
916ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
917ae8b887cSAdrian Hunter
918ae8b887cSAdrian Hunter	def GetRoot(self):
9194a0979d4SAdrian Hunter		return CallTreeRootItem(self.glb, self.params)
920ae8b887cSAdrian Hunter
921ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
922*b3b66079SAdrian Hunter		if self.params.have_ipc:
923*b3b66079SAdrian Hunter			return 12
924*b3b66079SAdrian Hunter		else:
925ae8b887cSAdrian Hunter			return 7
926ae8b887cSAdrian Hunter
927ae8b887cSAdrian Hunter	def columnHeader(self, column):
928*b3b66079SAdrian Hunter		if self.params.have_ipc:
929*b3b66079SAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
930*b3b66079SAdrian Hunter		else:
931ae8b887cSAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
932ae8b887cSAdrian Hunter		return headers[column]
933ae8b887cSAdrian Hunter
934ae8b887cSAdrian Hunter	def columnAlignment(self, column):
935*b3b66079SAdrian Hunter		if self.params.have_ipc:
936*b3b66079SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
937*b3b66079SAdrian Hunter		else:
938ae8b887cSAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
939ae8b887cSAdrian Hunter		return alignment[column]
940ae8b887cSAdrian Hunter
941ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
942ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
943ae8b887cSAdrian Hunter						" FROM calls"
944ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
945ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
946ae8b887cSAdrian Hunter						" WHERE symbols.name" + match +
947ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
948ae8b887cSAdrian Hunter
949ae8b887cSAdrian Hunter	def FindPath(self, query):
950ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
951ae8b887cSAdrian Hunter		# to open the tree at the right place.
952ae8b887cSAdrian Hunter		ids = []
953ae8b887cSAdrian Hunter		parent_id = query.value(0)
954ae8b887cSAdrian Hunter		while parent_id:
955ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
956ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
957ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
958ae8b887cSAdrian Hunter					" FROM calls"
959ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
960ae8b887cSAdrian Hunter			if not q2.next():
961ae8b887cSAdrian Hunter				break
962ae8b887cSAdrian Hunter			parent_id = q2.value(0)
963ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
964ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
965ae8b887cSAdrian Hunter		return ids
966ae8b887cSAdrian Hunter
967ebd70c7dSAdrian Hunter# Vertical widget layout
968ebd70c7dSAdrian Hunter
969ebd70c7dSAdrian Hunterclass VBox():
970ebd70c7dSAdrian Hunter
971ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
972ebd70c7dSAdrian Hunter		self.vbox = QWidget()
973ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
974ebd70c7dSAdrian Hunter
975ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
976ebd70c7dSAdrian Hunter
977ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
978ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
979ebd70c7dSAdrian Hunter		if w3:
980ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
981ebd70c7dSAdrian Hunter
982ebd70c7dSAdrian Hunter	def Widget(self):
983ebd70c7dSAdrian Hunter		return self.vbox
984ebd70c7dSAdrian Hunter
985a731cc4cSAdrian Hunter# Tree window base
9861beb5c7bSAdrian Hunter
987a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
9881beb5c7bSAdrian Hunter
989a731cc4cSAdrian Hunter	def __init__(self, parent=None):
990a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
9911beb5c7bSAdrian Hunter
992a731cc4cSAdrian Hunter		self.model = None
993a731cc4cSAdrian Hunter		self.find_bar = None
9941beb5c7bSAdrian Hunter
995be6e7471SAdrian Hunter		self.view = QTreeView()
99696c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
99796c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
998be6e7471SAdrian Hunter
9999bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
10009bc4e4bfSAdrian Hunter
1001ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
1002ebd70c7dSAdrian Hunter		if not len(ids):
1003ebd70c7dSAdrian Hunter			return False
1004ebd70c7dSAdrian Hunter		parent = QModelIndex()
1005ebd70c7dSAdrian Hunter		for dbid in ids:
1006ebd70c7dSAdrian Hunter			found = False
1007ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
1008ebd70c7dSAdrian Hunter			for row in xrange(n):
1009ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
1010ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
1011ebd70c7dSAdrian Hunter					found = True
1012ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
1013ebd70c7dSAdrian Hunter					parent = child
1014ebd70c7dSAdrian Hunter					break
1015ebd70c7dSAdrian Hunter			if not found:
1016ebd70c7dSAdrian Hunter				break
1017ebd70c7dSAdrian Hunter		return found
1018ebd70c7dSAdrian Hunter
1019ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
1020ebd70c7dSAdrian Hunter		self.view.setFocus()
1021ebd70c7dSAdrian Hunter		self.find_bar.Busy()
1022ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
1023ebd70c7dSAdrian Hunter
1024ebd70c7dSAdrian Hunter	def FindDone(self, ids):
1025ebd70c7dSAdrian Hunter		found = True
1026ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
1027ebd70c7dSAdrian Hunter			found = False
1028ebd70c7dSAdrian Hunter		self.find_bar.Idle()
1029ebd70c7dSAdrian Hunter		if not found:
1030ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
1031ebd70c7dSAdrian Hunter
1032a731cc4cSAdrian Hunter
1033a731cc4cSAdrian Hunter# Context-sensitive call graph window
1034a731cc4cSAdrian Hunter
1035a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
1036a731cc4cSAdrian Hunter
1037a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
1038a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
1039a731cc4cSAdrian Hunter
1040a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
1041a731cc4cSAdrian Hunter
1042a731cc4cSAdrian Hunter		self.view.setModel(self.model)
1043a731cc4cSAdrian Hunter
1044a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
1045a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
1046a731cc4cSAdrian Hunter
1047a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
1048a731cc4cSAdrian Hunter
1049a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1050a731cc4cSAdrian Hunter
1051a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1052a731cc4cSAdrian Hunter
1053a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
1054a731cc4cSAdrian Hunter
1055ae8b887cSAdrian Hunter# Call tree window
1056ae8b887cSAdrian Hunter
1057ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
1058ae8b887cSAdrian Hunter
1059ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
1060ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
1061ae8b887cSAdrian Hunter
1062ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
1063ae8b887cSAdrian Hunter
1064ae8b887cSAdrian Hunter		self.view.setModel(self.model)
1065ae8b887cSAdrian Hunter
1066ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
1067ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
1068ae8b887cSAdrian Hunter
1069ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
1070ae8b887cSAdrian Hunter
1071ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1072ae8b887cSAdrian Hunter
1073ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1074ae8b887cSAdrian Hunter
1075ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
1076ae8b887cSAdrian Hunter
10778392b74bSAdrian Hunter# Child data item  finder
10788392b74bSAdrian Hunter
10798392b74bSAdrian Hunterclass ChildDataItemFinder():
10808392b74bSAdrian Hunter
10818392b74bSAdrian Hunter	def __init__(self, root):
10828392b74bSAdrian Hunter		self.root = root
10838392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
10848392b74bSAdrian Hunter		self.rows = []
10858392b74bSAdrian Hunter		self.pos = 0
10868392b74bSAdrian Hunter
10878392b74bSAdrian Hunter	def FindSelect(self):
10888392b74bSAdrian Hunter		self.rows = []
10898392b74bSAdrian Hunter		if self.pattern:
10908392b74bSAdrian Hunter			pattern = re.compile(self.value)
10918392b74bSAdrian Hunter			for child in self.root.child_items:
10928392b74bSAdrian Hunter				for column_data in child.data:
10938392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
10948392b74bSAdrian Hunter						self.rows.append(child.row)
10958392b74bSAdrian Hunter						break
10968392b74bSAdrian Hunter		else:
10978392b74bSAdrian Hunter			for child in self.root.child_items:
10988392b74bSAdrian Hunter				for column_data in child.data:
10998392b74bSAdrian Hunter					if self.value in str(column_data):
11008392b74bSAdrian Hunter						self.rows.append(child.row)
11018392b74bSAdrian Hunter						break
11028392b74bSAdrian Hunter
11038392b74bSAdrian Hunter	def FindValue(self):
11048392b74bSAdrian Hunter		self.pos = 0
11058392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
11068392b74bSAdrian Hunter			self.FindSelect()
11078392b74bSAdrian Hunter		if not len(self.rows):
11088392b74bSAdrian Hunter			return -1
11098392b74bSAdrian Hunter		return self.rows[self.pos]
11108392b74bSAdrian Hunter
11118392b74bSAdrian Hunter	def FindThread(self):
11128392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
11138392b74bSAdrian Hunter			row = self.FindValue()
11148392b74bSAdrian Hunter		elif len(self.rows):
11158392b74bSAdrian Hunter			if self.direction > 0:
11168392b74bSAdrian Hunter				self.pos += 1
11178392b74bSAdrian Hunter				if self.pos >= len(self.rows):
11188392b74bSAdrian Hunter					self.pos = 0
11198392b74bSAdrian Hunter			else:
11208392b74bSAdrian Hunter				self.pos -= 1
11218392b74bSAdrian Hunter				if self.pos < 0:
11228392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
11238392b74bSAdrian Hunter			row = self.rows[self.pos]
11248392b74bSAdrian Hunter		else:
11258392b74bSAdrian Hunter			row = -1
11268392b74bSAdrian Hunter		return (True, row)
11278392b74bSAdrian Hunter
11288392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
11298392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
11308392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
11318392b74bSAdrian Hunter		thread = Thread(self.FindThread)
11328392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
11338392b74bSAdrian Hunter		thread.start()
11348392b74bSAdrian Hunter
11358392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
11368392b74bSAdrian Hunter		callback(row)
11378392b74bSAdrian Hunter
11388392b74bSAdrian Hunter# Number of database records to fetch in one go
11398392b74bSAdrian Hunter
11408392b74bSAdrian Hunterglb_chunk_sz = 10000
11418392b74bSAdrian Hunter
11428392b74bSAdrian Hunter# Background process for SQL data fetcher
11438392b74bSAdrian Hunter
11448392b74bSAdrian Hunterclass SQLFetcherProcess():
11458392b74bSAdrian Hunter
11468392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
11478392b74bSAdrian Hunter		# Need a unique connection name
11488392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
11498392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
11508392b74bSAdrian Hunter		self.sql = sql
11518392b74bSAdrian Hunter		self.buffer = buffer
11528392b74bSAdrian Hunter		self.head = head
11538392b74bSAdrian Hunter		self.tail = tail
11548392b74bSAdrian Hunter		self.fetch_count = fetch_count
11558392b74bSAdrian Hunter		self.fetching_done = fetching_done
11568392b74bSAdrian Hunter		self.process_target = process_target
11578392b74bSAdrian Hunter		self.wait_event = wait_event
11588392b74bSAdrian Hunter		self.fetched_event = fetched_event
11598392b74bSAdrian Hunter		self.prep = prep
11608392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
11618392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
11628392b74bSAdrian Hunter		self.last_id = -1
11638392b74bSAdrian Hunter		self.fetched = 0
11648392b74bSAdrian Hunter		self.more = True
11658392b74bSAdrian Hunter		self.local_head = self.head.value
11668392b74bSAdrian Hunter		self.local_tail = self.tail.value
11678392b74bSAdrian Hunter
11688392b74bSAdrian Hunter	def Select(self):
11698392b74bSAdrian Hunter		if self.query_limit:
11708392b74bSAdrian Hunter			if self.query_limit == 1:
11718392b74bSAdrian Hunter				return
11728392b74bSAdrian Hunter			self.query_limit -= 1
11738392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
11748392b74bSAdrian Hunter		QueryExec(self.query, stmt)
11758392b74bSAdrian Hunter
11768392b74bSAdrian Hunter	def Next(self):
11778392b74bSAdrian Hunter		if not self.query.next():
11788392b74bSAdrian Hunter			self.Select()
11798392b74bSAdrian Hunter			if not self.query.next():
11808392b74bSAdrian Hunter				return None
11818392b74bSAdrian Hunter		self.last_id = self.query.value(0)
11828392b74bSAdrian Hunter		return self.prep(self.query)
11838392b74bSAdrian Hunter
11848392b74bSAdrian Hunter	def WaitForTarget(self):
11858392b74bSAdrian Hunter		while True:
11868392b74bSAdrian Hunter			self.wait_event.clear()
11878392b74bSAdrian Hunter			target = self.process_target.value
11888392b74bSAdrian Hunter			if target > self.fetched or target < 0:
11898392b74bSAdrian Hunter				break
11908392b74bSAdrian Hunter			self.wait_event.wait()
11918392b74bSAdrian Hunter		return target
11928392b74bSAdrian Hunter
11938392b74bSAdrian Hunter	def HasSpace(self, sz):
11948392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
11958392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
11968392b74bSAdrian Hunter			if space > sz:
11978392b74bSAdrian Hunter				return True
11988392b74bSAdrian Hunter			if space >= glb_nsz:
11998392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
1200beda0e72STony Jones				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
12018392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
12028392b74bSAdrian Hunter			self.local_head = 0
12038392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
12048392b74bSAdrian Hunter			return True
12058392b74bSAdrian Hunter		return False
12068392b74bSAdrian Hunter
12078392b74bSAdrian Hunter	def WaitForSpace(self, sz):
12088392b74bSAdrian Hunter		if self.HasSpace(sz):
12098392b74bSAdrian Hunter			return
12108392b74bSAdrian Hunter		while True:
12118392b74bSAdrian Hunter			self.wait_event.clear()
12128392b74bSAdrian Hunter			self.local_tail = self.tail.value
12138392b74bSAdrian Hunter			if self.HasSpace(sz):
12148392b74bSAdrian Hunter				return
12158392b74bSAdrian Hunter			self.wait_event.wait()
12168392b74bSAdrian Hunter
12178392b74bSAdrian Hunter	def AddToBuffer(self, obj):
1218beda0e72STony Jones		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
12198392b74bSAdrian Hunter		n = len(d)
1220beda0e72STony Jones		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
12218392b74bSAdrian Hunter		sz = n + glb_nsz
12228392b74bSAdrian Hunter		self.WaitForSpace(sz)
12238392b74bSAdrian Hunter		pos = self.local_head
12248392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
12258392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
12268392b74bSAdrian Hunter		self.local_head += sz
12278392b74bSAdrian Hunter
12288392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
12298392b74bSAdrian Hunter		fetched = 0
12308392b74bSAdrian Hunter		while batch_size > fetched:
12318392b74bSAdrian Hunter			obj = self.Next()
12328392b74bSAdrian Hunter			if obj is None:
12338392b74bSAdrian Hunter				self.more = False
12348392b74bSAdrian Hunter				break
12358392b74bSAdrian Hunter			self.AddToBuffer(obj)
12368392b74bSAdrian Hunter			fetched += 1
12378392b74bSAdrian Hunter		if fetched:
12388392b74bSAdrian Hunter			self.fetched += fetched
12398392b74bSAdrian Hunter			with self.fetch_count.get_lock():
12408392b74bSAdrian Hunter				self.fetch_count.value += fetched
12418392b74bSAdrian Hunter			self.head.value = self.local_head
12428392b74bSAdrian Hunter			self.fetched_event.set()
12438392b74bSAdrian Hunter
12448392b74bSAdrian Hunter	def Run(self):
12458392b74bSAdrian Hunter		while self.more:
12468392b74bSAdrian Hunter			target = self.WaitForTarget()
12478392b74bSAdrian Hunter			if target < 0:
12488392b74bSAdrian Hunter				break
12498392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
12508392b74bSAdrian Hunter			self.FetchBatch(batch_size)
12518392b74bSAdrian Hunter		self.fetching_done.value = True
12528392b74bSAdrian Hunter		self.fetched_event.set()
12538392b74bSAdrian Hunter
12548392b74bSAdrian Hunterdef SQLFetcherFn(*x):
12558392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
12568392b74bSAdrian Hunter	process.Run()
12578392b74bSAdrian Hunter
12588392b74bSAdrian Hunter# SQL data fetcher
12598392b74bSAdrian Hunter
12608392b74bSAdrian Hunterclass SQLFetcher(QObject):
12618392b74bSAdrian Hunter
12628392b74bSAdrian Hunter	done = Signal(object)
12638392b74bSAdrian Hunter
12648392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
12658392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
12668392b74bSAdrian Hunter		self.process_data = process_data
12678392b74bSAdrian Hunter		self.more = True
12688392b74bSAdrian Hunter		self.target = 0
12698392b74bSAdrian Hunter		self.last_target = 0
12708392b74bSAdrian Hunter		self.fetched = 0
12718392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
12728392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
12738392b74bSAdrian Hunter		self.head = Value(c_longlong)
12748392b74bSAdrian Hunter		self.tail = Value(c_longlong)
12758392b74bSAdrian Hunter		self.local_tail = 0
12768392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
12778392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
12788392b74bSAdrian Hunter		self.last_count = 0
12798392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
12808392b74bSAdrian Hunter		self.wait_event = Event()
12818392b74bSAdrian Hunter		self.fetched_event = Event()
12828392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
12838392b74bSAdrian 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))
12848392b74bSAdrian Hunter		self.process.start()
12858392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
12868392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
12878392b74bSAdrian Hunter		self.thread.start()
12888392b74bSAdrian Hunter
12898392b74bSAdrian Hunter	def Shutdown(self):
12908392b74bSAdrian Hunter		# Tell the thread and process to exit
12918392b74bSAdrian Hunter		self.process_target.value = -1
12928392b74bSAdrian Hunter		self.wait_event.set()
12938392b74bSAdrian Hunter		self.more = False
12948392b74bSAdrian Hunter		self.fetching_done.value = True
12958392b74bSAdrian Hunter		self.fetched_event.set()
12968392b74bSAdrian Hunter
12978392b74bSAdrian Hunter	def Thread(self):
12988392b74bSAdrian Hunter		if not self.more:
12998392b74bSAdrian Hunter			return True, 0
13008392b74bSAdrian Hunter		while True:
13018392b74bSAdrian Hunter			self.fetched_event.clear()
13028392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
13038392b74bSAdrian Hunter			if fetch_count != self.last_count:
13048392b74bSAdrian Hunter				break
13058392b74bSAdrian Hunter			if self.fetching_done.value:
13068392b74bSAdrian Hunter				self.more = False
13078392b74bSAdrian Hunter				return True, 0
13088392b74bSAdrian Hunter			self.fetched_event.wait()
13098392b74bSAdrian Hunter		count = fetch_count - self.last_count
13108392b74bSAdrian Hunter		self.last_count = fetch_count
13118392b74bSAdrian Hunter		self.fetched += count
13128392b74bSAdrian Hunter		return False, count
13138392b74bSAdrian Hunter
13148392b74bSAdrian Hunter	def Fetch(self, nr):
13158392b74bSAdrian Hunter		if not self.more:
13168392b74bSAdrian Hunter			# -1 inidcates there are no more
13178392b74bSAdrian Hunter			return -1
13188392b74bSAdrian Hunter		result = self.fetched
13198392b74bSAdrian Hunter		extra = result + nr - self.target
13208392b74bSAdrian Hunter		if extra > 0:
13218392b74bSAdrian Hunter			self.target += extra
13228392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
13238392b74bSAdrian Hunter			if self.process_target.value >= 0:
13248392b74bSAdrian Hunter				self.process_target.value = self.target
13258392b74bSAdrian Hunter			self.wait_event.set()
13268392b74bSAdrian Hunter		return result
13278392b74bSAdrian Hunter
13288392b74bSAdrian Hunter	def RemoveFromBuffer(self):
13298392b74bSAdrian Hunter		pos = self.local_tail
13308392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
13318392b74bSAdrian Hunter			pos = 0
1332beda0e72STony Jones		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
13338392b74bSAdrian Hunter		if n == 0:
13348392b74bSAdrian Hunter			pos = 0
1335beda0e72STony Jones			n = pickle.loads(self.buffer[0 : glb_nsz])
13368392b74bSAdrian Hunter		pos += glb_nsz
1337beda0e72STony Jones		obj = pickle.loads(self.buffer[pos : pos + n])
13388392b74bSAdrian Hunter		self.local_tail = pos + n
13398392b74bSAdrian Hunter		return obj
13408392b74bSAdrian Hunter
13418392b74bSAdrian Hunter	def ProcessData(self, count):
13428392b74bSAdrian Hunter		for i in xrange(count):
13438392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
13448392b74bSAdrian Hunter			self.process_data(obj)
13458392b74bSAdrian Hunter		self.tail.value = self.local_tail
13468392b74bSAdrian Hunter		self.wait_event.set()
13478392b74bSAdrian Hunter		self.done.emit(count)
13488392b74bSAdrian Hunter
13498392b74bSAdrian Hunter# Fetch more records bar
13508392b74bSAdrian Hunter
13518392b74bSAdrian Hunterclass FetchMoreRecordsBar():
13528392b74bSAdrian Hunter
13538392b74bSAdrian Hunter	def __init__(self, model, parent):
13548392b74bSAdrian Hunter		self.model = model
13558392b74bSAdrian Hunter
13568392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
13578392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
13588392b74bSAdrian Hunter
13598392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
13608392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
13618392b74bSAdrian Hunter		self.fetch_count.setValue(10)
13628392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
13638392b74bSAdrian Hunter
13648392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
13658392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
13668392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
13678392b74bSAdrian Hunter
13688392b74bSAdrian Hunter		self.progress = QProgressBar()
13698392b74bSAdrian Hunter		self.progress.setRange(0, 100)
13708392b74bSAdrian Hunter		self.progress.hide()
13718392b74bSAdrian Hunter
13728392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
13738392b74bSAdrian Hunter		self.done_label.hide()
13748392b74bSAdrian Hunter
13758392b74bSAdrian Hunter		self.spacer = QLabel("")
13768392b74bSAdrian Hunter
13778392b74bSAdrian Hunter		self.close_button = QToolButton()
13788392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
13798392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
13808392b74bSAdrian Hunter
13818392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
13828392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
13838392b74bSAdrian Hunter
13848392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
13858392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
13868392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
13878392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
13888392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
13898392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
13908392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
13918392b74bSAdrian Hunter
13928392b74bSAdrian Hunter		self.bar = QWidget()
13938392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
13948392b74bSAdrian Hunter		self.bar.show()
13958392b74bSAdrian Hunter
13968392b74bSAdrian Hunter		self.in_progress = False
13978392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
13988392b74bSAdrian Hunter
13998392b74bSAdrian Hunter		self.done = False
14008392b74bSAdrian Hunter
14018392b74bSAdrian Hunter		if not model.HasMoreRecords():
14028392b74bSAdrian Hunter			self.Done()
14038392b74bSAdrian Hunter
14048392b74bSAdrian Hunter	def Widget(self):
14058392b74bSAdrian Hunter		return self.bar
14068392b74bSAdrian Hunter
14078392b74bSAdrian Hunter	def Activate(self):
14088392b74bSAdrian Hunter		self.bar.show()
14098392b74bSAdrian Hunter		self.fetch.setFocus()
14108392b74bSAdrian Hunter
14118392b74bSAdrian Hunter	def Deactivate(self):
14128392b74bSAdrian Hunter		self.bar.hide()
14138392b74bSAdrian Hunter
14148392b74bSAdrian Hunter	def Enable(self, enable):
14158392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
14168392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
14178392b74bSAdrian Hunter
14188392b74bSAdrian Hunter	def Busy(self):
14198392b74bSAdrian Hunter		self.Enable(False)
14208392b74bSAdrian Hunter		self.fetch.hide()
14218392b74bSAdrian Hunter		self.spacer.hide()
14228392b74bSAdrian Hunter		self.progress.show()
14238392b74bSAdrian Hunter
14248392b74bSAdrian Hunter	def Idle(self):
14258392b74bSAdrian Hunter		self.in_progress = False
14268392b74bSAdrian Hunter		self.Enable(True)
14278392b74bSAdrian Hunter		self.progress.hide()
14288392b74bSAdrian Hunter		self.fetch.show()
14298392b74bSAdrian Hunter		self.spacer.show()
14308392b74bSAdrian Hunter
14318392b74bSAdrian Hunter	def Target(self):
14328392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
14338392b74bSAdrian Hunter
14348392b74bSAdrian Hunter	def Done(self):
14358392b74bSAdrian Hunter		self.done = True
14368392b74bSAdrian Hunter		self.Idle()
14378392b74bSAdrian Hunter		self.label.hide()
14388392b74bSAdrian Hunter		self.fetch_count.hide()
14398392b74bSAdrian Hunter		self.fetch.hide()
14408392b74bSAdrian Hunter		self.spacer.hide()
14418392b74bSAdrian Hunter		self.done_label.show()
14428392b74bSAdrian Hunter
14438392b74bSAdrian Hunter	def Progress(self, count):
14448392b74bSAdrian Hunter		if self.in_progress:
14458392b74bSAdrian Hunter			if count:
14468392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
14478392b74bSAdrian Hunter				if percent >= 100:
14488392b74bSAdrian Hunter					self.Idle()
14498392b74bSAdrian Hunter				else:
14508392b74bSAdrian Hunter					self.progress.setValue(percent)
14518392b74bSAdrian Hunter		if not count:
14528392b74bSAdrian Hunter			# Count value of zero means no more records
14538392b74bSAdrian Hunter			self.Done()
14548392b74bSAdrian Hunter
14558392b74bSAdrian Hunter	def FetchMoreRecords(self):
14568392b74bSAdrian Hunter		if self.done:
14578392b74bSAdrian Hunter			return
14588392b74bSAdrian Hunter		self.progress.setValue(0)
14598392b74bSAdrian Hunter		self.Busy()
14608392b74bSAdrian Hunter		self.in_progress = True
14618392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
14628392b74bSAdrian Hunter
146376099f98SAdrian Hunter# Brance data model level two item
146476099f98SAdrian Hunter
146576099f98SAdrian Hunterclass BranchLevelTwoItem():
146676099f98SAdrian Hunter
1467530e22fdSAdrian Hunter	def __init__(self, row, col, text, parent_item):
146876099f98SAdrian Hunter		self.row = row
146976099f98SAdrian Hunter		self.parent_item = parent_item
1470530e22fdSAdrian Hunter		self.data = [""] * (col + 1)
1471530e22fdSAdrian Hunter		self.data[col] = text
147276099f98SAdrian Hunter		self.level = 2
147376099f98SAdrian Hunter
147476099f98SAdrian Hunter	def getParentItem(self):
147576099f98SAdrian Hunter		return self.parent_item
147676099f98SAdrian Hunter
147776099f98SAdrian Hunter	def getRow(self):
147876099f98SAdrian Hunter		return self.row
147976099f98SAdrian Hunter
148076099f98SAdrian Hunter	def childCount(self):
148176099f98SAdrian Hunter		return 0
148276099f98SAdrian Hunter
148376099f98SAdrian Hunter	def hasChildren(self):
148476099f98SAdrian Hunter		return False
148576099f98SAdrian Hunter
148676099f98SAdrian Hunter	def getData(self, column):
148776099f98SAdrian Hunter		return self.data[column]
148876099f98SAdrian Hunter
148976099f98SAdrian Hunter# Brance data model level one item
149076099f98SAdrian Hunter
149176099f98SAdrian Hunterclass BranchLevelOneItem():
149276099f98SAdrian Hunter
149376099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
149476099f98SAdrian Hunter		self.glb = glb
149576099f98SAdrian Hunter		self.row = row
149676099f98SAdrian Hunter		self.parent_item = parent_item
149776099f98SAdrian Hunter		self.child_count = 0
149876099f98SAdrian Hunter		self.child_items = []
149976099f98SAdrian Hunter		self.data = data[1:]
150076099f98SAdrian Hunter		self.dbid = data[0]
150176099f98SAdrian Hunter		self.level = 1
150276099f98SAdrian Hunter		self.query_done = False
1503530e22fdSAdrian Hunter		self.br_col = len(self.data) - 1
150476099f98SAdrian Hunter
150576099f98SAdrian Hunter	def getChildItem(self, row):
150676099f98SAdrian Hunter		return self.child_items[row]
150776099f98SAdrian Hunter
150876099f98SAdrian Hunter	def getParentItem(self):
150976099f98SAdrian Hunter		return self.parent_item
151076099f98SAdrian Hunter
151176099f98SAdrian Hunter	def getRow(self):
151276099f98SAdrian Hunter		return self.row
151376099f98SAdrian Hunter
151476099f98SAdrian Hunter	def Select(self):
151576099f98SAdrian Hunter		self.query_done = True
151676099f98SAdrian Hunter
151776099f98SAdrian Hunter		if not self.glb.have_disassembler:
151876099f98SAdrian Hunter			return
151976099f98SAdrian Hunter
152076099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
152176099f98SAdrian Hunter
152276099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
152376099f98SAdrian Hunter				  " FROM samples"
152476099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
152576099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
152676099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
152776099f98SAdrian Hunter		if not query.next():
152876099f98SAdrian Hunter			return
152976099f98SAdrian Hunter		cpu = query.value(0)
153076099f98SAdrian Hunter		dso = query.value(1)
153176099f98SAdrian Hunter		sym = query.value(2)
153276099f98SAdrian Hunter		if dso == 0 or sym == 0:
153376099f98SAdrian Hunter			return
153476099f98SAdrian Hunter		off = query.value(3)
153576099f98SAdrian Hunter		short_name = query.value(4)
153676099f98SAdrian Hunter		long_name = query.value(5)
153776099f98SAdrian Hunter		build_id = query.value(6)
153876099f98SAdrian Hunter		sym_start = query.value(7)
153976099f98SAdrian Hunter		ip = query.value(8)
154076099f98SAdrian Hunter
154176099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
154276099f98SAdrian Hunter				  " FROM samples"
154376099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
154476099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
154576099f98SAdrian Hunter				  " ORDER BY samples.id"
154676099f98SAdrian Hunter				  " LIMIT 1")
154776099f98SAdrian Hunter		if not query.next():
154876099f98SAdrian Hunter			return
154976099f98SAdrian Hunter		if query.value(0) != dso:
155076099f98SAdrian Hunter			# Cannot disassemble from one dso to another
155176099f98SAdrian Hunter			return
155276099f98SAdrian Hunter		bsym = query.value(1)
155376099f98SAdrian Hunter		boff = query.value(2)
155476099f98SAdrian Hunter		bsym_start = query.value(3)
155576099f98SAdrian Hunter		if bsym == 0:
155676099f98SAdrian Hunter			return
155776099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
155876099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
155976099f98SAdrian Hunter			return
156076099f98SAdrian Hunter
156176099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
156276099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
156376099f98SAdrian Hunter		if not f:
156476099f98SAdrian Hunter			return
156576099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
156676099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
156776099f98SAdrian Hunter
156876099f98SAdrian Hunter		buf_sz = tot + 16
156976099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
157076099f98SAdrian Hunter		f.seek(sym_start + off)
157176099f98SAdrian Hunter		buf.value = f.read(buf_sz)
157276099f98SAdrian Hunter		buf_ptr = addressof(buf)
157376099f98SAdrian Hunter		i = 0
157476099f98SAdrian Hunter		while tot > 0:
157576099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
157676099f98SAdrian Hunter			if cnt:
157776099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
157876099f98SAdrian Hunter				for k in xrange(cnt):
157976099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
158076099f98SAdrian Hunter					i += 1
158176099f98SAdrian Hunter				while k < 15:
158276099f98SAdrian Hunter					byte_str += "   "
158376099f98SAdrian Hunter					k += 1
1584530e22fdSAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
158576099f98SAdrian Hunter				self.child_count += 1
158676099f98SAdrian Hunter			else:
158776099f98SAdrian Hunter				return
158876099f98SAdrian Hunter			buf_ptr += cnt
158976099f98SAdrian Hunter			tot -= cnt
159076099f98SAdrian Hunter			buf_sz -= cnt
159176099f98SAdrian Hunter			ip += cnt
159276099f98SAdrian Hunter
159376099f98SAdrian Hunter	def childCount(self):
159476099f98SAdrian Hunter		if not self.query_done:
159576099f98SAdrian Hunter			self.Select()
159676099f98SAdrian Hunter			if not self.child_count:
159776099f98SAdrian Hunter				return -1
159876099f98SAdrian Hunter		return self.child_count
159976099f98SAdrian Hunter
160076099f98SAdrian Hunter	def hasChildren(self):
160176099f98SAdrian Hunter		if not self.query_done:
160276099f98SAdrian Hunter			return True
160376099f98SAdrian Hunter		return self.child_count > 0
160476099f98SAdrian Hunter
160576099f98SAdrian Hunter	def getData(self, column):
160676099f98SAdrian Hunter		return self.data[column]
160776099f98SAdrian Hunter
160876099f98SAdrian Hunter# Brance data model root item
160976099f98SAdrian Hunter
161076099f98SAdrian Hunterclass BranchRootItem():
161176099f98SAdrian Hunter
161276099f98SAdrian Hunter	def __init__(self):
161376099f98SAdrian Hunter		self.child_count = 0
161476099f98SAdrian Hunter		self.child_items = []
161576099f98SAdrian Hunter		self.level = 0
161676099f98SAdrian Hunter
161776099f98SAdrian Hunter	def getChildItem(self, row):
161876099f98SAdrian Hunter		return self.child_items[row]
161976099f98SAdrian Hunter
162076099f98SAdrian Hunter	def getParentItem(self):
162176099f98SAdrian Hunter		return None
162276099f98SAdrian Hunter
162376099f98SAdrian Hunter	def getRow(self):
162476099f98SAdrian Hunter		return 0
162576099f98SAdrian Hunter
162676099f98SAdrian Hunter	def childCount(self):
162776099f98SAdrian Hunter		return self.child_count
162876099f98SAdrian Hunter
162976099f98SAdrian Hunter	def hasChildren(self):
163076099f98SAdrian Hunter		return self.child_count > 0
163176099f98SAdrian Hunter
163276099f98SAdrian Hunter	def getData(self, column):
163376099f98SAdrian Hunter		return ""
163476099f98SAdrian Hunter
1635530e22fdSAdrian Hunter# Calculate instructions per cycle
1636530e22fdSAdrian Hunter
1637530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt):
1638530e22fdSAdrian Hunter	if cyc_cnt and insn_cnt:
1639530e22fdSAdrian Hunter		ipc = Decimal(float(insn_cnt) / cyc_cnt)
1640530e22fdSAdrian Hunter		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
1641530e22fdSAdrian Hunter	else:
1642530e22fdSAdrian Hunter		ipc = "0"
1643530e22fdSAdrian Hunter	return ipc
1644530e22fdSAdrian Hunter
164576099f98SAdrian Hunter# Branch data preparation
164676099f98SAdrian Hunter
1647530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data):
1648530e22fdSAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
1649530e22fdSAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
1650530e22fdSAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
1651530e22fdSAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
1652530e22fdSAdrian Hunter
1653530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data):
1654530e22fdSAdrian Hunter	insn_cnt = query.value(16)
1655530e22fdSAdrian Hunter	cyc_cnt = query.value(17)
1656530e22fdSAdrian Hunter	ipc = CalcIPC(cyc_cnt, insn_cnt)
1657530e22fdSAdrian Hunter	data.append(insn_cnt)
1658530e22fdSAdrian Hunter	data.append(cyc_cnt)
1659530e22fdSAdrian Hunter	data.append(ipc)
1660530e22fdSAdrian Hunter
166176099f98SAdrian Hunterdef BranchDataPrep(query):
166276099f98SAdrian Hunter	data = []
166376099f98SAdrian Hunter	for i in xrange(0, 8):
166476099f98SAdrian Hunter		data.append(query.value(i))
1665530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
166676099f98SAdrian Hunter	return data
166776099f98SAdrian Hunter
16688453c936SAdrian Hunterdef BranchDataPrepWA(query):
16698453c936SAdrian Hunter	data = []
16708453c936SAdrian Hunter	data.append(query.value(0))
16718453c936SAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
16728453c936SAdrian Hunter	data.append("{:>19}".format(query.value(1)))
16738453c936SAdrian Hunter	for i in xrange(2, 8):
16748453c936SAdrian Hunter		data.append(query.value(i))
1675530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1676530e22fdSAdrian Hunter	return data
1677530e22fdSAdrian Hunter
1678530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query):
1679530e22fdSAdrian Hunter	data = []
1680530e22fdSAdrian Hunter	for i in xrange(0, 8):
1681530e22fdSAdrian Hunter		data.append(query.value(i))
1682530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1683530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1684530e22fdSAdrian Hunter	return data
1685530e22fdSAdrian Hunter
1686530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query):
1687530e22fdSAdrian Hunter	data = []
1688530e22fdSAdrian Hunter	data.append(query.value(0))
1689530e22fdSAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
1690530e22fdSAdrian Hunter	data.append("{:>19}".format(query.value(1)))
1691530e22fdSAdrian Hunter	for i in xrange(2, 8):
1692530e22fdSAdrian Hunter		data.append(query.value(i))
1693530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1694530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
16958453c936SAdrian Hunter	return data
16968453c936SAdrian Hunter
169776099f98SAdrian Hunter# Branch data model
169876099f98SAdrian Hunter
169976099f98SAdrian Hunterclass BranchModel(TreeModel):
170076099f98SAdrian Hunter
170176099f98SAdrian Hunter	progress = Signal(object)
170276099f98SAdrian Hunter
170376099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
17044a0979d4SAdrian Hunter		super(BranchModel, self).__init__(glb, None, parent)
170576099f98SAdrian Hunter		self.event_id = event_id
170676099f98SAdrian Hunter		self.more = True
170776099f98SAdrian Hunter		self.populated = 0
1708530e22fdSAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
1709530e22fdSAdrian Hunter		if self.have_ipc:
1710530e22fdSAdrian Hunter			select_ipc = ", insn_count, cyc_count"
1711530e22fdSAdrian Hunter			prep_fn = BranchDataWithIPCPrep
1712530e22fdSAdrian Hunter			prep_wa_fn = BranchDataWithIPCPrepWA
1713530e22fdSAdrian Hunter		else:
1714530e22fdSAdrian Hunter			select_ipc = ""
1715530e22fdSAdrian Hunter			prep_fn = BranchDataPrep
1716530e22fdSAdrian Hunter			prep_wa_fn = BranchDataPrepWA
171776099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
171876099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
171976099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
172076099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
1721530e22fdSAdrian Hunter			+ select_ipc +
172276099f98SAdrian Hunter			" FROM samples"
172376099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
172476099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
172576099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
172676099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
172776099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
172876099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
172976099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
173076099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
173176099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
173276099f98SAdrian Hunter			" ORDER BY samples.id"
173376099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
17348453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
1735530e22fdSAdrian Hunter			prep = prep_fn
17368453c936SAdrian Hunter		else:
1737530e22fdSAdrian Hunter			prep = prep_wa_fn
17388453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
173976099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
174076099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
174176099f98SAdrian Hunter
1742a448ba23SAdrian Hunter	def GetRoot(self):
1743a448ba23SAdrian Hunter		return BranchRootItem()
1744a448ba23SAdrian Hunter
174576099f98SAdrian Hunter	def columnCount(self, parent=None):
1746530e22fdSAdrian Hunter		if self.have_ipc:
1747530e22fdSAdrian Hunter			return 11
1748530e22fdSAdrian Hunter		else:
174976099f98SAdrian Hunter			return 8
175076099f98SAdrian Hunter
175176099f98SAdrian Hunter	def columnHeader(self, column):
1752530e22fdSAdrian Hunter		if self.have_ipc:
1753530e22fdSAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
1754530e22fdSAdrian Hunter		else:
175576099f98SAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
175676099f98SAdrian Hunter
175776099f98SAdrian Hunter	def columnFont(self, column):
1758530e22fdSAdrian Hunter		if self.have_ipc:
1759530e22fdSAdrian Hunter			br_col = 10
1760530e22fdSAdrian Hunter		else:
1761530e22fdSAdrian Hunter			br_col = 7
1762530e22fdSAdrian Hunter		if column != br_col:
176376099f98SAdrian Hunter			return None
176476099f98SAdrian Hunter		return QFont("Monospace")
176576099f98SAdrian Hunter
176676099f98SAdrian Hunter	def DisplayData(self, item, index):
176776099f98SAdrian Hunter		if item.level == 1:
176876099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
176976099f98SAdrian Hunter		return item.getData(index.column())
177076099f98SAdrian Hunter
177176099f98SAdrian Hunter	def AddSample(self, data):
177276099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
177376099f98SAdrian Hunter		self.root.child_items.append(child)
177476099f98SAdrian Hunter		self.populated += 1
177576099f98SAdrian Hunter
177676099f98SAdrian Hunter	def Update(self, fetched):
177776099f98SAdrian Hunter		if not fetched:
177876099f98SAdrian Hunter			self.more = False
177976099f98SAdrian Hunter			self.progress.emit(0)
178076099f98SAdrian Hunter		child_count = self.root.child_count
178176099f98SAdrian Hunter		count = self.populated - child_count
178276099f98SAdrian Hunter		if count > 0:
178376099f98SAdrian Hunter			parent = QModelIndex()
178476099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
178576099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
178676099f98SAdrian Hunter			self.root.child_count += count
178776099f98SAdrian Hunter			self.endInsertRows()
178876099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
178976099f98SAdrian Hunter
179076099f98SAdrian Hunter	def FetchMoreRecords(self, count):
179176099f98SAdrian Hunter		current = self.root.child_count
179276099f98SAdrian Hunter		if self.more:
179376099f98SAdrian Hunter			self.fetcher.Fetch(count)
179476099f98SAdrian Hunter		else:
179576099f98SAdrian Hunter			self.progress.emit(0)
179676099f98SAdrian Hunter		return current
179776099f98SAdrian Hunter
179876099f98SAdrian Hunter	def HasMoreRecords(self):
179976099f98SAdrian Hunter		return self.more
180076099f98SAdrian Hunter
18010bf0947aSAdrian Hunter# Report Variables
18020bf0947aSAdrian Hunter
18030bf0947aSAdrian Hunterclass ReportVars():
18040bf0947aSAdrian Hunter
1805cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1806947cc38dSAdrian Hunter		self.name = name
18070bf0947aSAdrian Hunter		self.where_clause = where_clause
1808cd358012SAdrian Hunter		self.limit = limit
18090bf0947aSAdrian Hunter
18100bf0947aSAdrian Hunter	def UniqueId(self):
1811cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
18120bf0947aSAdrian Hunter
181376099f98SAdrian Hunter# Branch window
181476099f98SAdrian Hunter
181576099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
181676099f98SAdrian Hunter
1817947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
181876099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
181976099f98SAdrian Hunter
18200bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
182176099f98SAdrian Hunter
18220bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
182376099f98SAdrian Hunter
182476099f98SAdrian Hunter		self.view = QTreeView()
182576099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
182696c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
182796c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
182876099f98SAdrian Hunter		self.view.setModel(self.model)
182976099f98SAdrian Hunter
183076099f98SAdrian Hunter		self.ResizeColumnsToContents()
183176099f98SAdrian Hunter
18329bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
18339bc4e4bfSAdrian Hunter
183476099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
183576099f98SAdrian Hunter
183676099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
183776099f98SAdrian Hunter
183876099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
183976099f98SAdrian Hunter
184076099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
184176099f98SAdrian Hunter
184276099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
184376099f98SAdrian Hunter
1844947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
184576099f98SAdrian Hunter
184676099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
184776099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
184876099f98SAdrian Hunter		# so implement a crude alternative
184976099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
185076099f98SAdrian Hunter		font = self.view.font()
185176099f98SAdrian Hunter		metrics = QFontMetrics(font)
185276099f98SAdrian Hunter		max = 0
185376099f98SAdrian Hunter		for row in xrange(n):
185476099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
185576099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
185676099f98SAdrian Hunter			max = len if len > max else max
185776099f98SAdrian Hunter		val = self.model.columnHeader(column)
185876099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
185976099f98SAdrian Hunter		max = len if len > max else max
186076099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
186176099f98SAdrian Hunter
186276099f98SAdrian Hunter	def ResizeColumnsToContents(self):
186376099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
186476099f98SAdrian Hunter		if n < 1:
186576099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
186676099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
186776099f98SAdrian Hunter			return
186876099f98SAdrian Hunter		columns = self.model.columnCount()
186976099f98SAdrian Hunter		for i in xrange(columns):
187076099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
187176099f98SAdrian Hunter
187276099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
187376099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
187476099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
187576099f98SAdrian Hunter		self.ResizeColumnsToContents()
187676099f98SAdrian Hunter
187776099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
187876099f98SAdrian Hunter		self.view.setFocus()
187976099f98SAdrian Hunter		self.find_bar.Busy()
188076099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
188176099f98SAdrian Hunter
188276099f98SAdrian Hunter	def FindDone(self, row):
188376099f98SAdrian Hunter		self.find_bar.Idle()
188476099f98SAdrian Hunter		if row >= 0:
188576099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
188676099f98SAdrian Hunter		else:
188776099f98SAdrian Hunter			self.find_bar.NotFound()
188876099f98SAdrian Hunter
18891c3ca1b3SAdrian Hunter# Line edit data item
18901c3ca1b3SAdrian Hunter
18911c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
18921c3ca1b3SAdrian Hunter
1893cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
18941c3ca1b3SAdrian Hunter		self.glb = glb
18951c3ca1b3SAdrian Hunter		self.label = label
18961c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
18971c3ca1b3SAdrian Hunter		self.parent = parent
18981c3ca1b3SAdrian Hunter		self.id = id
18991c3ca1b3SAdrian Hunter
1900cd358012SAdrian Hunter		self.value = default
19011c3ca1b3SAdrian Hunter
1902cd358012SAdrian Hunter		self.widget = QLineEdit(default)
19031c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
19041c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
19051c3ca1b3SAdrian Hunter		self.red = False
19061c3ca1b3SAdrian Hunter		self.error = ""
19071c3ca1b3SAdrian Hunter		self.validated = True
19081c3ca1b3SAdrian Hunter
19091c3ca1b3SAdrian Hunter		if placeholder_text:
19101c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
19111c3ca1b3SAdrian Hunter
19121c3ca1b3SAdrian Hunter	def TurnTextRed(self):
19131c3ca1b3SAdrian Hunter		if not self.red:
19141c3ca1b3SAdrian Hunter			palette = QPalette()
19151c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
19161c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
19171c3ca1b3SAdrian Hunter			self.red = True
19181c3ca1b3SAdrian Hunter
19191c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
19201c3ca1b3SAdrian Hunter		if self.red:
19211c3ca1b3SAdrian Hunter			palette = QPalette()
19221c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
19231c3ca1b3SAdrian Hunter			self.red = False
19241c3ca1b3SAdrian Hunter
19251c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
19261c3ca1b3SAdrian Hunter		self.value = ""
19271c3ca1b3SAdrian Hunter		self.TurnTextRed()
19281c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
19291c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
19301c3ca1b3SAdrian Hunter
19311c3ca1b3SAdrian Hunter	def Invalidate(self):
19321c3ca1b3SAdrian Hunter		self.validated = False
19331c3ca1b3SAdrian Hunter
19341c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
19351c3ca1b3SAdrian Hunter		self.value = input_string.strip()
19361c3ca1b3SAdrian Hunter
19371c3ca1b3SAdrian Hunter	def Validate(self):
19381c3ca1b3SAdrian Hunter		self.validated = True
19391c3ca1b3SAdrian Hunter		self.error = ""
19401c3ca1b3SAdrian Hunter		self.TurnTextNormal()
19411c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
19421c3ca1b3SAdrian Hunter		input_string = self.widget.text()
19431c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
19441c3ca1b3SAdrian Hunter			self.value = ""
19451c3ca1b3SAdrian Hunter			return
19461c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
19471c3ca1b3SAdrian Hunter
19481c3ca1b3SAdrian Hunter	def IsValid(self):
19491c3ca1b3SAdrian Hunter		if not self.validated:
19501c3ca1b3SAdrian Hunter			self.Validate()
19511c3ca1b3SAdrian Hunter		if len(self.error):
19521c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
19531c3ca1b3SAdrian Hunter			return False
19541c3ca1b3SAdrian Hunter		return True
19551c3ca1b3SAdrian Hunter
19561c3ca1b3SAdrian Hunter	def IsNumber(self, value):
19571c3ca1b3SAdrian Hunter		try:
19581c3ca1b3SAdrian Hunter			x = int(value)
19591c3ca1b3SAdrian Hunter		except:
19601c3ca1b3SAdrian Hunter			x = 0
19611c3ca1b3SAdrian Hunter		return str(x) == value
19621c3ca1b3SAdrian Hunter
19631c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
19641c3ca1b3SAdrian Hunter
19651c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
19661c3ca1b3SAdrian Hunter
19671c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
19681c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
19691c3ca1b3SAdrian Hunter
19701c3ca1b3SAdrian Hunter		self.column_name = column_name
19711c3ca1b3SAdrian Hunter
19721c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
19731c3ca1b3SAdrian Hunter		singles = []
19741c3ca1b3SAdrian Hunter		ranges = []
19751c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
19761c3ca1b3SAdrian Hunter			if "-" in value:
19771c3ca1b3SAdrian Hunter				vrange = value.split("-")
19781c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
19791c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
19801c3ca1b3SAdrian Hunter				ranges.append(vrange)
19811c3ca1b3SAdrian Hunter			else:
19821c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
19831c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
19841c3ca1b3SAdrian Hunter				singles.append(value)
19851c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
19861c3ca1b3SAdrian Hunter		if len(singles):
19871c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
19881c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
19891c3ca1b3SAdrian Hunter
1990cd358012SAdrian Hunter# Positive integer dialog data item
1991cd358012SAdrian Hunter
1992cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
1993cd358012SAdrian Hunter
1994cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1995cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1996cd358012SAdrian Hunter
1997cd358012SAdrian Hunter	def DoValidate(self, input_string):
1998cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
1999cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2000cd358012SAdrian Hunter		value = int(input_string.strip())
2001cd358012SAdrian Hunter		if value <= 0:
2002cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2003cd358012SAdrian Hunter		self.value = str(value)
2004cd358012SAdrian Hunter
20051c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
20061c3ca1b3SAdrian Hunter
20071c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
20081c3ca1b3SAdrian Hunter
20091c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
20101c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
20111c3ca1b3SAdrian Hunter
20121c3ca1b3SAdrian Hunter		self.table_name = table_name
20131c3ca1b3SAdrian Hunter		self.match_column = match_column
20141c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
20151c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
20161c3ca1b3SAdrian Hunter
20171c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
20181c3ca1b3SAdrian Hunter		ids = []
20191c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
20201c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
20211c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
20221c3ca1b3SAdrian Hunter		if ret:
20231c3ca1b3SAdrian Hunter			while query.next():
20241c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
20251c3ca1b3SAdrian Hunter		return ids
20261c3ca1b3SAdrian Hunter
20271c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
20281c3ca1b3SAdrian Hunter		all_ids = []
20291c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
20301c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
20311c3ca1b3SAdrian Hunter			if len(ids):
20321c3ca1b3SAdrian Hunter				all_ids.extend(ids)
20331c3ca1b3SAdrian Hunter			else:
20341c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
20351c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
20361c3ca1b3SAdrian Hunter		if self.column_name2:
20371c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
20381c3ca1b3SAdrian Hunter
20391c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
20401c3ca1b3SAdrian Hunter
20411c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
20421c3ca1b3SAdrian Hunter
20431c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
20441c3ca1b3SAdrian Hunter		self.column_name = column_name
20451c3ca1b3SAdrian Hunter
20461c3ca1b3SAdrian Hunter		self.last_id = 0
20471c3ca1b3SAdrian Hunter		self.first_time = 0
20481c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
20491c3ca1b3SAdrian Hunter
20501c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
20511c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
20521c3ca1b3SAdrian Hunter		if query.next():
20531c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
20541c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
20551c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
20561c3ca1b3SAdrian Hunter		if query.next():
20571c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
20581c3ca1b3SAdrian Hunter		if placeholder_text:
20591c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
20601c3ca1b3SAdrian Hunter
20611c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
20621c3ca1b3SAdrian Hunter
20631c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
20641c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
20651c3ca1b3SAdrian Hunter		if query.next():
20661c3ca1b3SAdrian Hunter			return True, int(query.value(0))
20671c3ca1b3SAdrian Hunter		else:
20681c3ca1b3SAdrian Hunter			return False, 0
20691c3ca1b3SAdrian Hunter
20701c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
20711c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
20721c3ca1b3SAdrian Hunter		while True:
20731c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
20741c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
20751c3ca1b3SAdrian Hunter			if not query.next():
20761c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
20771c3ca1b3SAdrian Hunter				if not ok:
20781c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
20791c3ca1b3SAdrian Hunter					if not ok:
20801c3ca1b3SAdrian Hunter						return str(higher_id)
20811c3ca1b3SAdrian Hunter				next_id = dbid
20821c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
20831c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
20841c3ca1b3SAdrian Hunter			if get_floor:
20851c3ca1b3SAdrian Hunter				if target_time > next_time:
20861c3ca1b3SAdrian Hunter					lower_id = next_id
20871c3ca1b3SAdrian Hunter				else:
20881c3ca1b3SAdrian Hunter					higher_id = next_id
20891c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
20901c3ca1b3SAdrian Hunter					return str(higher_id)
20911c3ca1b3SAdrian Hunter			else:
20921c3ca1b3SAdrian Hunter				if target_time >= next_time:
20931c3ca1b3SAdrian Hunter					lower_id = next_id
20941c3ca1b3SAdrian Hunter				else:
20951c3ca1b3SAdrian Hunter					higher_id = next_id
20961c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
20971c3ca1b3SAdrian Hunter					return str(lower_id)
20981c3ca1b3SAdrian Hunter
20991c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
21001c3ca1b3SAdrian Hunter		mult = 1
21011c3ca1b3SAdrian Hunter		suffix = val[-2:]
21021c3ca1b3SAdrian Hunter		if suffix == "ms":
21031c3ca1b3SAdrian Hunter			mult = 1000000
21041c3ca1b3SAdrian Hunter		elif suffix == "us":
21051c3ca1b3SAdrian Hunter			mult = 1000
21061c3ca1b3SAdrian Hunter		elif suffix == "ns":
21071c3ca1b3SAdrian Hunter			mult = 1
21081c3ca1b3SAdrian Hunter		else:
21091c3ca1b3SAdrian Hunter			return val
21101c3ca1b3SAdrian Hunter		val = val[:-2].strip()
21111c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
21121c3ca1b3SAdrian Hunter			return val
21131c3ca1b3SAdrian Hunter		val = int(val) * mult
21141c3ca1b3SAdrian Hunter		if val >= 0:
21151c3ca1b3SAdrian Hunter			val += self.first_time
21161c3ca1b3SAdrian Hunter		else:
21171c3ca1b3SAdrian Hunter			val += self.last_time
21181c3ca1b3SAdrian Hunter		return str(val)
21191c3ca1b3SAdrian Hunter
21201c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
21211c3ca1b3SAdrian Hunter		if vrange[0] == "":
21221c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
21231c3ca1b3SAdrian Hunter		if vrange[1] == "":
21241c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
21251c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
21261c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
21271c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
21281c3ca1b3SAdrian Hunter			return False
21291c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
21301c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
21311c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
21321c3ca1b3SAdrian Hunter			return False
21331c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
21341c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
21351c3ca1b3SAdrian Hunter		return True
21361c3ca1b3SAdrian Hunter
21371c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
21381c3ca1b3SAdrian Hunter		n = value.count("-")
21391c3ca1b3SAdrian Hunter		if n == 1:
21401c3ca1b3SAdrian Hunter			pass
21411c3ca1b3SAdrian Hunter		elif n == 2:
21421c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
21431c3ca1b3SAdrian Hunter				n = 1
21441c3ca1b3SAdrian Hunter		elif n == 3:
21451c3ca1b3SAdrian Hunter			n = 2
21461c3ca1b3SAdrian Hunter		else:
21471c3ca1b3SAdrian Hunter			return False
21481c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
21491c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
21501c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
21511c3ca1b3SAdrian Hunter			ranges.append(vrange)
21521c3ca1b3SAdrian Hunter			return True
21531c3ca1b3SAdrian Hunter		return False
21541c3ca1b3SAdrian Hunter
21551c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
21561c3ca1b3SAdrian Hunter		ranges = []
21571c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
21581c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
21591c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
21601c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
21611c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
21621c3ca1b3SAdrian Hunter
21630924cd68SAdrian Hunter# Report Dialog Base
2164210cf1f9SAdrian Hunter
21650924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
2166210cf1f9SAdrian Hunter
21670924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
21680924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
2169210cf1f9SAdrian Hunter
2170210cf1f9SAdrian Hunter		self.glb = glb
2171210cf1f9SAdrian Hunter
21720bf0947aSAdrian Hunter		self.report_vars = ReportVars()
2173210cf1f9SAdrian Hunter
21740924cd68SAdrian Hunter		self.setWindowTitle(title)
2175210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
2176210cf1f9SAdrian Hunter
21771c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
2178210cf1f9SAdrian Hunter
21790924cd68SAdrian Hunter		self.partial = partial
21800924cd68SAdrian Hunter
2181210cf1f9SAdrian Hunter		self.grid = QGridLayout()
2182210cf1f9SAdrian Hunter
2183210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
2184210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
2185210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
2186210cf1f9SAdrian Hunter
2187210cf1f9SAdrian Hunter		self.status = QLabel()
2188210cf1f9SAdrian Hunter
2189210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
2190210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
2191210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
2192210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2193210cf1f9SAdrian Hunter
2194210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
2195210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
2196210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2197210cf1f9SAdrian Hunter
2198210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
2199210cf1f9SAdrian Hunter		#self.hbox.addStretch()
2200210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
2201210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
2202210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
2203210cf1f9SAdrian Hunter
2204210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
2205210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
2206210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
2207210cf1f9SAdrian Hunter
2208210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
2209210cf1f9SAdrian Hunter
2210210cf1f9SAdrian Hunter	def Ok(self):
22110bf0947aSAdrian Hunter		vars = self.report_vars
22121c3ca1b3SAdrian Hunter		for d in self.data_items:
22131c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
22141c3ca1b3SAdrian Hunter				vars.name = d.value
2215947cc38dSAdrian Hunter		if not vars.name:
2216210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2217210cf1f9SAdrian Hunter			return
2218210cf1f9SAdrian Hunter		for d in self.data_items:
2219210cf1f9SAdrian Hunter			if not d.IsValid():
2220210cf1f9SAdrian Hunter				return
2221210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2222cd358012SAdrian Hunter			if d.id == "LIMIT":
2223cd358012SAdrian Hunter				vars.limit = d.value
2224cd358012SAdrian Hunter			elif len(d.value):
22250bf0947aSAdrian Hunter				if len(vars.where_clause):
22260bf0947aSAdrian Hunter					vars.where_clause += " AND "
22270bf0947aSAdrian Hunter				vars.where_clause += d.value
22280bf0947aSAdrian Hunter		if len(vars.where_clause):
22290924cd68SAdrian Hunter			if self.partial:
22300bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2231210cf1f9SAdrian Hunter			else:
22320bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
2233210cf1f9SAdrian Hunter		self.accept()
2234210cf1f9SAdrian Hunter
2235210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2236210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2237210cf1f9SAdrian Hunter
2238210cf1f9SAdrian Hunter	def ClearMessage(self):
2239210cf1f9SAdrian Hunter		self.status.setText("")
2240210cf1f9SAdrian Hunter
22410924cd68SAdrian Hunter# Selected branch report creation dialog
22420924cd68SAdrian Hunter
22430924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
22440924cd68SAdrian Hunter
22450924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
22460924cd68SAdrian Hunter		title = "Selected Branches"
22471c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
22481c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
22491c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
22501c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
22511c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
22521c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
22531c3ca1b3SAdrian 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),
22541c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
22551c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
22560924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
22570924cd68SAdrian Hunter
225876099f98SAdrian Hunter# Event list
225976099f98SAdrian Hunter
226076099f98SAdrian Hunterdef GetEventList(db):
226176099f98SAdrian Hunter	events = []
226276099f98SAdrian Hunter	query = QSqlQuery(db)
226376099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
226476099f98SAdrian Hunter	while query.next():
226576099f98SAdrian Hunter		events.append(query.value(0))
226676099f98SAdrian Hunter	return events
226776099f98SAdrian Hunter
2268655cb952SAdrian Hunter# Is a table selectable
2269655cb952SAdrian Hunter
2270530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"):
2271655cb952SAdrian Hunter	query = QSqlQuery(db)
2272655cb952SAdrian Hunter	try:
2273530e22fdSAdrian Hunter		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
2274655cb952SAdrian Hunter	except:
2275655cb952SAdrian Hunter		return False
2276655cb952SAdrian Hunter	return True
2277655cb952SAdrian Hunter
22788392b74bSAdrian Hunter# SQL table data model item
22798392b74bSAdrian Hunter
22808392b74bSAdrian Hunterclass SQLTableItem():
22818392b74bSAdrian Hunter
22828392b74bSAdrian Hunter	def __init__(self, row, data):
22838392b74bSAdrian Hunter		self.row = row
22848392b74bSAdrian Hunter		self.data = data
22858392b74bSAdrian Hunter
22868392b74bSAdrian Hunter	def getData(self, column):
22878392b74bSAdrian Hunter		return self.data[column]
22888392b74bSAdrian Hunter
22898392b74bSAdrian Hunter# SQL table data model
22908392b74bSAdrian Hunter
22918392b74bSAdrian Hunterclass SQLTableModel(TableModel):
22928392b74bSAdrian Hunter
22938392b74bSAdrian Hunter	progress = Signal(object)
22948392b74bSAdrian Hunter
22958c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
22968392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
22978392b74bSAdrian Hunter		self.glb = glb
22988392b74bSAdrian Hunter		self.more = True
22998392b74bSAdrian Hunter		self.populated = 0
23008c90fef9SAdrian Hunter		self.column_headers = column_headers
23018453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample)
23028392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
23038392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
23048392b74bSAdrian Hunter
23058392b74bSAdrian Hunter	def DisplayData(self, item, index):
23068392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
23078392b74bSAdrian Hunter		return item.getData(index.column())
23088392b74bSAdrian Hunter
23098392b74bSAdrian Hunter	def AddSample(self, data):
23108392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
23118392b74bSAdrian Hunter		self.child_items.append(child)
23128392b74bSAdrian Hunter		self.populated += 1
23138392b74bSAdrian Hunter
23148392b74bSAdrian Hunter	def Update(self, fetched):
23158392b74bSAdrian Hunter		if not fetched:
23168392b74bSAdrian Hunter			self.more = False
23178392b74bSAdrian Hunter			self.progress.emit(0)
23188392b74bSAdrian Hunter		child_count = self.child_count
23198392b74bSAdrian Hunter		count = self.populated - child_count
23208392b74bSAdrian Hunter		if count > 0:
23218392b74bSAdrian Hunter			parent = QModelIndex()
23228392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
23238392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
23248392b74bSAdrian Hunter			self.child_count += count
23258392b74bSAdrian Hunter			self.endInsertRows()
23268392b74bSAdrian Hunter			self.progress.emit(self.child_count)
23278392b74bSAdrian Hunter
23288392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
23298392b74bSAdrian Hunter		current = self.child_count
23308392b74bSAdrian Hunter		if self.more:
23318392b74bSAdrian Hunter			self.fetcher.Fetch(count)
23328392b74bSAdrian Hunter		else:
23338392b74bSAdrian Hunter			self.progress.emit(0)
23348392b74bSAdrian Hunter		return current
23358392b74bSAdrian Hunter
23368392b74bSAdrian Hunter	def HasMoreRecords(self):
23378392b74bSAdrian Hunter		return self.more
23388392b74bSAdrian Hunter
23398c90fef9SAdrian Hunter	def columnCount(self, parent=None):
23408c90fef9SAdrian Hunter		return len(self.column_headers)
23418c90fef9SAdrian Hunter
23428c90fef9SAdrian Hunter	def columnHeader(self, column):
23438c90fef9SAdrian Hunter		return self.column_headers[column]
23448c90fef9SAdrian Hunter
23458453c936SAdrian Hunter	def SQLTableDataPrep(self, query, count):
23468453c936SAdrian Hunter		data = []
23478453c936SAdrian Hunter		for i in xrange(count):
23488453c936SAdrian Hunter			data.append(query.value(i))
23498453c936SAdrian Hunter		return data
23508453c936SAdrian Hunter
23518392b74bSAdrian Hunter# SQL automatic table data model
23528392b74bSAdrian Hunter
23538392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
23548392b74bSAdrian Hunter
23558392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
23568392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
23578392b74bSAdrian Hunter		if table_name == "comm_threads_view":
23588392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
23598392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
23608c90fef9SAdrian Hunter		column_headers = []
23618392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
23628392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
23638392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
23648392b74bSAdrian Hunter			while query.next():
23658c90fef9SAdrian Hunter				column_headers.append(query.value(1))
23668392b74bSAdrian Hunter			if table_name == "sqlite_master":
23678392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
23688392b74bSAdrian Hunter		else:
23698392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
23708392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
23718392b74bSAdrian Hunter				select_table_name = table_name[19:]
23728392b74bSAdrian Hunter				schema = "information_schema"
23738392b74bSAdrian Hunter			else:
23748392b74bSAdrian Hunter				select_table_name = table_name
23758392b74bSAdrian Hunter				schema = "public"
23768392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
23778392b74bSAdrian Hunter			while query.next():
23788c90fef9SAdrian Hunter				column_headers.append(query.value(0))
23798453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
23808453c936SAdrian Hunter			if table_name == "samples_view":
23818453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_view_DataPrep
23828453c936SAdrian Hunter			if table_name == "samples":
23838453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_DataPrep
23848c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
23858392b74bSAdrian Hunter
23868453c936SAdrian Hunter	def samples_view_DataPrep(self, query, count):
23878453c936SAdrian Hunter		data = []
23888453c936SAdrian Hunter		data.append(query.value(0))
23898453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
23908453c936SAdrian Hunter		data.append("{:>19}".format(query.value(1)))
23918453c936SAdrian Hunter		for i in xrange(2, count):
23928453c936SAdrian Hunter			data.append(query.value(i))
23938453c936SAdrian Hunter		return data
23948453c936SAdrian Hunter
23958453c936SAdrian Hunter	def samples_DataPrep(self, query, count):
23968453c936SAdrian Hunter		data = []
23978453c936SAdrian Hunter		for i in xrange(9):
23988453c936SAdrian Hunter			data.append(query.value(i))
23998453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
24008453c936SAdrian Hunter		data.append("{:>19}".format(query.value(9)))
24018453c936SAdrian Hunter		for i in xrange(10, count):
24028453c936SAdrian Hunter			data.append(query.value(i))
24038453c936SAdrian Hunter		return data
24048453c936SAdrian Hunter
24058392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
24068392b74bSAdrian Hunter
24078392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
24088392b74bSAdrian Hunter
24098392b74bSAdrian Hunter	def __init__(self, parent=None):
24108392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
24118392b74bSAdrian Hunter
24128392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
24138392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
24148392b74bSAdrian Hunter		# so implement a crude alternative
24158392b74bSAdrian Hunter		font = self.view.font()
24168392b74bSAdrian Hunter		metrics = QFontMetrics(font)
24178392b74bSAdrian Hunter		max = 0
24188392b74bSAdrian Hunter		for row in xrange(n):
24198392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
24208392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
24218392b74bSAdrian Hunter			max = len if len > max else max
24228392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
24238392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
24248392b74bSAdrian Hunter		max = len if len > max else max
24258392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
24268392b74bSAdrian Hunter
24278392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
24288392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
24298392b74bSAdrian Hunter		if n < 1:
24308392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
24318392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
24328392b74bSAdrian Hunter			return
24338392b74bSAdrian Hunter		columns = self.data_model.columnCount()
24348392b74bSAdrian Hunter		for i in xrange(columns):
24358392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
24368392b74bSAdrian Hunter
24378392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
24388392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
24398392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
24408392b74bSAdrian Hunter		self.ResizeColumnsToContents()
24418392b74bSAdrian Hunter
244296c43b9aSAdrian Hunter# Convert value to CSV
244396c43b9aSAdrian Hunter
244496c43b9aSAdrian Hunterdef ToCSValue(val):
244596c43b9aSAdrian Hunter	if '"' in val:
244696c43b9aSAdrian Hunter		val = val.replace('"', '""')
244796c43b9aSAdrian Hunter	if "," in val or '"' in val:
244896c43b9aSAdrian Hunter		val = '"' + val + '"'
244996c43b9aSAdrian Hunter	return val
245096c43b9aSAdrian Hunter
245196c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
245296c43b9aSAdrian Hunter
245396c43b9aSAdrian Hunterglb_max_cols = 1000
245496c43b9aSAdrian Hunter
245596c43b9aSAdrian Hunterdef RowColumnKey(a):
245696c43b9aSAdrian Hunter	return a.row() * glb_max_cols + a.column()
245796c43b9aSAdrian Hunter
245896c43b9aSAdrian Hunter# Copy selected table cells to clipboard
245996c43b9aSAdrian Hunter
246096c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
246196c43b9aSAdrian Hunter	indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
246296c43b9aSAdrian Hunter	idx_cnt = len(indexes)
246396c43b9aSAdrian Hunter	if not idx_cnt:
246496c43b9aSAdrian Hunter		return
246596c43b9aSAdrian Hunter	if idx_cnt == 1:
246696c43b9aSAdrian Hunter		with_hdr=False
246796c43b9aSAdrian Hunter	min_row = indexes[0].row()
246896c43b9aSAdrian Hunter	max_row = indexes[0].row()
246996c43b9aSAdrian Hunter	min_col = indexes[0].column()
247096c43b9aSAdrian Hunter	max_col = indexes[0].column()
247196c43b9aSAdrian Hunter	for i in indexes:
247296c43b9aSAdrian Hunter		min_row = min(min_row, i.row())
247396c43b9aSAdrian Hunter		max_row = max(max_row, i.row())
247496c43b9aSAdrian Hunter		min_col = min(min_col, i.column())
247596c43b9aSAdrian Hunter		max_col = max(max_col, i.column())
247696c43b9aSAdrian Hunter	if max_col > glb_max_cols:
247796c43b9aSAdrian Hunter		raise RuntimeError("glb_max_cols is too low")
247896c43b9aSAdrian Hunter	max_width = [0] * (1 + max_col - min_col)
247996c43b9aSAdrian Hunter	for i in indexes:
248096c43b9aSAdrian Hunter		c = i.column() - min_col
248196c43b9aSAdrian Hunter		max_width[c] = max(max_width[c], len(str(i.data())))
248296c43b9aSAdrian Hunter	text = ""
248396c43b9aSAdrian Hunter	pad = ""
248496c43b9aSAdrian Hunter	sep = ""
248596c43b9aSAdrian Hunter	if with_hdr:
248696c43b9aSAdrian Hunter		model = indexes[0].model()
248796c43b9aSAdrian Hunter		for col in range(min_col, max_col + 1):
248896c43b9aSAdrian Hunter			val = model.headerData(col, Qt.Horizontal)
248996c43b9aSAdrian Hunter			if as_csv:
249096c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
249196c43b9aSAdrian Hunter				sep = ","
249296c43b9aSAdrian Hunter			else:
249396c43b9aSAdrian Hunter				c = col - min_col
249496c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
249596c43b9aSAdrian Hunter				width = max_width[c]
249696c43b9aSAdrian Hunter				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
249796c43b9aSAdrian Hunter				if align & Qt.AlignRight:
249896c43b9aSAdrian Hunter					val = val.rjust(width)
249996c43b9aSAdrian Hunter				text += pad + sep + val
250096c43b9aSAdrian Hunter				pad = " " * (width - len(val))
250196c43b9aSAdrian Hunter				sep = "  "
250296c43b9aSAdrian Hunter		text += "\n"
250396c43b9aSAdrian Hunter		pad = ""
250496c43b9aSAdrian Hunter		sep = ""
250596c43b9aSAdrian Hunter	last_row = min_row
250696c43b9aSAdrian Hunter	for i in indexes:
250796c43b9aSAdrian Hunter		if i.row() > last_row:
250896c43b9aSAdrian Hunter			last_row = i.row()
250996c43b9aSAdrian Hunter			text += "\n"
251096c43b9aSAdrian Hunter			pad = ""
251196c43b9aSAdrian Hunter			sep = ""
251296c43b9aSAdrian Hunter		if as_csv:
251396c43b9aSAdrian Hunter			text += sep + ToCSValue(str(i.data()))
251496c43b9aSAdrian Hunter			sep = ","
251596c43b9aSAdrian Hunter		else:
251696c43b9aSAdrian Hunter			width = max_width[i.column() - min_col]
251796c43b9aSAdrian Hunter			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
251896c43b9aSAdrian Hunter				val = str(i.data()).rjust(width)
251996c43b9aSAdrian Hunter			else:
252096c43b9aSAdrian Hunter				val = str(i.data())
252196c43b9aSAdrian Hunter			text += pad + sep + val
252296c43b9aSAdrian Hunter			pad = " " * (width - len(val))
252396c43b9aSAdrian Hunter			sep = "  "
252496c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
252596c43b9aSAdrian Hunter
252696c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
252796c43b9aSAdrian Hunter	indexes = view.selectedIndexes()
252896c43b9aSAdrian Hunter	if not len(indexes):
252996c43b9aSAdrian Hunter		return
253096c43b9aSAdrian Hunter
253196c43b9aSAdrian Hunter	selection = view.selectionModel()
253296c43b9aSAdrian Hunter
253396c43b9aSAdrian Hunter	first = None
253496c43b9aSAdrian Hunter	for i in indexes:
253596c43b9aSAdrian Hunter		above = view.indexAbove(i)
253696c43b9aSAdrian Hunter		if not selection.isSelected(above):
253796c43b9aSAdrian Hunter			first = i
253896c43b9aSAdrian Hunter			break
253996c43b9aSAdrian Hunter
254096c43b9aSAdrian Hunter	if first is None:
254196c43b9aSAdrian Hunter		raise RuntimeError("CopyTreeCellsToClipboard internal error")
254296c43b9aSAdrian Hunter
254396c43b9aSAdrian Hunter	model = first.model()
254496c43b9aSAdrian Hunter	row_cnt = 0
254596c43b9aSAdrian Hunter	col_cnt = model.columnCount(first)
254696c43b9aSAdrian Hunter	max_width = [0] * col_cnt
254796c43b9aSAdrian Hunter
254896c43b9aSAdrian Hunter	indent_sz = 2
254996c43b9aSAdrian Hunter	indent_str = " " * indent_sz
255096c43b9aSAdrian Hunter
255196c43b9aSAdrian Hunter	expanded_mark_sz = 2
255296c43b9aSAdrian Hunter	if sys.version_info[0] == 3:
255396c43b9aSAdrian Hunter		expanded_mark = "\u25BC "
255496c43b9aSAdrian Hunter		not_expanded_mark = "\u25B6 "
255596c43b9aSAdrian Hunter	else:
255696c43b9aSAdrian Hunter		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
255796c43b9aSAdrian Hunter		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
255896c43b9aSAdrian Hunter	leaf_mark = "  "
255996c43b9aSAdrian Hunter
256096c43b9aSAdrian Hunter	if not as_csv:
256196c43b9aSAdrian Hunter		pos = first
256296c43b9aSAdrian Hunter		while True:
256396c43b9aSAdrian Hunter			row_cnt += 1
256496c43b9aSAdrian Hunter			row = pos.row()
256596c43b9aSAdrian Hunter			for c in range(col_cnt):
256696c43b9aSAdrian Hunter				i = pos.sibling(row, c)
256796c43b9aSAdrian Hunter				if c:
256896c43b9aSAdrian Hunter					n = len(str(i.data()))
256996c43b9aSAdrian Hunter				else:
257096c43b9aSAdrian Hunter					n = len(str(i.data()).strip())
257196c43b9aSAdrian Hunter					n += (i.internalPointer().level - 1) * indent_sz
257296c43b9aSAdrian Hunter					n += expanded_mark_sz
257396c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], n)
257496c43b9aSAdrian Hunter			pos = view.indexBelow(pos)
257596c43b9aSAdrian Hunter			if not selection.isSelected(pos):
257696c43b9aSAdrian Hunter				break
257796c43b9aSAdrian Hunter
257896c43b9aSAdrian Hunter	text = ""
257996c43b9aSAdrian Hunter	pad = ""
258096c43b9aSAdrian Hunter	sep = ""
258196c43b9aSAdrian Hunter	if with_hdr:
258296c43b9aSAdrian Hunter		for c in range(col_cnt):
258396c43b9aSAdrian Hunter			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
258496c43b9aSAdrian Hunter			if as_csv:
258596c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
258696c43b9aSAdrian Hunter				sep = ","
258796c43b9aSAdrian Hunter			else:
258896c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
258996c43b9aSAdrian Hunter				width = max_width[c]
259096c43b9aSAdrian Hunter				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
259196c43b9aSAdrian Hunter				if align & Qt.AlignRight:
259296c43b9aSAdrian Hunter					val = val.rjust(width)
259396c43b9aSAdrian Hunter				text += pad + sep + val
259496c43b9aSAdrian Hunter				pad = " " * (width - len(val))
259596c43b9aSAdrian Hunter				sep = "   "
259696c43b9aSAdrian Hunter		text += "\n"
259796c43b9aSAdrian Hunter		pad = ""
259896c43b9aSAdrian Hunter		sep = ""
259996c43b9aSAdrian Hunter
260096c43b9aSAdrian Hunter	pos = first
260196c43b9aSAdrian Hunter	while True:
260296c43b9aSAdrian Hunter		row = pos.row()
260396c43b9aSAdrian Hunter		for c in range(col_cnt):
260496c43b9aSAdrian Hunter			i = pos.sibling(row, c)
260596c43b9aSAdrian Hunter			val = str(i.data())
260696c43b9aSAdrian Hunter			if not c:
260796c43b9aSAdrian Hunter				if model.hasChildren(i):
260896c43b9aSAdrian Hunter					if view.isExpanded(i):
260996c43b9aSAdrian Hunter						mark = expanded_mark
261096c43b9aSAdrian Hunter					else:
261196c43b9aSAdrian Hunter						mark = not_expanded_mark
261296c43b9aSAdrian Hunter				else:
261396c43b9aSAdrian Hunter					mark = leaf_mark
261496c43b9aSAdrian Hunter				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
261596c43b9aSAdrian Hunter			if as_csv:
261696c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
261796c43b9aSAdrian Hunter				sep = ","
261896c43b9aSAdrian Hunter			else:
261996c43b9aSAdrian Hunter				width = max_width[c]
262096c43b9aSAdrian Hunter				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
262196c43b9aSAdrian Hunter					val = val.rjust(width)
262296c43b9aSAdrian Hunter				text += pad + sep + val
262396c43b9aSAdrian Hunter				pad = " " * (width - len(val))
262496c43b9aSAdrian Hunter				sep = "   "
262596c43b9aSAdrian Hunter		pos = view.indexBelow(pos)
262696c43b9aSAdrian Hunter		if not selection.isSelected(pos):
262796c43b9aSAdrian Hunter			break
262896c43b9aSAdrian Hunter		text = text.rstrip() + "\n"
262996c43b9aSAdrian Hunter		pad = ""
263096c43b9aSAdrian Hunter		sep = ""
263196c43b9aSAdrian Hunter
263296c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
263396c43b9aSAdrian Hunter
263496c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
263596c43b9aSAdrian Hunter	view.CopyCellsToClipboard(view, as_csv, with_hdr)
263696c43b9aSAdrian Hunter
263796c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view):
263896c43b9aSAdrian Hunter	CopyCellsToClipboard(view, False, True)
263996c43b9aSAdrian Hunter
264096c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view):
264196c43b9aSAdrian Hunter	CopyCellsToClipboard(view, True, True)
264296c43b9aSAdrian Hunter
26439bc4e4bfSAdrian Hunter# Context menu
26449bc4e4bfSAdrian Hunter
26459bc4e4bfSAdrian Hunterclass ContextMenu(object):
26469bc4e4bfSAdrian Hunter
26479bc4e4bfSAdrian Hunter	def __init__(self, view):
26489bc4e4bfSAdrian Hunter		self.view = view
26499bc4e4bfSAdrian Hunter		self.view.setContextMenuPolicy(Qt.CustomContextMenu)
26509bc4e4bfSAdrian Hunter		self.view.customContextMenuRequested.connect(self.ShowContextMenu)
26519bc4e4bfSAdrian Hunter
26529bc4e4bfSAdrian Hunter	def ShowContextMenu(self, pos):
26539bc4e4bfSAdrian Hunter		menu = QMenu(self.view)
26549bc4e4bfSAdrian Hunter		self.AddActions(menu)
26559bc4e4bfSAdrian Hunter		menu.exec_(self.view.mapToGlobal(pos))
26569bc4e4bfSAdrian Hunter
26579bc4e4bfSAdrian Hunter	def AddCopy(self, menu):
26589bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
26599bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
26609bc4e4bfSAdrian Hunter
26619bc4e4bfSAdrian Hunter	def AddActions(self, menu):
26629bc4e4bfSAdrian Hunter		self.AddCopy(menu)
26639bc4e4bfSAdrian Hunter
26649bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu):
26659bc4e4bfSAdrian Hunter
26669bc4e4bfSAdrian Hunter	def __init__(self, view):
26679bc4e4bfSAdrian Hunter		super(TreeContextMenu, self).__init__(view)
26689bc4e4bfSAdrian Hunter
26699bc4e4bfSAdrian Hunter	def AddActions(self, menu):
26709bc4e4bfSAdrian Hunter		i = self.view.currentIndex()
26719bc4e4bfSAdrian Hunter		text = str(i.data()).strip()
26729bc4e4bfSAdrian Hunter		if len(text):
26739bc4e4bfSAdrian Hunter			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
26749bc4e4bfSAdrian Hunter		self.AddCopy(menu)
26759bc4e4bfSAdrian Hunter
26768392b74bSAdrian Hunter# Table window
26778392b74bSAdrian Hunter
26788392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
26798392b74bSAdrian Hunter
26808392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
26818392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
26828392b74bSAdrian Hunter
26838392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
26848392b74bSAdrian Hunter
26858392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
26868392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
26878392b74bSAdrian Hunter
26888392b74bSAdrian Hunter		self.view = QTableView()
26898392b74bSAdrian Hunter		self.view.setModel(self.model)
26908392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
26918392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
26928392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
26938392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
269496c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
269596c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
26968392b74bSAdrian Hunter
26978392b74bSAdrian Hunter		self.ResizeColumnsToContents()
26988392b74bSAdrian Hunter
26999bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
27009bc4e4bfSAdrian Hunter
27018392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
27028392b74bSAdrian Hunter
27038392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
27048392b74bSAdrian Hunter
27058392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
27068392b74bSAdrian Hunter
27078392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
27088392b74bSAdrian Hunter
27098392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
27108392b74bSAdrian Hunter
27118392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
27128392b74bSAdrian Hunter
27138392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
27148392b74bSAdrian Hunter		self.view.setFocus()
27158392b74bSAdrian Hunter		self.find_bar.Busy()
27168392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
27178392b74bSAdrian Hunter
27188392b74bSAdrian Hunter	def FindDone(self, row):
27198392b74bSAdrian Hunter		self.find_bar.Idle()
27208392b74bSAdrian Hunter		if row >= 0:
272135fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
27228392b74bSAdrian Hunter		else:
27238392b74bSAdrian Hunter			self.find_bar.NotFound()
27248392b74bSAdrian Hunter
27258392b74bSAdrian Hunter# Table list
27268392b74bSAdrian Hunter
27278392b74bSAdrian Hunterdef GetTableList(glb):
27288392b74bSAdrian Hunter	tables = []
27298392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
27308392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
27318392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
27328392b74bSAdrian Hunter	else:
27338392b74bSAdrian 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")
27348392b74bSAdrian Hunter	while query.next():
27358392b74bSAdrian Hunter		tables.append(query.value(0))
27368392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
27378392b74bSAdrian Hunter		tables.append("sqlite_master")
27388392b74bSAdrian Hunter	else:
27398392b74bSAdrian Hunter		tables.append("information_schema.tables")
27408392b74bSAdrian Hunter		tables.append("information_schema.views")
27418392b74bSAdrian Hunter		tables.append("information_schema.columns")
27428392b74bSAdrian Hunter	return tables
27438392b74bSAdrian Hunter
2744cd358012SAdrian Hunter# Top Calls data model
2745cd358012SAdrian Hunter
2746cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2747cd358012SAdrian Hunter
2748cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2749cd358012SAdrian Hunter		text = ""
2750cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2751cd358012SAdrian Hunter			text = "::text"
2752cd358012SAdrian Hunter		limit = ""
2753cd358012SAdrian Hunter		if len(report_vars.limit):
2754cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2755cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2756cd358012SAdrian Hunter			" CASE"
2757cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2758cd358012SAdrian Hunter			" ELSE short_name"
2759cd358012SAdrian Hunter			" END AS dso,"
2760cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2761cd358012SAdrian Hunter			" CASE"
2762cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2763cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2764cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2765cd358012SAdrian Hunter			" ELSE ''" + text +
2766cd358012SAdrian Hunter			" END AS flags"
2767cd358012SAdrian Hunter			" FROM calls"
2768cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2769cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2770cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2771cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2772cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2773cd358012SAdrian Hunter			report_vars.where_clause +
2774cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2775cd358012SAdrian Hunter			limit
2776cd358012SAdrian Hunter			)
2777cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2778cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2779cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2780cd358012SAdrian Hunter
2781cd358012SAdrian Hunter	def columnAlignment(self, column):
2782cd358012SAdrian Hunter		return self.alignment[column]
2783cd358012SAdrian Hunter
2784cd358012SAdrian Hunter# Top Calls report creation dialog
2785cd358012SAdrian Hunter
2786cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2787cd358012SAdrian Hunter
2788cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2789cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2790cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2791cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2792cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2793cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2794cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2795cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2796cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2797cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2798cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2799cd358012SAdrian Hunter
2800cd358012SAdrian Hunter# Top Calls window
2801cd358012SAdrian Hunter
2802cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2803cd358012SAdrian Hunter
2804cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2805cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2806cd358012SAdrian Hunter
2807cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2808cd358012SAdrian Hunter		self.model = self.data_model
2809cd358012SAdrian Hunter
2810cd358012SAdrian Hunter		self.view = QTableView()
2811cd358012SAdrian Hunter		self.view.setModel(self.model)
2812cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2813cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
281496c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
281596c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
2816cd358012SAdrian Hunter
28179bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
28189bc4e4bfSAdrian Hunter
2819cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2820cd358012SAdrian Hunter
2821cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2822cd358012SAdrian Hunter
2823cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2824cd358012SAdrian Hunter
2825cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2826cd358012SAdrian Hunter
2827cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2828cd358012SAdrian Hunter
2829cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2830cd358012SAdrian Hunter
2831cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2832cd358012SAdrian Hunter
2833cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2834cd358012SAdrian Hunter		self.view.setFocus()
2835cd358012SAdrian Hunter		self.find_bar.Busy()
2836cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2837cd358012SAdrian Hunter
2838cd358012SAdrian Hunter	def FindDone(self, row):
2839cd358012SAdrian Hunter		self.find_bar.Idle()
2840cd358012SAdrian Hunter		if row >= 0:
2841cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2842cd358012SAdrian Hunter		else:
2843cd358012SAdrian Hunter			self.find_bar.NotFound()
2844cd358012SAdrian Hunter
28451beb5c7bSAdrian Hunter# Action Definition
28461beb5c7bSAdrian Hunter
28471beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
28481beb5c7bSAdrian Hunter	action = QAction(label, parent)
28491beb5c7bSAdrian Hunter	if shortcut != None:
28501beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
28511beb5c7bSAdrian Hunter	action.setStatusTip(tip)
28521beb5c7bSAdrian Hunter	action.triggered.connect(callback)
28531beb5c7bSAdrian Hunter	return action
28541beb5c7bSAdrian Hunter
28551beb5c7bSAdrian Hunter# Typical application actions
28561beb5c7bSAdrian Hunter
28571beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
28581beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
28591beb5c7bSAdrian Hunter
28601beb5c7bSAdrian Hunter# Typical MDI actions
28611beb5c7bSAdrian Hunter
28621beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
28631beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
28641beb5c7bSAdrian Hunter
28651beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
28661beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
28671beb5c7bSAdrian Hunter
28681beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
28691beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
28701beb5c7bSAdrian Hunter
28711beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
28721beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
28731beb5c7bSAdrian Hunter
28741beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
28751beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
28761beb5c7bSAdrian Hunter
28771beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
28781beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
28791beb5c7bSAdrian Hunter
28801beb5c7bSAdrian Hunter# Typical MDI window menu
28811beb5c7bSAdrian Hunter
28821beb5c7bSAdrian Hunterclass WindowMenu():
28831beb5c7bSAdrian Hunter
28841beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
28851beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
28861beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
28871beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
28881beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
28891beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
28901beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
28911beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
28921beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
28931beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
28941beb5c7bSAdrian Hunter
28951beb5c7bSAdrian Hunter	def Update(self):
28961beb5c7bSAdrian Hunter		self.window_menu.clear()
28971beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
28981beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
28991beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
29001beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
29011beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
29021beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
29031beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
29041beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
29051beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
29061beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
29071beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29081beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
29091beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
29101beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29111beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
29121beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
29131beb5c7bSAdrian Hunter		if sub_window_count == 0:
29141beb5c7bSAdrian Hunter			return
29151beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29161beb5c7bSAdrian Hunter		nr = 1
29171beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
29181beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
29191beb5c7bSAdrian Hunter			if nr < 10:
29201beb5c7bSAdrian Hunter				label = "&" + label
29211beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
29221beb5c7bSAdrian Hunter			action.setCheckable(True)
29231beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
2924df8ea22aSAdrian Hunter			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
29251beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
29261beb5c7bSAdrian Hunter			nr += 1
29271beb5c7bSAdrian Hunter
29281beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
29291beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
29301beb5c7bSAdrian Hunter
293165b24292SAdrian Hunter# Help text
293265b24292SAdrian Hunter
293365b24292SAdrian Hunterglb_help_text = """
293465b24292SAdrian Hunter<h1>Contents</h1>
293565b24292SAdrian Hunter<style>
293665b24292SAdrian Hunterp.c1 {
293765b24292SAdrian Hunter    text-indent: 40px;
293865b24292SAdrian Hunter}
293965b24292SAdrian Hunterp.c2 {
294065b24292SAdrian Hunter    text-indent: 80px;
294165b24292SAdrian Hunter}
294265b24292SAdrian Hunter}
294365b24292SAdrian Hunter</style>
294465b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
294565b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2946ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
2947ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
2948ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
2949ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
295065b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
295165b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
295265b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
295365b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
295465b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
295565b24292SAdrian Hunterwidths to suit will display something like:
295665b24292SAdrian Hunter<pre>
295765b24292SAdrian Hunter                                         Call Graph: pt_example
295865b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
295965b24292SAdrian Hunterv- ls
296065b24292SAdrian Hunter    v- 2638:2638
296165b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
296265b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
296365b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
296465b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
296565b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
296665b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
296765b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
296865b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
296965b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
297065b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
297165b24292SAdrian Hunter</pre>
297265b24292SAdrian Hunter<h3>Points to note:</h3>
297365b24292SAdrian Hunter<ul>
297465b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
297565b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
297665b24292SAdrian Hunter<li>Subsequent levels are functions</li>
297765b24292SAdrian Hunter<li>'Count' is the number of calls</li>
297865b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
297965b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
298065b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
298165b24292SAdrian Hunter</ul>
298265b24292SAdrian Hunter<h3>Find</h3>
298365b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
298465b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
2985ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
2986ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
2987ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
2988ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
298965b24292SAdrian HunterThe All branches report displays all branches in chronological order.
299065b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
299165b24292SAdrian Hunter<h3>Disassembly</h3>
299265b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
299365b24292SAdrian Hunter<ol>
299465b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
299565b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
299665b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
299765b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
299865b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
299965b24292SAdrian Hunter</ol>
300065b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
300165b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
300265b24292SAdrian Hunter<pre>
300365b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
300465b24292SAdrian Huntergit clone https://github.com/intelxed/xed
300565b24292SAdrian Huntercd xed
300665b24292SAdrian Hunter./mfile.py --share
300765b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
300865b24292SAdrian Huntersudo ldconfig
300965b24292SAdrian Hunter</pre>
3010530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3>
3011530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
3012530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
3013530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known.
3014530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
3015530e22fdSAdrian Huntersince the previous displayed 'IPC'.
301665b24292SAdrian Hunter<h3>Find</h3>
301765b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
301865b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
301965b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
3020ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
302165b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
302265b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
3023ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
302465b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
302565b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
302665b24292SAdrian Hunter<pre>
302765b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
302865b24292SAdrian Hunter	100us-200us		From 100us to 200us
302965b24292SAdrian Hunter	10ms-			From 10ms to the end
303065b24292SAdrian Hunter	-100ns			The first 100ns
303165b24292SAdrian Hunter	-10ms-			The last 10ms
303265b24292SAdrian Hunter</pre>
303365b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
3034ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
3035cd358012SAdrian 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.
3036cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
3037cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
303865b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
303965b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
304065b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
304165b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
304265b24292SAdrian Hunterbut that can be slow for large tables.
304365b24292SAdrian Hunter<p>There are also tables of database meta-information.
304465b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
304565b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
304665b24292SAdrian Hunter<h3>Find</h3>
304765b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
304865b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
304965b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
305035fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
305135fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
305265b24292SAdrian Hunter"""
305365b24292SAdrian Hunter
305465b24292SAdrian Hunter# Help window
305565b24292SAdrian Hunter
305665b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
305765b24292SAdrian Hunter
305865b24292SAdrian Hunter	def __init__(self, glb, parent=None):
305965b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
306065b24292SAdrian Hunter
306165b24292SAdrian Hunter		self.text = QTextBrowser()
306265b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
306365b24292SAdrian Hunter		self.text.setReadOnly(True)
306465b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
306565b24292SAdrian Hunter
306665b24292SAdrian Hunter		self.setWidget(self.text)
306765b24292SAdrian Hunter
306865b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
306965b24292SAdrian Hunter
307065b24292SAdrian Hunter# Main window that only displays the help text
307165b24292SAdrian Hunter
307265b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
307365b24292SAdrian Hunter
307465b24292SAdrian Hunter	def __init__(self, parent=None):
307565b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
307665b24292SAdrian Hunter
307765b24292SAdrian Hunter		self.setMinimumSize(200, 100)
307865b24292SAdrian Hunter		self.resize(800, 600)
307965b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
308065b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
308165b24292SAdrian Hunter
308265b24292SAdrian Hunter		self.text = QTextBrowser()
308365b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
308465b24292SAdrian Hunter		self.text.setReadOnly(True)
308565b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
308665b24292SAdrian Hunter
308765b24292SAdrian Hunter		self.setCentralWidget(self.text)
308865b24292SAdrian Hunter
3089b62d18abSAdrian Hunter# PostqreSQL server version
3090b62d18abSAdrian Hunter
3091b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db):
3092b62d18abSAdrian Hunter	query = QSqlQuery(db)
3093b62d18abSAdrian Hunter	QueryExec(query, "SELECT VERSION()")
3094b62d18abSAdrian Hunter	if query.next():
3095b62d18abSAdrian Hunter		v_str = query.value(0)
3096b62d18abSAdrian Hunter		v_list = v_str.strip().split(" ")
3097b62d18abSAdrian Hunter		if v_list[0] == "PostgreSQL" and v_list[2] == "on":
3098b62d18abSAdrian Hunter			return v_list[1]
3099b62d18abSAdrian Hunter		return v_str
3100b62d18abSAdrian Hunter	return "Unknown"
3101b62d18abSAdrian Hunter
3102b62d18abSAdrian Hunter# SQLite version
3103b62d18abSAdrian Hunter
3104b62d18abSAdrian Hunterdef SQLiteVersion(db):
3105b62d18abSAdrian Hunter	query = QSqlQuery(db)
3106b62d18abSAdrian Hunter	QueryExec(query, "SELECT sqlite_version()")
3107b62d18abSAdrian Hunter	if query.next():
3108b62d18abSAdrian Hunter		return query.value(0)
3109b62d18abSAdrian Hunter	return "Unknown"
3110b62d18abSAdrian Hunter
3111b62d18abSAdrian Hunter# About dialog
3112b62d18abSAdrian Hunter
3113b62d18abSAdrian Hunterclass AboutDialog(QDialog):
3114b62d18abSAdrian Hunter
3115b62d18abSAdrian Hunter	def __init__(self, glb, parent=None):
3116b62d18abSAdrian Hunter		super(AboutDialog, self).__init__(parent)
3117b62d18abSAdrian Hunter
3118b62d18abSAdrian Hunter		self.setWindowTitle("About Exported SQL Viewer")
3119b62d18abSAdrian Hunter		self.setMinimumWidth(300)
3120b62d18abSAdrian Hunter
3121b62d18abSAdrian Hunter		pyside_version = "1" if pyside_version_1 else "2"
3122b62d18abSAdrian Hunter
3123b62d18abSAdrian Hunter		text = "<pre>"
3124b62d18abSAdrian Hunter		text += "Python version:     " + sys.version.split(" ")[0] + "\n"
3125b62d18abSAdrian Hunter		text += "PySide version:     " + pyside_version + "\n"
3126b62d18abSAdrian Hunter		text += "Qt version:         " + qVersion() + "\n"
3127b62d18abSAdrian Hunter		if glb.dbref.is_sqlite3:
3128b62d18abSAdrian Hunter			text += "SQLite version:     " + SQLiteVersion(glb.db) + "\n"
3129b62d18abSAdrian Hunter		else:
3130b62d18abSAdrian Hunter			text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
3131b62d18abSAdrian Hunter		text += "</pre>"
3132b62d18abSAdrian Hunter
3133b62d18abSAdrian Hunter		self.text = QTextBrowser()
3134b62d18abSAdrian Hunter		self.text.setHtml(text)
3135b62d18abSAdrian Hunter		self.text.setReadOnly(True)
3136b62d18abSAdrian Hunter		self.text.setOpenExternalLinks(True)
3137b62d18abSAdrian Hunter
3138b62d18abSAdrian Hunter		self.vbox = QVBoxLayout()
3139b62d18abSAdrian Hunter		self.vbox.addWidget(self.text)
3140b62d18abSAdrian Hunter
3141b62d18abSAdrian Hunter		self.setLayout(self.vbox);
3142b62d18abSAdrian Hunter
314382f68e28SAdrian Hunter# Font resize
314482f68e28SAdrian Hunter
314582f68e28SAdrian Hunterdef ResizeFont(widget, diff):
314682f68e28SAdrian Hunter	font = widget.font()
314782f68e28SAdrian Hunter	sz = font.pointSize()
314882f68e28SAdrian Hunter	font.setPointSize(sz + diff)
314982f68e28SAdrian Hunter	widget.setFont(font)
315082f68e28SAdrian Hunter
315182f68e28SAdrian Hunterdef ShrinkFont(widget):
315282f68e28SAdrian Hunter	ResizeFont(widget, -1)
315382f68e28SAdrian Hunter
315482f68e28SAdrian Hunterdef EnlargeFont(widget):
315582f68e28SAdrian Hunter	ResizeFont(widget, 1)
315682f68e28SAdrian Hunter
31571beb5c7bSAdrian Hunter# Unique name for sub-windows
31581beb5c7bSAdrian Hunter
31591beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
31601beb5c7bSAdrian Hunter	if nr > 1:
31611beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
31621beb5c7bSAdrian Hunter	return name
31631beb5c7bSAdrian Hunter
31641beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
31651beb5c7bSAdrian Hunter	nr = 1
31661beb5c7bSAdrian Hunter	while True:
31671beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
31681beb5c7bSAdrian Hunter		ok = True
31691beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
31701beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
31711beb5c7bSAdrian Hunter				ok = False
31721beb5c7bSAdrian Hunter				break
31731beb5c7bSAdrian Hunter		if ok:
31741beb5c7bSAdrian Hunter			return unique_name
31751beb5c7bSAdrian Hunter		nr += 1
31761beb5c7bSAdrian Hunter
31771beb5c7bSAdrian Hunter# Add a sub-window
31781beb5c7bSAdrian Hunter
31791beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
31801beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
31811beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
31821beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
31831beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
31841beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
31851beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
31861beb5c7bSAdrian Hunter	sub_window.name = unique_name
31871beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
31881beb5c7bSAdrian Hunter	sub_window.show()
31891beb5c7bSAdrian Hunter
3190031c2a00SAdrian Hunter# Main window
3191031c2a00SAdrian Hunter
3192031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
3193031c2a00SAdrian Hunter
3194031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
3195031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
3196031c2a00SAdrian Hunter
3197031c2a00SAdrian Hunter		self.glb = glb
3198031c2a00SAdrian Hunter
31991beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
3200031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
3201031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
3202031c2a00SAdrian Hunter
32031beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
32041beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
32051beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
3206031c2a00SAdrian Hunter
32071beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
3208031c2a00SAdrian Hunter
32091beb5c7bSAdrian Hunter		menu = self.menuBar()
3210031c2a00SAdrian Hunter
32111beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
32121beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
32131beb5c7bSAdrian Hunter
3214ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
321596c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
321696c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
3217ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
32188392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
321982f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
322082f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
3221ebd70c7dSAdrian Hunter
32221beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
3223655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
32241beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
32251beb5c7bSAdrian Hunter
3226ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
3227ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
3228ae8b887cSAdrian Hunter
322976099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
323076099f98SAdrian Hunter
3231cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
3232cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
3233cd358012SAdrian Hunter
32348392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
32358392b74bSAdrian Hunter
32361beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
32371beb5c7bSAdrian Hunter
323865b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
323965b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
3240b62d18abSAdrian Hunter		help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
324165b24292SAdrian Hunter
32424b208453SAdrian Hunter	def Try(self, fn):
32434b208453SAdrian Hunter		win = self.mdi_area.activeSubWindow()
32444b208453SAdrian Hunter		if win:
32454b208453SAdrian Hunter			try:
32464b208453SAdrian Hunter				fn(win.view)
32474b208453SAdrian Hunter			except:
32484b208453SAdrian Hunter				pass
32494b208453SAdrian Hunter
325096c43b9aSAdrian Hunter	def CopyToClipboard(self):
325196c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardHdr)
325296c43b9aSAdrian Hunter
325396c43b9aSAdrian Hunter	def CopyToClipboardCSV(self):
325496c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardCSV)
325596c43b9aSAdrian Hunter
3256ebd70c7dSAdrian Hunter	def Find(self):
3257ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
3258ebd70c7dSAdrian Hunter		if win:
3259ebd70c7dSAdrian Hunter			try:
3260ebd70c7dSAdrian Hunter				win.find_bar.Activate()
3261ebd70c7dSAdrian Hunter			except:
3262ebd70c7dSAdrian Hunter				pass
3263ebd70c7dSAdrian Hunter
32648392b74bSAdrian Hunter	def FetchMoreRecords(self):
32658392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
32668392b74bSAdrian Hunter		if win:
32678392b74bSAdrian Hunter			try:
32688392b74bSAdrian Hunter				win.fetch_bar.Activate()
32698392b74bSAdrian Hunter			except:
32708392b74bSAdrian Hunter				pass
32718392b74bSAdrian Hunter
327282f68e28SAdrian Hunter	def ShrinkFont(self):
32734b208453SAdrian Hunter		self.Try(ShrinkFont)
327482f68e28SAdrian Hunter
327582f68e28SAdrian Hunter	def EnlargeFont(self):
32764b208453SAdrian Hunter		self.Try(EnlargeFont)
327782f68e28SAdrian Hunter
327876099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
327976099f98SAdrian Hunter		branches_events = 0
328076099f98SAdrian Hunter		for event in events:
328176099f98SAdrian Hunter			event = event.split(":")[0]
328276099f98SAdrian Hunter			if event == "branches":
328376099f98SAdrian Hunter				branches_events += 1
328476099f98SAdrian Hunter		dbid = 0
328576099f98SAdrian Hunter		for event in events:
328676099f98SAdrian Hunter			dbid += 1
328776099f98SAdrian Hunter			event = event.split(":")[0]
328876099f98SAdrian Hunter			if event == "branches":
328976099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
3290df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
3291210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
3292df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
329376099f98SAdrian Hunter
32948392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
32958392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
32968392b74bSAdrian Hunter		for table in tables:
3297df8ea22aSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
32988392b74bSAdrian Hunter
32991beb5c7bSAdrian Hunter	def NewCallGraph(self):
33001beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
3301031c2a00SAdrian Hunter
3302ae8b887cSAdrian Hunter	def NewCallTree(self):
3303ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
3304ae8b887cSAdrian Hunter
3305cd358012SAdrian Hunter	def NewTopCalls(self):
3306cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
3307cd358012SAdrian Hunter		ret = dialog.exec_()
3308cd358012SAdrian Hunter		if ret:
3309cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
3310cd358012SAdrian Hunter
331176099f98SAdrian Hunter	def NewBranchView(self, event_id):
3312947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
331376099f98SAdrian Hunter
3314210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
3315210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
3316210cf1f9SAdrian Hunter		ret = dialog.exec_()
3317210cf1f9SAdrian Hunter		if ret:
3318947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
3319210cf1f9SAdrian Hunter
33208392b74bSAdrian Hunter	def NewTableView(self, table_name):
33218392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
33228392b74bSAdrian Hunter
332365b24292SAdrian Hunter	def Help(self):
332465b24292SAdrian Hunter		HelpWindow(self.glb, self)
332565b24292SAdrian Hunter
3326b62d18abSAdrian Hunter	def About(self):
3327b62d18abSAdrian Hunter		dialog = AboutDialog(self.glb, self)
3328b62d18abSAdrian Hunter		dialog.exec_()
3329b62d18abSAdrian Hunter
333076099f98SAdrian Hunter# XED Disassembler
333176099f98SAdrian Hunter
333276099f98SAdrian Hunterclass xed_state_t(Structure):
333376099f98SAdrian Hunter
333476099f98SAdrian Hunter	_fields_ = [
333576099f98SAdrian Hunter		("mode", c_int),
333676099f98SAdrian Hunter		("width", c_int)
333776099f98SAdrian Hunter	]
333876099f98SAdrian Hunter
333976099f98SAdrian Hunterclass XEDInstruction():
334076099f98SAdrian Hunter
334176099f98SAdrian Hunter	def __init__(self, libxed):
334276099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
334376099f98SAdrian Hunter		xedd_t = c_byte * 512
334476099f98SAdrian Hunter		self.xedd = xedd_t()
334576099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
334676099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
334776099f98SAdrian Hunter		self.state = xed_state_t()
334876099f98SAdrian Hunter		self.statep = addressof(self.state)
334976099f98SAdrian Hunter		# Buffer for disassembled instruction text
335076099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
335176099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
335276099f98SAdrian Hunter
335376099f98SAdrian Hunterclass LibXED():
335476099f98SAdrian Hunter
335576099f98SAdrian Hunter	def __init__(self):
33565ed4419dSAdrian Hunter		try:
335776099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
33585ed4419dSAdrian Hunter		except:
33595ed4419dSAdrian Hunter			self.libxed = None
33605ed4419dSAdrian Hunter		if not self.libxed:
33615ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
336276099f98SAdrian Hunter
336376099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
336476099f98SAdrian Hunter		self.xed_tables_init.restype = None
336576099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
336676099f98SAdrian Hunter
336776099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
336876099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
336976099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
337076099f98SAdrian Hunter
337176099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
337276099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
337376099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
337476099f98SAdrian Hunter
337576099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
337676099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
337776099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
337876099f98SAdrian Hunter
337976099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
338076099f98SAdrian Hunter		self.xed_decode.restype = c_int
338176099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
338276099f98SAdrian Hunter
338376099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
338476099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
338576099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
338676099f98SAdrian Hunter
338776099f98SAdrian Hunter		self.xed_tables_init()
338876099f98SAdrian Hunter
338976099f98SAdrian Hunter	def Instruction(self):
339076099f98SAdrian Hunter		return XEDInstruction(self)
339176099f98SAdrian Hunter
339276099f98SAdrian Hunter	def SetMode(self, inst, mode):
339376099f98SAdrian Hunter		if mode:
339476099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
339576099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
339676099f98SAdrian Hunter		else:
339776099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
339876099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
339976099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
340076099f98SAdrian Hunter
340176099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
340276099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
340376099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
340476099f98SAdrian Hunter		if err:
340576099f98SAdrian Hunter			return 0, ""
340676099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
340776099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
340876099f98SAdrian Hunter		if not ok:
340976099f98SAdrian Hunter			return 0, ""
3410606bd60aSAdrian Hunter		if sys.version_info[0] == 2:
3411606bd60aSAdrian Hunter			result = inst.buffer.value
3412606bd60aSAdrian Hunter		else:
3413606bd60aSAdrian Hunter			result = inst.buffer.value.decode()
341476099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
341576099f98SAdrian Hunter		# For now, assume the length is in byte 166
3416606bd60aSAdrian Hunter		return inst.xedd[166], result
341776099f98SAdrian Hunter
341876099f98SAdrian Hunterdef TryOpen(file_name):
341976099f98SAdrian Hunter	try:
342076099f98SAdrian Hunter		return open(file_name, "rb")
342176099f98SAdrian Hunter	except:
342276099f98SAdrian Hunter		return None
342376099f98SAdrian Hunter
342476099f98SAdrian Hunterdef Is64Bit(f):
342576099f98SAdrian Hunter	result = sizeof(c_void_p)
342676099f98SAdrian Hunter	# ELF support only
342776099f98SAdrian Hunter	pos = f.tell()
342876099f98SAdrian Hunter	f.seek(0)
342976099f98SAdrian Hunter	header = f.read(7)
343076099f98SAdrian Hunter	f.seek(pos)
343176099f98SAdrian Hunter	magic = header[0:4]
3432606bd60aSAdrian Hunter	if sys.version_info[0] == 2:
343376099f98SAdrian Hunter		eclass = ord(header[4])
343476099f98SAdrian Hunter		encoding = ord(header[5])
343576099f98SAdrian Hunter		version = ord(header[6])
3436606bd60aSAdrian Hunter	else:
3437606bd60aSAdrian Hunter		eclass = header[4]
3438606bd60aSAdrian Hunter		encoding = header[5]
3439606bd60aSAdrian Hunter		version = header[6]
344076099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
344176099f98SAdrian Hunter		result = True if eclass == 2 else False
344276099f98SAdrian Hunter	return result
344376099f98SAdrian Hunter
3444031c2a00SAdrian Hunter# Global data
3445031c2a00SAdrian Hunter
3446031c2a00SAdrian Hunterclass Glb():
3447031c2a00SAdrian Hunter
3448031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
3449031c2a00SAdrian Hunter		self.dbref = dbref
3450031c2a00SAdrian Hunter		self.db = db
3451031c2a00SAdrian Hunter		self.dbname = dbname
345276099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
345376099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
345476099f98SAdrian Hunter		if self.buildid_dir:
345576099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
345676099f98SAdrian Hunter		else:
345776099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
3458031c2a00SAdrian Hunter		self.app = None
3459031c2a00SAdrian Hunter		self.mainwindow = None
34608392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
346176099f98SAdrian Hunter		try:
346276099f98SAdrian Hunter			self.disassembler = LibXED()
346376099f98SAdrian Hunter			self.have_disassembler = True
346476099f98SAdrian Hunter		except:
346576099f98SAdrian Hunter			self.have_disassembler = False
346676099f98SAdrian Hunter
346776099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
346876099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
346976099f98SAdrian Hunter		return TryOpen(file_name)
347076099f98SAdrian Hunter
347176099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
347276099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
347376099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
347476099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
347576099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
347676099f98SAdrian Hunter			if f:
347776099f98SAdrian Hunter				return f
347876099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
347976099f98SAdrian Hunter			f = TryOpen(long_name)
348076099f98SAdrian Hunter			if f:
348176099f98SAdrian Hunter				return f
348276099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
348376099f98SAdrian Hunter		if f:
348476099f98SAdrian Hunter			return f
348576099f98SAdrian Hunter		return None
34868392b74bSAdrian Hunter
34878392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
34888392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
34898392b74bSAdrian Hunter
34908392b74bSAdrian Hunter	# Shutdown any background processes or threads
34918392b74bSAdrian Hunter	def ShutdownInstances(self):
34928392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
34938392b74bSAdrian Hunter			try:
34948392b74bSAdrian Hunter				x.Shutdown()
34958392b74bSAdrian Hunter			except:
34968392b74bSAdrian Hunter				pass
3497031c2a00SAdrian Hunter
3498031c2a00SAdrian Hunter# Database reference
3499031c2a00SAdrian Hunter
3500031c2a00SAdrian Hunterclass DBRef():
3501031c2a00SAdrian Hunter
3502031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
3503031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
3504031c2a00SAdrian Hunter		self.dbname = dbname
3505031c2a00SAdrian Hunter
3506031c2a00SAdrian Hunter	def Open(self, connection_name):
3507031c2a00SAdrian Hunter		dbname = self.dbname
3508031c2a00SAdrian Hunter		if self.is_sqlite3:
3509031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
3510031c2a00SAdrian Hunter		else:
3511031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
3512031c2a00SAdrian Hunter			opts = dbname.split()
3513031c2a00SAdrian Hunter			for opt in opts:
3514031c2a00SAdrian Hunter				if "=" in opt:
3515031c2a00SAdrian Hunter					opt = opt.split("=")
3516031c2a00SAdrian Hunter					if opt[0] == "hostname":
3517031c2a00SAdrian Hunter						db.setHostName(opt[1])
3518031c2a00SAdrian Hunter					elif opt[0] == "port":
3519031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
3520031c2a00SAdrian Hunter					elif opt[0] == "username":
3521031c2a00SAdrian Hunter						db.setUserName(opt[1])
3522031c2a00SAdrian Hunter					elif opt[0] == "password":
3523031c2a00SAdrian Hunter						db.setPassword(opt[1])
3524031c2a00SAdrian Hunter					elif opt[0] == "dbname":
3525031c2a00SAdrian Hunter						dbname = opt[1]
3526031c2a00SAdrian Hunter				else:
3527031c2a00SAdrian Hunter					dbname = opt
3528031c2a00SAdrian Hunter
3529031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
3530031c2a00SAdrian Hunter		if not db.open():
3531031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
3532031c2a00SAdrian Hunter		return db, dbname
3533031c2a00SAdrian Hunter
3534031c2a00SAdrian Hunter# Main
3535031c2a00SAdrian Hunter
3536031c2a00SAdrian Hunterdef Main():
35371ed7f47fSAdrian Hunter	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
35381ed7f47fSAdrian Hunter			"   or: exported-sql-viewer.py --help-only"
35391ed7f47fSAdrian Hunter	ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
3540df8ea22aSAdrian Hunter	ap.add_argument("--pyside-version-1", action='store_true')
35411ed7f47fSAdrian Hunter	ap.add_argument("dbname", nargs="?")
35421ed7f47fSAdrian Hunter	ap.add_argument("--help-only", action='store_true')
35431ed7f47fSAdrian Hunter	args = ap.parse_args()
3544031c2a00SAdrian Hunter
35451ed7f47fSAdrian Hunter	if args.help_only:
354665b24292SAdrian Hunter		app = QApplication(sys.argv)
354765b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
354865b24292SAdrian Hunter		mainwindow.show()
354965b24292SAdrian Hunter		err = app.exec_()
355065b24292SAdrian Hunter		sys.exit(err)
3551031c2a00SAdrian Hunter
35521ed7f47fSAdrian Hunter	dbname = args.dbname
35531ed7f47fSAdrian Hunter	if dbname is None:
35541ed7f47fSAdrian Hunter		ap.print_usage()
35551ed7f47fSAdrian Hunter		print("Too few arguments")
35561ed7f47fSAdrian Hunter		sys.exit(1)
35571ed7f47fSAdrian Hunter
3558031c2a00SAdrian Hunter	is_sqlite3 = False
3559031c2a00SAdrian Hunter	try:
3560beda0e72STony Jones		f = open(dbname, "rb")
3561beda0e72STony Jones		if f.read(15) == b'SQLite format 3':
3562031c2a00SAdrian Hunter			is_sqlite3 = True
3563031c2a00SAdrian Hunter		f.close()
3564031c2a00SAdrian Hunter	except:
3565031c2a00SAdrian Hunter		pass
3566031c2a00SAdrian Hunter
3567031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
3568031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
3569031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
3570031c2a00SAdrian Hunter	app = QApplication(sys.argv)
3571031c2a00SAdrian Hunter	glb.app = app
3572031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
3573031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
3574031c2a00SAdrian Hunter	mainwindow.show()
3575031c2a00SAdrian Hunter	err = app.exec_()
35768392b74bSAdrian Hunter	glb.ShutdownInstances()
3577031c2a00SAdrian Hunter	db.close()
3578031c2a00SAdrian Hunter	sys.exit(err)
3579031c2a00SAdrian Hunter
3580031c2a00SAdrian Hunterif __name__ == "__main__":
3581031c2a00SAdrian Hunter	Main()
3582