1031c2a00SAdrian Hunter#!/usr/bin/python2
2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5031c2a00SAdrian Hunter
6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8031c2a00SAdrian Hunter# scripts for details.
9031c2a00SAdrian Hunter#
10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12031c2a00SAdrian Hunter#
13031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14031c2a00SAdrian Hunter#
15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17031c2a00SAdrian Hunter#
18031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19031c2a00SAdrian Hunter#
20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22031c2a00SAdrian Hunter# widths to suit will display something like:
23031c2a00SAdrian Hunter#
24031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26031c2a00SAdrian Hunter# v- ls
27031c2a00SAdrian Hunter#     v- 2638:2638
28031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38031c2a00SAdrian Hunter#
39031c2a00SAdrian Hunter# Points to note:
40031c2a00SAdrian Hunter#	The top level is a command name (comm)
41031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42031c2a00SAdrian Hunter#	Subsequent levels are functions
43031c2a00SAdrian Hunter#	'Count' is the number of calls
44031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45031c2a00SAdrian Hunter#	Percentages are relative to the level above
46031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47031c2a00SAdrian Hunter#       functions that it calls
48031c2a00SAdrian Hunter
4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
5076099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
5376099f98SAdrian Hunter# libxed.so:
5476099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
5576099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
5676099f98SAdrian Hunter#            cd xed
5776099f98SAdrian Hunter#            ./mfile.py --share
5876099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
5976099f98SAdrian Hunter#            sudo ldconfig
6076099f98SAdrian Hunter#
6176099f98SAdrian Hunter# Example report:
6276099f98SAdrian Hunter#
6376099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6476099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6576099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6676099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6776099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6876099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6976099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7076099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7176099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
7276099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7376099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
7476099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
7576099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
7676099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7776099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
7876099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7976099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8076099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
8176099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8276099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8376099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8476099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8576099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8676099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8776099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8876099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8976099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9076099f98SAdrian Hunter
91031c2a00SAdrian Hunterimport sys
921beb5c7bSAdrian Hunterimport weakref
931beb5c7bSAdrian Hunterimport threading
94ebd70c7dSAdrian Hunterimport string
958392b74bSAdrian Hunterimport cPickle
968392b74bSAdrian Hunterimport re
978392b74bSAdrian Hunterimport os
98031c2a00SAdrian Hunterfrom PySide.QtCore import *
99031c2a00SAdrian Hunterfrom PySide.QtGui import *
100031c2a00SAdrian Hunterfrom PySide.QtSql import *
101031c2a00SAdrian Hunterfrom decimal import *
1028392b74bSAdrian Hunterfrom ctypes import *
1038392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
104031c2a00SAdrian Hunter
105031c2a00SAdrian Hunter# Data formatting helpers
106031c2a00SAdrian Hunter
10776099f98SAdrian Hunterdef tohex(ip):
10876099f98SAdrian Hunter	if ip < 0:
10976099f98SAdrian Hunter		ip += 1 << 64
11076099f98SAdrian Hunter	return "%x" % ip
11176099f98SAdrian Hunter
11276099f98SAdrian Hunterdef offstr(offset):
11376099f98SAdrian Hunter	if offset:
11476099f98SAdrian Hunter		return "+0x%x" % offset
11576099f98SAdrian Hunter	return ""
11676099f98SAdrian Hunter
117031c2a00SAdrian Hunterdef dsoname(name):
118031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
119031c2a00SAdrian Hunter		return "[kernel]"
120031c2a00SAdrian Hunter	return name
121031c2a00SAdrian Hunter
122*210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
123*210cf1f9SAdrian Hunter	pos = s.find(sub)
124*210cf1f9SAdrian Hunter	if pos < 0:
125*210cf1f9SAdrian Hunter		return pos
126*210cf1f9SAdrian Hunter	if n <= 1:
127*210cf1f9SAdrian Hunter		return offs + pos
128*210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
129*210cf1f9SAdrian Hunter
130031c2a00SAdrian Hunter# Percent to one decimal place
131031c2a00SAdrian Hunter
132031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
133031c2a00SAdrian Hunter	if not d:
134031c2a00SAdrian Hunter		return "0.0"
135031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
136031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
137031c2a00SAdrian Hunter
138031c2a00SAdrian Hunter# Helper for queries that must not fail
139031c2a00SAdrian Hunter
140031c2a00SAdrian Hunterdef QueryExec(query, stmt):
141031c2a00SAdrian Hunter	ret = query.exec_(stmt)
142031c2a00SAdrian Hunter	if not ret:
143031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
144031c2a00SAdrian Hunter
145ebd70c7dSAdrian Hunter# Background thread
146ebd70c7dSAdrian Hunter
147ebd70c7dSAdrian Hunterclass Thread(QThread):
148ebd70c7dSAdrian Hunter
149ebd70c7dSAdrian Hunter	done = Signal(object)
150ebd70c7dSAdrian Hunter
151ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
152ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
153ebd70c7dSAdrian Hunter		self.task = task
154ebd70c7dSAdrian Hunter		self.param = param
155ebd70c7dSAdrian Hunter
156ebd70c7dSAdrian Hunter	def run(self):
157ebd70c7dSAdrian Hunter		while True:
158ebd70c7dSAdrian Hunter			if self.param is None:
159ebd70c7dSAdrian Hunter				done, result = self.task()
160ebd70c7dSAdrian Hunter			else:
161ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
162ebd70c7dSAdrian Hunter			self.done.emit(result)
163ebd70c7dSAdrian Hunter			if done:
164ebd70c7dSAdrian Hunter				break
165ebd70c7dSAdrian Hunter
166031c2a00SAdrian Hunter# Tree data model
167031c2a00SAdrian Hunter
168031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
169031c2a00SAdrian Hunter
170031c2a00SAdrian Hunter	def __init__(self, root, parent=None):
171031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
172031c2a00SAdrian Hunter		self.root = root
173031c2a00SAdrian Hunter		self.last_row_read = 0
174031c2a00SAdrian Hunter
175031c2a00SAdrian Hunter	def Item(self, parent):
176031c2a00SAdrian Hunter		if parent.isValid():
177031c2a00SAdrian Hunter			return parent.internalPointer()
178031c2a00SAdrian Hunter		else:
179031c2a00SAdrian Hunter			return self.root
180031c2a00SAdrian Hunter
181031c2a00SAdrian Hunter	def rowCount(self, parent):
182031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
183031c2a00SAdrian Hunter		if result < 0:
184031c2a00SAdrian Hunter			result = 0
185031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
186031c2a00SAdrian Hunter		return result
187031c2a00SAdrian Hunter
188031c2a00SAdrian Hunter	def hasChildren(self, parent):
189031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
190031c2a00SAdrian Hunter
191031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
192031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
193031c2a00SAdrian Hunter			return self.columnAlignment(section)
194031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
195031c2a00SAdrian Hunter			return None
196031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
197031c2a00SAdrian Hunter			return None
198031c2a00SAdrian Hunter		return self.columnHeader(section)
199031c2a00SAdrian Hunter
200031c2a00SAdrian Hunter	def parent(self, child):
201031c2a00SAdrian Hunter		child_item = child.internalPointer()
202031c2a00SAdrian Hunter		if child_item is self.root:
203031c2a00SAdrian Hunter			return QModelIndex()
204031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
205031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
206031c2a00SAdrian Hunter
207031c2a00SAdrian Hunter	def index(self, row, column, parent):
208031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
209031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
210031c2a00SAdrian Hunter
211031c2a00SAdrian Hunter	def DisplayData(self, item, index):
212031c2a00SAdrian Hunter		return item.getData(index.column())
213031c2a00SAdrian Hunter
2148392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2158392b74bSAdrian Hunter		if row > self.last_row_read:
2168392b74bSAdrian Hunter			self.last_row_read = row
2178392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2188392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2198392b74bSAdrian Hunter
2208392b74bSAdrian Hunter	def columnAlignment(self, column):
2218392b74bSAdrian Hunter		return Qt.AlignLeft
2228392b74bSAdrian Hunter
2238392b74bSAdrian Hunter	def columnFont(self, column):
2248392b74bSAdrian Hunter		return None
2258392b74bSAdrian Hunter
2268392b74bSAdrian Hunter	def data(self, index, role):
2278392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2288392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2298392b74bSAdrian Hunter		if role == Qt.FontRole:
2308392b74bSAdrian Hunter			return self.columnFont(index.column())
2318392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2328392b74bSAdrian Hunter			return None
2338392b74bSAdrian Hunter		item = index.internalPointer()
2348392b74bSAdrian Hunter		return self.DisplayData(item, index)
2358392b74bSAdrian Hunter
2368392b74bSAdrian Hunter# Table data model
2378392b74bSAdrian Hunter
2388392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2398392b74bSAdrian Hunter
2408392b74bSAdrian Hunter	def __init__(self, parent=None):
2418392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2428392b74bSAdrian Hunter		self.child_count = 0
2438392b74bSAdrian Hunter		self.child_items = []
2448392b74bSAdrian Hunter		self.last_row_read = 0
2458392b74bSAdrian Hunter
2468392b74bSAdrian Hunter	def Item(self, parent):
2478392b74bSAdrian Hunter		if parent.isValid():
2488392b74bSAdrian Hunter			return parent.internalPointer()
2498392b74bSAdrian Hunter		else:
2508392b74bSAdrian Hunter			return self
2518392b74bSAdrian Hunter
2528392b74bSAdrian Hunter	def rowCount(self, parent):
2538392b74bSAdrian Hunter		return self.child_count
2548392b74bSAdrian Hunter
2558392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2568392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2578392b74bSAdrian Hunter			return self.columnAlignment(section)
2588392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2598392b74bSAdrian Hunter			return None
2608392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2618392b74bSAdrian Hunter			return None
2628392b74bSAdrian Hunter		return self.columnHeader(section)
2638392b74bSAdrian Hunter
2648392b74bSAdrian Hunter	def index(self, row, column, parent):
2658392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2668392b74bSAdrian Hunter
2678392b74bSAdrian Hunter	def DisplayData(self, item, index):
2688392b74bSAdrian Hunter		return item.getData(index.column())
2698392b74bSAdrian Hunter
2708392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2718392b74bSAdrian Hunter		if row > self.last_row_read:
2728392b74bSAdrian Hunter			self.last_row_read = row
2738392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2748392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2758392b74bSAdrian Hunter
276031c2a00SAdrian Hunter	def columnAlignment(self, column):
277031c2a00SAdrian Hunter		return Qt.AlignLeft
278031c2a00SAdrian Hunter
279031c2a00SAdrian Hunter	def columnFont(self, column):
280031c2a00SAdrian Hunter		return None
281031c2a00SAdrian Hunter
282031c2a00SAdrian Hunter	def data(self, index, role):
283031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
284031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
285031c2a00SAdrian Hunter		if role == Qt.FontRole:
286031c2a00SAdrian Hunter			return self.columnFont(index.column())
287031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
288031c2a00SAdrian Hunter			return None
289031c2a00SAdrian Hunter		item = index.internalPointer()
290031c2a00SAdrian Hunter		return self.DisplayData(item, index)
291031c2a00SAdrian Hunter
2921beb5c7bSAdrian Hunter# Model cache
2931beb5c7bSAdrian Hunter
2941beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
2951beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
2961beb5c7bSAdrian Hunter
2971beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
2981beb5c7bSAdrian Hunter	model_cache_lock.acquire()
2991beb5c7bSAdrian Hunter	try:
3001beb5c7bSAdrian Hunter		model = model_cache[model_name]
3011beb5c7bSAdrian Hunter	except:
3021beb5c7bSAdrian Hunter		model = None
3031beb5c7bSAdrian Hunter	if model is None:
3041beb5c7bSAdrian Hunter		model = create_fn()
3051beb5c7bSAdrian Hunter		model_cache[model_name] = model
3061beb5c7bSAdrian Hunter	model_cache_lock.release()
3071beb5c7bSAdrian Hunter	return model
3081beb5c7bSAdrian Hunter
309ebd70c7dSAdrian Hunter# Find bar
310ebd70c7dSAdrian Hunter
311ebd70c7dSAdrian Hunterclass FindBar():
312ebd70c7dSAdrian Hunter
313ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
314ebd70c7dSAdrian Hunter		self.finder = finder
315ebd70c7dSAdrian Hunter		self.context = []
316ebd70c7dSAdrian Hunter		self.last_value = None
317ebd70c7dSAdrian Hunter		self.last_pattern = None
318ebd70c7dSAdrian Hunter
319ebd70c7dSAdrian Hunter		label = QLabel("Find:")
320ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
321ebd70c7dSAdrian Hunter
322ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
323ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
324ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
325ebd70c7dSAdrian Hunter
326ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
327ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
328ebd70c7dSAdrian Hunter		self.progress.hide()
329ebd70c7dSAdrian Hunter
330ebd70c7dSAdrian Hunter		if is_reg_expr:
331ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
332ebd70c7dSAdrian Hunter		else:
333ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
334ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
335ebd70c7dSAdrian Hunter
336ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
337ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
338ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
339ebd70c7dSAdrian Hunter
340ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
341ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
342ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
343ebd70c7dSAdrian Hunter
344ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
345ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
346ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
347ebd70c7dSAdrian Hunter
348ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
349ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
350ebd70c7dSAdrian Hunter
351ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
352ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
353ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
354ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
355ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
356ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
357ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
358ebd70c7dSAdrian Hunter
359ebd70c7dSAdrian Hunter		self.bar = QWidget()
360ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
361ebd70c7dSAdrian Hunter		self.bar.hide()
362ebd70c7dSAdrian Hunter
363ebd70c7dSAdrian Hunter	def Widget(self):
364ebd70c7dSAdrian Hunter		return self.bar
365ebd70c7dSAdrian Hunter
366ebd70c7dSAdrian Hunter	def Activate(self):
367ebd70c7dSAdrian Hunter		self.bar.show()
368ebd70c7dSAdrian Hunter		self.textbox.setFocus()
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter	def Deactivate(self):
371ebd70c7dSAdrian Hunter		self.bar.hide()
372ebd70c7dSAdrian Hunter
373ebd70c7dSAdrian Hunter	def Busy(self):
374ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
375ebd70c7dSAdrian Hunter		self.pattern.hide()
376ebd70c7dSAdrian Hunter		self.next_button.hide()
377ebd70c7dSAdrian Hunter		self.prev_button.hide()
378ebd70c7dSAdrian Hunter		self.progress.show()
379ebd70c7dSAdrian Hunter
380ebd70c7dSAdrian Hunter	def Idle(self):
381ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
382ebd70c7dSAdrian Hunter		self.progress.hide()
383ebd70c7dSAdrian Hunter		self.pattern.show()
384ebd70c7dSAdrian Hunter		self.next_button.show()
385ebd70c7dSAdrian Hunter		self.prev_button.show()
386ebd70c7dSAdrian Hunter
387ebd70c7dSAdrian Hunter	def Find(self, direction):
388ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
389ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
390ebd70c7dSAdrian Hunter		self.last_value = value
391ebd70c7dSAdrian Hunter		self.last_pattern = pattern
392ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
393ebd70c7dSAdrian Hunter
394ebd70c7dSAdrian Hunter	def ValueChanged(self):
395ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
396ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
397ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
398ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
399ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
400ebd70c7dSAdrian Hunter		if data == None:
401ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
402ebd70c7dSAdrian Hunter		else:
403ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
404ebd70c7dSAdrian Hunter		self.Find(0)
405ebd70c7dSAdrian Hunter
406ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
407ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
408ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
409ebd70c7dSAdrian Hunter		if value != self.last_value:
410ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
411ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
412ebd70c7dSAdrian Hunter			if index < 0:
413ebd70c7dSAdrian Hunter				index = self.textbox.count()
414ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
415ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
416ebd70c7dSAdrian Hunter				return
417ebd70c7dSAdrian Hunter			else:
418ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
419ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
420ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
421ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
422ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
423ebd70c7dSAdrian Hunter		self.Find(direction)
424ebd70c7dSAdrian Hunter
425ebd70c7dSAdrian Hunter	def NotFound(self):
426ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
427ebd70c7dSAdrian Hunter
428031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
429031c2a00SAdrian Hunter
430031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
431031c2a00SAdrian Hunter
432031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
433031c2a00SAdrian Hunter		self.glb = glb
434031c2a00SAdrian Hunter		self.row = row
435031c2a00SAdrian Hunter		self.parent_item = parent_item
436031c2a00SAdrian Hunter		self.query_done = False;
437031c2a00SAdrian Hunter		self.child_count = 0
438031c2a00SAdrian Hunter		self.child_items = []
439031c2a00SAdrian Hunter
440031c2a00SAdrian Hunter	def getChildItem(self, row):
441031c2a00SAdrian Hunter		return self.child_items[row]
442031c2a00SAdrian Hunter
443031c2a00SAdrian Hunter	def getParentItem(self):
444031c2a00SAdrian Hunter		return self.parent_item
445031c2a00SAdrian Hunter
446031c2a00SAdrian Hunter	def getRow(self):
447031c2a00SAdrian Hunter		return self.row
448031c2a00SAdrian Hunter
449031c2a00SAdrian Hunter	def childCount(self):
450031c2a00SAdrian Hunter		if not self.query_done:
451031c2a00SAdrian Hunter			self.Select()
452031c2a00SAdrian Hunter			if not self.child_count:
453031c2a00SAdrian Hunter				return -1
454031c2a00SAdrian Hunter		return self.child_count
455031c2a00SAdrian Hunter
456031c2a00SAdrian Hunter	def hasChildren(self):
457031c2a00SAdrian Hunter		if not self.query_done:
458031c2a00SAdrian Hunter			return True
459031c2a00SAdrian Hunter		return self.child_count > 0
460031c2a00SAdrian Hunter
461031c2a00SAdrian Hunter	def getData(self, column):
462031c2a00SAdrian Hunter		return self.data[column]
463031c2a00SAdrian Hunter
464031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
465031c2a00SAdrian Hunter
466031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
467031c2a00SAdrian Hunter
468031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
469031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
470031c2a00SAdrian Hunter		self.comm_id = comm_id
471031c2a00SAdrian Hunter		self.thread_id = thread_id
472031c2a00SAdrian Hunter		self.call_path_id = call_path_id
473031c2a00SAdrian Hunter		self.branch_count = branch_count
474031c2a00SAdrian Hunter		self.time = time
475031c2a00SAdrian Hunter
476031c2a00SAdrian Hunter	def Select(self):
477031c2a00SAdrian Hunter		self.query_done = True;
478031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
479031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
480031c2a00SAdrian Hunter					" FROM calls"
481031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
482031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
483031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
484031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
485031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
486031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
487031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
488031c2a00SAdrian Hunter					" ORDER BY call_path_id")
489031c2a00SAdrian Hunter		while query.next():
490031c2a00SAdrian Hunter			child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
491031c2a00SAdrian Hunter			self.child_items.append(child_item)
492031c2a00SAdrian Hunter			self.child_count += 1
493031c2a00SAdrian Hunter
494031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
497031c2a00SAdrian Hunter
498031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
499031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
500031c2a00SAdrian Hunter		dso = dsoname(dso)
501031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
502031c2a00SAdrian Hunter		self.dbid = call_path_id
503031c2a00SAdrian Hunter
504031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
505031c2a00SAdrian Hunter
506031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
507031c2a00SAdrian Hunter
508031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
509031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
510031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
511031c2a00SAdrian Hunter		self.dbid = thread_id
512031c2a00SAdrian Hunter
513031c2a00SAdrian Hunter	def Select(self):
514031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
515031c2a00SAdrian Hunter		for child_item in self.child_items:
516031c2a00SAdrian Hunter			self.time += child_item.time
517031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
518031c2a00SAdrian Hunter		for child_item in self.child_items:
519031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
520031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
521031c2a00SAdrian Hunter
522031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
523031c2a00SAdrian Hunter
524031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
525031c2a00SAdrian Hunter
526031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
527031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
528031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
529031c2a00SAdrian Hunter		self.dbid = comm_id
530031c2a00SAdrian Hunter
531031c2a00SAdrian Hunter	def Select(self):
532031c2a00SAdrian Hunter		self.query_done = True;
533031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
534031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
535031c2a00SAdrian Hunter					" FROM comm_threads"
536031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
537031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
538031c2a00SAdrian Hunter		while query.next():
539031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
540031c2a00SAdrian Hunter			self.child_items.append(child_item)
541031c2a00SAdrian Hunter			self.child_count += 1
542031c2a00SAdrian Hunter
543031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
544031c2a00SAdrian Hunter
545031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
546031c2a00SAdrian Hunter
547031c2a00SAdrian Hunter	def __init__(self, glb):
548031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
549031c2a00SAdrian Hunter		self.dbid = 0
550031c2a00SAdrian Hunter		self.query_done = True;
551031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
552031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
553031c2a00SAdrian Hunter		while query.next():
554031c2a00SAdrian Hunter			if not query.value(0):
555031c2a00SAdrian Hunter				continue
556031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
557031c2a00SAdrian Hunter			self.child_items.append(child_item)
558031c2a00SAdrian Hunter			self.child_count += 1
559031c2a00SAdrian Hunter
560031c2a00SAdrian Hunter# Context-sensitive call graph data model
561031c2a00SAdrian Hunter
562031c2a00SAdrian Hunterclass CallGraphModel(TreeModel):
563031c2a00SAdrian Hunter
564031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
565031c2a00SAdrian Hunter		super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
566031c2a00SAdrian Hunter		self.glb = glb
567031c2a00SAdrian Hunter
568031c2a00SAdrian Hunter	def columnCount(self, parent=None):
569031c2a00SAdrian Hunter		return 7
570031c2a00SAdrian Hunter
571031c2a00SAdrian Hunter	def columnHeader(self, column):
572031c2a00SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
573031c2a00SAdrian Hunter		return headers[column]
574031c2a00SAdrian Hunter
575031c2a00SAdrian Hunter	def columnAlignment(self, column):
576031c2a00SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
577031c2a00SAdrian Hunter		return alignment[column]
578031c2a00SAdrian Hunter
579ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
580ebd70c7dSAdrian Hunter		if pattern:
581ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
582ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
583ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
584ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
585ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
586ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
587ebd70c7dSAdrian Hunter				# Escape % and _
588ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
589ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
590ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
591ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
592ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
593ebd70c7dSAdrian Hunter			else:
594ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
595ebd70c7dSAdrian Hunter		else:
596ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
597ebd70c7dSAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
598ebd70c7dSAdrian Hunter						" FROM calls"
599ebd70c7dSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
600ebd70c7dSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
601ebd70c7dSAdrian Hunter						" WHERE symbols.name" + match +
602ebd70c7dSAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
603ebd70c7dSAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
604ebd70c7dSAdrian Hunter
605ebd70c7dSAdrian Hunter	def FindPath(self, query):
606ebd70c7dSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
607ebd70c7dSAdrian Hunter		# to open the tree at the right place.
608ebd70c7dSAdrian Hunter		ids = []
609ebd70c7dSAdrian Hunter		parent_id = query.value(0)
610ebd70c7dSAdrian Hunter		while parent_id:
611ebd70c7dSAdrian Hunter			ids.insert(0, parent_id)
612ebd70c7dSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
613ebd70c7dSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
614ebd70c7dSAdrian Hunter					" FROM call_paths"
615ebd70c7dSAdrian Hunter					" WHERE id = " + str(parent_id))
616ebd70c7dSAdrian Hunter			if not q2.next():
617ebd70c7dSAdrian Hunter				break
618ebd70c7dSAdrian Hunter			parent_id = q2.value(0)
619ebd70c7dSAdrian Hunter		# The call path root is not used
620ebd70c7dSAdrian Hunter		if ids[0] == 1:
621ebd70c7dSAdrian Hunter			del ids[0]
622ebd70c7dSAdrian Hunter		ids.insert(0, query.value(2))
623ebd70c7dSAdrian Hunter		ids.insert(0, query.value(1))
624ebd70c7dSAdrian Hunter		return ids
625ebd70c7dSAdrian Hunter
626ebd70c7dSAdrian Hunter	def Found(self, query, found):
627ebd70c7dSAdrian Hunter		if found:
628ebd70c7dSAdrian Hunter			return self.FindPath(query)
629ebd70c7dSAdrian Hunter		return []
630ebd70c7dSAdrian Hunter
631ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
632ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
633ebd70c7dSAdrian Hunter			found = query.first()
634ebd70c7dSAdrian Hunter		else:
635ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
636ebd70c7dSAdrian Hunter			found = query.next()
637ebd70c7dSAdrian Hunter		return self.Found(query, found)
638ebd70c7dSAdrian Hunter
639ebd70c7dSAdrian Hunter	def FindNext(self, query):
640ebd70c7dSAdrian Hunter		found = query.next()
641ebd70c7dSAdrian Hunter		if not found:
642ebd70c7dSAdrian Hunter			found = query.first()
643ebd70c7dSAdrian Hunter		return self.Found(query, found)
644ebd70c7dSAdrian Hunter
645ebd70c7dSAdrian Hunter	def FindPrev(self, query):
646ebd70c7dSAdrian Hunter		found = query.previous()
647ebd70c7dSAdrian Hunter		if not found:
648ebd70c7dSAdrian Hunter			found = query.last()
649ebd70c7dSAdrian Hunter		return self.Found(query, found)
650ebd70c7dSAdrian Hunter
651ebd70c7dSAdrian Hunter	def FindThread(self, c):
652ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
653ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
654ebd70c7dSAdrian Hunter		elif c.direction > 0:
655ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
656ebd70c7dSAdrian Hunter		else:
657ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
658ebd70c7dSAdrian Hunter		return (True, ids)
659ebd70c7dSAdrian Hunter
660ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
661ebd70c7dSAdrian Hunter		class Context():
662ebd70c7dSAdrian Hunter			def __init__(self, *x):
663ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
664ebd70c7dSAdrian Hunter			def Update(self, *x):
665ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
666ebd70c7dSAdrian Hunter		if len(context):
667ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
668ebd70c7dSAdrian Hunter		else:
669ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
670ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
671ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
672ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
673ebd70c7dSAdrian Hunter		thread.start()
674ebd70c7dSAdrian Hunter
675ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
676ebd70c7dSAdrian Hunter		callback(ids)
677ebd70c7dSAdrian Hunter
678ebd70c7dSAdrian Hunter# Vertical widget layout
679ebd70c7dSAdrian Hunter
680ebd70c7dSAdrian Hunterclass VBox():
681ebd70c7dSAdrian Hunter
682ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
683ebd70c7dSAdrian Hunter		self.vbox = QWidget()
684ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
685ebd70c7dSAdrian Hunter
686ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
687ebd70c7dSAdrian Hunter
688ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
689ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
690ebd70c7dSAdrian Hunter		if w3:
691ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
692ebd70c7dSAdrian Hunter
693ebd70c7dSAdrian Hunter	def Widget(self):
694ebd70c7dSAdrian Hunter		return self.vbox
695ebd70c7dSAdrian Hunter
6961beb5c7bSAdrian Hunter# Context-sensitive call graph window
6971beb5c7bSAdrian Hunter
6981beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow):
6991beb5c7bSAdrian Hunter
7001beb5c7bSAdrian Hunter	def __init__(self, glb, parent=None):
7011beb5c7bSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
7021beb5c7bSAdrian Hunter
7031beb5c7bSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
7041beb5c7bSAdrian Hunter
7051beb5c7bSAdrian Hunter		self.view = QTreeView()
7061beb5c7bSAdrian Hunter		self.view.setModel(self.model)
7071beb5c7bSAdrian Hunter
7081beb5c7bSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
7091beb5c7bSAdrian Hunter			self.view.setColumnWidth(c, w)
7101beb5c7bSAdrian Hunter
711ebd70c7dSAdrian Hunter		self.find_bar = FindBar(self, self)
712ebd70c7dSAdrian Hunter
713ebd70c7dSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
714ebd70c7dSAdrian Hunter
715ebd70c7dSAdrian Hunter		self.setWidget(self.vbox.Widget())
7161beb5c7bSAdrian Hunter
7171beb5c7bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
7181beb5c7bSAdrian Hunter
719ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
720ebd70c7dSAdrian Hunter		if not len(ids):
721ebd70c7dSAdrian Hunter			return False
722ebd70c7dSAdrian Hunter		parent = QModelIndex()
723ebd70c7dSAdrian Hunter		for dbid in ids:
724ebd70c7dSAdrian Hunter			found = False
725ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
726ebd70c7dSAdrian Hunter			for row in xrange(n):
727ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
728ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
729ebd70c7dSAdrian Hunter					found = True
730ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
731ebd70c7dSAdrian Hunter					parent = child
732ebd70c7dSAdrian Hunter					break
733ebd70c7dSAdrian Hunter			if not found:
734ebd70c7dSAdrian Hunter				break
735ebd70c7dSAdrian Hunter		return found
736ebd70c7dSAdrian Hunter
737ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
738ebd70c7dSAdrian Hunter		self.view.setFocus()
739ebd70c7dSAdrian Hunter		self.find_bar.Busy()
740ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
741ebd70c7dSAdrian Hunter
742ebd70c7dSAdrian Hunter	def FindDone(self, ids):
743ebd70c7dSAdrian Hunter		found = True
744ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
745ebd70c7dSAdrian Hunter			found = False
746ebd70c7dSAdrian Hunter		self.find_bar.Idle()
747ebd70c7dSAdrian Hunter		if not found:
748ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
749ebd70c7dSAdrian Hunter
7508392b74bSAdrian Hunter# Child data item  finder
7518392b74bSAdrian Hunter
7528392b74bSAdrian Hunterclass ChildDataItemFinder():
7538392b74bSAdrian Hunter
7548392b74bSAdrian Hunter	def __init__(self, root):
7558392b74bSAdrian Hunter		self.root = root
7568392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
7578392b74bSAdrian Hunter		self.rows = []
7588392b74bSAdrian Hunter		self.pos = 0
7598392b74bSAdrian Hunter
7608392b74bSAdrian Hunter	def FindSelect(self):
7618392b74bSAdrian Hunter		self.rows = []
7628392b74bSAdrian Hunter		if self.pattern:
7638392b74bSAdrian Hunter			pattern = re.compile(self.value)
7648392b74bSAdrian Hunter			for child in self.root.child_items:
7658392b74bSAdrian Hunter				for column_data in child.data:
7668392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
7678392b74bSAdrian Hunter						self.rows.append(child.row)
7688392b74bSAdrian Hunter						break
7698392b74bSAdrian Hunter		else:
7708392b74bSAdrian Hunter			for child in self.root.child_items:
7718392b74bSAdrian Hunter				for column_data in child.data:
7728392b74bSAdrian Hunter					if self.value in str(column_data):
7738392b74bSAdrian Hunter						self.rows.append(child.row)
7748392b74bSAdrian Hunter						break
7758392b74bSAdrian Hunter
7768392b74bSAdrian Hunter	def FindValue(self):
7778392b74bSAdrian Hunter		self.pos = 0
7788392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
7798392b74bSAdrian Hunter			self.FindSelect()
7808392b74bSAdrian Hunter		if not len(self.rows):
7818392b74bSAdrian Hunter			return -1
7828392b74bSAdrian Hunter		return self.rows[self.pos]
7838392b74bSAdrian Hunter
7848392b74bSAdrian Hunter	def FindThread(self):
7858392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
7868392b74bSAdrian Hunter			row = self.FindValue()
7878392b74bSAdrian Hunter		elif len(self.rows):
7888392b74bSAdrian Hunter			if self.direction > 0:
7898392b74bSAdrian Hunter				self.pos += 1
7908392b74bSAdrian Hunter				if self.pos >= len(self.rows):
7918392b74bSAdrian Hunter					self.pos = 0
7928392b74bSAdrian Hunter			else:
7938392b74bSAdrian Hunter				self.pos -= 1
7948392b74bSAdrian Hunter				if self.pos < 0:
7958392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
7968392b74bSAdrian Hunter			row = self.rows[self.pos]
7978392b74bSAdrian Hunter		else:
7988392b74bSAdrian Hunter			row = -1
7998392b74bSAdrian Hunter		return (True, row)
8008392b74bSAdrian Hunter
8018392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
8028392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
8038392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
8048392b74bSAdrian Hunter		thread = Thread(self.FindThread)
8058392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
8068392b74bSAdrian Hunter		thread.start()
8078392b74bSAdrian Hunter
8088392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
8098392b74bSAdrian Hunter		callback(row)
8108392b74bSAdrian Hunter
8118392b74bSAdrian Hunter# Number of database records to fetch in one go
8128392b74bSAdrian Hunter
8138392b74bSAdrian Hunterglb_chunk_sz = 10000
8148392b74bSAdrian Hunter
8158392b74bSAdrian Hunter# size of pickled integer big enough for record size
8168392b74bSAdrian Hunter
8178392b74bSAdrian Hunterglb_nsz = 8
8188392b74bSAdrian Hunter
8198392b74bSAdrian Hunter# Background process for SQL data fetcher
8208392b74bSAdrian Hunter
8218392b74bSAdrian Hunterclass SQLFetcherProcess():
8228392b74bSAdrian Hunter
8238392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
8248392b74bSAdrian Hunter		# Need a unique connection name
8258392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
8268392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
8278392b74bSAdrian Hunter		self.sql = sql
8288392b74bSAdrian Hunter		self.buffer = buffer
8298392b74bSAdrian Hunter		self.head = head
8308392b74bSAdrian Hunter		self.tail = tail
8318392b74bSAdrian Hunter		self.fetch_count = fetch_count
8328392b74bSAdrian Hunter		self.fetching_done = fetching_done
8338392b74bSAdrian Hunter		self.process_target = process_target
8348392b74bSAdrian Hunter		self.wait_event = wait_event
8358392b74bSAdrian Hunter		self.fetched_event = fetched_event
8368392b74bSAdrian Hunter		self.prep = prep
8378392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
8388392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
8398392b74bSAdrian Hunter		self.last_id = -1
8408392b74bSAdrian Hunter		self.fetched = 0
8418392b74bSAdrian Hunter		self.more = True
8428392b74bSAdrian Hunter		self.local_head = self.head.value
8438392b74bSAdrian Hunter		self.local_tail = self.tail.value
8448392b74bSAdrian Hunter
8458392b74bSAdrian Hunter	def Select(self):
8468392b74bSAdrian Hunter		if self.query_limit:
8478392b74bSAdrian Hunter			if self.query_limit == 1:
8488392b74bSAdrian Hunter				return
8498392b74bSAdrian Hunter			self.query_limit -= 1
8508392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
8518392b74bSAdrian Hunter		QueryExec(self.query, stmt)
8528392b74bSAdrian Hunter
8538392b74bSAdrian Hunter	def Next(self):
8548392b74bSAdrian Hunter		if not self.query.next():
8558392b74bSAdrian Hunter			self.Select()
8568392b74bSAdrian Hunter			if not self.query.next():
8578392b74bSAdrian Hunter				return None
8588392b74bSAdrian Hunter		self.last_id = self.query.value(0)
8598392b74bSAdrian Hunter		return self.prep(self.query)
8608392b74bSAdrian Hunter
8618392b74bSAdrian Hunter	def WaitForTarget(self):
8628392b74bSAdrian Hunter		while True:
8638392b74bSAdrian Hunter			self.wait_event.clear()
8648392b74bSAdrian Hunter			target = self.process_target.value
8658392b74bSAdrian Hunter			if target > self.fetched or target < 0:
8668392b74bSAdrian Hunter				break
8678392b74bSAdrian Hunter			self.wait_event.wait()
8688392b74bSAdrian Hunter		return target
8698392b74bSAdrian Hunter
8708392b74bSAdrian Hunter	def HasSpace(self, sz):
8718392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
8728392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
8738392b74bSAdrian Hunter			if space > sz:
8748392b74bSAdrian Hunter				return True
8758392b74bSAdrian Hunter			if space >= glb_nsz:
8768392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
8778392b74bSAdrian Hunter				nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
8788392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
8798392b74bSAdrian Hunter			self.local_head = 0
8808392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
8818392b74bSAdrian Hunter			return True
8828392b74bSAdrian Hunter		return False
8838392b74bSAdrian Hunter
8848392b74bSAdrian Hunter	def WaitForSpace(self, sz):
8858392b74bSAdrian Hunter		if self.HasSpace(sz):
8868392b74bSAdrian Hunter			return
8878392b74bSAdrian Hunter		while True:
8888392b74bSAdrian Hunter			self.wait_event.clear()
8898392b74bSAdrian Hunter			self.local_tail = self.tail.value
8908392b74bSAdrian Hunter			if self.HasSpace(sz):
8918392b74bSAdrian Hunter				return
8928392b74bSAdrian Hunter			self.wait_event.wait()
8938392b74bSAdrian Hunter
8948392b74bSAdrian Hunter	def AddToBuffer(self, obj):
8958392b74bSAdrian Hunter		d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
8968392b74bSAdrian Hunter		n = len(d)
8978392b74bSAdrian Hunter		nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
8988392b74bSAdrian Hunter		sz = n + glb_nsz
8998392b74bSAdrian Hunter		self.WaitForSpace(sz)
9008392b74bSAdrian Hunter		pos = self.local_head
9018392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
9028392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
9038392b74bSAdrian Hunter		self.local_head += sz
9048392b74bSAdrian Hunter
9058392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
9068392b74bSAdrian Hunter		fetched = 0
9078392b74bSAdrian Hunter		while batch_size > fetched:
9088392b74bSAdrian Hunter			obj = self.Next()
9098392b74bSAdrian Hunter			if obj is None:
9108392b74bSAdrian Hunter				self.more = False
9118392b74bSAdrian Hunter				break
9128392b74bSAdrian Hunter			self.AddToBuffer(obj)
9138392b74bSAdrian Hunter			fetched += 1
9148392b74bSAdrian Hunter		if fetched:
9158392b74bSAdrian Hunter			self.fetched += fetched
9168392b74bSAdrian Hunter			with self.fetch_count.get_lock():
9178392b74bSAdrian Hunter				self.fetch_count.value += fetched
9188392b74bSAdrian Hunter			self.head.value = self.local_head
9198392b74bSAdrian Hunter			self.fetched_event.set()
9208392b74bSAdrian Hunter
9218392b74bSAdrian Hunter	def Run(self):
9228392b74bSAdrian Hunter		while self.more:
9238392b74bSAdrian Hunter			target = self.WaitForTarget()
9248392b74bSAdrian Hunter			if target < 0:
9258392b74bSAdrian Hunter				break
9268392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
9278392b74bSAdrian Hunter			self.FetchBatch(batch_size)
9288392b74bSAdrian Hunter		self.fetching_done.value = True
9298392b74bSAdrian Hunter		self.fetched_event.set()
9308392b74bSAdrian Hunter
9318392b74bSAdrian Hunterdef SQLFetcherFn(*x):
9328392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
9338392b74bSAdrian Hunter	process.Run()
9348392b74bSAdrian Hunter
9358392b74bSAdrian Hunter# SQL data fetcher
9368392b74bSAdrian Hunter
9378392b74bSAdrian Hunterclass SQLFetcher(QObject):
9388392b74bSAdrian Hunter
9398392b74bSAdrian Hunter	done = Signal(object)
9408392b74bSAdrian Hunter
9418392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
9428392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
9438392b74bSAdrian Hunter		self.process_data = process_data
9448392b74bSAdrian Hunter		self.more = True
9458392b74bSAdrian Hunter		self.target = 0
9468392b74bSAdrian Hunter		self.last_target = 0
9478392b74bSAdrian Hunter		self.fetched = 0
9488392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
9498392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
9508392b74bSAdrian Hunter		self.head = Value(c_longlong)
9518392b74bSAdrian Hunter		self.tail = Value(c_longlong)
9528392b74bSAdrian Hunter		self.local_tail = 0
9538392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
9548392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
9558392b74bSAdrian Hunter		self.last_count = 0
9568392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
9578392b74bSAdrian Hunter		self.wait_event = Event()
9588392b74bSAdrian Hunter		self.fetched_event = Event()
9598392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
9608392b74bSAdrian 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))
9618392b74bSAdrian Hunter		self.process.start()
9628392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
9638392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
9648392b74bSAdrian Hunter		self.thread.start()
9658392b74bSAdrian Hunter
9668392b74bSAdrian Hunter	def Shutdown(self):
9678392b74bSAdrian Hunter		# Tell the thread and process to exit
9688392b74bSAdrian Hunter		self.process_target.value = -1
9698392b74bSAdrian Hunter		self.wait_event.set()
9708392b74bSAdrian Hunter		self.more = False
9718392b74bSAdrian Hunter		self.fetching_done.value = True
9728392b74bSAdrian Hunter		self.fetched_event.set()
9738392b74bSAdrian Hunter
9748392b74bSAdrian Hunter	def Thread(self):
9758392b74bSAdrian Hunter		if not self.more:
9768392b74bSAdrian Hunter			return True, 0
9778392b74bSAdrian Hunter		while True:
9788392b74bSAdrian Hunter			self.fetched_event.clear()
9798392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
9808392b74bSAdrian Hunter			if fetch_count != self.last_count:
9818392b74bSAdrian Hunter				break
9828392b74bSAdrian Hunter			if self.fetching_done.value:
9838392b74bSAdrian Hunter				self.more = False
9848392b74bSAdrian Hunter				return True, 0
9858392b74bSAdrian Hunter			self.fetched_event.wait()
9868392b74bSAdrian Hunter		count = fetch_count - self.last_count
9878392b74bSAdrian Hunter		self.last_count = fetch_count
9888392b74bSAdrian Hunter		self.fetched += count
9898392b74bSAdrian Hunter		return False, count
9908392b74bSAdrian Hunter
9918392b74bSAdrian Hunter	def Fetch(self, nr):
9928392b74bSAdrian Hunter		if not self.more:
9938392b74bSAdrian Hunter			# -1 inidcates there are no more
9948392b74bSAdrian Hunter			return -1
9958392b74bSAdrian Hunter		result = self.fetched
9968392b74bSAdrian Hunter		extra = result + nr - self.target
9978392b74bSAdrian Hunter		if extra > 0:
9988392b74bSAdrian Hunter			self.target += extra
9998392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
10008392b74bSAdrian Hunter			if self.process_target.value >= 0:
10018392b74bSAdrian Hunter				self.process_target.value = self.target
10028392b74bSAdrian Hunter			self.wait_event.set()
10038392b74bSAdrian Hunter		return result
10048392b74bSAdrian Hunter
10058392b74bSAdrian Hunter	def RemoveFromBuffer(self):
10068392b74bSAdrian Hunter		pos = self.local_tail
10078392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
10088392b74bSAdrian Hunter			pos = 0
10098392b74bSAdrian Hunter		n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
10108392b74bSAdrian Hunter		if n == 0:
10118392b74bSAdrian Hunter			pos = 0
10128392b74bSAdrian Hunter			n = cPickle.loads(self.buffer[0 : glb_nsz])
10138392b74bSAdrian Hunter		pos += glb_nsz
10148392b74bSAdrian Hunter		obj = cPickle.loads(self.buffer[pos : pos + n])
10158392b74bSAdrian Hunter		self.local_tail = pos + n
10168392b74bSAdrian Hunter		return obj
10178392b74bSAdrian Hunter
10188392b74bSAdrian Hunter	def ProcessData(self, count):
10198392b74bSAdrian Hunter		for i in xrange(count):
10208392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
10218392b74bSAdrian Hunter			self.process_data(obj)
10228392b74bSAdrian Hunter		self.tail.value = self.local_tail
10238392b74bSAdrian Hunter		self.wait_event.set()
10248392b74bSAdrian Hunter		self.done.emit(count)
10258392b74bSAdrian Hunter
10268392b74bSAdrian Hunter# Fetch more records bar
10278392b74bSAdrian Hunter
10288392b74bSAdrian Hunterclass FetchMoreRecordsBar():
10298392b74bSAdrian Hunter
10308392b74bSAdrian Hunter	def __init__(self, model, parent):
10318392b74bSAdrian Hunter		self.model = model
10328392b74bSAdrian Hunter
10338392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
10348392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10358392b74bSAdrian Hunter
10368392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
10378392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
10388392b74bSAdrian Hunter		self.fetch_count.setValue(10)
10398392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10408392b74bSAdrian Hunter
10418392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
10428392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10438392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
10448392b74bSAdrian Hunter
10458392b74bSAdrian Hunter		self.progress = QProgressBar()
10468392b74bSAdrian Hunter		self.progress.setRange(0, 100)
10478392b74bSAdrian Hunter		self.progress.hide()
10488392b74bSAdrian Hunter
10498392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
10508392b74bSAdrian Hunter		self.done_label.hide()
10518392b74bSAdrian Hunter
10528392b74bSAdrian Hunter		self.spacer = QLabel("")
10538392b74bSAdrian Hunter
10548392b74bSAdrian Hunter		self.close_button = QToolButton()
10558392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
10568392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
10578392b74bSAdrian Hunter
10588392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
10598392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
10608392b74bSAdrian Hunter
10618392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
10628392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
10638392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
10648392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
10658392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
10668392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
10678392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
10688392b74bSAdrian Hunter
10698392b74bSAdrian Hunter		self.bar = QWidget()
10708392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
10718392b74bSAdrian Hunter		self.bar.show()
10728392b74bSAdrian Hunter
10738392b74bSAdrian Hunter		self.in_progress = False
10748392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter		self.done = False
10778392b74bSAdrian Hunter
10788392b74bSAdrian Hunter		if not model.HasMoreRecords():
10798392b74bSAdrian Hunter			self.Done()
10808392b74bSAdrian Hunter
10818392b74bSAdrian Hunter	def Widget(self):
10828392b74bSAdrian Hunter		return self.bar
10838392b74bSAdrian Hunter
10848392b74bSAdrian Hunter	def Activate(self):
10858392b74bSAdrian Hunter		self.bar.show()
10868392b74bSAdrian Hunter		self.fetch.setFocus()
10878392b74bSAdrian Hunter
10888392b74bSAdrian Hunter	def Deactivate(self):
10898392b74bSAdrian Hunter		self.bar.hide()
10908392b74bSAdrian Hunter
10918392b74bSAdrian Hunter	def Enable(self, enable):
10928392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
10938392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
10948392b74bSAdrian Hunter
10958392b74bSAdrian Hunter	def Busy(self):
10968392b74bSAdrian Hunter		self.Enable(False)
10978392b74bSAdrian Hunter		self.fetch.hide()
10988392b74bSAdrian Hunter		self.spacer.hide()
10998392b74bSAdrian Hunter		self.progress.show()
11008392b74bSAdrian Hunter
11018392b74bSAdrian Hunter	def Idle(self):
11028392b74bSAdrian Hunter		self.in_progress = False
11038392b74bSAdrian Hunter		self.Enable(True)
11048392b74bSAdrian Hunter		self.progress.hide()
11058392b74bSAdrian Hunter		self.fetch.show()
11068392b74bSAdrian Hunter		self.spacer.show()
11078392b74bSAdrian Hunter
11088392b74bSAdrian Hunter	def Target(self):
11098392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
11108392b74bSAdrian Hunter
11118392b74bSAdrian Hunter	def Done(self):
11128392b74bSAdrian Hunter		self.done = True
11138392b74bSAdrian Hunter		self.Idle()
11148392b74bSAdrian Hunter		self.label.hide()
11158392b74bSAdrian Hunter		self.fetch_count.hide()
11168392b74bSAdrian Hunter		self.fetch.hide()
11178392b74bSAdrian Hunter		self.spacer.hide()
11188392b74bSAdrian Hunter		self.done_label.show()
11198392b74bSAdrian Hunter
11208392b74bSAdrian Hunter	def Progress(self, count):
11218392b74bSAdrian Hunter		if self.in_progress:
11228392b74bSAdrian Hunter			if count:
11238392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
11248392b74bSAdrian Hunter				if percent >= 100:
11258392b74bSAdrian Hunter					self.Idle()
11268392b74bSAdrian Hunter				else:
11278392b74bSAdrian Hunter					self.progress.setValue(percent)
11288392b74bSAdrian Hunter		if not count:
11298392b74bSAdrian Hunter			# Count value of zero means no more records
11308392b74bSAdrian Hunter			self.Done()
11318392b74bSAdrian Hunter
11328392b74bSAdrian Hunter	def FetchMoreRecords(self):
11338392b74bSAdrian Hunter		if self.done:
11348392b74bSAdrian Hunter			return
11358392b74bSAdrian Hunter		self.progress.setValue(0)
11368392b74bSAdrian Hunter		self.Busy()
11378392b74bSAdrian Hunter		self.in_progress = True
11388392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
11398392b74bSAdrian Hunter
114076099f98SAdrian Hunter# Brance data model level two item
114176099f98SAdrian Hunter
114276099f98SAdrian Hunterclass BranchLevelTwoItem():
114376099f98SAdrian Hunter
114476099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
114576099f98SAdrian Hunter		self.row = row
114676099f98SAdrian Hunter		self.parent_item = parent_item
114776099f98SAdrian Hunter		self.data = [""] * 8
114876099f98SAdrian Hunter		self.data[7] = text
114976099f98SAdrian Hunter		self.level = 2
115076099f98SAdrian Hunter
115176099f98SAdrian Hunter	def getParentItem(self):
115276099f98SAdrian Hunter		return self.parent_item
115376099f98SAdrian Hunter
115476099f98SAdrian Hunter	def getRow(self):
115576099f98SAdrian Hunter		return self.row
115676099f98SAdrian Hunter
115776099f98SAdrian Hunter	def childCount(self):
115876099f98SAdrian Hunter		return 0
115976099f98SAdrian Hunter
116076099f98SAdrian Hunter	def hasChildren(self):
116176099f98SAdrian Hunter		return False
116276099f98SAdrian Hunter
116376099f98SAdrian Hunter	def getData(self, column):
116476099f98SAdrian Hunter		return self.data[column]
116576099f98SAdrian Hunter
116676099f98SAdrian Hunter# Brance data model level one item
116776099f98SAdrian Hunter
116876099f98SAdrian Hunterclass BranchLevelOneItem():
116976099f98SAdrian Hunter
117076099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
117176099f98SAdrian Hunter		self.glb = glb
117276099f98SAdrian Hunter		self.row = row
117376099f98SAdrian Hunter		self.parent_item = parent_item
117476099f98SAdrian Hunter		self.child_count = 0
117576099f98SAdrian Hunter		self.child_items = []
117676099f98SAdrian Hunter		self.data = data[1:]
117776099f98SAdrian Hunter		self.dbid = data[0]
117876099f98SAdrian Hunter		self.level = 1
117976099f98SAdrian Hunter		self.query_done = False
118076099f98SAdrian Hunter
118176099f98SAdrian Hunter	def getChildItem(self, row):
118276099f98SAdrian Hunter		return self.child_items[row]
118376099f98SAdrian Hunter
118476099f98SAdrian Hunter	def getParentItem(self):
118576099f98SAdrian Hunter		return self.parent_item
118676099f98SAdrian Hunter
118776099f98SAdrian Hunter	def getRow(self):
118876099f98SAdrian Hunter		return self.row
118976099f98SAdrian Hunter
119076099f98SAdrian Hunter	def Select(self):
119176099f98SAdrian Hunter		self.query_done = True
119276099f98SAdrian Hunter
119376099f98SAdrian Hunter		if not self.glb.have_disassembler:
119476099f98SAdrian Hunter			return
119576099f98SAdrian Hunter
119676099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
119776099f98SAdrian Hunter
119876099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
119976099f98SAdrian Hunter				  " FROM samples"
120076099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
120176099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
120276099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
120376099f98SAdrian Hunter		if not query.next():
120476099f98SAdrian Hunter			return
120576099f98SAdrian Hunter		cpu = query.value(0)
120676099f98SAdrian Hunter		dso = query.value(1)
120776099f98SAdrian Hunter		sym = query.value(2)
120876099f98SAdrian Hunter		if dso == 0 or sym == 0:
120976099f98SAdrian Hunter			return
121076099f98SAdrian Hunter		off = query.value(3)
121176099f98SAdrian Hunter		short_name = query.value(4)
121276099f98SAdrian Hunter		long_name = query.value(5)
121376099f98SAdrian Hunter		build_id = query.value(6)
121476099f98SAdrian Hunter		sym_start = query.value(7)
121576099f98SAdrian Hunter		ip = query.value(8)
121676099f98SAdrian Hunter
121776099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
121876099f98SAdrian Hunter				  " FROM samples"
121976099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
122076099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
122176099f98SAdrian Hunter				  " ORDER BY samples.id"
122276099f98SAdrian Hunter				  " LIMIT 1")
122376099f98SAdrian Hunter		if not query.next():
122476099f98SAdrian Hunter			return
122576099f98SAdrian Hunter		if query.value(0) != dso:
122676099f98SAdrian Hunter			# Cannot disassemble from one dso to another
122776099f98SAdrian Hunter			return
122876099f98SAdrian Hunter		bsym = query.value(1)
122976099f98SAdrian Hunter		boff = query.value(2)
123076099f98SAdrian Hunter		bsym_start = query.value(3)
123176099f98SAdrian Hunter		if bsym == 0:
123276099f98SAdrian Hunter			return
123376099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
123476099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
123576099f98SAdrian Hunter			return
123676099f98SAdrian Hunter
123776099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
123876099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
123976099f98SAdrian Hunter		if not f:
124076099f98SAdrian Hunter			return
124176099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
124276099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
124376099f98SAdrian Hunter
124476099f98SAdrian Hunter		buf_sz = tot + 16
124576099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
124676099f98SAdrian Hunter		f.seek(sym_start + off)
124776099f98SAdrian Hunter		buf.value = f.read(buf_sz)
124876099f98SAdrian Hunter		buf_ptr = addressof(buf)
124976099f98SAdrian Hunter		i = 0
125076099f98SAdrian Hunter		while tot > 0:
125176099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
125276099f98SAdrian Hunter			if cnt:
125376099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
125476099f98SAdrian Hunter				for k in xrange(cnt):
125576099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
125676099f98SAdrian Hunter					i += 1
125776099f98SAdrian Hunter				while k < 15:
125876099f98SAdrian Hunter					byte_str += "   "
125976099f98SAdrian Hunter					k += 1
126076099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
126176099f98SAdrian Hunter				self.child_count += 1
126276099f98SAdrian Hunter			else:
126376099f98SAdrian Hunter				return
126476099f98SAdrian Hunter			buf_ptr += cnt
126576099f98SAdrian Hunter			tot -= cnt
126676099f98SAdrian Hunter			buf_sz -= cnt
126776099f98SAdrian Hunter			ip += cnt
126876099f98SAdrian Hunter
126976099f98SAdrian Hunter	def childCount(self):
127076099f98SAdrian Hunter		if not self.query_done:
127176099f98SAdrian Hunter			self.Select()
127276099f98SAdrian Hunter			if not self.child_count:
127376099f98SAdrian Hunter				return -1
127476099f98SAdrian Hunter		return self.child_count
127576099f98SAdrian Hunter
127676099f98SAdrian Hunter	def hasChildren(self):
127776099f98SAdrian Hunter		if not self.query_done:
127876099f98SAdrian Hunter			return True
127976099f98SAdrian Hunter		return self.child_count > 0
128076099f98SAdrian Hunter
128176099f98SAdrian Hunter	def getData(self, column):
128276099f98SAdrian Hunter		return self.data[column]
128376099f98SAdrian Hunter
128476099f98SAdrian Hunter# Brance data model root item
128576099f98SAdrian Hunter
128676099f98SAdrian Hunterclass BranchRootItem():
128776099f98SAdrian Hunter
128876099f98SAdrian Hunter	def __init__(self):
128976099f98SAdrian Hunter		self.child_count = 0
129076099f98SAdrian Hunter		self.child_items = []
129176099f98SAdrian Hunter		self.level = 0
129276099f98SAdrian Hunter
129376099f98SAdrian Hunter	def getChildItem(self, row):
129476099f98SAdrian Hunter		return self.child_items[row]
129576099f98SAdrian Hunter
129676099f98SAdrian Hunter	def getParentItem(self):
129776099f98SAdrian Hunter		return None
129876099f98SAdrian Hunter
129976099f98SAdrian Hunter	def getRow(self):
130076099f98SAdrian Hunter		return 0
130176099f98SAdrian Hunter
130276099f98SAdrian Hunter	def childCount(self):
130376099f98SAdrian Hunter		return self.child_count
130476099f98SAdrian Hunter
130576099f98SAdrian Hunter	def hasChildren(self):
130676099f98SAdrian Hunter		return self.child_count > 0
130776099f98SAdrian Hunter
130876099f98SAdrian Hunter	def getData(self, column):
130976099f98SAdrian Hunter		return ""
131076099f98SAdrian Hunter
131176099f98SAdrian Hunter# Branch data preparation
131276099f98SAdrian Hunter
131376099f98SAdrian Hunterdef BranchDataPrep(query):
131476099f98SAdrian Hunter	data = []
131576099f98SAdrian Hunter	for i in xrange(0, 8):
131676099f98SAdrian Hunter		data.append(query.value(i))
131776099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
131876099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
131976099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
132076099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
132176099f98SAdrian Hunter	return data
132276099f98SAdrian Hunter
132376099f98SAdrian Hunter# Branch data model
132476099f98SAdrian Hunter
132576099f98SAdrian Hunterclass BranchModel(TreeModel):
132676099f98SAdrian Hunter
132776099f98SAdrian Hunter	progress = Signal(object)
132876099f98SAdrian Hunter
132976099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
133076099f98SAdrian Hunter		super(BranchModel, self).__init__(BranchRootItem(), parent)
133176099f98SAdrian Hunter		self.glb = glb
133276099f98SAdrian Hunter		self.event_id = event_id
133376099f98SAdrian Hunter		self.more = True
133476099f98SAdrian Hunter		self.populated = 0
133576099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
133676099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
133776099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
133876099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
133976099f98SAdrian Hunter			" FROM samples"
134076099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
134176099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
134276099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
134376099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
134476099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
134576099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
134676099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
134776099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
134876099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
134976099f98SAdrian Hunter			" ORDER BY samples.id"
135076099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
135176099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
135276099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
135376099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
135476099f98SAdrian Hunter
135576099f98SAdrian Hunter	def columnCount(self, parent=None):
135676099f98SAdrian Hunter		return 8
135776099f98SAdrian Hunter
135876099f98SAdrian Hunter	def columnHeader(self, column):
135976099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
136076099f98SAdrian Hunter
136176099f98SAdrian Hunter	def columnFont(self, column):
136276099f98SAdrian Hunter		if column != 7:
136376099f98SAdrian Hunter			return None
136476099f98SAdrian Hunter		return QFont("Monospace")
136576099f98SAdrian Hunter
136676099f98SAdrian Hunter	def DisplayData(self, item, index):
136776099f98SAdrian Hunter		if item.level == 1:
136876099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
136976099f98SAdrian Hunter		return item.getData(index.column())
137076099f98SAdrian Hunter
137176099f98SAdrian Hunter	def AddSample(self, data):
137276099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
137376099f98SAdrian Hunter		self.root.child_items.append(child)
137476099f98SAdrian Hunter		self.populated += 1
137576099f98SAdrian Hunter
137676099f98SAdrian Hunter	def Update(self, fetched):
137776099f98SAdrian Hunter		if not fetched:
137876099f98SAdrian Hunter			self.more = False
137976099f98SAdrian Hunter			self.progress.emit(0)
138076099f98SAdrian Hunter		child_count = self.root.child_count
138176099f98SAdrian Hunter		count = self.populated - child_count
138276099f98SAdrian Hunter		if count > 0:
138376099f98SAdrian Hunter			parent = QModelIndex()
138476099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
138576099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
138676099f98SAdrian Hunter			self.root.child_count += count
138776099f98SAdrian Hunter			self.endInsertRows()
138876099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
138976099f98SAdrian Hunter
139076099f98SAdrian Hunter	def FetchMoreRecords(self, count):
139176099f98SAdrian Hunter		current = self.root.child_count
139276099f98SAdrian Hunter		if self.more:
139376099f98SAdrian Hunter			self.fetcher.Fetch(count)
139476099f98SAdrian Hunter		else:
139576099f98SAdrian Hunter			self.progress.emit(0)
139676099f98SAdrian Hunter		return current
139776099f98SAdrian Hunter
139876099f98SAdrian Hunter	def HasMoreRecords(self):
139976099f98SAdrian Hunter		return self.more
140076099f98SAdrian Hunter
140176099f98SAdrian Hunter# Branch window
140276099f98SAdrian Hunter
140376099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
140476099f98SAdrian Hunter
140576099f98SAdrian Hunter	def __init__(self, glb, event_id, name, where_clause, parent=None):
140676099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
140776099f98SAdrian Hunter
140876099f98SAdrian Hunter		model_name = "Branch Events " + str(event_id)
140976099f98SAdrian Hunter		if len(where_clause):
141076099f98SAdrian Hunter			model_name = where_clause + " " + model_name
141176099f98SAdrian Hunter
141276099f98SAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause))
141376099f98SAdrian Hunter
141476099f98SAdrian Hunter		self.view = QTreeView()
141576099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
141676099f98SAdrian Hunter		self.view.setModel(self.model)
141776099f98SAdrian Hunter
141876099f98SAdrian Hunter		self.ResizeColumnsToContents()
141976099f98SAdrian Hunter
142076099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
142176099f98SAdrian Hunter
142276099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
142376099f98SAdrian Hunter
142476099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
142576099f98SAdrian Hunter
142676099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
142776099f98SAdrian Hunter
142876099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
142976099f98SAdrian Hunter
143076099f98SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events")
143176099f98SAdrian Hunter
143276099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
143376099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
143476099f98SAdrian Hunter		# so implement a crude alternative
143576099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
143676099f98SAdrian Hunter		font = self.view.font()
143776099f98SAdrian Hunter		metrics = QFontMetrics(font)
143876099f98SAdrian Hunter		max = 0
143976099f98SAdrian Hunter		for row in xrange(n):
144076099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
144176099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
144276099f98SAdrian Hunter			max = len if len > max else max
144376099f98SAdrian Hunter		val = self.model.columnHeader(column)
144476099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
144576099f98SAdrian Hunter		max = len if len > max else max
144676099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
144776099f98SAdrian Hunter
144876099f98SAdrian Hunter	def ResizeColumnsToContents(self):
144976099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
145076099f98SAdrian Hunter		if n < 1:
145176099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
145276099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
145376099f98SAdrian Hunter			return
145476099f98SAdrian Hunter		columns = self.model.columnCount()
145576099f98SAdrian Hunter		for i in xrange(columns):
145676099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
145776099f98SAdrian Hunter
145876099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
145976099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
146076099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
146176099f98SAdrian Hunter		self.ResizeColumnsToContents()
146276099f98SAdrian Hunter
146376099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
146476099f98SAdrian Hunter		self.view.setFocus()
146576099f98SAdrian Hunter		self.find_bar.Busy()
146676099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
146776099f98SAdrian Hunter
146876099f98SAdrian Hunter	def FindDone(self, row):
146976099f98SAdrian Hunter		self.find_bar.Idle()
147076099f98SAdrian Hunter		if row >= 0:
147176099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
147276099f98SAdrian Hunter		else:
147376099f98SAdrian Hunter			self.find_bar.NotFound()
147476099f98SAdrian Hunter
1475*210cf1f9SAdrian Hunter# Dialog data item converted and validated using a SQL table
1476*210cf1f9SAdrian Hunter
1477*210cf1f9SAdrian Hunterclass SQLTableDialogDataItem():
1478*210cf1f9SAdrian Hunter
1479*210cf1f9SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
1480*210cf1f9SAdrian Hunter		self.glb = glb
1481*210cf1f9SAdrian Hunter		self.label = label
1482*210cf1f9SAdrian Hunter		self.placeholder_text = placeholder_text
1483*210cf1f9SAdrian Hunter		self.table_name = table_name
1484*210cf1f9SAdrian Hunter		self.match_column = match_column
1485*210cf1f9SAdrian Hunter		self.column_name1 = column_name1
1486*210cf1f9SAdrian Hunter		self.column_name2 = column_name2
1487*210cf1f9SAdrian Hunter		self.parent = parent
1488*210cf1f9SAdrian Hunter
1489*210cf1f9SAdrian Hunter		self.value = ""
1490*210cf1f9SAdrian Hunter
1491*210cf1f9SAdrian Hunter		self.widget = QLineEdit()
1492*210cf1f9SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
1493*210cf1f9SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
1494*210cf1f9SAdrian Hunter		self.red = False
1495*210cf1f9SAdrian Hunter		self.error = ""
1496*210cf1f9SAdrian Hunter		self.validated = True
1497*210cf1f9SAdrian Hunter
1498*210cf1f9SAdrian Hunter		self.last_id = 0
1499*210cf1f9SAdrian Hunter		self.first_time = 0
1500*210cf1f9SAdrian Hunter		self.last_time = 2 ** 64
1501*210cf1f9SAdrian Hunter		if self.table_name == "<timeranges>":
1502*210cf1f9SAdrian Hunter			query = QSqlQuery(self.glb.db)
1503*210cf1f9SAdrian Hunter			QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1504*210cf1f9SAdrian Hunter			if query.next():
1505*210cf1f9SAdrian Hunter				self.last_id = int(query.value(0))
1506*210cf1f9SAdrian Hunter				self.last_time = int(query.value(1))
1507*210cf1f9SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1508*210cf1f9SAdrian Hunter			if query.next():
1509*210cf1f9SAdrian Hunter				self.first_time = int(query.value(0))
1510*210cf1f9SAdrian Hunter			if placeholder_text:
1511*210cf1f9SAdrian Hunter				placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1512*210cf1f9SAdrian Hunter
1513*210cf1f9SAdrian Hunter		if placeholder_text:
1514*210cf1f9SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
1515*210cf1f9SAdrian Hunter
1516*210cf1f9SAdrian Hunter	def ValueToIds(self, value):
1517*210cf1f9SAdrian Hunter		ids = []
1518*210cf1f9SAdrian Hunter		query = QSqlQuery(self.glb.db)
1519*210cf1f9SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
1520*210cf1f9SAdrian Hunter		ret = query.exec_(stmt)
1521*210cf1f9SAdrian Hunter		if ret:
1522*210cf1f9SAdrian Hunter			while query.next():
1523*210cf1f9SAdrian Hunter				ids.append(str(query.value(0)))
1524*210cf1f9SAdrian Hunter		return ids
1525*210cf1f9SAdrian Hunter
1526*210cf1f9SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
1527*210cf1f9SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
1528*210cf1f9SAdrian Hunter		if query.next():
1529*210cf1f9SAdrian Hunter			return True, int(query.value(0))
1530*210cf1f9SAdrian Hunter		else:
1531*210cf1f9SAdrian Hunter			return False, 0
1532*210cf1f9SAdrian Hunter
1533*210cf1f9SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
1534*210cf1f9SAdrian Hunter		query = QSqlQuery(self.glb.db)
1535*210cf1f9SAdrian Hunter		while True:
1536*210cf1f9SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
1537*210cf1f9SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1538*210cf1f9SAdrian Hunter			if not query.next():
1539*210cf1f9SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
1540*210cf1f9SAdrian Hunter				if not ok:
1541*210cf1f9SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
1542*210cf1f9SAdrian Hunter					if not ok:
1543*210cf1f9SAdrian Hunter						return str(higher_id)
1544*210cf1f9SAdrian Hunter				next_id = dbid
1545*210cf1f9SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1546*210cf1f9SAdrian Hunter			next_time = int(query.value(0))
1547*210cf1f9SAdrian Hunter			if get_floor:
1548*210cf1f9SAdrian Hunter				if target_time > next_time:
1549*210cf1f9SAdrian Hunter					lower_id = next_id
1550*210cf1f9SAdrian Hunter				else:
1551*210cf1f9SAdrian Hunter					higher_id = next_id
1552*210cf1f9SAdrian Hunter				if higher_id <= lower_id + 1:
1553*210cf1f9SAdrian Hunter					return str(higher_id)
1554*210cf1f9SAdrian Hunter			else:
1555*210cf1f9SAdrian Hunter				if target_time >= next_time:
1556*210cf1f9SAdrian Hunter					lower_id = next_id
1557*210cf1f9SAdrian Hunter				else:
1558*210cf1f9SAdrian Hunter					higher_id = next_id
1559*210cf1f9SAdrian Hunter				if higher_id <= lower_id + 1:
1560*210cf1f9SAdrian Hunter					return str(lower_id)
1561*210cf1f9SAdrian Hunter
1562*210cf1f9SAdrian Hunter	def ConvertRelativeTime(self, val):
1563*210cf1f9SAdrian Hunter		print "val ", val
1564*210cf1f9SAdrian Hunter		mult = 1
1565*210cf1f9SAdrian Hunter		suffix = val[-2:]
1566*210cf1f9SAdrian Hunter		if suffix == "ms":
1567*210cf1f9SAdrian Hunter			mult = 1000000
1568*210cf1f9SAdrian Hunter		elif suffix == "us":
1569*210cf1f9SAdrian Hunter			mult = 1000
1570*210cf1f9SAdrian Hunter		elif suffix == "ns":
1571*210cf1f9SAdrian Hunter			mult = 1
1572*210cf1f9SAdrian Hunter		else:
1573*210cf1f9SAdrian Hunter			return val
1574*210cf1f9SAdrian Hunter		val = val[:-2].strip()
1575*210cf1f9SAdrian Hunter		if not self.IsNumber(val):
1576*210cf1f9SAdrian Hunter			return val
1577*210cf1f9SAdrian Hunter		val = int(val) * mult
1578*210cf1f9SAdrian Hunter		if val >= 0:
1579*210cf1f9SAdrian Hunter			val += self.first_time
1580*210cf1f9SAdrian Hunter		else:
1581*210cf1f9SAdrian Hunter			val += self.last_time
1582*210cf1f9SAdrian Hunter		return str(val)
1583*210cf1f9SAdrian Hunter
1584*210cf1f9SAdrian Hunter	def ConvertTimeRange(self, vrange):
1585*210cf1f9SAdrian Hunter		print "vrange ", vrange
1586*210cf1f9SAdrian Hunter		if vrange[0] == "":
1587*210cf1f9SAdrian Hunter			vrange[0] = str(self.first_time)
1588*210cf1f9SAdrian Hunter		if vrange[1] == "":
1589*210cf1f9SAdrian Hunter			vrange[1] = str(self.last_time)
1590*210cf1f9SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
1591*210cf1f9SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
1592*210cf1f9SAdrian Hunter		print "vrange2 ", vrange
1593*210cf1f9SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1594*210cf1f9SAdrian Hunter			return False
1595*210cf1f9SAdrian Hunter		print "ok1"
1596*210cf1f9SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
1597*210cf1f9SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
1598*210cf1f9SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
1599*210cf1f9SAdrian Hunter			return False
1600*210cf1f9SAdrian Hunter		print "ok2"
1601*210cf1f9SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
1602*210cf1f9SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
1603*210cf1f9SAdrian Hunter		print "vrange3 ", vrange
1604*210cf1f9SAdrian Hunter		return True
1605*210cf1f9SAdrian Hunter
1606*210cf1f9SAdrian Hunter	def AddTimeRange(self, value, ranges):
1607*210cf1f9SAdrian Hunter		print "value ", value
1608*210cf1f9SAdrian Hunter		n = value.count("-")
1609*210cf1f9SAdrian Hunter		if n == 1:
1610*210cf1f9SAdrian Hunter			pass
1611*210cf1f9SAdrian Hunter		elif n == 2:
1612*210cf1f9SAdrian Hunter			if value.split("-")[1].strip() == "":
1613*210cf1f9SAdrian Hunter				n = 1
1614*210cf1f9SAdrian Hunter		elif n == 3:
1615*210cf1f9SAdrian Hunter			n = 2
1616*210cf1f9SAdrian Hunter		else:
1617*210cf1f9SAdrian Hunter			return False
1618*210cf1f9SAdrian Hunter		pos = findnth(value, "-", n)
1619*210cf1f9SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
1620*210cf1f9SAdrian Hunter		if self.ConvertTimeRange(vrange):
1621*210cf1f9SAdrian Hunter			ranges.append(vrange)
1622*210cf1f9SAdrian Hunter			return True
1623*210cf1f9SAdrian Hunter		return False
1624*210cf1f9SAdrian Hunter
1625*210cf1f9SAdrian Hunter	def InvalidValue(self, value):
1626*210cf1f9SAdrian Hunter		self.value = ""
1627*210cf1f9SAdrian Hunter		palette = QPalette()
1628*210cf1f9SAdrian Hunter		palette.setColor(QPalette.Text,Qt.red)
1629*210cf1f9SAdrian Hunter		self.widget.setPalette(palette)
1630*210cf1f9SAdrian Hunter		self.red = True
1631*210cf1f9SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
1632*210cf1f9SAdrian Hunter		self.parent.ShowMessage(self.error)
1633*210cf1f9SAdrian Hunter
1634*210cf1f9SAdrian Hunter	def IsNumber(self, value):
1635*210cf1f9SAdrian Hunter		try:
1636*210cf1f9SAdrian Hunter			x = int(value)
1637*210cf1f9SAdrian Hunter		except:
1638*210cf1f9SAdrian Hunter			x = 0
1639*210cf1f9SAdrian Hunter		return str(x) == value
1640*210cf1f9SAdrian Hunter
1641*210cf1f9SAdrian Hunter	def Invalidate(self):
1642*210cf1f9SAdrian Hunter		self.validated = False
1643*210cf1f9SAdrian Hunter
1644*210cf1f9SAdrian Hunter	def Validate(self):
1645*210cf1f9SAdrian Hunter		input_string = self.widget.text()
1646*210cf1f9SAdrian Hunter		self.validated = True
1647*210cf1f9SAdrian Hunter		if self.red:
1648*210cf1f9SAdrian Hunter			palette = QPalette()
1649*210cf1f9SAdrian Hunter			self.widget.setPalette(palette)
1650*210cf1f9SAdrian Hunter			self.red = False
1651*210cf1f9SAdrian Hunter		if not len(input_string.strip()):
1652*210cf1f9SAdrian Hunter			self.error = ""
1653*210cf1f9SAdrian Hunter			self.value = ""
1654*210cf1f9SAdrian Hunter			return
1655*210cf1f9SAdrian Hunter		if self.table_name == "<timeranges>":
1656*210cf1f9SAdrian Hunter			ranges = []
1657*210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1658*210cf1f9SAdrian Hunter				if not self.AddTimeRange(value, ranges):
1659*210cf1f9SAdrian Hunter					return self.InvalidValue(value)
1660*210cf1f9SAdrian Hunter			ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1661*210cf1f9SAdrian Hunter			self.value = " OR ".join(ranges)
1662*210cf1f9SAdrian Hunter		elif self.table_name == "<ranges>":
1663*210cf1f9SAdrian Hunter			singles = []
1664*210cf1f9SAdrian Hunter			ranges = []
1665*210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1666*210cf1f9SAdrian Hunter				if "-" in value:
1667*210cf1f9SAdrian Hunter					vrange = value.split("-")
1668*210cf1f9SAdrian Hunter					if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1669*210cf1f9SAdrian Hunter						return self.InvalidValue(value)
1670*210cf1f9SAdrian Hunter					ranges.append(vrange)
1671*210cf1f9SAdrian Hunter				else:
1672*210cf1f9SAdrian Hunter					if not self.IsNumber(value):
1673*210cf1f9SAdrian Hunter						return self.InvalidValue(value)
1674*210cf1f9SAdrian Hunter					singles.append(value)
1675*210cf1f9SAdrian Hunter			ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1676*210cf1f9SAdrian Hunter			if len(singles):
1677*210cf1f9SAdrian Hunter				ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")")
1678*210cf1f9SAdrian Hunter			self.value = " OR ".join(ranges)
1679*210cf1f9SAdrian Hunter		elif self.table_name:
1680*210cf1f9SAdrian Hunter			all_ids = []
1681*210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1682*210cf1f9SAdrian Hunter				ids = self.ValueToIds(value)
1683*210cf1f9SAdrian Hunter				if len(ids):
1684*210cf1f9SAdrian Hunter					all_ids.extend(ids)
1685*210cf1f9SAdrian Hunter				else:
1686*210cf1f9SAdrian Hunter					return self.InvalidValue(value)
1687*210cf1f9SAdrian Hunter			self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1688*210cf1f9SAdrian Hunter			if self.column_name2:
1689*210cf1f9SAdrian Hunter				self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1690*210cf1f9SAdrian Hunter		else:
1691*210cf1f9SAdrian Hunter			self.value = input_string.strip()
1692*210cf1f9SAdrian Hunter		self.error = ""
1693*210cf1f9SAdrian Hunter		self.parent.ClearMessage()
1694*210cf1f9SAdrian Hunter
1695*210cf1f9SAdrian Hunter	def IsValid(self):
1696*210cf1f9SAdrian Hunter		if not self.validated:
1697*210cf1f9SAdrian Hunter			self.Validate()
1698*210cf1f9SAdrian Hunter		if len(self.error):
1699*210cf1f9SAdrian Hunter			self.parent.ShowMessage(self.error)
1700*210cf1f9SAdrian Hunter			return False
1701*210cf1f9SAdrian Hunter		return True
1702*210cf1f9SAdrian Hunter
1703*210cf1f9SAdrian Hunter# Selected branch report creation dialog
1704*210cf1f9SAdrian Hunter
1705*210cf1f9SAdrian Hunterclass SelectedBranchDialog(QDialog):
1706*210cf1f9SAdrian Hunter
1707*210cf1f9SAdrian Hunter	def __init__(self, glb, parent=None):
1708*210cf1f9SAdrian Hunter		super(SelectedBranchDialog, self).__init__(parent)
1709*210cf1f9SAdrian Hunter
1710*210cf1f9SAdrian Hunter		self.glb = glb
1711*210cf1f9SAdrian Hunter
1712*210cf1f9SAdrian Hunter		self.name = ""
1713*210cf1f9SAdrian Hunter		self.where_clause = ""
1714*210cf1f9SAdrian Hunter
1715*210cf1f9SAdrian Hunter		self.setWindowTitle("Selected Branches")
1716*210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
1717*210cf1f9SAdrian Hunter
1718*210cf1f9SAdrian Hunter		items = (
1719*210cf1f9SAdrian Hunter			("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""),
1720*210cf1f9SAdrian Hunter			("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""),
1721*210cf1f9SAdrian Hunter			("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""),
1722*210cf1f9SAdrian Hunter			("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""),
1723*210cf1f9SAdrian Hunter			("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""),
1724*210cf1f9SAdrian Hunter			("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""),
1725*210cf1f9SAdrian Hunter			("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"),
1726*210cf1f9SAdrian Hunter			("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"),
1727*210cf1f9SAdrian Hunter			("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""),
1728*210cf1f9SAdrian Hunter			)
1729*210cf1f9SAdrian Hunter		self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items]
1730*210cf1f9SAdrian Hunter
1731*210cf1f9SAdrian Hunter		self.grid = QGridLayout()
1732*210cf1f9SAdrian Hunter
1733*210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
1734*210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1735*210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
1736*210cf1f9SAdrian Hunter
1737*210cf1f9SAdrian Hunter		self.status = QLabel()
1738*210cf1f9SAdrian Hunter
1739*210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
1740*210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
1741*210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
1742*210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1743*210cf1f9SAdrian Hunter
1744*210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
1745*210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
1746*210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1747*210cf1f9SAdrian Hunter
1748*210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
1749*210cf1f9SAdrian Hunter		#self.hbox.addStretch()
1750*210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
1751*210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
1752*210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
1753*210cf1f9SAdrian Hunter
1754*210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
1755*210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
1756*210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
1757*210cf1f9SAdrian Hunter
1758*210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
1759*210cf1f9SAdrian Hunter
1760*210cf1f9SAdrian Hunter	def Ok(self):
1761*210cf1f9SAdrian Hunter		self.name = self.data_items[0].value
1762*210cf1f9SAdrian Hunter		if not self.name:
1763*210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
1764*210cf1f9SAdrian Hunter			return
1765*210cf1f9SAdrian Hunter		for d in self.data_items:
1766*210cf1f9SAdrian Hunter			if not d.IsValid():
1767*210cf1f9SAdrian Hunter				return
1768*210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
1769*210cf1f9SAdrian Hunter			if len(d.value):
1770*210cf1f9SAdrian Hunter				if len(self.where_clause):
1771*210cf1f9SAdrian Hunter					self.where_clause += " AND "
1772*210cf1f9SAdrian Hunter				self.where_clause += d.value
1773*210cf1f9SAdrian Hunter		if len(self.where_clause):
1774*210cf1f9SAdrian Hunter			self.where_clause = " AND ( " + self.where_clause + " ) "
1775*210cf1f9SAdrian Hunter		else:
1776*210cf1f9SAdrian Hunter			self.ShowMessage("No selection")
1777*210cf1f9SAdrian Hunter			return
1778*210cf1f9SAdrian Hunter		self.accept()
1779*210cf1f9SAdrian Hunter
1780*210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
1781*210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
1782*210cf1f9SAdrian Hunter
1783*210cf1f9SAdrian Hunter	def ClearMessage(self):
1784*210cf1f9SAdrian Hunter		self.status.setText("")
1785*210cf1f9SAdrian Hunter
178676099f98SAdrian Hunter# Event list
178776099f98SAdrian Hunter
178876099f98SAdrian Hunterdef GetEventList(db):
178976099f98SAdrian Hunter	events = []
179076099f98SAdrian Hunter	query = QSqlQuery(db)
179176099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
179276099f98SAdrian Hunter	while query.next():
179376099f98SAdrian Hunter		events.append(query.value(0))
179476099f98SAdrian Hunter	return events
179576099f98SAdrian Hunter
17968392b74bSAdrian Hunter# SQL data preparation
17978392b74bSAdrian Hunter
17988392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
17998392b74bSAdrian Hunter	data = []
18008392b74bSAdrian Hunter	for i in xrange(count):
18018392b74bSAdrian Hunter		data.append(query.value(i))
18028392b74bSAdrian Hunter	return data
18038392b74bSAdrian Hunter
18048392b74bSAdrian Hunter# SQL table data model item
18058392b74bSAdrian Hunter
18068392b74bSAdrian Hunterclass SQLTableItem():
18078392b74bSAdrian Hunter
18088392b74bSAdrian Hunter	def __init__(self, row, data):
18098392b74bSAdrian Hunter		self.row = row
18108392b74bSAdrian Hunter		self.data = data
18118392b74bSAdrian Hunter
18128392b74bSAdrian Hunter	def getData(self, column):
18138392b74bSAdrian Hunter		return self.data[column]
18148392b74bSAdrian Hunter
18158392b74bSAdrian Hunter# SQL table data model
18168392b74bSAdrian Hunter
18178392b74bSAdrian Hunterclass SQLTableModel(TableModel):
18188392b74bSAdrian Hunter
18198392b74bSAdrian Hunter	progress = Signal(object)
18208392b74bSAdrian Hunter
18218392b74bSAdrian Hunter	def __init__(self, glb, sql, column_count, parent=None):
18228392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
18238392b74bSAdrian Hunter		self.glb = glb
18248392b74bSAdrian Hunter		self.more = True
18258392b74bSAdrian Hunter		self.populated = 0
18268392b74bSAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample)
18278392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
18288392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
18298392b74bSAdrian Hunter
18308392b74bSAdrian Hunter	def DisplayData(self, item, index):
18318392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
18328392b74bSAdrian Hunter		return item.getData(index.column())
18338392b74bSAdrian Hunter
18348392b74bSAdrian Hunter	def AddSample(self, data):
18358392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
18368392b74bSAdrian Hunter		self.child_items.append(child)
18378392b74bSAdrian Hunter		self.populated += 1
18388392b74bSAdrian Hunter
18398392b74bSAdrian Hunter	def Update(self, fetched):
18408392b74bSAdrian Hunter		if not fetched:
18418392b74bSAdrian Hunter			self.more = False
18428392b74bSAdrian Hunter			self.progress.emit(0)
18438392b74bSAdrian Hunter		child_count = self.child_count
18448392b74bSAdrian Hunter		count = self.populated - child_count
18458392b74bSAdrian Hunter		if count > 0:
18468392b74bSAdrian Hunter			parent = QModelIndex()
18478392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
18488392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
18498392b74bSAdrian Hunter			self.child_count += count
18508392b74bSAdrian Hunter			self.endInsertRows()
18518392b74bSAdrian Hunter			self.progress.emit(self.child_count)
18528392b74bSAdrian Hunter
18538392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
18548392b74bSAdrian Hunter		current = self.child_count
18558392b74bSAdrian Hunter		if self.more:
18568392b74bSAdrian Hunter			self.fetcher.Fetch(count)
18578392b74bSAdrian Hunter		else:
18588392b74bSAdrian Hunter			self.progress.emit(0)
18598392b74bSAdrian Hunter		return current
18608392b74bSAdrian Hunter
18618392b74bSAdrian Hunter	def HasMoreRecords(self):
18628392b74bSAdrian Hunter		return self.more
18638392b74bSAdrian Hunter
18648392b74bSAdrian Hunter# SQL automatic table data model
18658392b74bSAdrian Hunter
18668392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
18678392b74bSAdrian Hunter
18688392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
18698392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
18708392b74bSAdrian Hunter		if table_name == "comm_threads_view":
18718392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
18728392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
18738392b74bSAdrian Hunter		self.column_headers = []
18748392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
18758392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
18768392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
18778392b74bSAdrian Hunter			while query.next():
18788392b74bSAdrian Hunter				self.column_headers.append(query.value(1))
18798392b74bSAdrian Hunter			if table_name == "sqlite_master":
18808392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
18818392b74bSAdrian Hunter		else:
18828392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
18838392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
18848392b74bSAdrian Hunter				select_table_name = table_name[19:]
18858392b74bSAdrian Hunter				schema = "information_schema"
18868392b74bSAdrian Hunter			else:
18878392b74bSAdrian Hunter				select_table_name = table_name
18888392b74bSAdrian Hunter				schema = "public"
18898392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
18908392b74bSAdrian Hunter			while query.next():
18918392b74bSAdrian Hunter				self.column_headers.append(query.value(0))
18928392b74bSAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent)
18938392b74bSAdrian Hunter
18948392b74bSAdrian Hunter	def columnCount(self, parent=None):
18958392b74bSAdrian Hunter		return len(self.column_headers)
18968392b74bSAdrian Hunter
18978392b74bSAdrian Hunter	def columnHeader(self, column):
18988392b74bSAdrian Hunter		return self.column_headers[column]
18998392b74bSAdrian Hunter
19008392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
19018392b74bSAdrian Hunter
19028392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
19038392b74bSAdrian Hunter
19048392b74bSAdrian Hunter	def __init__(self, parent=None):
19058392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
19068392b74bSAdrian Hunter
19078392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
19088392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
19098392b74bSAdrian Hunter		# so implement a crude alternative
19108392b74bSAdrian Hunter		font = self.view.font()
19118392b74bSAdrian Hunter		metrics = QFontMetrics(font)
19128392b74bSAdrian Hunter		max = 0
19138392b74bSAdrian Hunter		for row in xrange(n):
19148392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
19158392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
19168392b74bSAdrian Hunter			max = len if len > max else max
19178392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
19188392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
19198392b74bSAdrian Hunter		max = len if len > max else max
19208392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
19218392b74bSAdrian Hunter
19228392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
19238392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
19248392b74bSAdrian Hunter		if n < 1:
19258392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
19268392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
19278392b74bSAdrian Hunter			return
19288392b74bSAdrian Hunter		columns = self.data_model.columnCount()
19298392b74bSAdrian Hunter		for i in xrange(columns):
19308392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
19318392b74bSAdrian Hunter
19328392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
19338392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
19348392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
19358392b74bSAdrian Hunter		self.ResizeColumnsToContents()
19368392b74bSAdrian Hunter
19378392b74bSAdrian Hunter# Table window
19388392b74bSAdrian Hunter
19398392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
19408392b74bSAdrian Hunter
19418392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
19428392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
19438392b74bSAdrian Hunter
19448392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
19458392b74bSAdrian Hunter
19468392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
19478392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
19488392b74bSAdrian Hunter
19498392b74bSAdrian Hunter		self.view = QTableView()
19508392b74bSAdrian Hunter		self.view.setModel(self.model)
19518392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
19528392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
19538392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
19548392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
19558392b74bSAdrian Hunter
19568392b74bSAdrian Hunter		self.ResizeColumnsToContents()
19578392b74bSAdrian Hunter
19588392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
19598392b74bSAdrian Hunter
19608392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
19618392b74bSAdrian Hunter
19628392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
19638392b74bSAdrian Hunter
19648392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
19658392b74bSAdrian Hunter
19668392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
19678392b74bSAdrian Hunter
19688392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
19698392b74bSAdrian Hunter
19708392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
19718392b74bSAdrian Hunter		self.view.setFocus()
19728392b74bSAdrian Hunter		self.find_bar.Busy()
19738392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
19748392b74bSAdrian Hunter
19758392b74bSAdrian Hunter	def FindDone(self, row):
19768392b74bSAdrian Hunter		self.find_bar.Idle()
19778392b74bSAdrian Hunter		if row >= 0:
19788392b74bSAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
19798392b74bSAdrian Hunter		else:
19808392b74bSAdrian Hunter			self.find_bar.NotFound()
19818392b74bSAdrian Hunter
19828392b74bSAdrian Hunter# Table list
19838392b74bSAdrian Hunter
19848392b74bSAdrian Hunterdef GetTableList(glb):
19858392b74bSAdrian Hunter	tables = []
19868392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
19878392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
19888392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
19898392b74bSAdrian Hunter	else:
19908392b74bSAdrian 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")
19918392b74bSAdrian Hunter	while query.next():
19928392b74bSAdrian Hunter		tables.append(query.value(0))
19938392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
19948392b74bSAdrian Hunter		tables.append("sqlite_master")
19958392b74bSAdrian Hunter	else:
19968392b74bSAdrian Hunter		tables.append("information_schema.tables")
19978392b74bSAdrian Hunter		tables.append("information_schema.views")
19988392b74bSAdrian Hunter		tables.append("information_schema.columns")
19998392b74bSAdrian Hunter	return tables
20008392b74bSAdrian Hunter
20011beb5c7bSAdrian Hunter# Action Definition
20021beb5c7bSAdrian Hunter
20031beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
20041beb5c7bSAdrian Hunter	action = QAction(label, parent)
20051beb5c7bSAdrian Hunter	if shortcut != None:
20061beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
20071beb5c7bSAdrian Hunter	action.setStatusTip(tip)
20081beb5c7bSAdrian Hunter	action.triggered.connect(callback)
20091beb5c7bSAdrian Hunter	return action
20101beb5c7bSAdrian Hunter
20111beb5c7bSAdrian Hunter# Typical application actions
20121beb5c7bSAdrian Hunter
20131beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
20141beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
20151beb5c7bSAdrian Hunter
20161beb5c7bSAdrian Hunter# Typical MDI actions
20171beb5c7bSAdrian Hunter
20181beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
20191beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
20201beb5c7bSAdrian Hunter
20211beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
20221beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
20231beb5c7bSAdrian Hunter
20241beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
20251beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
20261beb5c7bSAdrian Hunter
20271beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
20281beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
20291beb5c7bSAdrian Hunter
20301beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
20311beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
20321beb5c7bSAdrian Hunter
20331beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
20341beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
20351beb5c7bSAdrian Hunter
20361beb5c7bSAdrian Hunter# Typical MDI window menu
20371beb5c7bSAdrian Hunter
20381beb5c7bSAdrian Hunterclass WindowMenu():
20391beb5c7bSAdrian Hunter
20401beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
20411beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
20421beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
20431beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
20441beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
20451beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
20461beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
20471beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
20481beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
20491beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
20501beb5c7bSAdrian Hunter
20511beb5c7bSAdrian Hunter	def Update(self):
20521beb5c7bSAdrian Hunter		self.window_menu.clear()
20531beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
20541beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
20551beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
20561beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
20571beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
20581beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
20591beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
20601beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
20611beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
20621beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
20631beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
20641beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
20651beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
20661beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
20671beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
20681beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
20691beb5c7bSAdrian Hunter		if sub_window_count == 0:
20701beb5c7bSAdrian Hunter			return
20711beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
20721beb5c7bSAdrian Hunter		nr = 1
20731beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
20741beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
20751beb5c7bSAdrian Hunter			if nr < 10:
20761beb5c7bSAdrian Hunter				label = "&" + label
20771beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
20781beb5c7bSAdrian Hunter			action.setCheckable(True)
20791beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
20801beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
20811beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
20821beb5c7bSAdrian Hunter			nr += 1
20831beb5c7bSAdrian Hunter
20841beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
20851beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
20861beb5c7bSAdrian Hunter
208782f68e28SAdrian Hunter# Font resize
208882f68e28SAdrian Hunter
208982f68e28SAdrian Hunterdef ResizeFont(widget, diff):
209082f68e28SAdrian Hunter	font = widget.font()
209182f68e28SAdrian Hunter	sz = font.pointSize()
209282f68e28SAdrian Hunter	font.setPointSize(sz + diff)
209382f68e28SAdrian Hunter	widget.setFont(font)
209482f68e28SAdrian Hunter
209582f68e28SAdrian Hunterdef ShrinkFont(widget):
209682f68e28SAdrian Hunter	ResizeFont(widget, -1)
209782f68e28SAdrian Hunter
209882f68e28SAdrian Hunterdef EnlargeFont(widget):
209982f68e28SAdrian Hunter	ResizeFont(widget, 1)
210082f68e28SAdrian Hunter
21011beb5c7bSAdrian Hunter# Unique name for sub-windows
21021beb5c7bSAdrian Hunter
21031beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
21041beb5c7bSAdrian Hunter	if nr > 1:
21051beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
21061beb5c7bSAdrian Hunter	return name
21071beb5c7bSAdrian Hunter
21081beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
21091beb5c7bSAdrian Hunter	nr = 1
21101beb5c7bSAdrian Hunter	while True:
21111beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
21121beb5c7bSAdrian Hunter		ok = True
21131beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
21141beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
21151beb5c7bSAdrian Hunter				ok = False
21161beb5c7bSAdrian Hunter				break
21171beb5c7bSAdrian Hunter		if ok:
21181beb5c7bSAdrian Hunter			return unique_name
21191beb5c7bSAdrian Hunter		nr += 1
21201beb5c7bSAdrian Hunter
21211beb5c7bSAdrian Hunter# Add a sub-window
21221beb5c7bSAdrian Hunter
21231beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
21241beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
21251beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
21261beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
21271beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
21281beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
21291beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
21301beb5c7bSAdrian Hunter	sub_window.name = unique_name
21311beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
21321beb5c7bSAdrian Hunter	sub_window.show()
21331beb5c7bSAdrian Hunter
2134031c2a00SAdrian Hunter# Main window
2135031c2a00SAdrian Hunter
2136031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
2137031c2a00SAdrian Hunter
2138031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
2139031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
2140031c2a00SAdrian Hunter
2141031c2a00SAdrian Hunter		self.glb = glb
2142031c2a00SAdrian Hunter
21431beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
2144031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
2145031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
2146031c2a00SAdrian Hunter
21471beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
21481beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
21491beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
2150031c2a00SAdrian Hunter
21511beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
2152031c2a00SAdrian Hunter
21531beb5c7bSAdrian Hunter		menu = self.menuBar()
2154031c2a00SAdrian Hunter
21551beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
21561beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
21571beb5c7bSAdrian Hunter
2158ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
2159ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
21608392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
216182f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
216282f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2163ebd70c7dSAdrian Hunter
21641beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
21651beb5c7bSAdrian Hunter		reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
21661beb5c7bSAdrian Hunter
216776099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
216876099f98SAdrian Hunter
21698392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
21708392b74bSAdrian Hunter
21711beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
21721beb5c7bSAdrian Hunter
2173ebd70c7dSAdrian Hunter	def Find(self):
2174ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
2175ebd70c7dSAdrian Hunter		if win:
2176ebd70c7dSAdrian Hunter			try:
2177ebd70c7dSAdrian Hunter				win.find_bar.Activate()
2178ebd70c7dSAdrian Hunter			except:
2179ebd70c7dSAdrian Hunter				pass
2180ebd70c7dSAdrian Hunter
21818392b74bSAdrian Hunter	def FetchMoreRecords(self):
21828392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
21838392b74bSAdrian Hunter		if win:
21848392b74bSAdrian Hunter			try:
21858392b74bSAdrian Hunter				win.fetch_bar.Activate()
21868392b74bSAdrian Hunter			except:
21878392b74bSAdrian Hunter				pass
21888392b74bSAdrian Hunter
218982f68e28SAdrian Hunter	def ShrinkFont(self):
219082f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
219182f68e28SAdrian Hunter		ShrinkFont(win.view)
219282f68e28SAdrian Hunter
219382f68e28SAdrian Hunter	def EnlargeFont(self):
219482f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
219582f68e28SAdrian Hunter		EnlargeFont(win.view)
219682f68e28SAdrian Hunter
219776099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
219876099f98SAdrian Hunter		branches_events = 0
219976099f98SAdrian Hunter		for event in events:
220076099f98SAdrian Hunter			event = event.split(":")[0]
220176099f98SAdrian Hunter			if event == "branches":
220276099f98SAdrian Hunter				branches_events += 1
220376099f98SAdrian Hunter		dbid = 0
220476099f98SAdrian Hunter		for event in events:
220576099f98SAdrian Hunter			dbid += 1
220676099f98SAdrian Hunter			event = event.split(":")[0]
220776099f98SAdrian Hunter			if event == "branches":
220876099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
220976099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2210*210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2211*210cf1f9SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
221276099f98SAdrian Hunter
22138392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
22148392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
22158392b74bSAdrian Hunter		for table in tables:
22168392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
22178392b74bSAdrian Hunter
22181beb5c7bSAdrian Hunter	def NewCallGraph(self):
22191beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
2220031c2a00SAdrian Hunter
222176099f98SAdrian Hunter	def NewBranchView(self, event_id):
222276099f98SAdrian Hunter		BranchWindow(self.glb, event_id, "", "", self)
222376099f98SAdrian Hunter
2224*210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
2225*210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
2226*210cf1f9SAdrian Hunter		ret = dialog.exec_()
2227*210cf1f9SAdrian Hunter		if ret:
2228*210cf1f9SAdrian Hunter			BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self)
2229*210cf1f9SAdrian Hunter
22308392b74bSAdrian Hunter	def NewTableView(self, table_name):
22318392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
22328392b74bSAdrian Hunter
223376099f98SAdrian Hunter# XED Disassembler
223476099f98SAdrian Hunter
223576099f98SAdrian Hunterclass xed_state_t(Structure):
223676099f98SAdrian Hunter
223776099f98SAdrian Hunter	_fields_ = [
223876099f98SAdrian Hunter		("mode", c_int),
223976099f98SAdrian Hunter		("width", c_int)
224076099f98SAdrian Hunter	]
224176099f98SAdrian Hunter
224276099f98SAdrian Hunterclass XEDInstruction():
224376099f98SAdrian Hunter
224476099f98SAdrian Hunter	def __init__(self, libxed):
224576099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
224676099f98SAdrian Hunter		xedd_t = c_byte * 512
224776099f98SAdrian Hunter		self.xedd = xedd_t()
224876099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
224976099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
225076099f98SAdrian Hunter		self.state = xed_state_t()
225176099f98SAdrian Hunter		self.statep = addressof(self.state)
225276099f98SAdrian Hunter		# Buffer for disassembled instruction text
225376099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
225476099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
225576099f98SAdrian Hunter
225676099f98SAdrian Hunterclass LibXED():
225776099f98SAdrian Hunter
225876099f98SAdrian Hunter	def __init__(self):
22595ed4419dSAdrian Hunter		try:
226076099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
22615ed4419dSAdrian Hunter		except:
22625ed4419dSAdrian Hunter			self.libxed = None
22635ed4419dSAdrian Hunter		if not self.libxed:
22645ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
226576099f98SAdrian Hunter
226676099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
226776099f98SAdrian Hunter		self.xed_tables_init.restype = None
226876099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
226976099f98SAdrian Hunter
227076099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
227176099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
227276099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
227376099f98SAdrian Hunter
227476099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
227576099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
227676099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
227776099f98SAdrian Hunter
227876099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
227976099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
228076099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
228176099f98SAdrian Hunter
228276099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
228376099f98SAdrian Hunter		self.xed_decode.restype = c_int
228476099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
228576099f98SAdrian Hunter
228676099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
228776099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
228876099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
228976099f98SAdrian Hunter
229076099f98SAdrian Hunter		self.xed_tables_init()
229176099f98SAdrian Hunter
229276099f98SAdrian Hunter	def Instruction(self):
229376099f98SAdrian Hunter		return XEDInstruction(self)
229476099f98SAdrian Hunter
229576099f98SAdrian Hunter	def SetMode(self, inst, mode):
229676099f98SAdrian Hunter		if mode:
229776099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
229876099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
229976099f98SAdrian Hunter		else:
230076099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
230176099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
230276099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
230376099f98SAdrian Hunter
230476099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
230576099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
230676099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
230776099f98SAdrian Hunter		if err:
230876099f98SAdrian Hunter			return 0, ""
230976099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
231076099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
231176099f98SAdrian Hunter		if not ok:
231276099f98SAdrian Hunter			return 0, ""
231376099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
231476099f98SAdrian Hunter		# For now, assume the length is in byte 166
231576099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
231676099f98SAdrian Hunter
231776099f98SAdrian Hunterdef TryOpen(file_name):
231876099f98SAdrian Hunter	try:
231976099f98SAdrian Hunter		return open(file_name, "rb")
232076099f98SAdrian Hunter	except:
232176099f98SAdrian Hunter		return None
232276099f98SAdrian Hunter
232376099f98SAdrian Hunterdef Is64Bit(f):
232476099f98SAdrian Hunter	result = sizeof(c_void_p)
232576099f98SAdrian Hunter	# ELF support only
232676099f98SAdrian Hunter	pos = f.tell()
232776099f98SAdrian Hunter	f.seek(0)
232876099f98SAdrian Hunter	header = f.read(7)
232976099f98SAdrian Hunter	f.seek(pos)
233076099f98SAdrian Hunter	magic = header[0:4]
233176099f98SAdrian Hunter	eclass = ord(header[4])
233276099f98SAdrian Hunter	encoding = ord(header[5])
233376099f98SAdrian Hunter	version = ord(header[6])
233476099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
233576099f98SAdrian Hunter		result = True if eclass == 2 else False
233676099f98SAdrian Hunter	return result
233776099f98SAdrian Hunter
2338031c2a00SAdrian Hunter# Global data
2339031c2a00SAdrian Hunter
2340031c2a00SAdrian Hunterclass Glb():
2341031c2a00SAdrian Hunter
2342031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2343031c2a00SAdrian Hunter		self.dbref = dbref
2344031c2a00SAdrian Hunter		self.db = db
2345031c2a00SAdrian Hunter		self.dbname = dbname
234676099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
234776099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
234876099f98SAdrian Hunter		if self.buildid_dir:
234976099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
235076099f98SAdrian Hunter		else:
235176099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2352031c2a00SAdrian Hunter		self.app = None
2353031c2a00SAdrian Hunter		self.mainwindow = None
23548392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
235576099f98SAdrian Hunter		try:
235676099f98SAdrian Hunter			self.disassembler = LibXED()
235776099f98SAdrian Hunter			self.have_disassembler = True
235876099f98SAdrian Hunter		except:
235976099f98SAdrian Hunter			self.have_disassembler = False
236076099f98SAdrian Hunter
236176099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
236276099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
236376099f98SAdrian Hunter		return TryOpen(file_name)
236476099f98SAdrian Hunter
236576099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
236676099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
236776099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
236876099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
236976099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
237076099f98SAdrian Hunter			if f:
237176099f98SAdrian Hunter				return f
237276099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
237376099f98SAdrian Hunter			f = TryOpen(long_name)
237476099f98SAdrian Hunter			if f:
237576099f98SAdrian Hunter				return f
237676099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
237776099f98SAdrian Hunter		if f:
237876099f98SAdrian Hunter			return f
237976099f98SAdrian Hunter		return None
23808392b74bSAdrian Hunter
23818392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
23828392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
23838392b74bSAdrian Hunter
23848392b74bSAdrian Hunter	# Shutdown any background processes or threads
23858392b74bSAdrian Hunter	def ShutdownInstances(self):
23868392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
23878392b74bSAdrian Hunter			try:
23888392b74bSAdrian Hunter				x.Shutdown()
23898392b74bSAdrian Hunter			except:
23908392b74bSAdrian Hunter				pass
2391031c2a00SAdrian Hunter
2392031c2a00SAdrian Hunter# Database reference
2393031c2a00SAdrian Hunter
2394031c2a00SAdrian Hunterclass DBRef():
2395031c2a00SAdrian Hunter
2396031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2397031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2398031c2a00SAdrian Hunter		self.dbname = dbname
2399031c2a00SAdrian Hunter
2400031c2a00SAdrian Hunter	def Open(self, connection_name):
2401031c2a00SAdrian Hunter		dbname = self.dbname
2402031c2a00SAdrian Hunter		if self.is_sqlite3:
2403031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2404031c2a00SAdrian Hunter		else:
2405031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2406031c2a00SAdrian Hunter			opts = dbname.split()
2407031c2a00SAdrian Hunter			for opt in opts:
2408031c2a00SAdrian Hunter				if "=" in opt:
2409031c2a00SAdrian Hunter					opt = opt.split("=")
2410031c2a00SAdrian Hunter					if opt[0] == "hostname":
2411031c2a00SAdrian Hunter						db.setHostName(opt[1])
2412031c2a00SAdrian Hunter					elif opt[0] == "port":
2413031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2414031c2a00SAdrian Hunter					elif opt[0] == "username":
2415031c2a00SAdrian Hunter						db.setUserName(opt[1])
2416031c2a00SAdrian Hunter					elif opt[0] == "password":
2417031c2a00SAdrian Hunter						db.setPassword(opt[1])
2418031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2419031c2a00SAdrian Hunter						dbname = opt[1]
2420031c2a00SAdrian Hunter				else:
2421031c2a00SAdrian Hunter					dbname = opt
2422031c2a00SAdrian Hunter
2423031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2424031c2a00SAdrian Hunter		if not db.open():
2425031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2426031c2a00SAdrian Hunter		return db, dbname
2427031c2a00SAdrian Hunter
2428031c2a00SAdrian Hunter# Main
2429031c2a00SAdrian Hunter
2430031c2a00SAdrian Hunterdef Main():
2431031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
2432031c2a00SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>"
2433031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2434031c2a00SAdrian Hunter
2435031c2a00SAdrian Hunter	dbname = sys.argv[1]
2436031c2a00SAdrian Hunter
2437031c2a00SAdrian Hunter	is_sqlite3 = False
2438031c2a00SAdrian Hunter	try:
2439031c2a00SAdrian Hunter		f = open(dbname)
2440031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
2441031c2a00SAdrian Hunter			is_sqlite3 = True
2442031c2a00SAdrian Hunter		f.close()
2443031c2a00SAdrian Hunter	except:
2444031c2a00SAdrian Hunter		pass
2445031c2a00SAdrian Hunter
2446031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
2447031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
2448031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
2449031c2a00SAdrian Hunter	app = QApplication(sys.argv)
2450031c2a00SAdrian Hunter	glb.app = app
2451031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
2452031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
2453031c2a00SAdrian Hunter	mainwindow.show()
2454031c2a00SAdrian Hunter	err = app.exec_()
24558392b74bSAdrian Hunter	glb.ShutdownInstances()
2456031c2a00SAdrian Hunter	db.close()
2457031c2a00SAdrian Hunter	sys.exit(err)
2458031c2a00SAdrian Hunter
2459031c2a00SAdrian Hunterif __name__ == "__main__":
2460031c2a00SAdrian Hunter	Main()
2461