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
344181ea40aSAdrian Hunterdef LookupModel(model_name):
345181ea40aSAdrian Hunter	model_cache_lock.acquire()
346181ea40aSAdrian Hunter	try:
347181ea40aSAdrian Hunter		model = model_cache[model_name]
348181ea40aSAdrian Hunter	except:
349181ea40aSAdrian Hunter		model = None
350181ea40aSAdrian Hunter	model_cache_lock.release()
351181ea40aSAdrian Hunter	return model
352181ea40aSAdrian Hunter
353ebd70c7dSAdrian Hunter# Find bar
354ebd70c7dSAdrian Hunter
355ebd70c7dSAdrian Hunterclass FindBar():
356ebd70c7dSAdrian Hunter
357ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
358ebd70c7dSAdrian Hunter		self.finder = finder
359ebd70c7dSAdrian Hunter		self.context = []
360ebd70c7dSAdrian Hunter		self.last_value = None
361ebd70c7dSAdrian Hunter		self.last_pattern = None
362ebd70c7dSAdrian Hunter
363ebd70c7dSAdrian Hunter		label = QLabel("Find:")
364ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
365ebd70c7dSAdrian Hunter
366ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
367ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
368ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
371ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
372ebd70c7dSAdrian Hunter		self.progress.hide()
373ebd70c7dSAdrian Hunter
374ebd70c7dSAdrian Hunter		if is_reg_expr:
375ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
376ebd70c7dSAdrian Hunter		else:
377ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
378ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
379ebd70c7dSAdrian Hunter
380ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
381ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
382ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
383ebd70c7dSAdrian Hunter
384ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
385ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
386ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
387ebd70c7dSAdrian Hunter
388ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
389ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
390ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
391ebd70c7dSAdrian Hunter
392ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
393ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
394ebd70c7dSAdrian Hunter
395ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
396ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
397ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
398ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
399ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
400ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
401ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
402ebd70c7dSAdrian Hunter
403ebd70c7dSAdrian Hunter		self.bar = QWidget()
40426688729SAdrian Hunter		self.bar.setLayout(self.hbox)
405ebd70c7dSAdrian Hunter		self.bar.hide()
406ebd70c7dSAdrian Hunter
407ebd70c7dSAdrian Hunter	def Widget(self):
408ebd70c7dSAdrian Hunter		return self.bar
409ebd70c7dSAdrian Hunter
410ebd70c7dSAdrian Hunter	def Activate(self):
411ebd70c7dSAdrian Hunter		self.bar.show()
41280b3fb64SAdrian Hunter		self.textbox.lineEdit().selectAll()
413ebd70c7dSAdrian Hunter		self.textbox.setFocus()
414ebd70c7dSAdrian Hunter
415ebd70c7dSAdrian Hunter	def Deactivate(self):
416ebd70c7dSAdrian Hunter		self.bar.hide()
417ebd70c7dSAdrian Hunter
418ebd70c7dSAdrian Hunter	def Busy(self):
419ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
420ebd70c7dSAdrian Hunter		self.pattern.hide()
421ebd70c7dSAdrian Hunter		self.next_button.hide()
422ebd70c7dSAdrian Hunter		self.prev_button.hide()
423ebd70c7dSAdrian Hunter		self.progress.show()
424ebd70c7dSAdrian Hunter
425ebd70c7dSAdrian Hunter	def Idle(self):
426ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
427ebd70c7dSAdrian Hunter		self.progress.hide()
428ebd70c7dSAdrian Hunter		self.pattern.show()
429ebd70c7dSAdrian Hunter		self.next_button.show()
430ebd70c7dSAdrian Hunter		self.prev_button.show()
431ebd70c7dSAdrian Hunter
432ebd70c7dSAdrian Hunter	def Find(self, direction):
433ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
434ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
435ebd70c7dSAdrian Hunter		self.last_value = value
436ebd70c7dSAdrian Hunter		self.last_pattern = pattern
437ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
438ebd70c7dSAdrian Hunter
439ebd70c7dSAdrian Hunter	def ValueChanged(self):
440ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
441ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
442ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
443ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
444ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
445ebd70c7dSAdrian Hunter		if data == None:
446ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
447ebd70c7dSAdrian Hunter		else:
448ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
449ebd70c7dSAdrian Hunter		self.Find(0)
450ebd70c7dSAdrian Hunter
451ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
452ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
453ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
454ebd70c7dSAdrian Hunter		if value != self.last_value:
455ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
456ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
457ebd70c7dSAdrian Hunter			if index < 0:
458ebd70c7dSAdrian Hunter				index = self.textbox.count()
459ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
460ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
461ebd70c7dSAdrian Hunter				return
462ebd70c7dSAdrian Hunter			else:
463ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
464ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
465ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
466ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
467ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
468ebd70c7dSAdrian Hunter		self.Find(direction)
469ebd70c7dSAdrian Hunter
470ebd70c7dSAdrian Hunter	def NotFound(self):
471ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
472ebd70c7dSAdrian Hunter
473031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
474031c2a00SAdrian Hunter
475031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
476031c2a00SAdrian Hunter
4774a0979d4SAdrian Hunter	def __init__(self, glb, params, row, parent_item):
478031c2a00SAdrian Hunter		self.glb = glb
4794a0979d4SAdrian Hunter		self.params = params
480031c2a00SAdrian Hunter		self.row = row
481031c2a00SAdrian Hunter		self.parent_item = parent_item
48226688729SAdrian Hunter		self.query_done = False
483031c2a00SAdrian Hunter		self.child_count = 0
484031c2a00SAdrian Hunter		self.child_items = []
4853ac641f4SAdrian Hunter		if parent_item:
4863ac641f4SAdrian Hunter			self.level = parent_item.level + 1
4873ac641f4SAdrian Hunter		else:
4883ac641f4SAdrian Hunter			self.level = 0
489031c2a00SAdrian Hunter
490031c2a00SAdrian Hunter	def getChildItem(self, row):
491031c2a00SAdrian Hunter		return self.child_items[row]
492031c2a00SAdrian Hunter
493031c2a00SAdrian Hunter	def getParentItem(self):
494031c2a00SAdrian Hunter		return self.parent_item
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunter	def getRow(self):
497031c2a00SAdrian Hunter		return self.row
498031c2a00SAdrian Hunter
499031c2a00SAdrian Hunter	def childCount(self):
500031c2a00SAdrian Hunter		if not self.query_done:
501031c2a00SAdrian Hunter			self.Select()
502031c2a00SAdrian Hunter			if not self.child_count:
503031c2a00SAdrian Hunter				return -1
504031c2a00SAdrian Hunter		return self.child_count
505031c2a00SAdrian Hunter
506031c2a00SAdrian Hunter	def hasChildren(self):
507031c2a00SAdrian Hunter		if not self.query_done:
508031c2a00SAdrian Hunter			return True
509031c2a00SAdrian Hunter		return self.child_count > 0
510031c2a00SAdrian Hunter
511031c2a00SAdrian Hunter	def getData(self, column):
512031c2a00SAdrian Hunter		return self.data[column]
513031c2a00SAdrian Hunter
514031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
515031c2a00SAdrian Hunter
516031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
517031c2a00SAdrian Hunter
51838a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
5194a0979d4SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
520031c2a00SAdrian Hunter		self.comm_id = comm_id
521031c2a00SAdrian Hunter		self.thread_id = thread_id
522031c2a00SAdrian Hunter		self.call_path_id = call_path_id
52338a846d4SAdrian Hunter		self.insn_cnt = insn_cnt
52438a846d4SAdrian Hunter		self.cyc_cnt = cyc_cnt
525031c2a00SAdrian Hunter		self.branch_count = branch_count
526031c2a00SAdrian Hunter		self.time = time
527031c2a00SAdrian Hunter
528031c2a00SAdrian Hunter	def Select(self):
52926688729SAdrian Hunter		self.query_done = True
530031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
53138a846d4SAdrian Hunter		if self.params.have_ipc:
53238a846d4SAdrian Hunter			ipc_str = ", SUM(insn_count), SUM(cyc_count)"
53338a846d4SAdrian Hunter		else:
53438a846d4SAdrian Hunter			ipc_str = ""
53538a846d4SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
536031c2a00SAdrian Hunter					" FROM calls"
537031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
538031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
539031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
540031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
541031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
542031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
543031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
544031c2a00SAdrian Hunter					" ORDER BY call_path_id")
545031c2a00SAdrian Hunter		while query.next():
54638a846d4SAdrian Hunter			if self.params.have_ipc:
54738a846d4SAdrian Hunter				insn_cnt = int(query.value(5))
54838a846d4SAdrian Hunter				cyc_cnt = int(query.value(6))
54938a846d4SAdrian Hunter				branch_count = int(query.value(7))
55038a846d4SAdrian Hunter			else:
55138a846d4SAdrian Hunter				insn_cnt = 0
55238a846d4SAdrian Hunter				cyc_cnt = 0
55338a846d4SAdrian Hunter				branch_count = int(query.value(5))
55438a846d4SAdrian 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)
555031c2a00SAdrian Hunter			self.child_items.append(child_item)
556031c2a00SAdrian Hunter			self.child_count += 1
557031c2a00SAdrian Hunter
558031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
559031c2a00SAdrian Hunter
560031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
561031c2a00SAdrian Hunter
56238a846d4SAdrian 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):
56338a846d4SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
564031c2a00SAdrian Hunter		dso = dsoname(dso)
56538a846d4SAdrian Hunter		if self.params.have_ipc:
56638a846d4SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
56738a846d4SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
56838a846d4SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
56938a846d4SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
57038a846d4SAdrian 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 ]
57138a846d4SAdrian Hunter		else:
572031c2a00SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
573031c2a00SAdrian Hunter		self.dbid = call_path_id
574031c2a00SAdrian Hunter
575031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
576031c2a00SAdrian Hunter
577031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
578031c2a00SAdrian Hunter
5794a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
58038a846d4SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
58138a846d4SAdrian Hunter		if self.params.have_ipc:
58238a846d4SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
58338a846d4SAdrian Hunter		else:
584031c2a00SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
585031c2a00SAdrian Hunter		self.dbid = thread_id
586031c2a00SAdrian Hunter
587031c2a00SAdrian Hunter	def Select(self):
588031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
589031c2a00SAdrian Hunter		for child_item in self.child_items:
590031c2a00SAdrian Hunter			self.time += child_item.time
59138a846d4SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
59238a846d4SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
593031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
594031c2a00SAdrian Hunter		for child_item in self.child_items:
595031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
59638a846d4SAdrian Hunter			if self.params.have_ipc:
59738a846d4SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
59838a846d4SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
59938a846d4SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
60038a846d4SAdrian Hunter			else:
601031c2a00SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
602031c2a00SAdrian Hunter
603031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
604031c2a00SAdrian Hunter
605031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
606031c2a00SAdrian Hunter
6074a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
6084a0979d4SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
60938a846d4SAdrian Hunter		if self.params.have_ipc:
61038a846d4SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
61138a846d4SAdrian Hunter		else:
612031c2a00SAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
613031c2a00SAdrian Hunter		self.dbid = comm_id
614031c2a00SAdrian Hunter
615031c2a00SAdrian Hunter	def Select(self):
61626688729SAdrian Hunter		self.query_done = True
617031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
618031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
619031c2a00SAdrian Hunter					" FROM comm_threads"
620031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
621031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
622031c2a00SAdrian Hunter		while query.next():
6234a0979d4SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
624031c2a00SAdrian Hunter			self.child_items.append(child_item)
625031c2a00SAdrian Hunter			self.child_count += 1
626031c2a00SAdrian Hunter
627031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
628031c2a00SAdrian Hunter
629031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
630031c2a00SAdrian Hunter
6314a0979d4SAdrian Hunter	def __init__(self, glb, params):
6324a0979d4SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, params, 0, None)
633031c2a00SAdrian Hunter		self.dbid = 0
63426688729SAdrian Hunter		self.query_done = True
63526c11206SAdrian Hunter		if_has_calls = ""
63626c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
63726c11206SAdrian Hunter			if_has_calls = " WHERE has_calls = TRUE"
638031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
63926c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
640031c2a00SAdrian Hunter		while query.next():
641031c2a00SAdrian Hunter			if not query.value(0):
642031c2a00SAdrian Hunter				continue
6434a0979d4SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
644031c2a00SAdrian Hunter			self.child_items.append(child_item)
645031c2a00SAdrian Hunter			self.child_count += 1
646031c2a00SAdrian Hunter
6474a0979d4SAdrian Hunter# Call graph model parameters
6484a0979d4SAdrian Hunter
6494a0979d4SAdrian Hunterclass CallGraphModelParams():
6504a0979d4SAdrian Hunter
6514a0979d4SAdrian Hunter	def __init__(self, glb, parent=None):
6524a0979d4SAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
6534a0979d4SAdrian Hunter
654254c0d82SAdrian Hunter# Context-sensitive call graph data model base
655031c2a00SAdrian Hunter
656254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
657031c2a00SAdrian Hunter
658031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
6594a0979d4SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
660031c2a00SAdrian Hunter
661ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
662ebd70c7dSAdrian Hunter		if pattern:
663ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
664ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
665ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
666ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
667ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
668ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
669ebd70c7dSAdrian Hunter				# Escape % and _
670ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
671ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
672ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
673ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
674ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
675ebd70c7dSAdrian Hunter			else:
676ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
677ebd70c7dSAdrian Hunter		else:
678ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
679254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
680ebd70c7dSAdrian Hunter
681ebd70c7dSAdrian Hunter	def Found(self, query, found):
682ebd70c7dSAdrian Hunter		if found:
683ebd70c7dSAdrian Hunter			return self.FindPath(query)
684ebd70c7dSAdrian Hunter		return []
685ebd70c7dSAdrian Hunter
686ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
687ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
688ebd70c7dSAdrian Hunter			found = query.first()
689ebd70c7dSAdrian Hunter		else:
690ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
691ebd70c7dSAdrian Hunter			found = query.next()
692ebd70c7dSAdrian Hunter		return self.Found(query, found)
693ebd70c7dSAdrian Hunter
694ebd70c7dSAdrian Hunter	def FindNext(self, query):
695ebd70c7dSAdrian Hunter		found = query.next()
696ebd70c7dSAdrian Hunter		if not found:
697ebd70c7dSAdrian Hunter			found = query.first()
698ebd70c7dSAdrian Hunter		return self.Found(query, found)
699ebd70c7dSAdrian Hunter
700ebd70c7dSAdrian Hunter	def FindPrev(self, query):
701ebd70c7dSAdrian Hunter		found = query.previous()
702ebd70c7dSAdrian Hunter		if not found:
703ebd70c7dSAdrian Hunter			found = query.last()
704ebd70c7dSAdrian Hunter		return self.Found(query, found)
705ebd70c7dSAdrian Hunter
706ebd70c7dSAdrian Hunter	def FindThread(self, c):
707ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
708ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
709ebd70c7dSAdrian Hunter		elif c.direction > 0:
710ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
711ebd70c7dSAdrian Hunter		else:
712ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
713ebd70c7dSAdrian Hunter		return (True, ids)
714ebd70c7dSAdrian Hunter
715ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
716ebd70c7dSAdrian Hunter		class Context():
717ebd70c7dSAdrian Hunter			def __init__(self, *x):
718ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
719ebd70c7dSAdrian Hunter			def Update(self, *x):
720ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
721ebd70c7dSAdrian Hunter		if len(context):
722ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
723ebd70c7dSAdrian Hunter		else:
724ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
725ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
726ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
727ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
728ebd70c7dSAdrian Hunter		thread.start()
729ebd70c7dSAdrian Hunter
730ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
731ebd70c7dSAdrian Hunter		callback(ids)
732ebd70c7dSAdrian Hunter
733254c0d82SAdrian Hunter# Context-sensitive call graph data model
734254c0d82SAdrian Hunter
735254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
736254c0d82SAdrian Hunter
737254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
738254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
739254c0d82SAdrian Hunter
740254c0d82SAdrian Hunter	def GetRoot(self):
7414a0979d4SAdrian Hunter		return CallGraphRootItem(self.glb, self.params)
742254c0d82SAdrian Hunter
743254c0d82SAdrian Hunter	def columnCount(self, parent=None):
74438a846d4SAdrian Hunter		if self.params.have_ipc:
74538a846d4SAdrian Hunter			return 12
74638a846d4SAdrian Hunter		else:
747254c0d82SAdrian Hunter			return 7
748254c0d82SAdrian Hunter
749254c0d82SAdrian Hunter	def columnHeader(self, column):
75038a846d4SAdrian Hunter		if self.params.have_ipc:
75138a846d4SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
75238a846d4SAdrian Hunter		else:
753254c0d82SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
754254c0d82SAdrian Hunter		return headers[column]
755254c0d82SAdrian Hunter
756254c0d82SAdrian Hunter	def columnAlignment(self, column):
75738a846d4SAdrian Hunter		if self.params.have_ipc:
75838a846d4SAdrian 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 ]
75938a846d4SAdrian Hunter		else:
760254c0d82SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
761254c0d82SAdrian Hunter		return alignment[column]
762254c0d82SAdrian Hunter
763254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
764254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
765254c0d82SAdrian Hunter						" FROM calls"
766254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
767254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
768254c0d82SAdrian Hunter						" WHERE symbols.name" + match +
769254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
770254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
771254c0d82SAdrian Hunter
772254c0d82SAdrian Hunter	def FindPath(self, query):
773254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
774254c0d82SAdrian Hunter		# to open the tree at the right place.
775254c0d82SAdrian Hunter		ids = []
776254c0d82SAdrian Hunter		parent_id = query.value(0)
777254c0d82SAdrian Hunter		while parent_id:
778254c0d82SAdrian Hunter			ids.insert(0, parent_id)
779254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
780254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
781254c0d82SAdrian Hunter					" FROM call_paths"
782254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
783254c0d82SAdrian Hunter			if not q2.next():
784254c0d82SAdrian Hunter				break
785254c0d82SAdrian Hunter			parent_id = q2.value(0)
786254c0d82SAdrian Hunter		# The call path root is not used
787254c0d82SAdrian Hunter		if ids[0] == 1:
788254c0d82SAdrian Hunter			del ids[0]
789254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
790254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
791254c0d82SAdrian Hunter		return ids
792254c0d82SAdrian Hunter
793ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
794ae8b887cSAdrian Hunter
795ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
796ae8b887cSAdrian Hunter
797da4264f5SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
7984a0979d4SAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
799ae8b887cSAdrian Hunter		self.comm_id = comm_id
800ae8b887cSAdrian Hunter		self.thread_id = thread_id
801ae8b887cSAdrian Hunter		self.calls_id = calls_id
802da4264f5SAdrian Hunter		self.call_time = call_time
803da4264f5SAdrian Hunter		self.time = time
804b3b66079SAdrian Hunter		self.insn_cnt = insn_cnt
805b3b66079SAdrian Hunter		self.cyc_cnt = cyc_cnt
806ae8b887cSAdrian Hunter		self.branch_count = branch_count
807ae8b887cSAdrian Hunter
808ae8b887cSAdrian Hunter	def Select(self):
80926688729SAdrian Hunter		self.query_done = True
810ae8b887cSAdrian Hunter		if self.calls_id == 0:
811ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
812ae8b887cSAdrian Hunter		else:
813ae8b887cSAdrian Hunter			comm_thread = ""
814b3b66079SAdrian Hunter		if self.params.have_ipc:
815b3b66079SAdrian Hunter			ipc_str = ", insn_count, cyc_count"
816b3b66079SAdrian Hunter		else:
817b3b66079SAdrian Hunter			ipc_str = ""
818ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
819b3b66079SAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
820ae8b887cSAdrian Hunter					" FROM calls"
821ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
822ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
823ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
824ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
825ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
826ae8b887cSAdrian Hunter		while query.next():
827b3b66079SAdrian Hunter			if self.params.have_ipc:
828b3b66079SAdrian Hunter				insn_cnt = int(query.value(5))
829b3b66079SAdrian Hunter				cyc_cnt = int(query.value(6))
830b3b66079SAdrian Hunter				branch_count = int(query.value(7))
831b3b66079SAdrian Hunter			else:
832b3b66079SAdrian Hunter				insn_cnt = 0
833b3b66079SAdrian Hunter				cyc_cnt = 0
834b3b66079SAdrian Hunter				branch_count = int(query.value(5))
835b3b66079SAdrian 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)
836ae8b887cSAdrian Hunter			self.child_items.append(child_item)
837ae8b887cSAdrian Hunter			self.child_count += 1
838ae8b887cSAdrian Hunter
839ae8b887cSAdrian Hunter# Call tree data model level three item
840ae8b887cSAdrian Hunter
841ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
842ae8b887cSAdrian Hunter
843da4264f5SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
844da4264f5SAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item)
845ae8b887cSAdrian Hunter		dso = dsoname(dso)
846b3b66079SAdrian Hunter		if self.params.have_ipc:
847b3b66079SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
848b3b66079SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
849b3b66079SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
850b3b66079SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
851da4264f5SAdrian Hunter			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
852b3b66079SAdrian Hunter		else:
853da4264f5SAdrian Hunter			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
854ae8b887cSAdrian Hunter		self.dbid = calls_id
855ae8b887cSAdrian Hunter
856ae8b887cSAdrian Hunter# Call tree data model level two item
857ae8b887cSAdrian Hunter
858ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
859ae8b887cSAdrian Hunter
8604a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
861da4264f5SAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, 0, parent_item)
862b3b66079SAdrian Hunter		if self.params.have_ipc:
863b3b66079SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
864b3b66079SAdrian Hunter		else:
865ae8b887cSAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
866ae8b887cSAdrian Hunter		self.dbid = thread_id
867ae8b887cSAdrian Hunter
868ae8b887cSAdrian Hunter	def Select(self):
869ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
870ae8b887cSAdrian Hunter		for child_item in self.child_items:
871ae8b887cSAdrian Hunter			self.time += child_item.time
872b3b66079SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
873b3b66079SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
874ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
875ae8b887cSAdrian Hunter		for child_item in self.child_items:
876ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
877b3b66079SAdrian Hunter			if self.params.have_ipc:
878b3b66079SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
879b3b66079SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
880b3b66079SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
881b3b66079SAdrian Hunter			else:
882ae8b887cSAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
883ae8b887cSAdrian Hunter
884ae8b887cSAdrian Hunter# Call tree data model level one item
885ae8b887cSAdrian Hunter
886ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
887ae8b887cSAdrian Hunter
8884a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
8894a0979d4SAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
890b3b66079SAdrian Hunter		if self.params.have_ipc:
891b3b66079SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
892b3b66079SAdrian Hunter		else:
893ae8b887cSAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
894ae8b887cSAdrian Hunter		self.dbid = comm_id
895ae8b887cSAdrian Hunter
896ae8b887cSAdrian Hunter	def Select(self):
89726688729SAdrian Hunter		self.query_done = True
898ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
899ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
900ae8b887cSAdrian Hunter					" FROM comm_threads"
901ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
902ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
903ae8b887cSAdrian Hunter		while query.next():
9044a0979d4SAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
905ae8b887cSAdrian Hunter			self.child_items.append(child_item)
906ae8b887cSAdrian Hunter			self.child_count += 1
907ae8b887cSAdrian Hunter
908ae8b887cSAdrian Hunter# Call tree data model root item
909ae8b887cSAdrian Hunter
910ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
911ae8b887cSAdrian Hunter
9124a0979d4SAdrian Hunter	def __init__(self, glb, params):
9134a0979d4SAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, params, 0, None)
914ae8b887cSAdrian Hunter		self.dbid = 0
91526688729SAdrian Hunter		self.query_done = True
91626c11206SAdrian Hunter		if_has_calls = ""
91726c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
91826c11206SAdrian Hunter			if_has_calls = " WHERE has_calls = TRUE"
919ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
92026c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
921ae8b887cSAdrian Hunter		while query.next():
922ae8b887cSAdrian Hunter			if not query.value(0):
923ae8b887cSAdrian Hunter				continue
9244a0979d4SAdrian Hunter			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
925ae8b887cSAdrian Hunter			self.child_items.append(child_item)
926ae8b887cSAdrian Hunter			self.child_count += 1
927ae8b887cSAdrian Hunter
928ae8b887cSAdrian Hunter# Call Tree data model
929ae8b887cSAdrian Hunter
930ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
931ae8b887cSAdrian Hunter
932ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
933ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
934ae8b887cSAdrian Hunter
935ae8b887cSAdrian Hunter	def GetRoot(self):
9364a0979d4SAdrian Hunter		return CallTreeRootItem(self.glb, self.params)
937ae8b887cSAdrian Hunter
938ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
939b3b66079SAdrian Hunter		if self.params.have_ipc:
940b3b66079SAdrian Hunter			return 12
941b3b66079SAdrian Hunter		else:
942ae8b887cSAdrian Hunter			return 7
943ae8b887cSAdrian Hunter
944ae8b887cSAdrian Hunter	def columnHeader(self, column):
945b3b66079SAdrian Hunter		if self.params.have_ipc:
946b3b66079SAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
947b3b66079SAdrian Hunter		else:
948ae8b887cSAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
949ae8b887cSAdrian Hunter		return headers[column]
950ae8b887cSAdrian Hunter
951ae8b887cSAdrian Hunter	def columnAlignment(self, column):
952b3b66079SAdrian Hunter		if self.params.have_ipc:
953b3b66079SAdrian 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 ]
954b3b66079SAdrian Hunter		else:
955ae8b887cSAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
956ae8b887cSAdrian Hunter		return alignment[column]
957ae8b887cSAdrian Hunter
958ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
959ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
960ae8b887cSAdrian Hunter						" FROM calls"
961ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
962ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
963ae8b887cSAdrian Hunter						" WHERE symbols.name" + match +
964ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
965ae8b887cSAdrian Hunter
966ae8b887cSAdrian Hunter	def FindPath(self, query):
967ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
968ae8b887cSAdrian Hunter		# to open the tree at the right place.
969ae8b887cSAdrian Hunter		ids = []
970ae8b887cSAdrian Hunter		parent_id = query.value(0)
971ae8b887cSAdrian Hunter		while parent_id:
972ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
973ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
974ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
975ae8b887cSAdrian Hunter					" FROM calls"
976ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
977ae8b887cSAdrian Hunter			if not q2.next():
978ae8b887cSAdrian Hunter				break
979ae8b887cSAdrian Hunter			parent_id = q2.value(0)
980ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
981ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
982ae8b887cSAdrian Hunter		return ids
983ae8b887cSAdrian Hunter
98442c303ffSAdrian Hunter# Vertical layout
98542c303ffSAdrian Hunter
98642c303ffSAdrian Hunterclass HBoxLayout(QHBoxLayout):
98742c303ffSAdrian Hunter
98842c303ffSAdrian Hunter	def __init__(self, *children):
98942c303ffSAdrian Hunter		super(HBoxLayout, self).__init__()
99042c303ffSAdrian Hunter
99142c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
99242c303ffSAdrian Hunter		for child in children:
99342c303ffSAdrian Hunter			if child.isWidgetType():
99442c303ffSAdrian Hunter				self.layout().addWidget(child)
99542c303ffSAdrian Hunter			else:
99642c303ffSAdrian Hunter				self.layout().addLayout(child)
99742c303ffSAdrian Hunter
99842c303ffSAdrian Hunter# Horizontal layout
99942c303ffSAdrian Hunter
100042c303ffSAdrian Hunterclass VBoxLayout(QVBoxLayout):
100142c303ffSAdrian Hunter
100242c303ffSAdrian Hunter	def __init__(self, *children):
100342c303ffSAdrian Hunter		super(VBoxLayout, self).__init__()
100442c303ffSAdrian Hunter
100542c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
100642c303ffSAdrian Hunter		for child in children:
100742c303ffSAdrian Hunter			if child.isWidgetType():
100842c303ffSAdrian Hunter				self.layout().addWidget(child)
100942c303ffSAdrian Hunter			else:
101042c303ffSAdrian Hunter				self.layout().addLayout(child)
101142c303ffSAdrian Hunter
101242c303ffSAdrian Hunter# Vertical layout widget
1013ebd70c7dSAdrian Hunter
1014ebd70c7dSAdrian Hunterclass VBox():
1015ebd70c7dSAdrian Hunter
101642c303ffSAdrian Hunter	def __init__(self, *children):
1017ebd70c7dSAdrian Hunter		self.vbox = QWidget()
101842c303ffSAdrian Hunter		self.vbox.setLayout(VBoxLayout(*children))
1019ebd70c7dSAdrian Hunter
1020ebd70c7dSAdrian Hunter	def Widget(self):
1021ebd70c7dSAdrian Hunter		return self.vbox
1022ebd70c7dSAdrian Hunter
1023a731cc4cSAdrian Hunter# Tree window base
10241beb5c7bSAdrian Hunter
1025a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
10261beb5c7bSAdrian Hunter
1027a731cc4cSAdrian Hunter	def __init__(self, parent=None):
1028a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
10291beb5c7bSAdrian Hunter
1030a731cc4cSAdrian Hunter		self.model = None
1031a731cc4cSAdrian Hunter		self.find_bar = None
10321beb5c7bSAdrian Hunter
1033be6e7471SAdrian Hunter		self.view = QTreeView()
103496c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
103596c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
1036be6e7471SAdrian Hunter
10379bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
10389bc4e4bfSAdrian Hunter
1039ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
1040ebd70c7dSAdrian Hunter		if not len(ids):
1041ebd70c7dSAdrian Hunter			return False
1042ebd70c7dSAdrian Hunter		parent = QModelIndex()
1043ebd70c7dSAdrian Hunter		for dbid in ids:
1044ebd70c7dSAdrian Hunter			found = False
1045ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
1046ebd70c7dSAdrian Hunter			for row in xrange(n):
1047ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
1048ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
1049ebd70c7dSAdrian Hunter					found = True
1050ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
1051ebd70c7dSAdrian Hunter					parent = child
1052ebd70c7dSAdrian Hunter					break
1053ebd70c7dSAdrian Hunter			if not found:
1054ebd70c7dSAdrian Hunter				break
1055ebd70c7dSAdrian Hunter		return found
1056ebd70c7dSAdrian Hunter
1057ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
1058ebd70c7dSAdrian Hunter		self.view.setFocus()
1059ebd70c7dSAdrian Hunter		self.find_bar.Busy()
1060ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
1061ebd70c7dSAdrian Hunter
1062ebd70c7dSAdrian Hunter	def FindDone(self, ids):
1063ebd70c7dSAdrian Hunter		found = True
1064ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
1065ebd70c7dSAdrian Hunter			found = False
1066ebd70c7dSAdrian Hunter		self.find_bar.Idle()
1067ebd70c7dSAdrian Hunter		if not found:
1068ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
1069ebd70c7dSAdrian Hunter
1070a731cc4cSAdrian Hunter
1071a731cc4cSAdrian Hunter# Context-sensitive call graph window
1072a731cc4cSAdrian Hunter
1073a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
1074a731cc4cSAdrian Hunter
1075a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
1076a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
1077a731cc4cSAdrian Hunter
1078a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
1079a731cc4cSAdrian Hunter
1080a731cc4cSAdrian Hunter		self.view.setModel(self.model)
1081a731cc4cSAdrian Hunter
1082a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
1083a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
1084a731cc4cSAdrian Hunter
1085a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
1086a731cc4cSAdrian Hunter
1087a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1088a731cc4cSAdrian Hunter
1089a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1090a731cc4cSAdrian Hunter
1091a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
1092a731cc4cSAdrian Hunter
1093ae8b887cSAdrian Hunter# Call tree window
1094ae8b887cSAdrian Hunter
1095ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
1096ae8b887cSAdrian Hunter
1097*e69d5df7SAdrian Hunter	def __init__(self, glb, parent=None, thread_at_time=None):
1098ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
1099ae8b887cSAdrian Hunter
1100ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
1101ae8b887cSAdrian Hunter
1102ae8b887cSAdrian Hunter		self.view.setModel(self.model)
1103ae8b887cSAdrian Hunter
1104ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
1105ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
1106ae8b887cSAdrian Hunter
1107ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
1108ae8b887cSAdrian Hunter
1109ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1110ae8b887cSAdrian Hunter
1111ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1112ae8b887cSAdrian Hunter
1113ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
1114ae8b887cSAdrian Hunter
1115*e69d5df7SAdrian Hunter		if thread_at_time:
1116*e69d5df7SAdrian Hunter			self.DisplayThreadAtTime(*thread_at_time)
1117*e69d5df7SAdrian Hunter
1118*e69d5df7SAdrian Hunter	def DisplayThreadAtTime(self, comm_id, thread_id, time):
1119*e69d5df7SAdrian Hunter		parent = QModelIndex()
1120*e69d5df7SAdrian Hunter		for dbid in (comm_id, thread_id):
1121*e69d5df7SAdrian Hunter			found = False
1122*e69d5df7SAdrian Hunter			n = self.model.rowCount(parent)
1123*e69d5df7SAdrian Hunter			for row in xrange(n):
1124*e69d5df7SAdrian Hunter				child = self.model.index(row, 0, parent)
1125*e69d5df7SAdrian Hunter				if child.internalPointer().dbid == dbid:
1126*e69d5df7SAdrian Hunter					found = True
1127*e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1128*e69d5df7SAdrian Hunter					parent = child
1129*e69d5df7SAdrian Hunter					break
1130*e69d5df7SAdrian Hunter			if not found:
1131*e69d5df7SAdrian Hunter				return
1132*e69d5df7SAdrian Hunter		found = False
1133*e69d5df7SAdrian Hunter		while True:
1134*e69d5df7SAdrian Hunter			n = self.model.rowCount(parent)
1135*e69d5df7SAdrian Hunter			if not n:
1136*e69d5df7SAdrian Hunter				return
1137*e69d5df7SAdrian Hunter			last_child = None
1138*e69d5df7SAdrian Hunter			for row in xrange(n):
1139*e69d5df7SAdrian Hunter				child = self.model.index(row, 0, parent)
1140*e69d5df7SAdrian Hunter				child_call_time = child.internalPointer().call_time
1141*e69d5df7SAdrian Hunter				if child_call_time < time:
1142*e69d5df7SAdrian Hunter					last_child = child
1143*e69d5df7SAdrian Hunter				elif child_call_time == time:
1144*e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1145*e69d5df7SAdrian Hunter					return
1146*e69d5df7SAdrian Hunter				elif child_call_time > time:
1147*e69d5df7SAdrian Hunter					break
1148*e69d5df7SAdrian Hunter			if not last_child:
1149*e69d5df7SAdrian Hunter				if not found:
1150*e69d5df7SAdrian Hunter					child = self.model.index(0, 0, parent)
1151*e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1152*e69d5df7SAdrian Hunter				return
1153*e69d5df7SAdrian Hunter			found = True
1154*e69d5df7SAdrian Hunter			self.view.setCurrentIndex(last_child)
1155*e69d5df7SAdrian Hunter			parent = last_child
1156*e69d5df7SAdrian Hunter
11578392b74bSAdrian Hunter# Child data item  finder
11588392b74bSAdrian Hunter
11598392b74bSAdrian Hunterclass ChildDataItemFinder():
11608392b74bSAdrian Hunter
11618392b74bSAdrian Hunter	def __init__(self, root):
11628392b74bSAdrian Hunter		self.root = root
11638392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
11648392b74bSAdrian Hunter		self.rows = []
11658392b74bSAdrian Hunter		self.pos = 0
11668392b74bSAdrian Hunter
11678392b74bSAdrian Hunter	def FindSelect(self):
11688392b74bSAdrian Hunter		self.rows = []
11698392b74bSAdrian Hunter		if self.pattern:
11708392b74bSAdrian Hunter			pattern = re.compile(self.value)
11718392b74bSAdrian Hunter			for child in self.root.child_items:
11728392b74bSAdrian Hunter				for column_data in child.data:
11738392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
11748392b74bSAdrian Hunter						self.rows.append(child.row)
11758392b74bSAdrian Hunter						break
11768392b74bSAdrian Hunter		else:
11778392b74bSAdrian Hunter			for child in self.root.child_items:
11788392b74bSAdrian Hunter				for column_data in child.data:
11798392b74bSAdrian Hunter					if self.value in str(column_data):
11808392b74bSAdrian Hunter						self.rows.append(child.row)
11818392b74bSAdrian Hunter						break
11828392b74bSAdrian Hunter
11838392b74bSAdrian Hunter	def FindValue(self):
11848392b74bSAdrian Hunter		self.pos = 0
11858392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
11868392b74bSAdrian Hunter			self.FindSelect()
11878392b74bSAdrian Hunter		if not len(self.rows):
11888392b74bSAdrian Hunter			return -1
11898392b74bSAdrian Hunter		return self.rows[self.pos]
11908392b74bSAdrian Hunter
11918392b74bSAdrian Hunter	def FindThread(self):
11928392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
11938392b74bSAdrian Hunter			row = self.FindValue()
11948392b74bSAdrian Hunter		elif len(self.rows):
11958392b74bSAdrian Hunter			if self.direction > 0:
11968392b74bSAdrian Hunter				self.pos += 1
11978392b74bSAdrian Hunter				if self.pos >= len(self.rows):
11988392b74bSAdrian Hunter					self.pos = 0
11998392b74bSAdrian Hunter			else:
12008392b74bSAdrian Hunter				self.pos -= 1
12018392b74bSAdrian Hunter				if self.pos < 0:
12028392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
12038392b74bSAdrian Hunter			row = self.rows[self.pos]
12048392b74bSAdrian Hunter		else:
12058392b74bSAdrian Hunter			row = -1
12068392b74bSAdrian Hunter		return (True, row)
12078392b74bSAdrian Hunter
12088392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
12098392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
12108392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
12118392b74bSAdrian Hunter		thread = Thread(self.FindThread)
12128392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
12138392b74bSAdrian Hunter		thread.start()
12148392b74bSAdrian Hunter
12158392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
12168392b74bSAdrian Hunter		callback(row)
12178392b74bSAdrian Hunter
12188392b74bSAdrian Hunter# Number of database records to fetch in one go
12198392b74bSAdrian Hunter
12208392b74bSAdrian Hunterglb_chunk_sz = 10000
12218392b74bSAdrian Hunter
12228392b74bSAdrian Hunter# Background process for SQL data fetcher
12238392b74bSAdrian Hunter
12248392b74bSAdrian Hunterclass SQLFetcherProcess():
12258392b74bSAdrian Hunter
12268392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
12278392b74bSAdrian Hunter		# Need a unique connection name
12288392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
12298392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
12308392b74bSAdrian Hunter		self.sql = sql
12318392b74bSAdrian Hunter		self.buffer = buffer
12328392b74bSAdrian Hunter		self.head = head
12338392b74bSAdrian Hunter		self.tail = tail
12348392b74bSAdrian Hunter		self.fetch_count = fetch_count
12358392b74bSAdrian Hunter		self.fetching_done = fetching_done
12368392b74bSAdrian Hunter		self.process_target = process_target
12378392b74bSAdrian Hunter		self.wait_event = wait_event
12388392b74bSAdrian Hunter		self.fetched_event = fetched_event
12398392b74bSAdrian Hunter		self.prep = prep
12408392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
12418392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
12428392b74bSAdrian Hunter		self.last_id = -1
12438392b74bSAdrian Hunter		self.fetched = 0
12448392b74bSAdrian Hunter		self.more = True
12458392b74bSAdrian Hunter		self.local_head = self.head.value
12468392b74bSAdrian Hunter		self.local_tail = self.tail.value
12478392b74bSAdrian Hunter
12488392b74bSAdrian Hunter	def Select(self):
12498392b74bSAdrian Hunter		if self.query_limit:
12508392b74bSAdrian Hunter			if self.query_limit == 1:
12518392b74bSAdrian Hunter				return
12528392b74bSAdrian Hunter			self.query_limit -= 1
12538392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
12548392b74bSAdrian Hunter		QueryExec(self.query, stmt)
12558392b74bSAdrian Hunter
12568392b74bSAdrian Hunter	def Next(self):
12578392b74bSAdrian Hunter		if not self.query.next():
12588392b74bSAdrian Hunter			self.Select()
12598392b74bSAdrian Hunter			if not self.query.next():
12608392b74bSAdrian Hunter				return None
12618392b74bSAdrian Hunter		self.last_id = self.query.value(0)
12628392b74bSAdrian Hunter		return self.prep(self.query)
12638392b74bSAdrian Hunter
12648392b74bSAdrian Hunter	def WaitForTarget(self):
12658392b74bSAdrian Hunter		while True:
12668392b74bSAdrian Hunter			self.wait_event.clear()
12678392b74bSAdrian Hunter			target = self.process_target.value
12688392b74bSAdrian Hunter			if target > self.fetched or target < 0:
12698392b74bSAdrian Hunter				break
12708392b74bSAdrian Hunter			self.wait_event.wait()
12718392b74bSAdrian Hunter		return target
12728392b74bSAdrian Hunter
12738392b74bSAdrian Hunter	def HasSpace(self, sz):
12748392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
12758392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
12768392b74bSAdrian Hunter			if space > sz:
12778392b74bSAdrian Hunter				return True
12788392b74bSAdrian Hunter			if space >= glb_nsz:
12798392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
1280beda0e72STony Jones				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
12818392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
12828392b74bSAdrian Hunter			self.local_head = 0
12838392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
12848392b74bSAdrian Hunter			return True
12858392b74bSAdrian Hunter		return False
12868392b74bSAdrian Hunter
12878392b74bSAdrian Hunter	def WaitForSpace(self, sz):
12888392b74bSAdrian Hunter		if self.HasSpace(sz):
12898392b74bSAdrian Hunter			return
12908392b74bSAdrian Hunter		while True:
12918392b74bSAdrian Hunter			self.wait_event.clear()
12928392b74bSAdrian Hunter			self.local_tail = self.tail.value
12938392b74bSAdrian Hunter			if self.HasSpace(sz):
12948392b74bSAdrian Hunter				return
12958392b74bSAdrian Hunter			self.wait_event.wait()
12968392b74bSAdrian Hunter
12978392b74bSAdrian Hunter	def AddToBuffer(self, obj):
1298beda0e72STony Jones		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
12998392b74bSAdrian Hunter		n = len(d)
1300beda0e72STony Jones		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
13018392b74bSAdrian Hunter		sz = n + glb_nsz
13028392b74bSAdrian Hunter		self.WaitForSpace(sz)
13038392b74bSAdrian Hunter		pos = self.local_head
13048392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
13058392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
13068392b74bSAdrian Hunter		self.local_head += sz
13078392b74bSAdrian Hunter
13088392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
13098392b74bSAdrian Hunter		fetched = 0
13108392b74bSAdrian Hunter		while batch_size > fetched:
13118392b74bSAdrian Hunter			obj = self.Next()
13128392b74bSAdrian Hunter			if obj is None:
13138392b74bSAdrian Hunter				self.more = False
13148392b74bSAdrian Hunter				break
13158392b74bSAdrian Hunter			self.AddToBuffer(obj)
13168392b74bSAdrian Hunter			fetched += 1
13178392b74bSAdrian Hunter		if fetched:
13188392b74bSAdrian Hunter			self.fetched += fetched
13198392b74bSAdrian Hunter			with self.fetch_count.get_lock():
13208392b74bSAdrian Hunter				self.fetch_count.value += fetched
13218392b74bSAdrian Hunter			self.head.value = self.local_head
13228392b74bSAdrian Hunter			self.fetched_event.set()
13238392b74bSAdrian Hunter
13248392b74bSAdrian Hunter	def Run(self):
13258392b74bSAdrian Hunter		while self.more:
13268392b74bSAdrian Hunter			target = self.WaitForTarget()
13278392b74bSAdrian Hunter			if target < 0:
13288392b74bSAdrian Hunter				break
13298392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
13308392b74bSAdrian Hunter			self.FetchBatch(batch_size)
13318392b74bSAdrian Hunter		self.fetching_done.value = True
13328392b74bSAdrian Hunter		self.fetched_event.set()
13338392b74bSAdrian Hunter
13348392b74bSAdrian Hunterdef SQLFetcherFn(*x):
13358392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
13368392b74bSAdrian Hunter	process.Run()
13378392b74bSAdrian Hunter
13388392b74bSAdrian Hunter# SQL data fetcher
13398392b74bSAdrian Hunter
13408392b74bSAdrian Hunterclass SQLFetcher(QObject):
13418392b74bSAdrian Hunter
13428392b74bSAdrian Hunter	done = Signal(object)
13438392b74bSAdrian Hunter
13448392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
13458392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
13468392b74bSAdrian Hunter		self.process_data = process_data
13478392b74bSAdrian Hunter		self.more = True
13488392b74bSAdrian Hunter		self.target = 0
13498392b74bSAdrian Hunter		self.last_target = 0
13508392b74bSAdrian Hunter		self.fetched = 0
13518392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
13528392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
13538392b74bSAdrian Hunter		self.head = Value(c_longlong)
13548392b74bSAdrian Hunter		self.tail = Value(c_longlong)
13558392b74bSAdrian Hunter		self.local_tail = 0
13568392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
13578392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
13588392b74bSAdrian Hunter		self.last_count = 0
13598392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
13608392b74bSAdrian Hunter		self.wait_event = Event()
13618392b74bSAdrian Hunter		self.fetched_event = Event()
13628392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
13638392b74bSAdrian 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))
13648392b74bSAdrian Hunter		self.process.start()
13658392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
13668392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
13678392b74bSAdrian Hunter		self.thread.start()
13688392b74bSAdrian Hunter
13698392b74bSAdrian Hunter	def Shutdown(self):
13708392b74bSAdrian Hunter		# Tell the thread and process to exit
13718392b74bSAdrian Hunter		self.process_target.value = -1
13728392b74bSAdrian Hunter		self.wait_event.set()
13738392b74bSAdrian Hunter		self.more = False
13748392b74bSAdrian Hunter		self.fetching_done.value = True
13758392b74bSAdrian Hunter		self.fetched_event.set()
13768392b74bSAdrian Hunter
13778392b74bSAdrian Hunter	def Thread(self):
13788392b74bSAdrian Hunter		if not self.more:
13798392b74bSAdrian Hunter			return True, 0
13808392b74bSAdrian Hunter		while True:
13818392b74bSAdrian Hunter			self.fetched_event.clear()
13828392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
13838392b74bSAdrian Hunter			if fetch_count != self.last_count:
13848392b74bSAdrian Hunter				break
13858392b74bSAdrian Hunter			if self.fetching_done.value:
13868392b74bSAdrian Hunter				self.more = False
13878392b74bSAdrian Hunter				return True, 0
13888392b74bSAdrian Hunter			self.fetched_event.wait()
13898392b74bSAdrian Hunter		count = fetch_count - self.last_count
13908392b74bSAdrian Hunter		self.last_count = fetch_count
13918392b74bSAdrian Hunter		self.fetched += count
13928392b74bSAdrian Hunter		return False, count
13938392b74bSAdrian Hunter
13948392b74bSAdrian Hunter	def Fetch(self, nr):
13958392b74bSAdrian Hunter		if not self.more:
13968392b74bSAdrian Hunter			# -1 inidcates there are no more
13978392b74bSAdrian Hunter			return -1
13988392b74bSAdrian Hunter		result = self.fetched
13998392b74bSAdrian Hunter		extra = result + nr - self.target
14008392b74bSAdrian Hunter		if extra > 0:
14018392b74bSAdrian Hunter			self.target += extra
14028392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
14038392b74bSAdrian Hunter			if self.process_target.value >= 0:
14048392b74bSAdrian Hunter				self.process_target.value = self.target
14058392b74bSAdrian Hunter			self.wait_event.set()
14068392b74bSAdrian Hunter		return result
14078392b74bSAdrian Hunter
14088392b74bSAdrian Hunter	def RemoveFromBuffer(self):
14098392b74bSAdrian Hunter		pos = self.local_tail
14108392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
14118392b74bSAdrian Hunter			pos = 0
1412beda0e72STony Jones		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
14138392b74bSAdrian Hunter		if n == 0:
14148392b74bSAdrian Hunter			pos = 0
1415beda0e72STony Jones			n = pickle.loads(self.buffer[0 : glb_nsz])
14168392b74bSAdrian Hunter		pos += glb_nsz
1417beda0e72STony Jones		obj = pickle.loads(self.buffer[pos : pos + n])
14188392b74bSAdrian Hunter		self.local_tail = pos + n
14198392b74bSAdrian Hunter		return obj
14208392b74bSAdrian Hunter
14218392b74bSAdrian Hunter	def ProcessData(self, count):
14228392b74bSAdrian Hunter		for i in xrange(count):
14238392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
14248392b74bSAdrian Hunter			self.process_data(obj)
14258392b74bSAdrian Hunter		self.tail.value = self.local_tail
14268392b74bSAdrian Hunter		self.wait_event.set()
14278392b74bSAdrian Hunter		self.done.emit(count)
14288392b74bSAdrian Hunter
14298392b74bSAdrian Hunter# Fetch more records bar
14308392b74bSAdrian Hunter
14318392b74bSAdrian Hunterclass FetchMoreRecordsBar():
14328392b74bSAdrian Hunter
14338392b74bSAdrian Hunter	def __init__(self, model, parent):
14348392b74bSAdrian Hunter		self.model = model
14358392b74bSAdrian Hunter
14368392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
14378392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
14388392b74bSAdrian Hunter
14398392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
14408392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
14418392b74bSAdrian Hunter		self.fetch_count.setValue(10)
14428392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
14438392b74bSAdrian Hunter
14448392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
14458392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
14468392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
14478392b74bSAdrian Hunter
14488392b74bSAdrian Hunter		self.progress = QProgressBar()
14498392b74bSAdrian Hunter		self.progress.setRange(0, 100)
14508392b74bSAdrian Hunter		self.progress.hide()
14518392b74bSAdrian Hunter
14528392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
14538392b74bSAdrian Hunter		self.done_label.hide()
14548392b74bSAdrian Hunter
14558392b74bSAdrian Hunter		self.spacer = QLabel("")
14568392b74bSAdrian Hunter
14578392b74bSAdrian Hunter		self.close_button = QToolButton()
14588392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
14598392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
14608392b74bSAdrian Hunter
14618392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
14628392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
14638392b74bSAdrian Hunter
14648392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
14658392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
14668392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
14678392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
14688392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
14698392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
14708392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
14718392b74bSAdrian Hunter
14728392b74bSAdrian Hunter		self.bar = QWidget()
147326688729SAdrian Hunter		self.bar.setLayout(self.hbox)
14748392b74bSAdrian Hunter		self.bar.show()
14758392b74bSAdrian Hunter
14768392b74bSAdrian Hunter		self.in_progress = False
14778392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
14788392b74bSAdrian Hunter
14798392b74bSAdrian Hunter		self.done = False
14808392b74bSAdrian Hunter
14818392b74bSAdrian Hunter		if not model.HasMoreRecords():
14828392b74bSAdrian Hunter			self.Done()
14838392b74bSAdrian Hunter
14848392b74bSAdrian Hunter	def Widget(self):
14858392b74bSAdrian Hunter		return self.bar
14868392b74bSAdrian Hunter
14878392b74bSAdrian Hunter	def Activate(self):
14888392b74bSAdrian Hunter		self.bar.show()
14898392b74bSAdrian Hunter		self.fetch.setFocus()
14908392b74bSAdrian Hunter
14918392b74bSAdrian Hunter	def Deactivate(self):
14928392b74bSAdrian Hunter		self.bar.hide()
14938392b74bSAdrian Hunter
14948392b74bSAdrian Hunter	def Enable(self, enable):
14958392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
14968392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
14978392b74bSAdrian Hunter
14988392b74bSAdrian Hunter	def Busy(self):
14998392b74bSAdrian Hunter		self.Enable(False)
15008392b74bSAdrian Hunter		self.fetch.hide()
15018392b74bSAdrian Hunter		self.spacer.hide()
15028392b74bSAdrian Hunter		self.progress.show()
15038392b74bSAdrian Hunter
15048392b74bSAdrian Hunter	def Idle(self):
15058392b74bSAdrian Hunter		self.in_progress = False
15068392b74bSAdrian Hunter		self.Enable(True)
15078392b74bSAdrian Hunter		self.progress.hide()
15088392b74bSAdrian Hunter		self.fetch.show()
15098392b74bSAdrian Hunter		self.spacer.show()
15108392b74bSAdrian Hunter
15118392b74bSAdrian Hunter	def Target(self):
15128392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
15138392b74bSAdrian Hunter
15148392b74bSAdrian Hunter	def Done(self):
15158392b74bSAdrian Hunter		self.done = True
15168392b74bSAdrian Hunter		self.Idle()
15178392b74bSAdrian Hunter		self.label.hide()
15188392b74bSAdrian Hunter		self.fetch_count.hide()
15198392b74bSAdrian Hunter		self.fetch.hide()
15208392b74bSAdrian Hunter		self.spacer.hide()
15218392b74bSAdrian Hunter		self.done_label.show()
15228392b74bSAdrian Hunter
15238392b74bSAdrian Hunter	def Progress(self, count):
15248392b74bSAdrian Hunter		if self.in_progress:
15258392b74bSAdrian Hunter			if count:
15268392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
15278392b74bSAdrian Hunter				if percent >= 100:
15288392b74bSAdrian Hunter					self.Idle()
15298392b74bSAdrian Hunter				else:
15308392b74bSAdrian Hunter					self.progress.setValue(percent)
15318392b74bSAdrian Hunter		if not count:
15328392b74bSAdrian Hunter			# Count value of zero means no more records
15338392b74bSAdrian Hunter			self.Done()
15348392b74bSAdrian Hunter
15358392b74bSAdrian Hunter	def FetchMoreRecords(self):
15368392b74bSAdrian Hunter		if self.done:
15378392b74bSAdrian Hunter			return
15388392b74bSAdrian Hunter		self.progress.setValue(0)
15398392b74bSAdrian Hunter		self.Busy()
15408392b74bSAdrian Hunter		self.in_progress = True
15418392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
15428392b74bSAdrian Hunter
154376099f98SAdrian Hunter# Brance data model level two item
154476099f98SAdrian Hunter
154576099f98SAdrian Hunterclass BranchLevelTwoItem():
154676099f98SAdrian Hunter
1547530e22fdSAdrian Hunter	def __init__(self, row, col, text, parent_item):
154876099f98SAdrian Hunter		self.row = row
154976099f98SAdrian Hunter		self.parent_item = parent_item
1550530e22fdSAdrian Hunter		self.data = [""] * (col + 1)
1551530e22fdSAdrian Hunter		self.data[col] = text
155276099f98SAdrian Hunter		self.level = 2
155376099f98SAdrian Hunter
155476099f98SAdrian Hunter	def getParentItem(self):
155576099f98SAdrian Hunter		return self.parent_item
155676099f98SAdrian Hunter
155776099f98SAdrian Hunter	def getRow(self):
155876099f98SAdrian Hunter		return self.row
155976099f98SAdrian Hunter
156076099f98SAdrian Hunter	def childCount(self):
156176099f98SAdrian Hunter		return 0
156276099f98SAdrian Hunter
156376099f98SAdrian Hunter	def hasChildren(self):
156476099f98SAdrian Hunter		return False
156576099f98SAdrian Hunter
156676099f98SAdrian Hunter	def getData(self, column):
156776099f98SAdrian Hunter		return self.data[column]
156876099f98SAdrian Hunter
156976099f98SAdrian Hunter# Brance data model level one item
157076099f98SAdrian Hunter
157176099f98SAdrian Hunterclass BranchLevelOneItem():
157276099f98SAdrian Hunter
157376099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
157476099f98SAdrian Hunter		self.glb = glb
157576099f98SAdrian Hunter		self.row = row
157676099f98SAdrian Hunter		self.parent_item = parent_item
157776099f98SAdrian Hunter		self.child_count = 0
157876099f98SAdrian Hunter		self.child_items = []
157976099f98SAdrian Hunter		self.data = data[1:]
158076099f98SAdrian Hunter		self.dbid = data[0]
158176099f98SAdrian Hunter		self.level = 1
158276099f98SAdrian Hunter		self.query_done = False
1583530e22fdSAdrian Hunter		self.br_col = len(self.data) - 1
158476099f98SAdrian Hunter
158576099f98SAdrian Hunter	def getChildItem(self, row):
158676099f98SAdrian Hunter		return self.child_items[row]
158776099f98SAdrian Hunter
158876099f98SAdrian Hunter	def getParentItem(self):
158976099f98SAdrian Hunter		return self.parent_item
159076099f98SAdrian Hunter
159176099f98SAdrian Hunter	def getRow(self):
159276099f98SAdrian Hunter		return self.row
159376099f98SAdrian Hunter
159476099f98SAdrian Hunter	def Select(self):
159576099f98SAdrian Hunter		self.query_done = True
159676099f98SAdrian Hunter
159776099f98SAdrian Hunter		if not self.glb.have_disassembler:
159876099f98SAdrian Hunter			return
159976099f98SAdrian Hunter
160076099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
160176099f98SAdrian Hunter
160276099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
160376099f98SAdrian Hunter				  " FROM samples"
160476099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
160576099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
160676099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
160776099f98SAdrian Hunter		if not query.next():
160876099f98SAdrian Hunter			return
160976099f98SAdrian Hunter		cpu = query.value(0)
161076099f98SAdrian Hunter		dso = query.value(1)
161176099f98SAdrian Hunter		sym = query.value(2)
161276099f98SAdrian Hunter		if dso == 0 or sym == 0:
161376099f98SAdrian Hunter			return
161476099f98SAdrian Hunter		off = query.value(3)
161576099f98SAdrian Hunter		short_name = query.value(4)
161676099f98SAdrian Hunter		long_name = query.value(5)
161776099f98SAdrian Hunter		build_id = query.value(6)
161876099f98SAdrian Hunter		sym_start = query.value(7)
161976099f98SAdrian Hunter		ip = query.value(8)
162076099f98SAdrian Hunter
162176099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
162276099f98SAdrian Hunter				  " FROM samples"
162376099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
162476099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
162576099f98SAdrian Hunter				  " ORDER BY samples.id"
162676099f98SAdrian Hunter				  " LIMIT 1")
162776099f98SAdrian Hunter		if not query.next():
162876099f98SAdrian Hunter			return
162976099f98SAdrian Hunter		if query.value(0) != dso:
163076099f98SAdrian Hunter			# Cannot disassemble from one dso to another
163176099f98SAdrian Hunter			return
163276099f98SAdrian Hunter		bsym = query.value(1)
163376099f98SAdrian Hunter		boff = query.value(2)
163476099f98SAdrian Hunter		bsym_start = query.value(3)
163576099f98SAdrian Hunter		if bsym == 0:
163676099f98SAdrian Hunter			return
163776099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
163876099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
163976099f98SAdrian Hunter			return
164076099f98SAdrian Hunter
164176099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
164276099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
164376099f98SAdrian Hunter		if not f:
164476099f98SAdrian Hunter			return
164576099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
164676099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
164776099f98SAdrian Hunter
164876099f98SAdrian Hunter		buf_sz = tot + 16
164976099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
165076099f98SAdrian Hunter		f.seek(sym_start + off)
165176099f98SAdrian Hunter		buf.value = f.read(buf_sz)
165276099f98SAdrian Hunter		buf_ptr = addressof(buf)
165376099f98SAdrian Hunter		i = 0
165476099f98SAdrian Hunter		while tot > 0:
165576099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
165676099f98SAdrian Hunter			if cnt:
165776099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
165876099f98SAdrian Hunter				for k in xrange(cnt):
165976099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
166076099f98SAdrian Hunter					i += 1
166176099f98SAdrian Hunter				while k < 15:
166276099f98SAdrian Hunter					byte_str += "   "
166376099f98SAdrian Hunter					k += 1
1664530e22fdSAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
166576099f98SAdrian Hunter				self.child_count += 1
166676099f98SAdrian Hunter			else:
166776099f98SAdrian Hunter				return
166876099f98SAdrian Hunter			buf_ptr += cnt
166976099f98SAdrian Hunter			tot -= cnt
167076099f98SAdrian Hunter			buf_sz -= cnt
167176099f98SAdrian Hunter			ip += cnt
167276099f98SAdrian Hunter
167376099f98SAdrian Hunter	def childCount(self):
167476099f98SAdrian Hunter		if not self.query_done:
167576099f98SAdrian Hunter			self.Select()
167676099f98SAdrian Hunter			if not self.child_count:
167776099f98SAdrian Hunter				return -1
167876099f98SAdrian Hunter		return self.child_count
167976099f98SAdrian Hunter
168076099f98SAdrian Hunter	def hasChildren(self):
168176099f98SAdrian Hunter		if not self.query_done:
168276099f98SAdrian Hunter			return True
168376099f98SAdrian Hunter		return self.child_count > 0
168476099f98SAdrian Hunter
168576099f98SAdrian Hunter	def getData(self, column):
168676099f98SAdrian Hunter		return self.data[column]
168776099f98SAdrian Hunter
168876099f98SAdrian Hunter# Brance data model root item
168976099f98SAdrian Hunter
169076099f98SAdrian Hunterclass BranchRootItem():
169176099f98SAdrian Hunter
169276099f98SAdrian Hunter	def __init__(self):
169376099f98SAdrian Hunter		self.child_count = 0
169476099f98SAdrian Hunter		self.child_items = []
169576099f98SAdrian Hunter		self.level = 0
169676099f98SAdrian Hunter
169776099f98SAdrian Hunter	def getChildItem(self, row):
169876099f98SAdrian Hunter		return self.child_items[row]
169976099f98SAdrian Hunter
170076099f98SAdrian Hunter	def getParentItem(self):
170176099f98SAdrian Hunter		return None
170276099f98SAdrian Hunter
170376099f98SAdrian Hunter	def getRow(self):
170476099f98SAdrian Hunter		return 0
170576099f98SAdrian Hunter
170676099f98SAdrian Hunter	def childCount(self):
170776099f98SAdrian Hunter		return self.child_count
170876099f98SAdrian Hunter
170976099f98SAdrian Hunter	def hasChildren(self):
171076099f98SAdrian Hunter		return self.child_count > 0
171176099f98SAdrian Hunter
171276099f98SAdrian Hunter	def getData(self, column):
171376099f98SAdrian Hunter		return ""
171476099f98SAdrian Hunter
1715530e22fdSAdrian Hunter# Calculate instructions per cycle
1716530e22fdSAdrian Hunter
1717530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt):
1718530e22fdSAdrian Hunter	if cyc_cnt and insn_cnt:
1719530e22fdSAdrian Hunter		ipc = Decimal(float(insn_cnt) / cyc_cnt)
1720530e22fdSAdrian Hunter		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
1721530e22fdSAdrian Hunter	else:
1722530e22fdSAdrian Hunter		ipc = "0"
1723530e22fdSAdrian Hunter	return ipc
1724530e22fdSAdrian Hunter
172576099f98SAdrian Hunter# Branch data preparation
172676099f98SAdrian Hunter
1727530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data):
1728530e22fdSAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
1729530e22fdSAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
1730530e22fdSAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
1731530e22fdSAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
1732530e22fdSAdrian Hunter
1733530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data):
1734530e22fdSAdrian Hunter	insn_cnt = query.value(16)
1735530e22fdSAdrian Hunter	cyc_cnt = query.value(17)
1736530e22fdSAdrian Hunter	ipc = CalcIPC(cyc_cnt, insn_cnt)
1737530e22fdSAdrian Hunter	data.append(insn_cnt)
1738530e22fdSAdrian Hunter	data.append(cyc_cnt)
1739530e22fdSAdrian Hunter	data.append(ipc)
1740530e22fdSAdrian Hunter
174176099f98SAdrian Hunterdef BranchDataPrep(query):
174276099f98SAdrian Hunter	data = []
174376099f98SAdrian Hunter	for i in xrange(0, 8):
174476099f98SAdrian Hunter		data.append(query.value(i))
1745530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
174676099f98SAdrian Hunter	return data
174776099f98SAdrian Hunter
17488453c936SAdrian Hunterdef BranchDataPrepWA(query):
17498453c936SAdrian Hunter	data = []
17508453c936SAdrian Hunter	data.append(query.value(0))
17518453c936SAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
17528453c936SAdrian Hunter	data.append("{:>19}".format(query.value(1)))
17538453c936SAdrian Hunter	for i in xrange(2, 8):
17548453c936SAdrian Hunter		data.append(query.value(i))
1755530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1756530e22fdSAdrian Hunter	return data
1757530e22fdSAdrian Hunter
1758530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query):
1759530e22fdSAdrian Hunter	data = []
1760530e22fdSAdrian Hunter	for i in xrange(0, 8):
1761530e22fdSAdrian Hunter		data.append(query.value(i))
1762530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1763530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1764530e22fdSAdrian Hunter	return data
1765530e22fdSAdrian Hunter
1766530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query):
1767530e22fdSAdrian Hunter	data = []
1768530e22fdSAdrian Hunter	data.append(query.value(0))
1769530e22fdSAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
1770530e22fdSAdrian Hunter	data.append("{:>19}".format(query.value(1)))
1771530e22fdSAdrian Hunter	for i in xrange(2, 8):
1772530e22fdSAdrian Hunter		data.append(query.value(i))
1773530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1774530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
17758453c936SAdrian Hunter	return data
17768453c936SAdrian Hunter
177776099f98SAdrian Hunter# Branch data model
177876099f98SAdrian Hunter
177976099f98SAdrian Hunterclass BranchModel(TreeModel):
178076099f98SAdrian Hunter
178176099f98SAdrian Hunter	progress = Signal(object)
178276099f98SAdrian Hunter
178376099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
17844a0979d4SAdrian Hunter		super(BranchModel, self).__init__(glb, None, parent)
178576099f98SAdrian Hunter		self.event_id = event_id
178676099f98SAdrian Hunter		self.more = True
178776099f98SAdrian Hunter		self.populated = 0
1788530e22fdSAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
1789530e22fdSAdrian Hunter		if self.have_ipc:
1790530e22fdSAdrian Hunter			select_ipc = ", insn_count, cyc_count"
1791530e22fdSAdrian Hunter			prep_fn = BranchDataWithIPCPrep
1792530e22fdSAdrian Hunter			prep_wa_fn = BranchDataWithIPCPrepWA
1793530e22fdSAdrian Hunter		else:
1794530e22fdSAdrian Hunter			select_ipc = ""
1795530e22fdSAdrian Hunter			prep_fn = BranchDataPrep
1796530e22fdSAdrian Hunter			prep_wa_fn = BranchDataPrepWA
179776099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
179876099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
179976099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
180076099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
1801530e22fdSAdrian Hunter			+ select_ipc +
180276099f98SAdrian Hunter			" FROM samples"
180376099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
180476099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
180576099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
180676099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
180776099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
180876099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
180976099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
181076099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
181176099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
181276099f98SAdrian Hunter			" ORDER BY samples.id"
181376099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
18148453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
1815530e22fdSAdrian Hunter			prep = prep_fn
18168453c936SAdrian Hunter		else:
1817530e22fdSAdrian Hunter			prep = prep_wa_fn
18188453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
181976099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
182076099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
182176099f98SAdrian Hunter
1822a448ba23SAdrian Hunter	def GetRoot(self):
1823a448ba23SAdrian Hunter		return BranchRootItem()
1824a448ba23SAdrian Hunter
182576099f98SAdrian Hunter	def columnCount(self, parent=None):
1826530e22fdSAdrian Hunter		if self.have_ipc:
1827530e22fdSAdrian Hunter			return 11
1828530e22fdSAdrian Hunter		else:
182976099f98SAdrian Hunter			return 8
183076099f98SAdrian Hunter
183176099f98SAdrian Hunter	def columnHeader(self, column):
1832530e22fdSAdrian Hunter		if self.have_ipc:
1833530e22fdSAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
1834530e22fdSAdrian Hunter		else:
183576099f98SAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
183676099f98SAdrian Hunter
183776099f98SAdrian Hunter	def columnFont(self, column):
1838530e22fdSAdrian Hunter		if self.have_ipc:
1839530e22fdSAdrian Hunter			br_col = 10
1840530e22fdSAdrian Hunter		else:
1841530e22fdSAdrian Hunter			br_col = 7
1842530e22fdSAdrian Hunter		if column != br_col:
184376099f98SAdrian Hunter			return None
184476099f98SAdrian Hunter		return QFont("Monospace")
184576099f98SAdrian Hunter
184676099f98SAdrian Hunter	def DisplayData(self, item, index):
184776099f98SAdrian Hunter		if item.level == 1:
184876099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
184976099f98SAdrian Hunter		return item.getData(index.column())
185076099f98SAdrian Hunter
185176099f98SAdrian Hunter	def AddSample(self, data):
185276099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
185376099f98SAdrian Hunter		self.root.child_items.append(child)
185476099f98SAdrian Hunter		self.populated += 1
185576099f98SAdrian Hunter
185676099f98SAdrian Hunter	def Update(self, fetched):
185776099f98SAdrian Hunter		if not fetched:
185876099f98SAdrian Hunter			self.more = False
185976099f98SAdrian Hunter			self.progress.emit(0)
186076099f98SAdrian Hunter		child_count = self.root.child_count
186176099f98SAdrian Hunter		count = self.populated - child_count
186276099f98SAdrian Hunter		if count > 0:
186376099f98SAdrian Hunter			parent = QModelIndex()
186476099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
186576099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
186676099f98SAdrian Hunter			self.root.child_count += count
186776099f98SAdrian Hunter			self.endInsertRows()
186876099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
186976099f98SAdrian Hunter
187076099f98SAdrian Hunter	def FetchMoreRecords(self, count):
187176099f98SAdrian Hunter		current = self.root.child_count
187276099f98SAdrian Hunter		if self.more:
187376099f98SAdrian Hunter			self.fetcher.Fetch(count)
187476099f98SAdrian Hunter		else:
187576099f98SAdrian Hunter			self.progress.emit(0)
187676099f98SAdrian Hunter		return current
187776099f98SAdrian Hunter
187876099f98SAdrian Hunter	def HasMoreRecords(self):
187976099f98SAdrian Hunter		return self.more
188076099f98SAdrian Hunter
18810bf0947aSAdrian Hunter# Report Variables
18820bf0947aSAdrian Hunter
18830bf0947aSAdrian Hunterclass ReportVars():
18840bf0947aSAdrian Hunter
1885cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1886947cc38dSAdrian Hunter		self.name = name
18870bf0947aSAdrian Hunter		self.where_clause = where_clause
1888cd358012SAdrian Hunter		self.limit = limit
18890bf0947aSAdrian Hunter
18900bf0947aSAdrian Hunter	def UniqueId(self):
1891cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
18920bf0947aSAdrian Hunter
189376099f98SAdrian Hunter# Branch window
189476099f98SAdrian Hunter
189576099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
189676099f98SAdrian Hunter
1897947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
189876099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
189976099f98SAdrian Hunter
19000bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
190176099f98SAdrian Hunter
19020bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
190376099f98SAdrian Hunter
190476099f98SAdrian Hunter		self.view = QTreeView()
190576099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
190696c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
190796c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
190876099f98SAdrian Hunter		self.view.setModel(self.model)
190976099f98SAdrian Hunter
191076099f98SAdrian Hunter		self.ResizeColumnsToContents()
191176099f98SAdrian Hunter
19129bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
19139bc4e4bfSAdrian Hunter
191476099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
191576099f98SAdrian Hunter
191676099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
191776099f98SAdrian Hunter
191876099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
191976099f98SAdrian Hunter
192076099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
192176099f98SAdrian Hunter
192276099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
192376099f98SAdrian Hunter
1924947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
192576099f98SAdrian Hunter
192676099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
192776099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
192876099f98SAdrian Hunter		# so implement a crude alternative
192976099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
193076099f98SAdrian Hunter		font = self.view.font()
193176099f98SAdrian Hunter		metrics = QFontMetrics(font)
193276099f98SAdrian Hunter		max = 0
193376099f98SAdrian Hunter		for row in xrange(n):
193476099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
193576099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
193676099f98SAdrian Hunter			max = len if len > max else max
193776099f98SAdrian Hunter		val = self.model.columnHeader(column)
193876099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
193976099f98SAdrian Hunter		max = len if len > max else max
194076099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
194176099f98SAdrian Hunter
194276099f98SAdrian Hunter	def ResizeColumnsToContents(self):
194376099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
194476099f98SAdrian Hunter		if n < 1:
194576099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
194676099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
194776099f98SAdrian Hunter			return
194876099f98SAdrian Hunter		columns = self.model.columnCount()
194976099f98SAdrian Hunter		for i in xrange(columns):
195076099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
195176099f98SAdrian Hunter
195276099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
195376099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
195476099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
195576099f98SAdrian Hunter		self.ResizeColumnsToContents()
195676099f98SAdrian Hunter
195776099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
195876099f98SAdrian Hunter		self.view.setFocus()
195976099f98SAdrian Hunter		self.find_bar.Busy()
196076099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
196176099f98SAdrian Hunter
196276099f98SAdrian Hunter	def FindDone(self, row):
196376099f98SAdrian Hunter		self.find_bar.Idle()
196476099f98SAdrian Hunter		if row >= 0:
196576099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
196676099f98SAdrian Hunter		else:
196776099f98SAdrian Hunter			self.find_bar.NotFound()
196876099f98SAdrian Hunter
19691c3ca1b3SAdrian Hunter# Line edit data item
19701c3ca1b3SAdrian Hunter
19711c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
19721c3ca1b3SAdrian Hunter
1973cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
19741c3ca1b3SAdrian Hunter		self.glb = glb
19751c3ca1b3SAdrian Hunter		self.label = label
19761c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
19771c3ca1b3SAdrian Hunter		self.parent = parent
19781c3ca1b3SAdrian Hunter		self.id = id
19791c3ca1b3SAdrian Hunter
1980cd358012SAdrian Hunter		self.value = default
19811c3ca1b3SAdrian Hunter
1982cd358012SAdrian Hunter		self.widget = QLineEdit(default)
19831c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
19841c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
19851c3ca1b3SAdrian Hunter		self.red = False
19861c3ca1b3SAdrian Hunter		self.error = ""
19871c3ca1b3SAdrian Hunter		self.validated = True
19881c3ca1b3SAdrian Hunter
19891c3ca1b3SAdrian Hunter		if placeholder_text:
19901c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
19911c3ca1b3SAdrian Hunter
19921c3ca1b3SAdrian Hunter	def TurnTextRed(self):
19931c3ca1b3SAdrian Hunter		if not self.red:
19941c3ca1b3SAdrian Hunter			palette = QPalette()
19951c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
19961c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
19971c3ca1b3SAdrian Hunter			self.red = True
19981c3ca1b3SAdrian Hunter
19991c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
20001c3ca1b3SAdrian Hunter		if self.red:
20011c3ca1b3SAdrian Hunter			palette = QPalette()
20021c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
20031c3ca1b3SAdrian Hunter			self.red = False
20041c3ca1b3SAdrian Hunter
20051c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
20061c3ca1b3SAdrian Hunter		self.value = ""
20071c3ca1b3SAdrian Hunter		self.TurnTextRed()
20081c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
20091c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
20101c3ca1b3SAdrian Hunter
20111c3ca1b3SAdrian Hunter	def Invalidate(self):
20121c3ca1b3SAdrian Hunter		self.validated = False
20131c3ca1b3SAdrian Hunter
20141c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
20151c3ca1b3SAdrian Hunter		self.value = input_string.strip()
20161c3ca1b3SAdrian Hunter
20171c3ca1b3SAdrian Hunter	def Validate(self):
20181c3ca1b3SAdrian Hunter		self.validated = True
20191c3ca1b3SAdrian Hunter		self.error = ""
20201c3ca1b3SAdrian Hunter		self.TurnTextNormal()
20211c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
20221c3ca1b3SAdrian Hunter		input_string = self.widget.text()
20231c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
20241c3ca1b3SAdrian Hunter			self.value = ""
20251c3ca1b3SAdrian Hunter			return
20261c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
20271c3ca1b3SAdrian Hunter
20281c3ca1b3SAdrian Hunter	def IsValid(self):
20291c3ca1b3SAdrian Hunter		if not self.validated:
20301c3ca1b3SAdrian Hunter			self.Validate()
20311c3ca1b3SAdrian Hunter		if len(self.error):
20321c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
20331c3ca1b3SAdrian Hunter			return False
20341c3ca1b3SAdrian Hunter		return True
20351c3ca1b3SAdrian Hunter
20361c3ca1b3SAdrian Hunter	def IsNumber(self, value):
20371c3ca1b3SAdrian Hunter		try:
20381c3ca1b3SAdrian Hunter			x = int(value)
20391c3ca1b3SAdrian Hunter		except:
20401c3ca1b3SAdrian Hunter			x = 0
20411c3ca1b3SAdrian Hunter		return str(x) == value
20421c3ca1b3SAdrian Hunter
20431c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
20441c3ca1b3SAdrian Hunter
20451c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
20461c3ca1b3SAdrian Hunter
20471c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
20481c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
20491c3ca1b3SAdrian Hunter
20501c3ca1b3SAdrian Hunter		self.column_name = column_name
20511c3ca1b3SAdrian Hunter
20521c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
20531c3ca1b3SAdrian Hunter		singles = []
20541c3ca1b3SAdrian Hunter		ranges = []
20551c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
20561c3ca1b3SAdrian Hunter			if "-" in value:
20571c3ca1b3SAdrian Hunter				vrange = value.split("-")
20581c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
20591c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
20601c3ca1b3SAdrian Hunter				ranges.append(vrange)
20611c3ca1b3SAdrian Hunter			else:
20621c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
20631c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
20641c3ca1b3SAdrian Hunter				singles.append(value)
20651c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
20661c3ca1b3SAdrian Hunter		if len(singles):
20671c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
20681c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
20691c3ca1b3SAdrian Hunter
2070cd358012SAdrian Hunter# Positive integer dialog data item
2071cd358012SAdrian Hunter
2072cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
2073cd358012SAdrian Hunter
2074cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
2075cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
2076cd358012SAdrian Hunter
2077cd358012SAdrian Hunter	def DoValidate(self, input_string):
2078cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
2079cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2080cd358012SAdrian Hunter		value = int(input_string.strip())
2081cd358012SAdrian Hunter		if value <= 0:
2082cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2083cd358012SAdrian Hunter		self.value = str(value)
2084cd358012SAdrian Hunter
20851c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
20861c3ca1b3SAdrian Hunter
20871c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
20881c3ca1b3SAdrian Hunter
20891c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
20901c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
20911c3ca1b3SAdrian Hunter
20921c3ca1b3SAdrian Hunter		self.table_name = table_name
20931c3ca1b3SAdrian Hunter		self.match_column = match_column
20941c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
20951c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
20961c3ca1b3SAdrian Hunter
20971c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
20981c3ca1b3SAdrian Hunter		ids = []
20991c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
21001c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
21011c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
21021c3ca1b3SAdrian Hunter		if ret:
21031c3ca1b3SAdrian Hunter			while query.next():
21041c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
21051c3ca1b3SAdrian Hunter		return ids
21061c3ca1b3SAdrian Hunter
21071c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
21081c3ca1b3SAdrian Hunter		all_ids = []
21091c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
21101c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
21111c3ca1b3SAdrian Hunter			if len(ids):
21121c3ca1b3SAdrian Hunter				all_ids.extend(ids)
21131c3ca1b3SAdrian Hunter			else:
21141c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
21151c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
21161c3ca1b3SAdrian Hunter		if self.column_name2:
21171c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
21181c3ca1b3SAdrian Hunter
21191c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
21201c3ca1b3SAdrian Hunter
21211c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
21221c3ca1b3SAdrian Hunter
21231c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
21241c3ca1b3SAdrian Hunter		self.column_name = column_name
21251c3ca1b3SAdrian Hunter
21261c3ca1b3SAdrian Hunter		self.last_id = 0
21271c3ca1b3SAdrian Hunter		self.first_time = 0
21281c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
21291c3ca1b3SAdrian Hunter
21301c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
21311c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
21321c3ca1b3SAdrian Hunter		if query.next():
21331c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
21349a9dae36SAdrian Hunter		self.first_time = int(glb.HostStartTime())
21359a9dae36SAdrian Hunter		self.last_time = int(glb.HostFinishTime())
21361c3ca1b3SAdrian Hunter		if placeholder_text:
21371c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
21381c3ca1b3SAdrian Hunter
21391c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
21401c3ca1b3SAdrian Hunter
21411c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
21421c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
21431c3ca1b3SAdrian Hunter		if query.next():
21441c3ca1b3SAdrian Hunter			return True, int(query.value(0))
21451c3ca1b3SAdrian Hunter		else:
21461c3ca1b3SAdrian Hunter			return False, 0
21471c3ca1b3SAdrian Hunter
21481c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
21491c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
21501c3ca1b3SAdrian Hunter		while True:
21511c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
21521c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
21531c3ca1b3SAdrian Hunter			if not query.next():
21541c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
21551c3ca1b3SAdrian Hunter				if not ok:
21561c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
21571c3ca1b3SAdrian Hunter					if not ok:
21581c3ca1b3SAdrian Hunter						return str(higher_id)
21591c3ca1b3SAdrian Hunter				next_id = dbid
21601c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
21611c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
21621c3ca1b3SAdrian Hunter			if get_floor:
21631c3ca1b3SAdrian Hunter				if target_time > next_time:
21641c3ca1b3SAdrian Hunter					lower_id = next_id
21651c3ca1b3SAdrian Hunter				else:
21661c3ca1b3SAdrian Hunter					higher_id = next_id
21671c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
21681c3ca1b3SAdrian Hunter					return str(higher_id)
21691c3ca1b3SAdrian Hunter			else:
21701c3ca1b3SAdrian Hunter				if target_time >= next_time:
21711c3ca1b3SAdrian Hunter					lower_id = next_id
21721c3ca1b3SAdrian Hunter				else:
21731c3ca1b3SAdrian Hunter					higher_id = next_id
21741c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
21751c3ca1b3SAdrian Hunter					return str(lower_id)
21761c3ca1b3SAdrian Hunter
21771c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
21781c3ca1b3SAdrian Hunter		mult = 1
21791c3ca1b3SAdrian Hunter		suffix = val[-2:]
21801c3ca1b3SAdrian Hunter		if suffix == "ms":
21811c3ca1b3SAdrian Hunter			mult = 1000000
21821c3ca1b3SAdrian Hunter		elif suffix == "us":
21831c3ca1b3SAdrian Hunter			mult = 1000
21841c3ca1b3SAdrian Hunter		elif suffix == "ns":
21851c3ca1b3SAdrian Hunter			mult = 1
21861c3ca1b3SAdrian Hunter		else:
21871c3ca1b3SAdrian Hunter			return val
21881c3ca1b3SAdrian Hunter		val = val[:-2].strip()
21891c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
21901c3ca1b3SAdrian Hunter			return val
21911c3ca1b3SAdrian Hunter		val = int(val) * mult
21921c3ca1b3SAdrian Hunter		if val >= 0:
21931c3ca1b3SAdrian Hunter			val += self.first_time
21941c3ca1b3SAdrian Hunter		else:
21951c3ca1b3SAdrian Hunter			val += self.last_time
21961c3ca1b3SAdrian Hunter		return str(val)
21971c3ca1b3SAdrian Hunter
21981c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
21991c3ca1b3SAdrian Hunter		if vrange[0] == "":
22001c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
22011c3ca1b3SAdrian Hunter		if vrange[1] == "":
22021c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
22031c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
22041c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
22051c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
22061c3ca1b3SAdrian Hunter			return False
22071c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
22081c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
22091c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
22101c3ca1b3SAdrian Hunter			return False
22111c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
22121c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
22131c3ca1b3SAdrian Hunter		return True
22141c3ca1b3SAdrian Hunter
22151c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
22161c3ca1b3SAdrian Hunter		n = value.count("-")
22171c3ca1b3SAdrian Hunter		if n == 1:
22181c3ca1b3SAdrian Hunter			pass
22191c3ca1b3SAdrian Hunter		elif n == 2:
22201c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
22211c3ca1b3SAdrian Hunter				n = 1
22221c3ca1b3SAdrian Hunter		elif n == 3:
22231c3ca1b3SAdrian Hunter			n = 2
22241c3ca1b3SAdrian Hunter		else:
22251c3ca1b3SAdrian Hunter			return False
22261c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
22271c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
22281c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
22291c3ca1b3SAdrian Hunter			ranges.append(vrange)
22301c3ca1b3SAdrian Hunter			return True
22311c3ca1b3SAdrian Hunter		return False
22321c3ca1b3SAdrian Hunter
22331c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
22341c3ca1b3SAdrian Hunter		ranges = []
22351c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
22361c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
22371c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
22381c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
22391c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
22401c3ca1b3SAdrian Hunter
22410924cd68SAdrian Hunter# Report Dialog Base
2242210cf1f9SAdrian Hunter
22430924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
2244210cf1f9SAdrian Hunter
22450924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
22460924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
2247210cf1f9SAdrian Hunter
2248210cf1f9SAdrian Hunter		self.glb = glb
2249210cf1f9SAdrian Hunter
22500bf0947aSAdrian Hunter		self.report_vars = ReportVars()
2251210cf1f9SAdrian Hunter
22520924cd68SAdrian Hunter		self.setWindowTitle(title)
2253210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
2254210cf1f9SAdrian Hunter
22551c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
2256210cf1f9SAdrian Hunter
22570924cd68SAdrian Hunter		self.partial = partial
22580924cd68SAdrian Hunter
2259210cf1f9SAdrian Hunter		self.grid = QGridLayout()
2260210cf1f9SAdrian Hunter
2261210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
2262210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
2263210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
2264210cf1f9SAdrian Hunter
2265210cf1f9SAdrian Hunter		self.status = QLabel()
2266210cf1f9SAdrian Hunter
2267210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
2268210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
2269210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
2270210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2271210cf1f9SAdrian Hunter
2272210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
2273210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
2274210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2275210cf1f9SAdrian Hunter
2276210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
2277210cf1f9SAdrian Hunter		#self.hbox.addStretch()
2278210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
2279210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
2280210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
2281210cf1f9SAdrian Hunter
2282210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
2283210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
2284210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
2285210cf1f9SAdrian Hunter
228626688729SAdrian Hunter		self.setLayout(self.vbox)
2287210cf1f9SAdrian Hunter
2288210cf1f9SAdrian Hunter	def Ok(self):
22890bf0947aSAdrian Hunter		vars = self.report_vars
22901c3ca1b3SAdrian Hunter		for d in self.data_items:
22911c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
22921c3ca1b3SAdrian Hunter				vars.name = d.value
2293947cc38dSAdrian Hunter		if not vars.name:
2294210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2295210cf1f9SAdrian Hunter			return
2296210cf1f9SAdrian Hunter		for d in self.data_items:
2297210cf1f9SAdrian Hunter			if not d.IsValid():
2298210cf1f9SAdrian Hunter				return
2299210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2300cd358012SAdrian Hunter			if d.id == "LIMIT":
2301cd358012SAdrian Hunter				vars.limit = d.value
2302cd358012SAdrian Hunter			elif len(d.value):
23030bf0947aSAdrian Hunter				if len(vars.where_clause):
23040bf0947aSAdrian Hunter					vars.where_clause += " AND "
23050bf0947aSAdrian Hunter				vars.where_clause += d.value
23060bf0947aSAdrian Hunter		if len(vars.where_clause):
23070924cd68SAdrian Hunter			if self.partial:
23080bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2309210cf1f9SAdrian Hunter			else:
23100bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
2311210cf1f9SAdrian Hunter		self.accept()
2312210cf1f9SAdrian Hunter
2313210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2314210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2315210cf1f9SAdrian Hunter
2316210cf1f9SAdrian Hunter	def ClearMessage(self):
2317210cf1f9SAdrian Hunter		self.status.setText("")
2318210cf1f9SAdrian Hunter
23190924cd68SAdrian Hunter# Selected branch report creation dialog
23200924cd68SAdrian Hunter
23210924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
23220924cd68SAdrian Hunter
23230924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
23240924cd68SAdrian Hunter		title = "Selected Branches"
23251c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
23261c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
23271c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
23281c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
23291c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
23301c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
23311c3ca1b3SAdrian 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),
23321c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
23331c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
23340924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
23350924cd68SAdrian Hunter
233676099f98SAdrian Hunter# Event list
233776099f98SAdrian Hunter
233876099f98SAdrian Hunterdef GetEventList(db):
233976099f98SAdrian Hunter	events = []
234076099f98SAdrian Hunter	query = QSqlQuery(db)
234176099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
234276099f98SAdrian Hunter	while query.next():
234376099f98SAdrian Hunter		events.append(query.value(0))
234476099f98SAdrian Hunter	return events
234576099f98SAdrian Hunter
2346655cb952SAdrian Hunter# Is a table selectable
2347655cb952SAdrian Hunter
2348530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"):
2349655cb952SAdrian Hunter	query = QSqlQuery(db)
2350655cb952SAdrian Hunter	try:
2351530e22fdSAdrian Hunter		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
2352655cb952SAdrian Hunter	except:
2353655cb952SAdrian Hunter		return False
2354655cb952SAdrian Hunter	return True
2355655cb952SAdrian Hunter
23568392b74bSAdrian Hunter# SQL table data model item
23578392b74bSAdrian Hunter
23588392b74bSAdrian Hunterclass SQLTableItem():
23598392b74bSAdrian Hunter
23608392b74bSAdrian Hunter	def __init__(self, row, data):
23618392b74bSAdrian Hunter		self.row = row
23628392b74bSAdrian Hunter		self.data = data
23638392b74bSAdrian Hunter
23648392b74bSAdrian Hunter	def getData(self, column):
23658392b74bSAdrian Hunter		return self.data[column]
23668392b74bSAdrian Hunter
23678392b74bSAdrian Hunter# SQL table data model
23688392b74bSAdrian Hunter
23698392b74bSAdrian Hunterclass SQLTableModel(TableModel):
23708392b74bSAdrian Hunter
23718392b74bSAdrian Hunter	progress = Signal(object)
23728392b74bSAdrian Hunter
23738c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
23748392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
23758392b74bSAdrian Hunter		self.glb = glb
23768392b74bSAdrian Hunter		self.more = True
23778392b74bSAdrian Hunter		self.populated = 0
23788c90fef9SAdrian Hunter		self.column_headers = column_headers
23798453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample)
23808392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
23818392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
23828392b74bSAdrian Hunter
23838392b74bSAdrian Hunter	def DisplayData(self, item, index):
23848392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
23858392b74bSAdrian Hunter		return item.getData(index.column())
23868392b74bSAdrian Hunter
23878392b74bSAdrian Hunter	def AddSample(self, data):
23888392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
23898392b74bSAdrian Hunter		self.child_items.append(child)
23908392b74bSAdrian Hunter		self.populated += 1
23918392b74bSAdrian Hunter
23928392b74bSAdrian Hunter	def Update(self, fetched):
23938392b74bSAdrian Hunter		if not fetched:
23948392b74bSAdrian Hunter			self.more = False
23958392b74bSAdrian Hunter			self.progress.emit(0)
23968392b74bSAdrian Hunter		child_count = self.child_count
23978392b74bSAdrian Hunter		count = self.populated - child_count
23988392b74bSAdrian Hunter		if count > 0:
23998392b74bSAdrian Hunter			parent = QModelIndex()
24008392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
24018392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
24028392b74bSAdrian Hunter			self.child_count += count
24038392b74bSAdrian Hunter			self.endInsertRows()
24048392b74bSAdrian Hunter			self.progress.emit(self.child_count)
24058392b74bSAdrian Hunter
24068392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
24078392b74bSAdrian Hunter		current = self.child_count
24088392b74bSAdrian Hunter		if self.more:
24098392b74bSAdrian Hunter			self.fetcher.Fetch(count)
24108392b74bSAdrian Hunter		else:
24118392b74bSAdrian Hunter			self.progress.emit(0)
24128392b74bSAdrian Hunter		return current
24138392b74bSAdrian Hunter
24148392b74bSAdrian Hunter	def HasMoreRecords(self):
24158392b74bSAdrian Hunter		return self.more
24168392b74bSAdrian Hunter
24178c90fef9SAdrian Hunter	def columnCount(self, parent=None):
24188c90fef9SAdrian Hunter		return len(self.column_headers)
24198c90fef9SAdrian Hunter
24208c90fef9SAdrian Hunter	def columnHeader(self, column):
24218c90fef9SAdrian Hunter		return self.column_headers[column]
24228c90fef9SAdrian Hunter
24238453c936SAdrian Hunter	def SQLTableDataPrep(self, query, count):
24248453c936SAdrian Hunter		data = []
24258453c936SAdrian Hunter		for i in xrange(count):
24268453c936SAdrian Hunter			data.append(query.value(i))
24278453c936SAdrian Hunter		return data
24288453c936SAdrian Hunter
24298392b74bSAdrian Hunter# SQL automatic table data model
24308392b74bSAdrian Hunter
24318392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
24328392b74bSAdrian Hunter
24338392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
24348392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
24358392b74bSAdrian Hunter		if table_name == "comm_threads_view":
24368392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
24378392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
24388c90fef9SAdrian Hunter		column_headers = []
24398392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
24408392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
24418392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
24428392b74bSAdrian Hunter			while query.next():
24438c90fef9SAdrian Hunter				column_headers.append(query.value(1))
24448392b74bSAdrian Hunter			if table_name == "sqlite_master":
24458392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
24468392b74bSAdrian Hunter		else:
24478392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
24488392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
24498392b74bSAdrian Hunter				select_table_name = table_name[19:]
24508392b74bSAdrian Hunter				schema = "information_schema"
24518392b74bSAdrian Hunter			else:
24528392b74bSAdrian Hunter				select_table_name = table_name
24538392b74bSAdrian Hunter				schema = "public"
24548392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
24558392b74bSAdrian Hunter			while query.next():
24568c90fef9SAdrian Hunter				column_headers.append(query.value(0))
24578453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
24588453c936SAdrian Hunter			if table_name == "samples_view":
24598453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_view_DataPrep
24608453c936SAdrian Hunter			if table_name == "samples":
24618453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_DataPrep
24628c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
24638392b74bSAdrian Hunter
24648453c936SAdrian Hunter	def samples_view_DataPrep(self, query, count):
24658453c936SAdrian Hunter		data = []
24668453c936SAdrian Hunter		data.append(query.value(0))
24678453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
24688453c936SAdrian Hunter		data.append("{:>19}".format(query.value(1)))
24698453c936SAdrian Hunter		for i in xrange(2, count):
24708453c936SAdrian Hunter			data.append(query.value(i))
24718453c936SAdrian Hunter		return data
24728453c936SAdrian Hunter
24738453c936SAdrian Hunter	def samples_DataPrep(self, query, count):
24748453c936SAdrian Hunter		data = []
24758453c936SAdrian Hunter		for i in xrange(9):
24768453c936SAdrian Hunter			data.append(query.value(i))
24778453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
24788453c936SAdrian Hunter		data.append("{:>19}".format(query.value(9)))
24798453c936SAdrian Hunter		for i in xrange(10, count):
24808453c936SAdrian Hunter			data.append(query.value(i))
24818453c936SAdrian Hunter		return data
24828453c936SAdrian Hunter
24838392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
24848392b74bSAdrian Hunter
24858392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
24868392b74bSAdrian Hunter
24878392b74bSAdrian Hunter	def __init__(self, parent=None):
24888392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
24898392b74bSAdrian Hunter
24908392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
24918392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
24928392b74bSAdrian Hunter		# so implement a crude alternative
24938392b74bSAdrian Hunter		font = self.view.font()
24948392b74bSAdrian Hunter		metrics = QFontMetrics(font)
24958392b74bSAdrian Hunter		max = 0
24968392b74bSAdrian Hunter		for row in xrange(n):
24978392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
24988392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
24998392b74bSAdrian Hunter			max = len if len > max else max
25008392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
25018392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
25028392b74bSAdrian Hunter		max = len if len > max else max
25038392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
25048392b74bSAdrian Hunter
25058392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
25068392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
25078392b74bSAdrian Hunter		if n < 1:
25088392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
25098392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
25108392b74bSAdrian Hunter			return
25118392b74bSAdrian Hunter		columns = self.data_model.columnCount()
25128392b74bSAdrian Hunter		for i in xrange(columns):
25138392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
25148392b74bSAdrian Hunter
25158392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
25168392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
25178392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
25188392b74bSAdrian Hunter		self.ResizeColumnsToContents()
25198392b74bSAdrian Hunter
252096c43b9aSAdrian Hunter# Convert value to CSV
252196c43b9aSAdrian Hunter
252296c43b9aSAdrian Hunterdef ToCSValue(val):
252396c43b9aSAdrian Hunter	if '"' in val:
252496c43b9aSAdrian Hunter		val = val.replace('"', '""')
252596c43b9aSAdrian Hunter	if "," in val or '"' in val:
252696c43b9aSAdrian Hunter		val = '"' + val + '"'
252796c43b9aSAdrian Hunter	return val
252896c43b9aSAdrian Hunter
252996c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
253096c43b9aSAdrian Hunter
253196c43b9aSAdrian Hunterglb_max_cols = 1000
253296c43b9aSAdrian Hunter
253396c43b9aSAdrian Hunterdef RowColumnKey(a):
253496c43b9aSAdrian Hunter	return a.row() * glb_max_cols + a.column()
253596c43b9aSAdrian Hunter
253696c43b9aSAdrian Hunter# Copy selected table cells to clipboard
253796c43b9aSAdrian Hunter
253896c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
253996c43b9aSAdrian Hunter	indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
254096c43b9aSAdrian Hunter	idx_cnt = len(indexes)
254196c43b9aSAdrian Hunter	if not idx_cnt:
254296c43b9aSAdrian Hunter		return
254396c43b9aSAdrian Hunter	if idx_cnt == 1:
254496c43b9aSAdrian Hunter		with_hdr=False
254596c43b9aSAdrian Hunter	min_row = indexes[0].row()
254696c43b9aSAdrian Hunter	max_row = indexes[0].row()
254796c43b9aSAdrian Hunter	min_col = indexes[0].column()
254896c43b9aSAdrian Hunter	max_col = indexes[0].column()
254996c43b9aSAdrian Hunter	for i in indexes:
255096c43b9aSAdrian Hunter		min_row = min(min_row, i.row())
255196c43b9aSAdrian Hunter		max_row = max(max_row, i.row())
255296c43b9aSAdrian Hunter		min_col = min(min_col, i.column())
255396c43b9aSAdrian Hunter		max_col = max(max_col, i.column())
255496c43b9aSAdrian Hunter	if max_col > glb_max_cols:
255596c43b9aSAdrian Hunter		raise RuntimeError("glb_max_cols is too low")
255696c43b9aSAdrian Hunter	max_width = [0] * (1 + max_col - min_col)
255796c43b9aSAdrian Hunter	for i in indexes:
255896c43b9aSAdrian Hunter		c = i.column() - min_col
255996c43b9aSAdrian Hunter		max_width[c] = max(max_width[c], len(str(i.data())))
256096c43b9aSAdrian Hunter	text = ""
256196c43b9aSAdrian Hunter	pad = ""
256296c43b9aSAdrian Hunter	sep = ""
256396c43b9aSAdrian Hunter	if with_hdr:
256496c43b9aSAdrian Hunter		model = indexes[0].model()
256596c43b9aSAdrian Hunter		for col in range(min_col, max_col + 1):
256696c43b9aSAdrian Hunter			val = model.headerData(col, Qt.Horizontal)
256796c43b9aSAdrian Hunter			if as_csv:
256896c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
256996c43b9aSAdrian Hunter				sep = ","
257096c43b9aSAdrian Hunter			else:
257196c43b9aSAdrian Hunter				c = col - min_col
257296c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
257396c43b9aSAdrian Hunter				width = max_width[c]
257496c43b9aSAdrian Hunter				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
257596c43b9aSAdrian Hunter				if align & Qt.AlignRight:
257696c43b9aSAdrian Hunter					val = val.rjust(width)
257796c43b9aSAdrian Hunter				text += pad + sep + val
257896c43b9aSAdrian Hunter				pad = " " * (width - len(val))
257996c43b9aSAdrian Hunter				sep = "  "
258096c43b9aSAdrian Hunter		text += "\n"
258196c43b9aSAdrian Hunter		pad = ""
258296c43b9aSAdrian Hunter		sep = ""
258396c43b9aSAdrian Hunter	last_row = min_row
258496c43b9aSAdrian Hunter	for i in indexes:
258596c43b9aSAdrian Hunter		if i.row() > last_row:
258696c43b9aSAdrian Hunter			last_row = i.row()
258796c43b9aSAdrian Hunter			text += "\n"
258896c43b9aSAdrian Hunter			pad = ""
258996c43b9aSAdrian Hunter			sep = ""
259096c43b9aSAdrian Hunter		if as_csv:
259196c43b9aSAdrian Hunter			text += sep + ToCSValue(str(i.data()))
259296c43b9aSAdrian Hunter			sep = ","
259396c43b9aSAdrian Hunter		else:
259496c43b9aSAdrian Hunter			width = max_width[i.column() - min_col]
259596c43b9aSAdrian Hunter			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
259696c43b9aSAdrian Hunter				val = str(i.data()).rjust(width)
259796c43b9aSAdrian Hunter			else:
259896c43b9aSAdrian Hunter				val = str(i.data())
259996c43b9aSAdrian Hunter			text += pad + sep + val
260096c43b9aSAdrian Hunter			pad = " " * (width - len(val))
260196c43b9aSAdrian Hunter			sep = "  "
260296c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
260396c43b9aSAdrian Hunter
260496c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
260596c43b9aSAdrian Hunter	indexes = view.selectedIndexes()
260696c43b9aSAdrian Hunter	if not len(indexes):
260796c43b9aSAdrian Hunter		return
260896c43b9aSAdrian Hunter
260996c43b9aSAdrian Hunter	selection = view.selectionModel()
261096c43b9aSAdrian Hunter
261196c43b9aSAdrian Hunter	first = None
261296c43b9aSAdrian Hunter	for i in indexes:
261396c43b9aSAdrian Hunter		above = view.indexAbove(i)
261496c43b9aSAdrian Hunter		if not selection.isSelected(above):
261596c43b9aSAdrian Hunter			first = i
261696c43b9aSAdrian Hunter			break
261796c43b9aSAdrian Hunter
261896c43b9aSAdrian Hunter	if first is None:
261996c43b9aSAdrian Hunter		raise RuntimeError("CopyTreeCellsToClipboard internal error")
262096c43b9aSAdrian Hunter
262196c43b9aSAdrian Hunter	model = first.model()
262296c43b9aSAdrian Hunter	row_cnt = 0
262396c43b9aSAdrian Hunter	col_cnt = model.columnCount(first)
262496c43b9aSAdrian Hunter	max_width = [0] * col_cnt
262596c43b9aSAdrian Hunter
262696c43b9aSAdrian Hunter	indent_sz = 2
262796c43b9aSAdrian Hunter	indent_str = " " * indent_sz
262896c43b9aSAdrian Hunter
262996c43b9aSAdrian Hunter	expanded_mark_sz = 2
263096c43b9aSAdrian Hunter	if sys.version_info[0] == 3:
263196c43b9aSAdrian Hunter		expanded_mark = "\u25BC "
263296c43b9aSAdrian Hunter		not_expanded_mark = "\u25B6 "
263396c43b9aSAdrian Hunter	else:
263496c43b9aSAdrian Hunter		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
263596c43b9aSAdrian Hunter		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
263696c43b9aSAdrian Hunter	leaf_mark = "  "
263796c43b9aSAdrian Hunter
263896c43b9aSAdrian Hunter	if not as_csv:
263996c43b9aSAdrian Hunter		pos = first
264096c43b9aSAdrian Hunter		while True:
264196c43b9aSAdrian Hunter			row_cnt += 1
264296c43b9aSAdrian Hunter			row = pos.row()
264396c43b9aSAdrian Hunter			for c in range(col_cnt):
264496c43b9aSAdrian Hunter				i = pos.sibling(row, c)
264596c43b9aSAdrian Hunter				if c:
264696c43b9aSAdrian Hunter					n = len(str(i.data()))
264796c43b9aSAdrian Hunter				else:
264896c43b9aSAdrian Hunter					n = len(str(i.data()).strip())
264996c43b9aSAdrian Hunter					n += (i.internalPointer().level - 1) * indent_sz
265096c43b9aSAdrian Hunter					n += expanded_mark_sz
265196c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], n)
265296c43b9aSAdrian Hunter			pos = view.indexBelow(pos)
265396c43b9aSAdrian Hunter			if not selection.isSelected(pos):
265496c43b9aSAdrian Hunter				break
265596c43b9aSAdrian Hunter
265696c43b9aSAdrian Hunter	text = ""
265796c43b9aSAdrian Hunter	pad = ""
265896c43b9aSAdrian Hunter	sep = ""
265996c43b9aSAdrian Hunter	if with_hdr:
266096c43b9aSAdrian Hunter		for c in range(col_cnt):
266196c43b9aSAdrian Hunter			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
266296c43b9aSAdrian Hunter			if as_csv:
266396c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
266496c43b9aSAdrian Hunter				sep = ","
266596c43b9aSAdrian Hunter			else:
266696c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
266796c43b9aSAdrian Hunter				width = max_width[c]
266896c43b9aSAdrian Hunter				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
266996c43b9aSAdrian Hunter				if align & Qt.AlignRight:
267096c43b9aSAdrian Hunter					val = val.rjust(width)
267196c43b9aSAdrian Hunter				text += pad + sep + val
267296c43b9aSAdrian Hunter				pad = " " * (width - len(val))
267396c43b9aSAdrian Hunter				sep = "   "
267496c43b9aSAdrian Hunter		text += "\n"
267596c43b9aSAdrian Hunter		pad = ""
267696c43b9aSAdrian Hunter		sep = ""
267796c43b9aSAdrian Hunter
267896c43b9aSAdrian Hunter	pos = first
267996c43b9aSAdrian Hunter	while True:
268096c43b9aSAdrian Hunter		row = pos.row()
268196c43b9aSAdrian Hunter		for c in range(col_cnt):
268296c43b9aSAdrian Hunter			i = pos.sibling(row, c)
268396c43b9aSAdrian Hunter			val = str(i.data())
268496c43b9aSAdrian Hunter			if not c:
268596c43b9aSAdrian Hunter				if model.hasChildren(i):
268696c43b9aSAdrian Hunter					if view.isExpanded(i):
268796c43b9aSAdrian Hunter						mark = expanded_mark
268896c43b9aSAdrian Hunter					else:
268996c43b9aSAdrian Hunter						mark = not_expanded_mark
269096c43b9aSAdrian Hunter				else:
269196c43b9aSAdrian Hunter					mark = leaf_mark
269296c43b9aSAdrian Hunter				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
269396c43b9aSAdrian Hunter			if as_csv:
269496c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
269596c43b9aSAdrian Hunter				sep = ","
269696c43b9aSAdrian Hunter			else:
269796c43b9aSAdrian Hunter				width = max_width[c]
269896c43b9aSAdrian Hunter				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
269996c43b9aSAdrian Hunter					val = val.rjust(width)
270096c43b9aSAdrian Hunter				text += pad + sep + val
270196c43b9aSAdrian Hunter				pad = " " * (width - len(val))
270296c43b9aSAdrian Hunter				sep = "   "
270396c43b9aSAdrian Hunter		pos = view.indexBelow(pos)
270496c43b9aSAdrian Hunter		if not selection.isSelected(pos):
270596c43b9aSAdrian Hunter			break
270696c43b9aSAdrian Hunter		text = text.rstrip() + "\n"
270796c43b9aSAdrian Hunter		pad = ""
270896c43b9aSAdrian Hunter		sep = ""
270996c43b9aSAdrian Hunter
271096c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
271196c43b9aSAdrian Hunter
271296c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
271396c43b9aSAdrian Hunter	view.CopyCellsToClipboard(view, as_csv, with_hdr)
271496c43b9aSAdrian Hunter
271596c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view):
271696c43b9aSAdrian Hunter	CopyCellsToClipboard(view, False, True)
271796c43b9aSAdrian Hunter
271896c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view):
271996c43b9aSAdrian Hunter	CopyCellsToClipboard(view, True, True)
272096c43b9aSAdrian Hunter
27219bc4e4bfSAdrian Hunter# Context menu
27229bc4e4bfSAdrian Hunter
27239bc4e4bfSAdrian Hunterclass ContextMenu(object):
27249bc4e4bfSAdrian Hunter
27259bc4e4bfSAdrian Hunter	def __init__(self, view):
27269bc4e4bfSAdrian Hunter		self.view = view
27279bc4e4bfSAdrian Hunter		self.view.setContextMenuPolicy(Qt.CustomContextMenu)
27289bc4e4bfSAdrian Hunter		self.view.customContextMenuRequested.connect(self.ShowContextMenu)
27299bc4e4bfSAdrian Hunter
27309bc4e4bfSAdrian Hunter	def ShowContextMenu(self, pos):
27319bc4e4bfSAdrian Hunter		menu = QMenu(self.view)
27329bc4e4bfSAdrian Hunter		self.AddActions(menu)
27339bc4e4bfSAdrian Hunter		menu.exec_(self.view.mapToGlobal(pos))
27349bc4e4bfSAdrian Hunter
27359bc4e4bfSAdrian Hunter	def AddCopy(self, menu):
27369bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
27379bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
27389bc4e4bfSAdrian Hunter
27399bc4e4bfSAdrian Hunter	def AddActions(self, menu):
27409bc4e4bfSAdrian Hunter		self.AddCopy(menu)
27419bc4e4bfSAdrian Hunter
27429bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu):
27439bc4e4bfSAdrian Hunter
27449bc4e4bfSAdrian Hunter	def __init__(self, view):
27459bc4e4bfSAdrian Hunter		super(TreeContextMenu, self).__init__(view)
27469bc4e4bfSAdrian Hunter
27479bc4e4bfSAdrian Hunter	def AddActions(self, menu):
27489bc4e4bfSAdrian Hunter		i = self.view.currentIndex()
27499bc4e4bfSAdrian Hunter		text = str(i.data()).strip()
27509bc4e4bfSAdrian Hunter		if len(text):
27519bc4e4bfSAdrian Hunter			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
27529bc4e4bfSAdrian Hunter		self.AddCopy(menu)
27539bc4e4bfSAdrian Hunter
27548392b74bSAdrian Hunter# Table window
27558392b74bSAdrian Hunter
27568392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
27578392b74bSAdrian Hunter
27588392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
27598392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
27608392b74bSAdrian Hunter
27618392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
27628392b74bSAdrian Hunter
27638392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
27648392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
27658392b74bSAdrian Hunter
27668392b74bSAdrian Hunter		self.view = QTableView()
27678392b74bSAdrian Hunter		self.view.setModel(self.model)
27688392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
27698392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
27708392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
27718392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
277296c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
277396c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
27748392b74bSAdrian Hunter
27758392b74bSAdrian Hunter		self.ResizeColumnsToContents()
27768392b74bSAdrian Hunter
27779bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
27789bc4e4bfSAdrian Hunter
27798392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
27808392b74bSAdrian Hunter
27818392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
27828392b74bSAdrian Hunter
27838392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
27848392b74bSAdrian Hunter
27858392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
27868392b74bSAdrian Hunter
27878392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
27888392b74bSAdrian Hunter
27898392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
27908392b74bSAdrian Hunter
27918392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
27928392b74bSAdrian Hunter		self.view.setFocus()
27938392b74bSAdrian Hunter		self.find_bar.Busy()
27948392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
27958392b74bSAdrian Hunter
27968392b74bSAdrian Hunter	def FindDone(self, row):
27978392b74bSAdrian Hunter		self.find_bar.Idle()
27988392b74bSAdrian Hunter		if row >= 0:
279935fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
28008392b74bSAdrian Hunter		else:
28018392b74bSAdrian Hunter			self.find_bar.NotFound()
28028392b74bSAdrian Hunter
28038392b74bSAdrian Hunter# Table list
28048392b74bSAdrian Hunter
28058392b74bSAdrian Hunterdef GetTableList(glb):
28068392b74bSAdrian Hunter	tables = []
28078392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
28088392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
28098392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
28108392b74bSAdrian Hunter	else:
28118392b74bSAdrian 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")
28128392b74bSAdrian Hunter	while query.next():
28138392b74bSAdrian Hunter		tables.append(query.value(0))
28148392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
28158392b74bSAdrian Hunter		tables.append("sqlite_master")
28168392b74bSAdrian Hunter	else:
28178392b74bSAdrian Hunter		tables.append("information_schema.tables")
28188392b74bSAdrian Hunter		tables.append("information_schema.views")
28198392b74bSAdrian Hunter		tables.append("information_schema.columns")
28208392b74bSAdrian Hunter	return tables
28218392b74bSAdrian Hunter
2822cd358012SAdrian Hunter# Top Calls data model
2823cd358012SAdrian Hunter
2824cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2825cd358012SAdrian Hunter
2826cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2827cd358012SAdrian Hunter		text = ""
2828cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2829cd358012SAdrian Hunter			text = "::text"
2830cd358012SAdrian Hunter		limit = ""
2831cd358012SAdrian Hunter		if len(report_vars.limit):
2832cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2833cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2834cd358012SAdrian Hunter			" CASE"
2835cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2836cd358012SAdrian Hunter			" ELSE short_name"
2837cd358012SAdrian Hunter			" END AS dso,"
2838cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2839cd358012SAdrian Hunter			" CASE"
2840cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2841cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2842cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2843cd358012SAdrian Hunter			" ELSE ''" + text +
2844cd358012SAdrian Hunter			" END AS flags"
2845cd358012SAdrian Hunter			" FROM calls"
2846cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2847cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2848cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2849cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2850cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2851cd358012SAdrian Hunter			report_vars.where_clause +
2852cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2853cd358012SAdrian Hunter			limit
2854cd358012SAdrian Hunter			)
2855cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2856cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2857cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2858cd358012SAdrian Hunter
2859cd358012SAdrian Hunter	def columnAlignment(self, column):
2860cd358012SAdrian Hunter		return self.alignment[column]
2861cd358012SAdrian Hunter
2862cd358012SAdrian Hunter# Top Calls report creation dialog
2863cd358012SAdrian Hunter
2864cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2865cd358012SAdrian Hunter
2866cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2867cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2868cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2869cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2870cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2871cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2872cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2873cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2874cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2875cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2876cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2877cd358012SAdrian Hunter
2878cd358012SAdrian Hunter# Top Calls window
2879cd358012SAdrian Hunter
2880cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2881cd358012SAdrian Hunter
2882cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2883cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2884cd358012SAdrian Hunter
2885cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2886cd358012SAdrian Hunter		self.model = self.data_model
2887cd358012SAdrian Hunter
2888cd358012SAdrian Hunter		self.view = QTableView()
2889cd358012SAdrian Hunter		self.view.setModel(self.model)
2890cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2891cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
289296c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
289396c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
2894cd358012SAdrian Hunter
28959bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
28969bc4e4bfSAdrian Hunter
2897cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2898cd358012SAdrian Hunter
2899cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2900cd358012SAdrian Hunter
2901cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2902cd358012SAdrian Hunter
2903cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2904cd358012SAdrian Hunter
2905cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2906cd358012SAdrian Hunter
2907cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2908cd358012SAdrian Hunter
2909cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2910cd358012SAdrian Hunter
2911cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2912cd358012SAdrian Hunter		self.view.setFocus()
2913cd358012SAdrian Hunter		self.find_bar.Busy()
2914cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2915cd358012SAdrian Hunter
2916cd358012SAdrian Hunter	def FindDone(self, row):
2917cd358012SAdrian Hunter		self.find_bar.Idle()
2918cd358012SAdrian Hunter		if row >= 0:
2919cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2920cd358012SAdrian Hunter		else:
2921cd358012SAdrian Hunter			self.find_bar.NotFound()
2922cd358012SAdrian Hunter
29231beb5c7bSAdrian Hunter# Action Definition
29241beb5c7bSAdrian Hunter
29251beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
29261beb5c7bSAdrian Hunter	action = QAction(label, parent)
29271beb5c7bSAdrian Hunter	if shortcut != None:
29281beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
29291beb5c7bSAdrian Hunter	action.setStatusTip(tip)
29301beb5c7bSAdrian Hunter	action.triggered.connect(callback)
29311beb5c7bSAdrian Hunter	return action
29321beb5c7bSAdrian Hunter
29331beb5c7bSAdrian Hunter# Typical application actions
29341beb5c7bSAdrian Hunter
29351beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
29361beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
29371beb5c7bSAdrian Hunter
29381beb5c7bSAdrian Hunter# Typical MDI actions
29391beb5c7bSAdrian Hunter
29401beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
29411beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
29421beb5c7bSAdrian Hunter
29431beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
29441beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
29451beb5c7bSAdrian Hunter
29461beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
29471beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
29481beb5c7bSAdrian Hunter
29491beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
29501beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
29511beb5c7bSAdrian Hunter
29521beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
29531beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
29541beb5c7bSAdrian Hunter
29551beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
29561beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
29571beb5c7bSAdrian Hunter
29581beb5c7bSAdrian Hunter# Typical MDI window menu
29591beb5c7bSAdrian Hunter
29601beb5c7bSAdrian Hunterclass WindowMenu():
29611beb5c7bSAdrian Hunter
29621beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
29631beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
29641beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
29651beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
29661beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
29671beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
29681beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
29691beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
29701beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
29711beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
29721beb5c7bSAdrian Hunter
29731beb5c7bSAdrian Hunter	def Update(self):
29741beb5c7bSAdrian Hunter		self.window_menu.clear()
29751beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
29761beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
29771beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
29781beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
29791beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
29801beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
29811beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
29821beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
29831beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
29841beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
29851beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29861beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
29871beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
29881beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29891beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
29901beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
29911beb5c7bSAdrian Hunter		if sub_window_count == 0:
29921beb5c7bSAdrian Hunter			return
29931beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29941beb5c7bSAdrian Hunter		nr = 1
29951beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
29961beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
29971beb5c7bSAdrian Hunter			if nr < 10:
29981beb5c7bSAdrian Hunter				label = "&" + label
29991beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
30001beb5c7bSAdrian Hunter			action.setCheckable(True)
30011beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
3002df8ea22aSAdrian Hunter			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
30031beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
30041beb5c7bSAdrian Hunter			nr += 1
30051beb5c7bSAdrian Hunter
30061beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
30071beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
30081beb5c7bSAdrian Hunter
300965b24292SAdrian Hunter# Help text
301065b24292SAdrian Hunter
301165b24292SAdrian Hunterglb_help_text = """
301265b24292SAdrian Hunter<h1>Contents</h1>
301365b24292SAdrian Hunter<style>
301465b24292SAdrian Hunterp.c1 {
301565b24292SAdrian Hunter    text-indent: 40px;
301665b24292SAdrian Hunter}
301765b24292SAdrian Hunterp.c2 {
301865b24292SAdrian Hunter    text-indent: 80px;
301965b24292SAdrian Hunter}
302065b24292SAdrian Hunter}
302165b24292SAdrian Hunter</style>
302265b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
302365b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
3024ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
3025ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
3026ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
3027ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
302865b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
302965b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
303065b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
303165b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
303265b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
303365b24292SAdrian Hunterwidths to suit will display something like:
303465b24292SAdrian Hunter<pre>
303565b24292SAdrian Hunter                                         Call Graph: pt_example
303665b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
303765b24292SAdrian Hunterv- ls
303865b24292SAdrian Hunter    v- 2638:2638
303965b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
304065b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
304165b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
304265b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
304365b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
304465b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
304565b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
304665b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
304765b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
304865b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
304965b24292SAdrian Hunter</pre>
305065b24292SAdrian Hunter<h3>Points to note:</h3>
305165b24292SAdrian Hunter<ul>
305265b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
305365b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
305465b24292SAdrian Hunter<li>Subsequent levels are functions</li>
305565b24292SAdrian Hunter<li>'Count' is the number of calls</li>
305665b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
305765b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
305865b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
305965b24292SAdrian Hunter</ul>
306065b24292SAdrian Hunter<h3>Find</h3>
306165b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
306265b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
3063ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
3064ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
3065ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
3066ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
306765b24292SAdrian HunterThe All branches report displays all branches in chronological order.
306865b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
306965b24292SAdrian Hunter<h3>Disassembly</h3>
307065b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
307165b24292SAdrian Hunter<ol>
307265b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
307365b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
307465b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
307565b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
307665b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
307765b24292SAdrian Hunter</ol>
307865b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
307965b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
308065b24292SAdrian Hunter<pre>
308165b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
308265b24292SAdrian Huntergit clone https://github.com/intelxed/xed
308365b24292SAdrian Huntercd xed
308465b24292SAdrian Hunter./mfile.py --share
308565b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
308665b24292SAdrian Huntersudo ldconfig
308765b24292SAdrian Hunter</pre>
3088530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3>
3089530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
3090530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
3091530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known.
3092530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
3093530e22fdSAdrian Huntersince the previous displayed 'IPC'.
309465b24292SAdrian Hunter<h3>Find</h3>
309565b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
309665b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
309765b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
3098ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
309965b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
310065b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
3101ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
310265b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
310365b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
310465b24292SAdrian Hunter<pre>
310565b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
310665b24292SAdrian Hunter	100us-200us		From 100us to 200us
310765b24292SAdrian Hunter	10ms-			From 10ms to the end
310865b24292SAdrian Hunter	-100ns			The first 100ns
310965b24292SAdrian Hunter	-10ms-			The last 10ms
311065b24292SAdrian Hunter</pre>
311165b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
3112ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
3113cd358012SAdrian 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.
3114cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
3115cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
311665b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
311765b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
311865b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
311965b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
312065b24292SAdrian Hunterbut that can be slow for large tables.
312165b24292SAdrian Hunter<p>There are also tables of database meta-information.
312265b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
312365b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
312465b24292SAdrian Hunter<h3>Find</h3>
312565b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
312665b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
312765b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
312835fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
312935fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
313065b24292SAdrian Hunter"""
313165b24292SAdrian Hunter
313265b24292SAdrian Hunter# Help window
313365b24292SAdrian Hunter
313465b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
313565b24292SAdrian Hunter
313665b24292SAdrian Hunter	def __init__(self, glb, parent=None):
313765b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
313865b24292SAdrian Hunter
313965b24292SAdrian Hunter		self.text = QTextBrowser()
314065b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
314165b24292SAdrian Hunter		self.text.setReadOnly(True)
314265b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
314365b24292SAdrian Hunter
314465b24292SAdrian Hunter		self.setWidget(self.text)
314565b24292SAdrian Hunter
314665b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
314765b24292SAdrian Hunter
314865b24292SAdrian Hunter# Main window that only displays the help text
314965b24292SAdrian Hunter
315065b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
315165b24292SAdrian Hunter
315265b24292SAdrian Hunter	def __init__(self, parent=None):
315365b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
315465b24292SAdrian Hunter
315565b24292SAdrian Hunter		self.setMinimumSize(200, 100)
315665b24292SAdrian Hunter		self.resize(800, 600)
315765b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
315865b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
315965b24292SAdrian Hunter
316065b24292SAdrian Hunter		self.text = QTextBrowser()
316165b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
316265b24292SAdrian Hunter		self.text.setReadOnly(True)
316365b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
316465b24292SAdrian Hunter
316565b24292SAdrian Hunter		self.setCentralWidget(self.text)
316665b24292SAdrian Hunter
3167b62d18abSAdrian Hunter# PostqreSQL server version
3168b62d18abSAdrian Hunter
3169b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db):
3170b62d18abSAdrian Hunter	query = QSqlQuery(db)
3171b62d18abSAdrian Hunter	QueryExec(query, "SELECT VERSION()")
3172b62d18abSAdrian Hunter	if query.next():
3173b62d18abSAdrian Hunter		v_str = query.value(0)
3174b62d18abSAdrian Hunter		v_list = v_str.strip().split(" ")
3175b62d18abSAdrian Hunter		if v_list[0] == "PostgreSQL" and v_list[2] == "on":
3176b62d18abSAdrian Hunter			return v_list[1]
3177b62d18abSAdrian Hunter		return v_str
3178b62d18abSAdrian Hunter	return "Unknown"
3179b62d18abSAdrian Hunter
3180b62d18abSAdrian Hunter# SQLite version
3181b62d18abSAdrian Hunter
3182b62d18abSAdrian Hunterdef SQLiteVersion(db):
3183b62d18abSAdrian Hunter	query = QSqlQuery(db)
3184b62d18abSAdrian Hunter	QueryExec(query, "SELECT sqlite_version()")
3185b62d18abSAdrian Hunter	if query.next():
3186b62d18abSAdrian Hunter		return query.value(0)
3187b62d18abSAdrian Hunter	return "Unknown"
3188b62d18abSAdrian Hunter
3189b62d18abSAdrian Hunter# About dialog
3190b62d18abSAdrian Hunter
3191b62d18abSAdrian Hunterclass AboutDialog(QDialog):
3192b62d18abSAdrian Hunter
3193b62d18abSAdrian Hunter	def __init__(self, glb, parent=None):
3194b62d18abSAdrian Hunter		super(AboutDialog, self).__init__(parent)
3195b62d18abSAdrian Hunter
3196b62d18abSAdrian Hunter		self.setWindowTitle("About Exported SQL Viewer")
3197b62d18abSAdrian Hunter		self.setMinimumWidth(300)
3198b62d18abSAdrian Hunter
3199b62d18abSAdrian Hunter		pyside_version = "1" if pyside_version_1 else "2"
3200b62d18abSAdrian Hunter
3201b62d18abSAdrian Hunter		text = "<pre>"
3202b62d18abSAdrian Hunter		text += "Python version:     " + sys.version.split(" ")[0] + "\n"
3203b62d18abSAdrian Hunter		text += "PySide version:     " + pyside_version + "\n"
3204b62d18abSAdrian Hunter		text += "Qt version:         " + qVersion() + "\n"
3205b62d18abSAdrian Hunter		if glb.dbref.is_sqlite3:
3206b62d18abSAdrian Hunter			text += "SQLite version:     " + SQLiteVersion(glb.db) + "\n"
3207b62d18abSAdrian Hunter		else:
3208b62d18abSAdrian Hunter			text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
3209b62d18abSAdrian Hunter		text += "</pre>"
3210b62d18abSAdrian Hunter
3211b62d18abSAdrian Hunter		self.text = QTextBrowser()
3212b62d18abSAdrian Hunter		self.text.setHtml(text)
3213b62d18abSAdrian Hunter		self.text.setReadOnly(True)
3214b62d18abSAdrian Hunter		self.text.setOpenExternalLinks(True)
3215b62d18abSAdrian Hunter
3216b62d18abSAdrian Hunter		self.vbox = QVBoxLayout()
3217b62d18abSAdrian Hunter		self.vbox.addWidget(self.text)
3218b62d18abSAdrian Hunter
321926688729SAdrian Hunter		self.setLayout(self.vbox)
3220b62d18abSAdrian Hunter
322182f68e28SAdrian Hunter# Font resize
322282f68e28SAdrian Hunter
322382f68e28SAdrian Hunterdef ResizeFont(widget, diff):
322482f68e28SAdrian Hunter	font = widget.font()
322582f68e28SAdrian Hunter	sz = font.pointSize()
322682f68e28SAdrian Hunter	font.setPointSize(sz + diff)
322782f68e28SAdrian Hunter	widget.setFont(font)
322882f68e28SAdrian Hunter
322982f68e28SAdrian Hunterdef ShrinkFont(widget):
323082f68e28SAdrian Hunter	ResizeFont(widget, -1)
323182f68e28SAdrian Hunter
323282f68e28SAdrian Hunterdef EnlargeFont(widget):
323382f68e28SAdrian Hunter	ResizeFont(widget, 1)
323482f68e28SAdrian Hunter
32351beb5c7bSAdrian Hunter# Unique name for sub-windows
32361beb5c7bSAdrian Hunter
32371beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
32381beb5c7bSAdrian Hunter	if nr > 1:
32391beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
32401beb5c7bSAdrian Hunter	return name
32411beb5c7bSAdrian Hunter
32421beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
32431beb5c7bSAdrian Hunter	nr = 1
32441beb5c7bSAdrian Hunter	while True:
32451beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
32461beb5c7bSAdrian Hunter		ok = True
32471beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
32481beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
32491beb5c7bSAdrian Hunter				ok = False
32501beb5c7bSAdrian Hunter				break
32511beb5c7bSAdrian Hunter		if ok:
32521beb5c7bSAdrian Hunter			return unique_name
32531beb5c7bSAdrian Hunter		nr += 1
32541beb5c7bSAdrian Hunter
32551beb5c7bSAdrian Hunter# Add a sub-window
32561beb5c7bSAdrian Hunter
32571beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
32581beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
32591beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
32601beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
32611beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
32621beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
32631beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
32641beb5c7bSAdrian Hunter	sub_window.name = unique_name
32651beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
32661beb5c7bSAdrian Hunter	sub_window.show()
32671beb5c7bSAdrian Hunter
3268031c2a00SAdrian Hunter# Main window
3269031c2a00SAdrian Hunter
3270031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
3271031c2a00SAdrian Hunter
3272031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
3273031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
3274031c2a00SAdrian Hunter
3275031c2a00SAdrian Hunter		self.glb = glb
3276031c2a00SAdrian Hunter
32771beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
3278031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
3279031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
3280031c2a00SAdrian Hunter
32811beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
32821beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
32831beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
3284031c2a00SAdrian Hunter
32851beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
3286031c2a00SAdrian Hunter
32871beb5c7bSAdrian Hunter		menu = self.menuBar()
3288031c2a00SAdrian Hunter
32891beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
32901beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
32911beb5c7bSAdrian Hunter
3292ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
329396c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
329496c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
3295ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
32968392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
329782f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
329882f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
3299ebd70c7dSAdrian Hunter
33001beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
3301655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
33021beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
33031beb5c7bSAdrian Hunter
3304ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
3305ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
3306ae8b887cSAdrian Hunter
330776099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
330876099f98SAdrian Hunter
3309cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
3310cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
3311cd358012SAdrian Hunter
33128392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
33138392b74bSAdrian Hunter
33141beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
33151beb5c7bSAdrian Hunter
331665b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
331765b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
3318b62d18abSAdrian Hunter		help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
331965b24292SAdrian Hunter
33204b208453SAdrian Hunter	def Try(self, fn):
33214b208453SAdrian Hunter		win = self.mdi_area.activeSubWindow()
33224b208453SAdrian Hunter		if win:
33234b208453SAdrian Hunter			try:
33244b208453SAdrian Hunter				fn(win.view)
33254b208453SAdrian Hunter			except:
33264b208453SAdrian Hunter				pass
33274b208453SAdrian Hunter
332896c43b9aSAdrian Hunter	def CopyToClipboard(self):
332996c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardHdr)
333096c43b9aSAdrian Hunter
333196c43b9aSAdrian Hunter	def CopyToClipboardCSV(self):
333296c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardCSV)
333396c43b9aSAdrian Hunter
3334ebd70c7dSAdrian Hunter	def Find(self):
3335ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
3336ebd70c7dSAdrian Hunter		if win:
3337ebd70c7dSAdrian Hunter			try:
3338ebd70c7dSAdrian Hunter				win.find_bar.Activate()
3339ebd70c7dSAdrian Hunter			except:
3340ebd70c7dSAdrian Hunter				pass
3341ebd70c7dSAdrian Hunter
33428392b74bSAdrian Hunter	def FetchMoreRecords(self):
33438392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
33448392b74bSAdrian Hunter		if win:
33458392b74bSAdrian Hunter			try:
33468392b74bSAdrian Hunter				win.fetch_bar.Activate()
33478392b74bSAdrian Hunter			except:
33488392b74bSAdrian Hunter				pass
33498392b74bSAdrian Hunter
335082f68e28SAdrian Hunter	def ShrinkFont(self):
33514b208453SAdrian Hunter		self.Try(ShrinkFont)
335282f68e28SAdrian Hunter
335382f68e28SAdrian Hunter	def EnlargeFont(self):
33544b208453SAdrian Hunter		self.Try(EnlargeFont)
335582f68e28SAdrian Hunter
335676099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
335776099f98SAdrian Hunter		branches_events = 0
335876099f98SAdrian Hunter		for event in events:
335976099f98SAdrian Hunter			event = event.split(":")[0]
336076099f98SAdrian Hunter			if event == "branches":
336176099f98SAdrian Hunter				branches_events += 1
336276099f98SAdrian Hunter		dbid = 0
336376099f98SAdrian Hunter		for event in events:
336476099f98SAdrian Hunter			dbid += 1
336576099f98SAdrian Hunter			event = event.split(":")[0]
336676099f98SAdrian Hunter			if event == "branches":
336776099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
3368df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
3369210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
3370df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
337176099f98SAdrian Hunter
33728392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
33738392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
33748392b74bSAdrian Hunter		for table in tables:
3375df8ea22aSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
33768392b74bSAdrian Hunter
33771beb5c7bSAdrian Hunter	def NewCallGraph(self):
33781beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
3379031c2a00SAdrian Hunter
3380ae8b887cSAdrian Hunter	def NewCallTree(self):
3381ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
3382ae8b887cSAdrian Hunter
3383cd358012SAdrian Hunter	def NewTopCalls(self):
3384cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
3385cd358012SAdrian Hunter		ret = dialog.exec_()
3386cd358012SAdrian Hunter		if ret:
3387cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
3388cd358012SAdrian Hunter
338976099f98SAdrian Hunter	def NewBranchView(self, event_id):
3390947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
339176099f98SAdrian Hunter
3392210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
3393210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
3394210cf1f9SAdrian Hunter		ret = dialog.exec_()
3395210cf1f9SAdrian Hunter		if ret:
3396947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
3397210cf1f9SAdrian Hunter
33988392b74bSAdrian Hunter	def NewTableView(self, table_name):
33998392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
34008392b74bSAdrian Hunter
340165b24292SAdrian Hunter	def Help(self):
340265b24292SAdrian Hunter		HelpWindow(self.glb, self)
340365b24292SAdrian Hunter
3404b62d18abSAdrian Hunter	def About(self):
3405b62d18abSAdrian Hunter		dialog = AboutDialog(self.glb, self)
3406b62d18abSAdrian Hunter		dialog.exec_()
3407b62d18abSAdrian Hunter
340876099f98SAdrian Hunter# XED Disassembler
340976099f98SAdrian Hunter
341076099f98SAdrian Hunterclass xed_state_t(Structure):
341176099f98SAdrian Hunter
341276099f98SAdrian Hunter	_fields_ = [
341376099f98SAdrian Hunter		("mode", c_int),
341476099f98SAdrian Hunter		("width", c_int)
341576099f98SAdrian Hunter	]
341676099f98SAdrian Hunter
341776099f98SAdrian Hunterclass XEDInstruction():
341876099f98SAdrian Hunter
341976099f98SAdrian Hunter	def __init__(self, libxed):
342076099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
342176099f98SAdrian Hunter		xedd_t = c_byte * 512
342276099f98SAdrian Hunter		self.xedd = xedd_t()
342376099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
342476099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
342576099f98SAdrian Hunter		self.state = xed_state_t()
342676099f98SAdrian Hunter		self.statep = addressof(self.state)
342776099f98SAdrian Hunter		# Buffer for disassembled instruction text
342876099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
342976099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
343076099f98SAdrian Hunter
343176099f98SAdrian Hunterclass LibXED():
343276099f98SAdrian Hunter
343376099f98SAdrian Hunter	def __init__(self):
34345ed4419dSAdrian Hunter		try:
343576099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
34365ed4419dSAdrian Hunter		except:
34375ed4419dSAdrian Hunter			self.libxed = None
34385ed4419dSAdrian Hunter		if not self.libxed:
34395ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
344076099f98SAdrian Hunter
344176099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
344276099f98SAdrian Hunter		self.xed_tables_init.restype = None
344376099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
344476099f98SAdrian Hunter
344576099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
344676099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
344776099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
344876099f98SAdrian Hunter
344976099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
345076099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
345176099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
345276099f98SAdrian Hunter
345376099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
345476099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
345576099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
345676099f98SAdrian Hunter
345776099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
345876099f98SAdrian Hunter		self.xed_decode.restype = c_int
345976099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
346076099f98SAdrian Hunter
346176099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
346276099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
346376099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
346476099f98SAdrian Hunter
346576099f98SAdrian Hunter		self.xed_tables_init()
346676099f98SAdrian Hunter
346776099f98SAdrian Hunter	def Instruction(self):
346876099f98SAdrian Hunter		return XEDInstruction(self)
346976099f98SAdrian Hunter
347076099f98SAdrian Hunter	def SetMode(self, inst, mode):
347176099f98SAdrian Hunter		if mode:
347276099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
347376099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
347476099f98SAdrian Hunter		else:
347576099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
347676099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
347776099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
347876099f98SAdrian Hunter
347976099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
348076099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
348176099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
348276099f98SAdrian Hunter		if err:
348376099f98SAdrian Hunter			return 0, ""
348476099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
348576099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
348676099f98SAdrian Hunter		if not ok:
348776099f98SAdrian Hunter			return 0, ""
3488606bd60aSAdrian Hunter		if sys.version_info[0] == 2:
3489606bd60aSAdrian Hunter			result = inst.buffer.value
3490606bd60aSAdrian Hunter		else:
3491606bd60aSAdrian Hunter			result = inst.buffer.value.decode()
349276099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
349376099f98SAdrian Hunter		# For now, assume the length is in byte 166
3494606bd60aSAdrian Hunter		return inst.xedd[166], result
349576099f98SAdrian Hunter
349676099f98SAdrian Hunterdef TryOpen(file_name):
349776099f98SAdrian Hunter	try:
349876099f98SAdrian Hunter		return open(file_name, "rb")
349976099f98SAdrian Hunter	except:
350076099f98SAdrian Hunter		return None
350176099f98SAdrian Hunter
350276099f98SAdrian Hunterdef Is64Bit(f):
350376099f98SAdrian Hunter	result = sizeof(c_void_p)
350476099f98SAdrian Hunter	# ELF support only
350576099f98SAdrian Hunter	pos = f.tell()
350676099f98SAdrian Hunter	f.seek(0)
350776099f98SAdrian Hunter	header = f.read(7)
350876099f98SAdrian Hunter	f.seek(pos)
350976099f98SAdrian Hunter	magic = header[0:4]
3510606bd60aSAdrian Hunter	if sys.version_info[0] == 2:
351176099f98SAdrian Hunter		eclass = ord(header[4])
351276099f98SAdrian Hunter		encoding = ord(header[5])
351376099f98SAdrian Hunter		version = ord(header[6])
3514606bd60aSAdrian Hunter	else:
3515606bd60aSAdrian Hunter		eclass = header[4]
3516606bd60aSAdrian Hunter		encoding = header[5]
3517606bd60aSAdrian Hunter		version = header[6]
351876099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
351976099f98SAdrian Hunter		result = True if eclass == 2 else False
352076099f98SAdrian Hunter	return result
352176099f98SAdrian Hunter
3522031c2a00SAdrian Hunter# Global data
3523031c2a00SAdrian Hunter
3524031c2a00SAdrian Hunterclass Glb():
3525031c2a00SAdrian Hunter
3526031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
3527031c2a00SAdrian Hunter		self.dbref = dbref
3528031c2a00SAdrian Hunter		self.db = db
3529031c2a00SAdrian Hunter		self.dbname = dbname
353076099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
353176099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
353276099f98SAdrian Hunter		if self.buildid_dir:
353376099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
353476099f98SAdrian Hunter		else:
353576099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
3536031c2a00SAdrian Hunter		self.app = None
3537031c2a00SAdrian Hunter		self.mainwindow = None
35388392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
353976099f98SAdrian Hunter		try:
354076099f98SAdrian Hunter			self.disassembler = LibXED()
354176099f98SAdrian Hunter			self.have_disassembler = True
354276099f98SAdrian Hunter		except:
354376099f98SAdrian Hunter			self.have_disassembler = False
35449a9dae36SAdrian Hunter		self.host_machine_id = 0
35459a9dae36SAdrian Hunter		self.host_start_time = 0
35469a9dae36SAdrian Hunter		self.host_finish_time = 0
354776099f98SAdrian Hunter
354876099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
354976099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
355076099f98SAdrian Hunter		return TryOpen(file_name)
355176099f98SAdrian Hunter
355276099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
355376099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
355476099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
355576099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
355676099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
355776099f98SAdrian Hunter			if f:
355876099f98SAdrian Hunter				return f
355976099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
356076099f98SAdrian Hunter			f = TryOpen(long_name)
356176099f98SAdrian Hunter			if f:
356276099f98SAdrian Hunter				return f
356376099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
356476099f98SAdrian Hunter		if f:
356576099f98SAdrian Hunter			return f
356676099f98SAdrian Hunter		return None
35678392b74bSAdrian Hunter
35688392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
35698392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
35708392b74bSAdrian Hunter
35718392b74bSAdrian Hunter	# Shutdown any background processes or threads
35728392b74bSAdrian Hunter	def ShutdownInstances(self):
35738392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
35748392b74bSAdrian Hunter			try:
35758392b74bSAdrian Hunter				x.Shutdown()
35768392b74bSAdrian Hunter			except:
35778392b74bSAdrian Hunter				pass
3578031c2a00SAdrian Hunter
35799a9dae36SAdrian Hunter	def GetHostMachineId(self):
35809a9dae36SAdrian Hunter		query = QSqlQuery(self.db)
35819a9dae36SAdrian Hunter		QueryExec(query, "SELECT id FROM machines WHERE pid = -1")
35829a9dae36SAdrian Hunter		if query.next():
35839a9dae36SAdrian Hunter			self.host_machine_id = query.value(0)
35849a9dae36SAdrian Hunter		else:
35859a9dae36SAdrian Hunter			self.host_machine_id = 0
35869a9dae36SAdrian Hunter		return self.host_machine_id
35879a9dae36SAdrian Hunter
35889a9dae36SAdrian Hunter	def HostMachineId(self):
35899a9dae36SAdrian Hunter		if self.host_machine_id:
35909a9dae36SAdrian Hunter			return self.host_machine_id
35919a9dae36SAdrian Hunter		return self.GetHostMachineId()
35929a9dae36SAdrian Hunter
35939a9dae36SAdrian Hunter	def SelectValue(self, sql):
35949a9dae36SAdrian Hunter		query = QSqlQuery(self.db)
35959a9dae36SAdrian Hunter		try:
35969a9dae36SAdrian Hunter			QueryExec(query, sql)
35979a9dae36SAdrian Hunter		except:
35989a9dae36SAdrian Hunter			return None
35999a9dae36SAdrian Hunter		if query.next():
36009a9dae36SAdrian Hunter			return Decimal(query.value(0))
36019a9dae36SAdrian Hunter		return None
36029a9dae36SAdrian Hunter
36039a9dae36SAdrian Hunter	def SwitchesMinTime(self, machine_id):
36049a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
36059a9dae36SAdrian Hunter					" FROM context_switches"
36069a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
36079a9dae36SAdrian Hunter					" ORDER BY id LIMIT 1")
36089a9dae36SAdrian Hunter
36099a9dae36SAdrian Hunter	def SwitchesMaxTime(self, machine_id):
36109a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
36119a9dae36SAdrian Hunter					" FROM context_switches"
36129a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
36139a9dae36SAdrian Hunter					" ORDER BY id DESC LIMIT 1")
36149a9dae36SAdrian Hunter
36159a9dae36SAdrian Hunter	def SamplesMinTime(self, machine_id):
36169a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
36179a9dae36SAdrian Hunter					" FROM samples"
36189a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
36199a9dae36SAdrian Hunter					" ORDER BY id LIMIT 1")
36209a9dae36SAdrian Hunter
36219a9dae36SAdrian Hunter	def SamplesMaxTime(self, machine_id):
36229a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
36239a9dae36SAdrian Hunter					" FROM samples"
36249a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
36259a9dae36SAdrian Hunter					" ORDER BY id DESC LIMIT 1")
36269a9dae36SAdrian Hunter
36279a9dae36SAdrian Hunter	def CallsMinTime(self, machine_id):
36289a9dae36SAdrian Hunter		return self.SelectValue("SELECT calls.call_time"
36299a9dae36SAdrian Hunter					" FROM calls"
36309a9dae36SAdrian Hunter					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
36319a9dae36SAdrian Hunter					" WHERE calls.call_time != 0 AND threads.machine_id = " + str(machine_id) +
36329a9dae36SAdrian Hunter					" ORDER BY calls.id LIMIT 1")
36339a9dae36SAdrian Hunter
36349a9dae36SAdrian Hunter	def CallsMaxTime(self, machine_id):
36359a9dae36SAdrian Hunter		return self.SelectValue("SELECT calls.return_time"
36369a9dae36SAdrian Hunter					" FROM calls"
36379a9dae36SAdrian Hunter					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
36389a9dae36SAdrian Hunter					" WHERE calls.return_time != 0 AND threads.machine_id = " + str(machine_id) +
36399a9dae36SAdrian Hunter					" ORDER BY calls.return_time DESC LIMIT 1")
36409a9dae36SAdrian Hunter
36419a9dae36SAdrian Hunter	def GetStartTime(self, machine_id):
36429a9dae36SAdrian Hunter		t0 = self.SwitchesMinTime(machine_id)
36439a9dae36SAdrian Hunter		t1 = self.SamplesMinTime(machine_id)
36449a9dae36SAdrian Hunter		t2 = self.CallsMinTime(machine_id)
36459a9dae36SAdrian Hunter		if t0 is None or (not(t1 is None) and t1 < t0):
36469a9dae36SAdrian Hunter			t0 = t1
36479a9dae36SAdrian Hunter		if t0 is None or (not(t2 is None) and t2 < t0):
36489a9dae36SAdrian Hunter			t0 = t2
36499a9dae36SAdrian Hunter		return t0
36509a9dae36SAdrian Hunter
36519a9dae36SAdrian Hunter	def GetFinishTime(self, machine_id):
36529a9dae36SAdrian Hunter		t0 = self.SwitchesMaxTime(machine_id)
36539a9dae36SAdrian Hunter		t1 = self.SamplesMaxTime(machine_id)
36549a9dae36SAdrian Hunter		t2 = self.CallsMaxTime(machine_id)
36559a9dae36SAdrian Hunter		if t0 is None or (not(t1 is None) and t1 > t0):
36569a9dae36SAdrian Hunter			t0 = t1
36579a9dae36SAdrian Hunter		if t0 is None or (not(t2 is None) and t2 > t0):
36589a9dae36SAdrian Hunter			t0 = t2
36599a9dae36SAdrian Hunter		return t0
36609a9dae36SAdrian Hunter
36619a9dae36SAdrian Hunter	def HostStartTime(self):
36629a9dae36SAdrian Hunter		if self.host_start_time:
36639a9dae36SAdrian Hunter			return self.host_start_time
36649a9dae36SAdrian Hunter		self.host_start_time = self.GetStartTime(self.HostMachineId())
36659a9dae36SAdrian Hunter		return self.host_start_time
36669a9dae36SAdrian Hunter
36679a9dae36SAdrian Hunter	def HostFinishTime(self):
36689a9dae36SAdrian Hunter		if self.host_finish_time:
36699a9dae36SAdrian Hunter			return self.host_finish_time
36709a9dae36SAdrian Hunter		self.host_finish_time = self.GetFinishTime(self.HostMachineId())
36719a9dae36SAdrian Hunter		return self.host_finish_time
36729a9dae36SAdrian Hunter
36739a9dae36SAdrian Hunter	def StartTime(self, machine_id):
36749a9dae36SAdrian Hunter		if machine_id == self.HostMachineId():
36759a9dae36SAdrian Hunter			return self.HostStartTime()
36769a9dae36SAdrian Hunter		return self.GetStartTime(machine_id)
36779a9dae36SAdrian Hunter
36789a9dae36SAdrian Hunter	def FinishTime(self, machine_id):
36799a9dae36SAdrian Hunter		if machine_id == self.HostMachineId():
36809a9dae36SAdrian Hunter			return self.HostFinishTime()
36819a9dae36SAdrian Hunter		return self.GetFinishTime(machine_id)
36829a9dae36SAdrian Hunter
3683031c2a00SAdrian Hunter# Database reference
3684031c2a00SAdrian Hunter
3685031c2a00SAdrian Hunterclass DBRef():
3686031c2a00SAdrian Hunter
3687031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
3688031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
3689031c2a00SAdrian Hunter		self.dbname = dbname
3690031c2a00SAdrian Hunter
3691031c2a00SAdrian Hunter	def Open(self, connection_name):
3692031c2a00SAdrian Hunter		dbname = self.dbname
3693031c2a00SAdrian Hunter		if self.is_sqlite3:
3694031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
3695031c2a00SAdrian Hunter		else:
3696031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
3697031c2a00SAdrian Hunter			opts = dbname.split()
3698031c2a00SAdrian Hunter			for opt in opts:
3699031c2a00SAdrian Hunter				if "=" in opt:
3700031c2a00SAdrian Hunter					opt = opt.split("=")
3701031c2a00SAdrian Hunter					if opt[0] == "hostname":
3702031c2a00SAdrian Hunter						db.setHostName(opt[1])
3703031c2a00SAdrian Hunter					elif opt[0] == "port":
3704031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
3705031c2a00SAdrian Hunter					elif opt[0] == "username":
3706031c2a00SAdrian Hunter						db.setUserName(opt[1])
3707031c2a00SAdrian Hunter					elif opt[0] == "password":
3708031c2a00SAdrian Hunter						db.setPassword(opt[1])
3709031c2a00SAdrian Hunter					elif opt[0] == "dbname":
3710031c2a00SAdrian Hunter						dbname = opt[1]
3711031c2a00SAdrian Hunter				else:
3712031c2a00SAdrian Hunter					dbname = opt
3713031c2a00SAdrian Hunter
3714031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
3715031c2a00SAdrian Hunter		if not db.open():
3716031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
3717031c2a00SAdrian Hunter		return db, dbname
3718031c2a00SAdrian Hunter
3719031c2a00SAdrian Hunter# Main
3720031c2a00SAdrian Hunter
3721031c2a00SAdrian Hunterdef Main():
37221ed7f47fSAdrian Hunter	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
37231ed7f47fSAdrian Hunter			"   or: exported-sql-viewer.py --help-only"
37241ed7f47fSAdrian Hunter	ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
3725df8ea22aSAdrian Hunter	ap.add_argument("--pyside-version-1", action='store_true')
37261ed7f47fSAdrian Hunter	ap.add_argument("dbname", nargs="?")
37271ed7f47fSAdrian Hunter	ap.add_argument("--help-only", action='store_true')
37281ed7f47fSAdrian Hunter	args = ap.parse_args()
3729031c2a00SAdrian Hunter
37301ed7f47fSAdrian Hunter	if args.help_only:
373165b24292SAdrian Hunter		app = QApplication(sys.argv)
373265b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
373365b24292SAdrian Hunter		mainwindow.show()
373465b24292SAdrian Hunter		err = app.exec_()
373565b24292SAdrian Hunter		sys.exit(err)
3736031c2a00SAdrian Hunter
37371ed7f47fSAdrian Hunter	dbname = args.dbname
37381ed7f47fSAdrian Hunter	if dbname is None:
37391ed7f47fSAdrian Hunter		ap.print_usage()
37401ed7f47fSAdrian Hunter		print("Too few arguments")
37411ed7f47fSAdrian Hunter		sys.exit(1)
37421ed7f47fSAdrian Hunter
3743031c2a00SAdrian Hunter	is_sqlite3 = False
3744031c2a00SAdrian Hunter	try:
3745beda0e72STony Jones		f = open(dbname, "rb")
3746beda0e72STony Jones		if f.read(15) == b'SQLite format 3':
3747031c2a00SAdrian Hunter			is_sqlite3 = True
3748031c2a00SAdrian Hunter		f.close()
3749031c2a00SAdrian Hunter	except:
3750031c2a00SAdrian Hunter		pass
3751031c2a00SAdrian Hunter
3752031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
3753031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
3754031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
3755031c2a00SAdrian Hunter	app = QApplication(sys.argv)
3756031c2a00SAdrian Hunter	glb.app = app
3757031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
3758031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
3759031c2a00SAdrian Hunter	mainwindow.show()
3760031c2a00SAdrian Hunter	err = app.exec_()
37618392b74bSAdrian Hunter	glb.ShutdownInstances()
3762031c2a00SAdrian Hunter	db.close()
3763031c2a00SAdrian Hunter	sys.exit(err)
3764031c2a00SAdrian Hunter
3765031c2a00SAdrian Hunterif __name__ == "__main__":
3766031c2a00SAdrian Hunter	Main()
3767