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
49*76099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
50*76099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
51*76099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
52*76099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
53*76099f98SAdrian Hunter# libxed.so:
54*76099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
55*76099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
56*76099f98SAdrian Hunter#            cd xed
57*76099f98SAdrian Hunter#            ./mfile.py --share
58*76099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
59*76099f98SAdrian Hunter#            sudo ldconfig
60*76099f98SAdrian Hunter#
61*76099f98SAdrian Hunter# Example report:
62*76099f98SAdrian Hunter#
63*76099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
64*76099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
65*76099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
66*76099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
67*76099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
68*76099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
69*76099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
70*76099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
71*76099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
72*76099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
73*76099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
74*76099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
75*76099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
76*76099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
77*76099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
78*76099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
79*76099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
80*76099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
81*76099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
82*76099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
83*76099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
84*76099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
85*76099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
86*76099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
87*76099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
88*76099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
89*76099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
90*76099f98SAdrian 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
107*76099f98SAdrian Hunterdef tohex(ip):
108*76099f98SAdrian Hunter	if ip < 0:
109*76099f98SAdrian Hunter		ip += 1 << 64
110*76099f98SAdrian Hunter	return "%x" % ip
111*76099f98SAdrian Hunter
112*76099f98SAdrian Hunterdef offstr(offset):
113*76099f98SAdrian Hunter	if offset:
114*76099f98SAdrian Hunter		return "+0x%x" % offset
115*76099f98SAdrian Hunter	return ""
116*76099f98SAdrian Hunter
117031c2a00SAdrian Hunterdef dsoname(name):
118031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
119031c2a00SAdrian Hunter		return "[kernel]"
120031c2a00SAdrian Hunter	return name
121031c2a00SAdrian Hunter
122031c2a00SAdrian Hunter# Percent to one decimal place
123031c2a00SAdrian Hunter
124031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
125031c2a00SAdrian Hunter	if not d:
126031c2a00SAdrian Hunter		return "0.0"
127031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
128031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
129031c2a00SAdrian Hunter
130031c2a00SAdrian Hunter# Helper for queries that must not fail
131031c2a00SAdrian Hunter
132031c2a00SAdrian Hunterdef QueryExec(query, stmt):
133031c2a00SAdrian Hunter	ret = query.exec_(stmt)
134031c2a00SAdrian Hunter	if not ret:
135031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
136031c2a00SAdrian Hunter
137ebd70c7dSAdrian Hunter# Background thread
138ebd70c7dSAdrian Hunter
139ebd70c7dSAdrian Hunterclass Thread(QThread):
140ebd70c7dSAdrian Hunter
141ebd70c7dSAdrian Hunter	done = Signal(object)
142ebd70c7dSAdrian Hunter
143ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
144ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
145ebd70c7dSAdrian Hunter		self.task = task
146ebd70c7dSAdrian Hunter		self.param = param
147ebd70c7dSAdrian Hunter
148ebd70c7dSAdrian Hunter	def run(self):
149ebd70c7dSAdrian Hunter		while True:
150ebd70c7dSAdrian Hunter			if self.param is None:
151ebd70c7dSAdrian Hunter				done, result = self.task()
152ebd70c7dSAdrian Hunter			else:
153ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
154ebd70c7dSAdrian Hunter			self.done.emit(result)
155ebd70c7dSAdrian Hunter			if done:
156ebd70c7dSAdrian Hunter				break
157ebd70c7dSAdrian Hunter
158031c2a00SAdrian Hunter# Tree data model
159031c2a00SAdrian Hunter
160031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
161031c2a00SAdrian Hunter
162031c2a00SAdrian Hunter	def __init__(self, root, parent=None):
163031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
164031c2a00SAdrian Hunter		self.root = root
165031c2a00SAdrian Hunter		self.last_row_read = 0
166031c2a00SAdrian Hunter
167031c2a00SAdrian Hunter	def Item(self, parent):
168031c2a00SAdrian Hunter		if parent.isValid():
169031c2a00SAdrian Hunter			return parent.internalPointer()
170031c2a00SAdrian Hunter		else:
171031c2a00SAdrian Hunter			return self.root
172031c2a00SAdrian Hunter
173031c2a00SAdrian Hunter	def rowCount(self, parent):
174031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
175031c2a00SAdrian Hunter		if result < 0:
176031c2a00SAdrian Hunter			result = 0
177031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
178031c2a00SAdrian Hunter		return result
179031c2a00SAdrian Hunter
180031c2a00SAdrian Hunter	def hasChildren(self, parent):
181031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
182031c2a00SAdrian Hunter
183031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
184031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
185031c2a00SAdrian Hunter			return self.columnAlignment(section)
186031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
187031c2a00SAdrian Hunter			return None
188031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
189031c2a00SAdrian Hunter			return None
190031c2a00SAdrian Hunter		return self.columnHeader(section)
191031c2a00SAdrian Hunter
192031c2a00SAdrian Hunter	def parent(self, child):
193031c2a00SAdrian Hunter		child_item = child.internalPointer()
194031c2a00SAdrian Hunter		if child_item is self.root:
195031c2a00SAdrian Hunter			return QModelIndex()
196031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
197031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
198031c2a00SAdrian Hunter
199031c2a00SAdrian Hunter	def index(self, row, column, parent):
200031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
201031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
202031c2a00SAdrian Hunter
203031c2a00SAdrian Hunter	def DisplayData(self, item, index):
204031c2a00SAdrian Hunter		return item.getData(index.column())
205031c2a00SAdrian Hunter
2068392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2078392b74bSAdrian Hunter		if row > self.last_row_read:
2088392b74bSAdrian Hunter			self.last_row_read = row
2098392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2108392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2118392b74bSAdrian Hunter
2128392b74bSAdrian Hunter	def columnAlignment(self, column):
2138392b74bSAdrian Hunter		return Qt.AlignLeft
2148392b74bSAdrian Hunter
2158392b74bSAdrian Hunter	def columnFont(self, column):
2168392b74bSAdrian Hunter		return None
2178392b74bSAdrian Hunter
2188392b74bSAdrian Hunter	def data(self, index, role):
2198392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2208392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2218392b74bSAdrian Hunter		if role == Qt.FontRole:
2228392b74bSAdrian Hunter			return self.columnFont(index.column())
2238392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2248392b74bSAdrian Hunter			return None
2258392b74bSAdrian Hunter		item = index.internalPointer()
2268392b74bSAdrian Hunter		return self.DisplayData(item, index)
2278392b74bSAdrian Hunter
2288392b74bSAdrian Hunter# Table data model
2298392b74bSAdrian Hunter
2308392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2318392b74bSAdrian Hunter
2328392b74bSAdrian Hunter	def __init__(self, parent=None):
2338392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2348392b74bSAdrian Hunter		self.child_count = 0
2358392b74bSAdrian Hunter		self.child_items = []
2368392b74bSAdrian Hunter		self.last_row_read = 0
2378392b74bSAdrian Hunter
2388392b74bSAdrian Hunter	def Item(self, parent):
2398392b74bSAdrian Hunter		if parent.isValid():
2408392b74bSAdrian Hunter			return parent.internalPointer()
2418392b74bSAdrian Hunter		else:
2428392b74bSAdrian Hunter			return self
2438392b74bSAdrian Hunter
2448392b74bSAdrian Hunter	def rowCount(self, parent):
2458392b74bSAdrian Hunter		return self.child_count
2468392b74bSAdrian Hunter
2478392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2488392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2498392b74bSAdrian Hunter			return self.columnAlignment(section)
2508392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2518392b74bSAdrian Hunter			return None
2528392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2538392b74bSAdrian Hunter			return None
2548392b74bSAdrian Hunter		return self.columnHeader(section)
2558392b74bSAdrian Hunter
2568392b74bSAdrian Hunter	def index(self, row, column, parent):
2578392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2588392b74bSAdrian Hunter
2598392b74bSAdrian Hunter	def DisplayData(self, item, index):
2608392b74bSAdrian Hunter		return item.getData(index.column())
2618392b74bSAdrian Hunter
2628392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2638392b74bSAdrian Hunter		if row > self.last_row_read:
2648392b74bSAdrian Hunter			self.last_row_read = row
2658392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2668392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2678392b74bSAdrian Hunter
268031c2a00SAdrian Hunter	def columnAlignment(self, column):
269031c2a00SAdrian Hunter		return Qt.AlignLeft
270031c2a00SAdrian Hunter
271031c2a00SAdrian Hunter	def columnFont(self, column):
272031c2a00SAdrian Hunter		return None
273031c2a00SAdrian Hunter
274031c2a00SAdrian Hunter	def data(self, index, role):
275031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
276031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
277031c2a00SAdrian Hunter		if role == Qt.FontRole:
278031c2a00SAdrian Hunter			return self.columnFont(index.column())
279031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
280031c2a00SAdrian Hunter			return None
281031c2a00SAdrian Hunter		item = index.internalPointer()
282031c2a00SAdrian Hunter		return self.DisplayData(item, index)
283031c2a00SAdrian Hunter
2841beb5c7bSAdrian Hunter# Model cache
2851beb5c7bSAdrian Hunter
2861beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
2871beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
2881beb5c7bSAdrian Hunter
2891beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
2901beb5c7bSAdrian Hunter	model_cache_lock.acquire()
2911beb5c7bSAdrian Hunter	try:
2921beb5c7bSAdrian Hunter		model = model_cache[model_name]
2931beb5c7bSAdrian Hunter	except:
2941beb5c7bSAdrian Hunter		model = None
2951beb5c7bSAdrian Hunter	if model is None:
2961beb5c7bSAdrian Hunter		model = create_fn()
2971beb5c7bSAdrian Hunter		model_cache[model_name] = model
2981beb5c7bSAdrian Hunter	model_cache_lock.release()
2991beb5c7bSAdrian Hunter	return model
3001beb5c7bSAdrian Hunter
301ebd70c7dSAdrian Hunter# Find bar
302ebd70c7dSAdrian Hunter
303ebd70c7dSAdrian Hunterclass FindBar():
304ebd70c7dSAdrian Hunter
305ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
306ebd70c7dSAdrian Hunter		self.finder = finder
307ebd70c7dSAdrian Hunter		self.context = []
308ebd70c7dSAdrian Hunter		self.last_value = None
309ebd70c7dSAdrian Hunter		self.last_pattern = None
310ebd70c7dSAdrian Hunter
311ebd70c7dSAdrian Hunter		label = QLabel("Find:")
312ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
313ebd70c7dSAdrian Hunter
314ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
315ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
316ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
317ebd70c7dSAdrian Hunter
318ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
319ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
320ebd70c7dSAdrian Hunter		self.progress.hide()
321ebd70c7dSAdrian Hunter
322ebd70c7dSAdrian Hunter		if is_reg_expr:
323ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
324ebd70c7dSAdrian Hunter		else:
325ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
326ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
327ebd70c7dSAdrian Hunter
328ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
329ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
330ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
331ebd70c7dSAdrian Hunter
332ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
333ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
334ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
335ebd70c7dSAdrian Hunter
336ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
337ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
338ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
339ebd70c7dSAdrian Hunter
340ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
341ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
342ebd70c7dSAdrian Hunter
343ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
344ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
345ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
346ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
347ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
348ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
349ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
350ebd70c7dSAdrian Hunter
351ebd70c7dSAdrian Hunter		self.bar = QWidget()
352ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
353ebd70c7dSAdrian Hunter		self.bar.hide()
354ebd70c7dSAdrian Hunter
355ebd70c7dSAdrian Hunter	def Widget(self):
356ebd70c7dSAdrian Hunter		return self.bar
357ebd70c7dSAdrian Hunter
358ebd70c7dSAdrian Hunter	def Activate(self):
359ebd70c7dSAdrian Hunter		self.bar.show()
360ebd70c7dSAdrian Hunter		self.textbox.setFocus()
361ebd70c7dSAdrian Hunter
362ebd70c7dSAdrian Hunter	def Deactivate(self):
363ebd70c7dSAdrian Hunter		self.bar.hide()
364ebd70c7dSAdrian Hunter
365ebd70c7dSAdrian Hunter	def Busy(self):
366ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
367ebd70c7dSAdrian Hunter		self.pattern.hide()
368ebd70c7dSAdrian Hunter		self.next_button.hide()
369ebd70c7dSAdrian Hunter		self.prev_button.hide()
370ebd70c7dSAdrian Hunter		self.progress.show()
371ebd70c7dSAdrian Hunter
372ebd70c7dSAdrian Hunter	def Idle(self):
373ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
374ebd70c7dSAdrian Hunter		self.progress.hide()
375ebd70c7dSAdrian Hunter		self.pattern.show()
376ebd70c7dSAdrian Hunter		self.next_button.show()
377ebd70c7dSAdrian Hunter		self.prev_button.show()
378ebd70c7dSAdrian Hunter
379ebd70c7dSAdrian Hunter	def Find(self, direction):
380ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
381ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
382ebd70c7dSAdrian Hunter		self.last_value = value
383ebd70c7dSAdrian Hunter		self.last_pattern = pattern
384ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
385ebd70c7dSAdrian Hunter
386ebd70c7dSAdrian Hunter	def ValueChanged(self):
387ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
388ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
389ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
390ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
391ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
392ebd70c7dSAdrian Hunter		if data == None:
393ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
394ebd70c7dSAdrian Hunter		else:
395ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
396ebd70c7dSAdrian Hunter		self.Find(0)
397ebd70c7dSAdrian Hunter
398ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
399ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
400ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
401ebd70c7dSAdrian Hunter		if value != self.last_value:
402ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
403ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
404ebd70c7dSAdrian Hunter			if index < 0:
405ebd70c7dSAdrian Hunter				index = self.textbox.count()
406ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
407ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
408ebd70c7dSAdrian Hunter				return
409ebd70c7dSAdrian Hunter			else:
410ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
411ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
412ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
413ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
414ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
415ebd70c7dSAdrian Hunter		self.Find(direction)
416ebd70c7dSAdrian Hunter
417ebd70c7dSAdrian Hunter	def NotFound(self):
418ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
419ebd70c7dSAdrian Hunter
420031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
421031c2a00SAdrian Hunter
422031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
423031c2a00SAdrian Hunter
424031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
425031c2a00SAdrian Hunter		self.glb = glb
426031c2a00SAdrian Hunter		self.row = row
427031c2a00SAdrian Hunter		self.parent_item = parent_item
428031c2a00SAdrian Hunter		self.query_done = False;
429031c2a00SAdrian Hunter		self.child_count = 0
430031c2a00SAdrian Hunter		self.child_items = []
431031c2a00SAdrian Hunter
432031c2a00SAdrian Hunter	def getChildItem(self, row):
433031c2a00SAdrian Hunter		return self.child_items[row]
434031c2a00SAdrian Hunter
435031c2a00SAdrian Hunter	def getParentItem(self):
436031c2a00SAdrian Hunter		return self.parent_item
437031c2a00SAdrian Hunter
438031c2a00SAdrian Hunter	def getRow(self):
439031c2a00SAdrian Hunter		return self.row
440031c2a00SAdrian Hunter
441031c2a00SAdrian Hunter	def childCount(self):
442031c2a00SAdrian Hunter		if not self.query_done:
443031c2a00SAdrian Hunter			self.Select()
444031c2a00SAdrian Hunter			if not self.child_count:
445031c2a00SAdrian Hunter				return -1
446031c2a00SAdrian Hunter		return self.child_count
447031c2a00SAdrian Hunter
448031c2a00SAdrian Hunter	def hasChildren(self):
449031c2a00SAdrian Hunter		if not self.query_done:
450031c2a00SAdrian Hunter			return True
451031c2a00SAdrian Hunter		return self.child_count > 0
452031c2a00SAdrian Hunter
453031c2a00SAdrian Hunter	def getData(self, column):
454031c2a00SAdrian Hunter		return self.data[column]
455031c2a00SAdrian Hunter
456031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
457031c2a00SAdrian Hunter
458031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
459031c2a00SAdrian Hunter
460031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
461031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
462031c2a00SAdrian Hunter		self.comm_id = comm_id
463031c2a00SAdrian Hunter		self.thread_id = thread_id
464031c2a00SAdrian Hunter		self.call_path_id = call_path_id
465031c2a00SAdrian Hunter		self.branch_count = branch_count
466031c2a00SAdrian Hunter		self.time = time
467031c2a00SAdrian Hunter
468031c2a00SAdrian Hunter	def Select(self):
469031c2a00SAdrian Hunter		self.query_done = True;
470031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
471031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
472031c2a00SAdrian Hunter					" FROM calls"
473031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
474031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
475031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
476031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
477031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
478031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
479031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
480031c2a00SAdrian Hunter					" ORDER BY call_path_id")
481031c2a00SAdrian Hunter		while query.next():
482031c2a00SAdrian 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)
483031c2a00SAdrian Hunter			self.child_items.append(child_item)
484031c2a00SAdrian Hunter			self.child_count += 1
485031c2a00SAdrian Hunter
486031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
487031c2a00SAdrian Hunter
488031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
489031c2a00SAdrian Hunter
490031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
491031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
492031c2a00SAdrian Hunter		dso = dsoname(dso)
493031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
494031c2a00SAdrian Hunter		self.dbid = call_path_id
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
497031c2a00SAdrian Hunter
498031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
499031c2a00SAdrian Hunter
500031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
501031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
502031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
503031c2a00SAdrian Hunter		self.dbid = thread_id
504031c2a00SAdrian Hunter
505031c2a00SAdrian Hunter	def Select(self):
506031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
507031c2a00SAdrian Hunter		for child_item in self.child_items:
508031c2a00SAdrian Hunter			self.time += child_item.time
509031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
510031c2a00SAdrian Hunter		for child_item in self.child_items:
511031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
512031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
513031c2a00SAdrian Hunter
514031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
515031c2a00SAdrian Hunter
516031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
517031c2a00SAdrian Hunter
518031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
519031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
520031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
521031c2a00SAdrian Hunter		self.dbid = comm_id
522031c2a00SAdrian Hunter
523031c2a00SAdrian Hunter	def Select(self):
524031c2a00SAdrian Hunter		self.query_done = True;
525031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
526031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
527031c2a00SAdrian Hunter					" FROM comm_threads"
528031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
529031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
530031c2a00SAdrian Hunter		while query.next():
531031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
532031c2a00SAdrian Hunter			self.child_items.append(child_item)
533031c2a00SAdrian Hunter			self.child_count += 1
534031c2a00SAdrian Hunter
535031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
536031c2a00SAdrian Hunter
537031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
538031c2a00SAdrian Hunter
539031c2a00SAdrian Hunter	def __init__(self, glb):
540031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
541031c2a00SAdrian Hunter		self.dbid = 0
542031c2a00SAdrian Hunter		self.query_done = True;
543031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
544031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
545031c2a00SAdrian Hunter		while query.next():
546031c2a00SAdrian Hunter			if not query.value(0):
547031c2a00SAdrian Hunter				continue
548031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
549031c2a00SAdrian Hunter			self.child_items.append(child_item)
550031c2a00SAdrian Hunter			self.child_count += 1
551031c2a00SAdrian Hunter
552031c2a00SAdrian Hunter# Context-sensitive call graph data model
553031c2a00SAdrian Hunter
554031c2a00SAdrian Hunterclass CallGraphModel(TreeModel):
555031c2a00SAdrian Hunter
556031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
557031c2a00SAdrian Hunter		super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
558031c2a00SAdrian Hunter		self.glb = glb
559031c2a00SAdrian Hunter
560031c2a00SAdrian Hunter	def columnCount(self, parent=None):
561031c2a00SAdrian Hunter		return 7
562031c2a00SAdrian Hunter
563031c2a00SAdrian Hunter	def columnHeader(self, column):
564031c2a00SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
565031c2a00SAdrian Hunter		return headers[column]
566031c2a00SAdrian Hunter
567031c2a00SAdrian Hunter	def columnAlignment(self, column):
568031c2a00SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
569031c2a00SAdrian Hunter		return alignment[column]
570031c2a00SAdrian Hunter
571ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
572ebd70c7dSAdrian Hunter		if pattern:
573ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
574ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
575ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
576ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
577ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
578ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
579ebd70c7dSAdrian Hunter				# Escape % and _
580ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
581ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
582ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
583ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
584ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
585ebd70c7dSAdrian Hunter			else:
586ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
587ebd70c7dSAdrian Hunter		else:
588ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
589ebd70c7dSAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
590ebd70c7dSAdrian Hunter						" FROM calls"
591ebd70c7dSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
592ebd70c7dSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
593ebd70c7dSAdrian Hunter						" WHERE symbols.name" + match +
594ebd70c7dSAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
595ebd70c7dSAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
596ebd70c7dSAdrian Hunter
597ebd70c7dSAdrian Hunter	def FindPath(self, query):
598ebd70c7dSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
599ebd70c7dSAdrian Hunter		# to open the tree at the right place.
600ebd70c7dSAdrian Hunter		ids = []
601ebd70c7dSAdrian Hunter		parent_id = query.value(0)
602ebd70c7dSAdrian Hunter		while parent_id:
603ebd70c7dSAdrian Hunter			ids.insert(0, parent_id)
604ebd70c7dSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
605ebd70c7dSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
606ebd70c7dSAdrian Hunter					" FROM call_paths"
607ebd70c7dSAdrian Hunter					" WHERE id = " + str(parent_id))
608ebd70c7dSAdrian Hunter			if not q2.next():
609ebd70c7dSAdrian Hunter				break
610ebd70c7dSAdrian Hunter			parent_id = q2.value(0)
611ebd70c7dSAdrian Hunter		# The call path root is not used
612ebd70c7dSAdrian Hunter		if ids[0] == 1:
613ebd70c7dSAdrian Hunter			del ids[0]
614ebd70c7dSAdrian Hunter		ids.insert(0, query.value(2))
615ebd70c7dSAdrian Hunter		ids.insert(0, query.value(1))
616ebd70c7dSAdrian Hunter		return ids
617ebd70c7dSAdrian Hunter
618ebd70c7dSAdrian Hunter	def Found(self, query, found):
619ebd70c7dSAdrian Hunter		if found:
620ebd70c7dSAdrian Hunter			return self.FindPath(query)
621ebd70c7dSAdrian Hunter		return []
622ebd70c7dSAdrian Hunter
623ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
624ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
625ebd70c7dSAdrian Hunter			found = query.first()
626ebd70c7dSAdrian Hunter		else:
627ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
628ebd70c7dSAdrian Hunter			found = query.next()
629ebd70c7dSAdrian Hunter		return self.Found(query, found)
630ebd70c7dSAdrian Hunter
631ebd70c7dSAdrian Hunter	def FindNext(self, query):
632ebd70c7dSAdrian Hunter		found = query.next()
633ebd70c7dSAdrian Hunter		if not found:
634ebd70c7dSAdrian Hunter			found = query.first()
635ebd70c7dSAdrian Hunter		return self.Found(query, found)
636ebd70c7dSAdrian Hunter
637ebd70c7dSAdrian Hunter	def FindPrev(self, query):
638ebd70c7dSAdrian Hunter		found = query.previous()
639ebd70c7dSAdrian Hunter		if not found:
640ebd70c7dSAdrian Hunter			found = query.last()
641ebd70c7dSAdrian Hunter		return self.Found(query, found)
642ebd70c7dSAdrian Hunter
643ebd70c7dSAdrian Hunter	def FindThread(self, c):
644ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
645ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
646ebd70c7dSAdrian Hunter		elif c.direction > 0:
647ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
648ebd70c7dSAdrian Hunter		else:
649ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
650ebd70c7dSAdrian Hunter		return (True, ids)
651ebd70c7dSAdrian Hunter
652ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
653ebd70c7dSAdrian Hunter		class Context():
654ebd70c7dSAdrian Hunter			def __init__(self, *x):
655ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
656ebd70c7dSAdrian Hunter			def Update(self, *x):
657ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
658ebd70c7dSAdrian Hunter		if len(context):
659ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
660ebd70c7dSAdrian Hunter		else:
661ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
662ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
663ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
664ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
665ebd70c7dSAdrian Hunter		thread.start()
666ebd70c7dSAdrian Hunter
667ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
668ebd70c7dSAdrian Hunter		callback(ids)
669ebd70c7dSAdrian Hunter
670ebd70c7dSAdrian Hunter# Vertical widget layout
671ebd70c7dSAdrian Hunter
672ebd70c7dSAdrian Hunterclass VBox():
673ebd70c7dSAdrian Hunter
674ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
675ebd70c7dSAdrian Hunter		self.vbox = QWidget()
676ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
677ebd70c7dSAdrian Hunter
678ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
679ebd70c7dSAdrian Hunter
680ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
681ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
682ebd70c7dSAdrian Hunter		if w3:
683ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
684ebd70c7dSAdrian Hunter
685ebd70c7dSAdrian Hunter	def Widget(self):
686ebd70c7dSAdrian Hunter		return self.vbox
687ebd70c7dSAdrian Hunter
6881beb5c7bSAdrian Hunter# Context-sensitive call graph window
6891beb5c7bSAdrian Hunter
6901beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow):
6911beb5c7bSAdrian Hunter
6921beb5c7bSAdrian Hunter	def __init__(self, glb, parent=None):
6931beb5c7bSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
6941beb5c7bSAdrian Hunter
6951beb5c7bSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
6961beb5c7bSAdrian Hunter
6971beb5c7bSAdrian Hunter		self.view = QTreeView()
6981beb5c7bSAdrian Hunter		self.view.setModel(self.model)
6991beb5c7bSAdrian Hunter
7001beb5c7bSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
7011beb5c7bSAdrian Hunter			self.view.setColumnWidth(c, w)
7021beb5c7bSAdrian Hunter
703ebd70c7dSAdrian Hunter		self.find_bar = FindBar(self, self)
704ebd70c7dSAdrian Hunter
705ebd70c7dSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
706ebd70c7dSAdrian Hunter
707ebd70c7dSAdrian Hunter		self.setWidget(self.vbox.Widget())
7081beb5c7bSAdrian Hunter
7091beb5c7bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
7101beb5c7bSAdrian Hunter
711ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
712ebd70c7dSAdrian Hunter		if not len(ids):
713ebd70c7dSAdrian Hunter			return False
714ebd70c7dSAdrian Hunter		parent = QModelIndex()
715ebd70c7dSAdrian Hunter		for dbid in ids:
716ebd70c7dSAdrian Hunter			found = False
717ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
718ebd70c7dSAdrian Hunter			for row in xrange(n):
719ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
720ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
721ebd70c7dSAdrian Hunter					found = True
722ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
723ebd70c7dSAdrian Hunter					parent = child
724ebd70c7dSAdrian Hunter					break
725ebd70c7dSAdrian Hunter			if not found:
726ebd70c7dSAdrian Hunter				break
727ebd70c7dSAdrian Hunter		return found
728ebd70c7dSAdrian Hunter
729ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
730ebd70c7dSAdrian Hunter		self.view.setFocus()
731ebd70c7dSAdrian Hunter		self.find_bar.Busy()
732ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
733ebd70c7dSAdrian Hunter
734ebd70c7dSAdrian Hunter	def FindDone(self, ids):
735ebd70c7dSAdrian Hunter		found = True
736ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
737ebd70c7dSAdrian Hunter			found = False
738ebd70c7dSAdrian Hunter		self.find_bar.Idle()
739ebd70c7dSAdrian Hunter		if not found:
740ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
741ebd70c7dSAdrian Hunter
7428392b74bSAdrian Hunter# Child data item  finder
7438392b74bSAdrian Hunter
7448392b74bSAdrian Hunterclass ChildDataItemFinder():
7458392b74bSAdrian Hunter
7468392b74bSAdrian Hunter	def __init__(self, root):
7478392b74bSAdrian Hunter		self.root = root
7488392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
7498392b74bSAdrian Hunter		self.rows = []
7508392b74bSAdrian Hunter		self.pos = 0
7518392b74bSAdrian Hunter
7528392b74bSAdrian Hunter	def FindSelect(self):
7538392b74bSAdrian Hunter		self.rows = []
7548392b74bSAdrian Hunter		if self.pattern:
7558392b74bSAdrian Hunter			pattern = re.compile(self.value)
7568392b74bSAdrian Hunter			for child in self.root.child_items:
7578392b74bSAdrian Hunter				for column_data in child.data:
7588392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
7598392b74bSAdrian Hunter						self.rows.append(child.row)
7608392b74bSAdrian Hunter						break
7618392b74bSAdrian Hunter		else:
7628392b74bSAdrian Hunter			for child in self.root.child_items:
7638392b74bSAdrian Hunter				for column_data in child.data:
7648392b74bSAdrian Hunter					if self.value in str(column_data):
7658392b74bSAdrian Hunter						self.rows.append(child.row)
7668392b74bSAdrian Hunter						break
7678392b74bSAdrian Hunter
7688392b74bSAdrian Hunter	def FindValue(self):
7698392b74bSAdrian Hunter		self.pos = 0
7708392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
7718392b74bSAdrian Hunter			self.FindSelect()
7728392b74bSAdrian Hunter		if not len(self.rows):
7738392b74bSAdrian Hunter			return -1
7748392b74bSAdrian Hunter		return self.rows[self.pos]
7758392b74bSAdrian Hunter
7768392b74bSAdrian Hunter	def FindThread(self):
7778392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
7788392b74bSAdrian Hunter			row = self.FindValue()
7798392b74bSAdrian Hunter		elif len(self.rows):
7808392b74bSAdrian Hunter			if self.direction > 0:
7818392b74bSAdrian Hunter				self.pos += 1
7828392b74bSAdrian Hunter				if self.pos >= len(self.rows):
7838392b74bSAdrian Hunter					self.pos = 0
7848392b74bSAdrian Hunter			else:
7858392b74bSAdrian Hunter				self.pos -= 1
7868392b74bSAdrian Hunter				if self.pos < 0:
7878392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
7888392b74bSAdrian Hunter			row = self.rows[self.pos]
7898392b74bSAdrian Hunter		else:
7908392b74bSAdrian Hunter			row = -1
7918392b74bSAdrian Hunter		return (True, row)
7928392b74bSAdrian Hunter
7938392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
7948392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
7958392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
7968392b74bSAdrian Hunter		thread = Thread(self.FindThread)
7978392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
7988392b74bSAdrian Hunter		thread.start()
7998392b74bSAdrian Hunter
8008392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
8018392b74bSAdrian Hunter		callback(row)
8028392b74bSAdrian Hunter
8038392b74bSAdrian Hunter# Number of database records to fetch in one go
8048392b74bSAdrian Hunter
8058392b74bSAdrian Hunterglb_chunk_sz = 10000
8068392b74bSAdrian Hunter
8078392b74bSAdrian Hunter# size of pickled integer big enough for record size
8088392b74bSAdrian Hunter
8098392b74bSAdrian Hunterglb_nsz = 8
8108392b74bSAdrian Hunter
8118392b74bSAdrian Hunter# Background process for SQL data fetcher
8128392b74bSAdrian Hunter
8138392b74bSAdrian Hunterclass SQLFetcherProcess():
8148392b74bSAdrian Hunter
8158392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
8168392b74bSAdrian Hunter		# Need a unique connection name
8178392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
8188392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
8198392b74bSAdrian Hunter		self.sql = sql
8208392b74bSAdrian Hunter		self.buffer = buffer
8218392b74bSAdrian Hunter		self.head = head
8228392b74bSAdrian Hunter		self.tail = tail
8238392b74bSAdrian Hunter		self.fetch_count = fetch_count
8248392b74bSAdrian Hunter		self.fetching_done = fetching_done
8258392b74bSAdrian Hunter		self.process_target = process_target
8268392b74bSAdrian Hunter		self.wait_event = wait_event
8278392b74bSAdrian Hunter		self.fetched_event = fetched_event
8288392b74bSAdrian Hunter		self.prep = prep
8298392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
8308392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
8318392b74bSAdrian Hunter		self.last_id = -1
8328392b74bSAdrian Hunter		self.fetched = 0
8338392b74bSAdrian Hunter		self.more = True
8348392b74bSAdrian Hunter		self.local_head = self.head.value
8358392b74bSAdrian Hunter		self.local_tail = self.tail.value
8368392b74bSAdrian Hunter
8378392b74bSAdrian Hunter	def Select(self):
8388392b74bSAdrian Hunter		if self.query_limit:
8398392b74bSAdrian Hunter			if self.query_limit == 1:
8408392b74bSAdrian Hunter				return
8418392b74bSAdrian Hunter			self.query_limit -= 1
8428392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
8438392b74bSAdrian Hunter		QueryExec(self.query, stmt)
8448392b74bSAdrian Hunter
8458392b74bSAdrian Hunter	def Next(self):
8468392b74bSAdrian Hunter		if not self.query.next():
8478392b74bSAdrian Hunter			self.Select()
8488392b74bSAdrian Hunter			if not self.query.next():
8498392b74bSAdrian Hunter				return None
8508392b74bSAdrian Hunter		self.last_id = self.query.value(0)
8518392b74bSAdrian Hunter		return self.prep(self.query)
8528392b74bSAdrian Hunter
8538392b74bSAdrian Hunter	def WaitForTarget(self):
8548392b74bSAdrian Hunter		while True:
8558392b74bSAdrian Hunter			self.wait_event.clear()
8568392b74bSAdrian Hunter			target = self.process_target.value
8578392b74bSAdrian Hunter			if target > self.fetched or target < 0:
8588392b74bSAdrian Hunter				break
8598392b74bSAdrian Hunter			self.wait_event.wait()
8608392b74bSAdrian Hunter		return target
8618392b74bSAdrian Hunter
8628392b74bSAdrian Hunter	def HasSpace(self, sz):
8638392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
8648392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
8658392b74bSAdrian Hunter			if space > sz:
8668392b74bSAdrian Hunter				return True
8678392b74bSAdrian Hunter			if space >= glb_nsz:
8688392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
8698392b74bSAdrian Hunter				nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
8708392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
8718392b74bSAdrian Hunter			self.local_head = 0
8728392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
8738392b74bSAdrian Hunter			return True
8748392b74bSAdrian Hunter		return False
8758392b74bSAdrian Hunter
8768392b74bSAdrian Hunter	def WaitForSpace(self, sz):
8778392b74bSAdrian Hunter		if self.HasSpace(sz):
8788392b74bSAdrian Hunter			return
8798392b74bSAdrian Hunter		while True:
8808392b74bSAdrian Hunter			self.wait_event.clear()
8818392b74bSAdrian Hunter			self.local_tail = self.tail.value
8828392b74bSAdrian Hunter			if self.HasSpace(sz):
8838392b74bSAdrian Hunter				return
8848392b74bSAdrian Hunter			self.wait_event.wait()
8858392b74bSAdrian Hunter
8868392b74bSAdrian Hunter	def AddToBuffer(self, obj):
8878392b74bSAdrian Hunter		d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
8888392b74bSAdrian Hunter		n = len(d)
8898392b74bSAdrian Hunter		nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
8908392b74bSAdrian Hunter		sz = n + glb_nsz
8918392b74bSAdrian Hunter		self.WaitForSpace(sz)
8928392b74bSAdrian Hunter		pos = self.local_head
8938392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
8948392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
8958392b74bSAdrian Hunter		self.local_head += sz
8968392b74bSAdrian Hunter
8978392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
8988392b74bSAdrian Hunter		fetched = 0
8998392b74bSAdrian Hunter		while batch_size > fetched:
9008392b74bSAdrian Hunter			obj = self.Next()
9018392b74bSAdrian Hunter			if obj is None:
9028392b74bSAdrian Hunter				self.more = False
9038392b74bSAdrian Hunter				break
9048392b74bSAdrian Hunter			self.AddToBuffer(obj)
9058392b74bSAdrian Hunter			fetched += 1
9068392b74bSAdrian Hunter		if fetched:
9078392b74bSAdrian Hunter			self.fetched += fetched
9088392b74bSAdrian Hunter			with self.fetch_count.get_lock():
9098392b74bSAdrian Hunter				self.fetch_count.value += fetched
9108392b74bSAdrian Hunter			self.head.value = self.local_head
9118392b74bSAdrian Hunter			self.fetched_event.set()
9128392b74bSAdrian Hunter
9138392b74bSAdrian Hunter	def Run(self):
9148392b74bSAdrian Hunter		while self.more:
9158392b74bSAdrian Hunter			target = self.WaitForTarget()
9168392b74bSAdrian Hunter			if target < 0:
9178392b74bSAdrian Hunter				break
9188392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
9198392b74bSAdrian Hunter			self.FetchBatch(batch_size)
9208392b74bSAdrian Hunter		self.fetching_done.value = True
9218392b74bSAdrian Hunter		self.fetched_event.set()
9228392b74bSAdrian Hunter
9238392b74bSAdrian Hunterdef SQLFetcherFn(*x):
9248392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
9258392b74bSAdrian Hunter	process.Run()
9268392b74bSAdrian Hunter
9278392b74bSAdrian Hunter# SQL data fetcher
9288392b74bSAdrian Hunter
9298392b74bSAdrian Hunterclass SQLFetcher(QObject):
9308392b74bSAdrian Hunter
9318392b74bSAdrian Hunter	done = Signal(object)
9328392b74bSAdrian Hunter
9338392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
9348392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
9358392b74bSAdrian Hunter		self.process_data = process_data
9368392b74bSAdrian Hunter		self.more = True
9378392b74bSAdrian Hunter		self.target = 0
9388392b74bSAdrian Hunter		self.last_target = 0
9398392b74bSAdrian Hunter		self.fetched = 0
9408392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
9418392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
9428392b74bSAdrian Hunter		self.head = Value(c_longlong)
9438392b74bSAdrian Hunter		self.tail = Value(c_longlong)
9448392b74bSAdrian Hunter		self.local_tail = 0
9458392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
9468392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
9478392b74bSAdrian Hunter		self.last_count = 0
9488392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
9498392b74bSAdrian Hunter		self.wait_event = Event()
9508392b74bSAdrian Hunter		self.fetched_event = Event()
9518392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
9528392b74bSAdrian 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))
9538392b74bSAdrian Hunter		self.process.start()
9548392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
9558392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
9568392b74bSAdrian Hunter		self.thread.start()
9578392b74bSAdrian Hunter
9588392b74bSAdrian Hunter	def Shutdown(self):
9598392b74bSAdrian Hunter		# Tell the thread and process to exit
9608392b74bSAdrian Hunter		self.process_target.value = -1
9618392b74bSAdrian Hunter		self.wait_event.set()
9628392b74bSAdrian Hunter		self.more = False
9638392b74bSAdrian Hunter		self.fetching_done.value = True
9648392b74bSAdrian Hunter		self.fetched_event.set()
9658392b74bSAdrian Hunter
9668392b74bSAdrian Hunter	def Thread(self):
9678392b74bSAdrian Hunter		if not self.more:
9688392b74bSAdrian Hunter			return True, 0
9698392b74bSAdrian Hunter		while True:
9708392b74bSAdrian Hunter			self.fetched_event.clear()
9718392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
9728392b74bSAdrian Hunter			if fetch_count != self.last_count:
9738392b74bSAdrian Hunter				break
9748392b74bSAdrian Hunter			if self.fetching_done.value:
9758392b74bSAdrian Hunter				self.more = False
9768392b74bSAdrian Hunter				return True, 0
9778392b74bSAdrian Hunter			self.fetched_event.wait()
9788392b74bSAdrian Hunter		count = fetch_count - self.last_count
9798392b74bSAdrian Hunter		self.last_count = fetch_count
9808392b74bSAdrian Hunter		self.fetched += count
9818392b74bSAdrian Hunter		return False, count
9828392b74bSAdrian Hunter
9838392b74bSAdrian Hunter	def Fetch(self, nr):
9848392b74bSAdrian Hunter		if not self.more:
9858392b74bSAdrian Hunter			# -1 inidcates there are no more
9868392b74bSAdrian Hunter			return -1
9878392b74bSAdrian Hunter		result = self.fetched
9888392b74bSAdrian Hunter		extra = result + nr - self.target
9898392b74bSAdrian Hunter		if extra > 0:
9908392b74bSAdrian Hunter			self.target += extra
9918392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
9928392b74bSAdrian Hunter			if self.process_target.value >= 0:
9938392b74bSAdrian Hunter				self.process_target.value = self.target
9948392b74bSAdrian Hunter			self.wait_event.set()
9958392b74bSAdrian Hunter		return result
9968392b74bSAdrian Hunter
9978392b74bSAdrian Hunter	def RemoveFromBuffer(self):
9988392b74bSAdrian Hunter		pos = self.local_tail
9998392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
10008392b74bSAdrian Hunter			pos = 0
10018392b74bSAdrian Hunter		n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
10028392b74bSAdrian Hunter		if n == 0:
10038392b74bSAdrian Hunter			pos = 0
10048392b74bSAdrian Hunter			n = cPickle.loads(self.buffer[0 : glb_nsz])
10058392b74bSAdrian Hunter		pos += glb_nsz
10068392b74bSAdrian Hunter		obj = cPickle.loads(self.buffer[pos : pos + n])
10078392b74bSAdrian Hunter		self.local_tail = pos + n
10088392b74bSAdrian Hunter		return obj
10098392b74bSAdrian Hunter
10108392b74bSAdrian Hunter	def ProcessData(self, count):
10118392b74bSAdrian Hunter		for i in xrange(count):
10128392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
10138392b74bSAdrian Hunter			self.process_data(obj)
10148392b74bSAdrian Hunter		self.tail.value = self.local_tail
10158392b74bSAdrian Hunter		self.wait_event.set()
10168392b74bSAdrian Hunter		self.done.emit(count)
10178392b74bSAdrian Hunter
10188392b74bSAdrian Hunter# Fetch more records bar
10198392b74bSAdrian Hunter
10208392b74bSAdrian Hunterclass FetchMoreRecordsBar():
10218392b74bSAdrian Hunter
10228392b74bSAdrian Hunter	def __init__(self, model, parent):
10238392b74bSAdrian Hunter		self.model = model
10248392b74bSAdrian Hunter
10258392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
10268392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10278392b74bSAdrian Hunter
10288392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
10298392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
10308392b74bSAdrian Hunter		self.fetch_count.setValue(10)
10318392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10328392b74bSAdrian Hunter
10338392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
10348392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10358392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
10368392b74bSAdrian Hunter
10378392b74bSAdrian Hunter		self.progress = QProgressBar()
10388392b74bSAdrian Hunter		self.progress.setRange(0, 100)
10398392b74bSAdrian Hunter		self.progress.hide()
10408392b74bSAdrian Hunter
10418392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
10428392b74bSAdrian Hunter		self.done_label.hide()
10438392b74bSAdrian Hunter
10448392b74bSAdrian Hunter		self.spacer = QLabel("")
10458392b74bSAdrian Hunter
10468392b74bSAdrian Hunter		self.close_button = QToolButton()
10478392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
10488392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
10498392b74bSAdrian Hunter
10508392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
10518392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
10528392b74bSAdrian Hunter
10538392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
10548392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
10558392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
10568392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
10578392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
10588392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
10598392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
10608392b74bSAdrian Hunter
10618392b74bSAdrian Hunter		self.bar = QWidget()
10628392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
10638392b74bSAdrian Hunter		self.bar.show()
10648392b74bSAdrian Hunter
10658392b74bSAdrian Hunter		self.in_progress = False
10668392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
10678392b74bSAdrian Hunter
10688392b74bSAdrian Hunter		self.done = False
10698392b74bSAdrian Hunter
10708392b74bSAdrian Hunter		if not model.HasMoreRecords():
10718392b74bSAdrian Hunter			self.Done()
10728392b74bSAdrian Hunter
10738392b74bSAdrian Hunter	def Widget(self):
10748392b74bSAdrian Hunter		return self.bar
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter	def Activate(self):
10778392b74bSAdrian Hunter		self.bar.show()
10788392b74bSAdrian Hunter		self.fetch.setFocus()
10798392b74bSAdrian Hunter
10808392b74bSAdrian Hunter	def Deactivate(self):
10818392b74bSAdrian Hunter		self.bar.hide()
10828392b74bSAdrian Hunter
10838392b74bSAdrian Hunter	def Enable(self, enable):
10848392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
10858392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
10868392b74bSAdrian Hunter
10878392b74bSAdrian Hunter	def Busy(self):
10888392b74bSAdrian Hunter		self.Enable(False)
10898392b74bSAdrian Hunter		self.fetch.hide()
10908392b74bSAdrian Hunter		self.spacer.hide()
10918392b74bSAdrian Hunter		self.progress.show()
10928392b74bSAdrian Hunter
10938392b74bSAdrian Hunter	def Idle(self):
10948392b74bSAdrian Hunter		self.in_progress = False
10958392b74bSAdrian Hunter		self.Enable(True)
10968392b74bSAdrian Hunter		self.progress.hide()
10978392b74bSAdrian Hunter		self.fetch.show()
10988392b74bSAdrian Hunter		self.spacer.show()
10998392b74bSAdrian Hunter
11008392b74bSAdrian Hunter	def Target(self):
11018392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
11028392b74bSAdrian Hunter
11038392b74bSAdrian Hunter	def Done(self):
11048392b74bSAdrian Hunter		self.done = True
11058392b74bSAdrian Hunter		self.Idle()
11068392b74bSAdrian Hunter		self.label.hide()
11078392b74bSAdrian Hunter		self.fetch_count.hide()
11088392b74bSAdrian Hunter		self.fetch.hide()
11098392b74bSAdrian Hunter		self.spacer.hide()
11108392b74bSAdrian Hunter		self.done_label.show()
11118392b74bSAdrian Hunter
11128392b74bSAdrian Hunter	def Progress(self, count):
11138392b74bSAdrian Hunter		if self.in_progress:
11148392b74bSAdrian Hunter			if count:
11158392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
11168392b74bSAdrian Hunter				if percent >= 100:
11178392b74bSAdrian Hunter					self.Idle()
11188392b74bSAdrian Hunter				else:
11198392b74bSAdrian Hunter					self.progress.setValue(percent)
11208392b74bSAdrian Hunter		if not count:
11218392b74bSAdrian Hunter			# Count value of zero means no more records
11228392b74bSAdrian Hunter			self.Done()
11238392b74bSAdrian Hunter
11248392b74bSAdrian Hunter	def FetchMoreRecords(self):
11258392b74bSAdrian Hunter		if self.done:
11268392b74bSAdrian Hunter			return
11278392b74bSAdrian Hunter		self.progress.setValue(0)
11288392b74bSAdrian Hunter		self.Busy()
11298392b74bSAdrian Hunter		self.in_progress = True
11308392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
11318392b74bSAdrian Hunter
1132*76099f98SAdrian Hunter# Brance data model level two item
1133*76099f98SAdrian Hunter
1134*76099f98SAdrian Hunterclass BranchLevelTwoItem():
1135*76099f98SAdrian Hunter
1136*76099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
1137*76099f98SAdrian Hunter		self.row = row
1138*76099f98SAdrian Hunter		self.parent_item = parent_item
1139*76099f98SAdrian Hunter		self.data = [""] * 8
1140*76099f98SAdrian Hunter		self.data[7] = text
1141*76099f98SAdrian Hunter		self.level = 2
1142*76099f98SAdrian Hunter
1143*76099f98SAdrian Hunter	def getParentItem(self):
1144*76099f98SAdrian Hunter		return self.parent_item
1145*76099f98SAdrian Hunter
1146*76099f98SAdrian Hunter	def getRow(self):
1147*76099f98SAdrian Hunter		return self.row
1148*76099f98SAdrian Hunter
1149*76099f98SAdrian Hunter	def childCount(self):
1150*76099f98SAdrian Hunter		return 0
1151*76099f98SAdrian Hunter
1152*76099f98SAdrian Hunter	def hasChildren(self):
1153*76099f98SAdrian Hunter		return False
1154*76099f98SAdrian Hunter
1155*76099f98SAdrian Hunter	def getData(self, column):
1156*76099f98SAdrian Hunter		return self.data[column]
1157*76099f98SAdrian Hunter
1158*76099f98SAdrian Hunter# Brance data model level one item
1159*76099f98SAdrian Hunter
1160*76099f98SAdrian Hunterclass BranchLevelOneItem():
1161*76099f98SAdrian Hunter
1162*76099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
1163*76099f98SAdrian Hunter		self.glb = glb
1164*76099f98SAdrian Hunter		self.row = row
1165*76099f98SAdrian Hunter		self.parent_item = parent_item
1166*76099f98SAdrian Hunter		self.child_count = 0
1167*76099f98SAdrian Hunter		self.child_items = []
1168*76099f98SAdrian Hunter		self.data = data[1:]
1169*76099f98SAdrian Hunter		self.dbid = data[0]
1170*76099f98SAdrian Hunter		self.level = 1
1171*76099f98SAdrian Hunter		self.query_done = False
1172*76099f98SAdrian Hunter
1173*76099f98SAdrian Hunter	def getChildItem(self, row):
1174*76099f98SAdrian Hunter		return self.child_items[row]
1175*76099f98SAdrian Hunter
1176*76099f98SAdrian Hunter	def getParentItem(self):
1177*76099f98SAdrian Hunter		return self.parent_item
1178*76099f98SAdrian Hunter
1179*76099f98SAdrian Hunter	def getRow(self):
1180*76099f98SAdrian Hunter		return self.row
1181*76099f98SAdrian Hunter
1182*76099f98SAdrian Hunter	def Select(self):
1183*76099f98SAdrian Hunter		self.query_done = True
1184*76099f98SAdrian Hunter
1185*76099f98SAdrian Hunter		if not self.glb.have_disassembler:
1186*76099f98SAdrian Hunter			return
1187*76099f98SAdrian Hunter
1188*76099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
1189*76099f98SAdrian Hunter
1190*76099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
1191*76099f98SAdrian Hunter				  " FROM samples"
1192*76099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
1193*76099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
1194*76099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
1195*76099f98SAdrian Hunter		if not query.next():
1196*76099f98SAdrian Hunter			return
1197*76099f98SAdrian Hunter		cpu = query.value(0)
1198*76099f98SAdrian Hunter		dso = query.value(1)
1199*76099f98SAdrian Hunter		sym = query.value(2)
1200*76099f98SAdrian Hunter		if dso == 0 or sym == 0:
1201*76099f98SAdrian Hunter			return
1202*76099f98SAdrian Hunter		off = query.value(3)
1203*76099f98SAdrian Hunter		short_name = query.value(4)
1204*76099f98SAdrian Hunter		long_name = query.value(5)
1205*76099f98SAdrian Hunter		build_id = query.value(6)
1206*76099f98SAdrian Hunter		sym_start = query.value(7)
1207*76099f98SAdrian Hunter		ip = query.value(8)
1208*76099f98SAdrian Hunter
1209*76099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
1210*76099f98SAdrian Hunter				  " FROM samples"
1211*76099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
1212*76099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
1213*76099f98SAdrian Hunter				  " ORDER BY samples.id"
1214*76099f98SAdrian Hunter				  " LIMIT 1")
1215*76099f98SAdrian Hunter		if not query.next():
1216*76099f98SAdrian Hunter			return
1217*76099f98SAdrian Hunter		if query.value(0) != dso:
1218*76099f98SAdrian Hunter			# Cannot disassemble from one dso to another
1219*76099f98SAdrian Hunter			return
1220*76099f98SAdrian Hunter		bsym = query.value(1)
1221*76099f98SAdrian Hunter		boff = query.value(2)
1222*76099f98SAdrian Hunter		bsym_start = query.value(3)
1223*76099f98SAdrian Hunter		if bsym == 0:
1224*76099f98SAdrian Hunter			return
1225*76099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
1226*76099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
1227*76099f98SAdrian Hunter			return
1228*76099f98SAdrian Hunter
1229*76099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
1230*76099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
1231*76099f98SAdrian Hunter		if not f:
1232*76099f98SAdrian Hunter			return
1233*76099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
1234*76099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
1235*76099f98SAdrian Hunter
1236*76099f98SAdrian Hunter		buf_sz = tot + 16
1237*76099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
1238*76099f98SAdrian Hunter		f.seek(sym_start + off)
1239*76099f98SAdrian Hunter		buf.value = f.read(buf_sz)
1240*76099f98SAdrian Hunter		buf_ptr = addressof(buf)
1241*76099f98SAdrian Hunter		i = 0
1242*76099f98SAdrian Hunter		while tot > 0:
1243*76099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
1244*76099f98SAdrian Hunter			if cnt:
1245*76099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
1246*76099f98SAdrian Hunter				for k in xrange(cnt):
1247*76099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
1248*76099f98SAdrian Hunter					i += 1
1249*76099f98SAdrian Hunter				while k < 15:
1250*76099f98SAdrian Hunter					byte_str += "   "
1251*76099f98SAdrian Hunter					k += 1
1252*76099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
1253*76099f98SAdrian Hunter				self.child_count += 1
1254*76099f98SAdrian Hunter			else:
1255*76099f98SAdrian Hunter				return
1256*76099f98SAdrian Hunter			buf_ptr += cnt
1257*76099f98SAdrian Hunter			tot -= cnt
1258*76099f98SAdrian Hunter			buf_sz -= cnt
1259*76099f98SAdrian Hunter			ip += cnt
1260*76099f98SAdrian Hunter
1261*76099f98SAdrian Hunter	def childCount(self):
1262*76099f98SAdrian Hunter		if not self.query_done:
1263*76099f98SAdrian Hunter			self.Select()
1264*76099f98SAdrian Hunter			if not self.child_count:
1265*76099f98SAdrian Hunter				return -1
1266*76099f98SAdrian Hunter		return self.child_count
1267*76099f98SAdrian Hunter
1268*76099f98SAdrian Hunter	def hasChildren(self):
1269*76099f98SAdrian Hunter		if not self.query_done:
1270*76099f98SAdrian Hunter			return True
1271*76099f98SAdrian Hunter		return self.child_count > 0
1272*76099f98SAdrian Hunter
1273*76099f98SAdrian Hunter	def getData(self, column):
1274*76099f98SAdrian Hunter		return self.data[column]
1275*76099f98SAdrian Hunter
1276*76099f98SAdrian Hunter# Brance data model root item
1277*76099f98SAdrian Hunter
1278*76099f98SAdrian Hunterclass BranchRootItem():
1279*76099f98SAdrian Hunter
1280*76099f98SAdrian Hunter	def __init__(self):
1281*76099f98SAdrian Hunter		self.child_count = 0
1282*76099f98SAdrian Hunter		self.child_items = []
1283*76099f98SAdrian Hunter		self.level = 0
1284*76099f98SAdrian Hunter
1285*76099f98SAdrian Hunter	def getChildItem(self, row):
1286*76099f98SAdrian Hunter		return self.child_items[row]
1287*76099f98SAdrian Hunter
1288*76099f98SAdrian Hunter	def getParentItem(self):
1289*76099f98SAdrian Hunter		return None
1290*76099f98SAdrian Hunter
1291*76099f98SAdrian Hunter	def getRow(self):
1292*76099f98SAdrian Hunter		return 0
1293*76099f98SAdrian Hunter
1294*76099f98SAdrian Hunter	def childCount(self):
1295*76099f98SAdrian Hunter		return self.child_count
1296*76099f98SAdrian Hunter
1297*76099f98SAdrian Hunter	def hasChildren(self):
1298*76099f98SAdrian Hunter		return self.child_count > 0
1299*76099f98SAdrian Hunter
1300*76099f98SAdrian Hunter	def getData(self, column):
1301*76099f98SAdrian Hunter		return ""
1302*76099f98SAdrian Hunter
1303*76099f98SAdrian Hunter# Branch data preparation
1304*76099f98SAdrian Hunter
1305*76099f98SAdrian Hunterdef BranchDataPrep(query):
1306*76099f98SAdrian Hunter	data = []
1307*76099f98SAdrian Hunter	for i in xrange(0, 8):
1308*76099f98SAdrian Hunter		data.append(query.value(i))
1309*76099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
1310*76099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
1311*76099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
1312*76099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
1313*76099f98SAdrian Hunter	return data
1314*76099f98SAdrian Hunter
1315*76099f98SAdrian Hunter# Branch data model
1316*76099f98SAdrian Hunter
1317*76099f98SAdrian Hunterclass BranchModel(TreeModel):
1318*76099f98SAdrian Hunter
1319*76099f98SAdrian Hunter	progress = Signal(object)
1320*76099f98SAdrian Hunter
1321*76099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
1322*76099f98SAdrian Hunter		super(BranchModel, self).__init__(BranchRootItem(), parent)
1323*76099f98SAdrian Hunter		self.glb = glb
1324*76099f98SAdrian Hunter		self.event_id = event_id
1325*76099f98SAdrian Hunter		self.more = True
1326*76099f98SAdrian Hunter		self.populated = 0
1327*76099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
1328*76099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
1329*76099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
1330*76099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
1331*76099f98SAdrian Hunter			" FROM samples"
1332*76099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
1333*76099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
1334*76099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
1335*76099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
1336*76099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
1337*76099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
1338*76099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
1339*76099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
1340*76099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
1341*76099f98SAdrian Hunter			" ORDER BY samples.id"
1342*76099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
1343*76099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
1344*76099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
1345*76099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
1346*76099f98SAdrian Hunter
1347*76099f98SAdrian Hunter	def columnCount(self, parent=None):
1348*76099f98SAdrian Hunter		return 8
1349*76099f98SAdrian Hunter
1350*76099f98SAdrian Hunter	def columnHeader(self, column):
1351*76099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
1352*76099f98SAdrian Hunter
1353*76099f98SAdrian Hunter	def columnFont(self, column):
1354*76099f98SAdrian Hunter		if column != 7:
1355*76099f98SAdrian Hunter			return None
1356*76099f98SAdrian Hunter		return QFont("Monospace")
1357*76099f98SAdrian Hunter
1358*76099f98SAdrian Hunter	def DisplayData(self, item, index):
1359*76099f98SAdrian Hunter		if item.level == 1:
1360*76099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
1361*76099f98SAdrian Hunter		return item.getData(index.column())
1362*76099f98SAdrian Hunter
1363*76099f98SAdrian Hunter	def AddSample(self, data):
1364*76099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
1365*76099f98SAdrian Hunter		self.root.child_items.append(child)
1366*76099f98SAdrian Hunter		self.populated += 1
1367*76099f98SAdrian Hunter
1368*76099f98SAdrian Hunter	def Update(self, fetched):
1369*76099f98SAdrian Hunter		if not fetched:
1370*76099f98SAdrian Hunter			self.more = False
1371*76099f98SAdrian Hunter			self.progress.emit(0)
1372*76099f98SAdrian Hunter		child_count = self.root.child_count
1373*76099f98SAdrian Hunter		count = self.populated - child_count
1374*76099f98SAdrian Hunter		if count > 0:
1375*76099f98SAdrian Hunter			parent = QModelIndex()
1376*76099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
1377*76099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
1378*76099f98SAdrian Hunter			self.root.child_count += count
1379*76099f98SAdrian Hunter			self.endInsertRows()
1380*76099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
1381*76099f98SAdrian Hunter
1382*76099f98SAdrian Hunter	def FetchMoreRecords(self, count):
1383*76099f98SAdrian Hunter		current = self.root.child_count
1384*76099f98SAdrian Hunter		if self.more:
1385*76099f98SAdrian Hunter			self.fetcher.Fetch(count)
1386*76099f98SAdrian Hunter		else:
1387*76099f98SAdrian Hunter			self.progress.emit(0)
1388*76099f98SAdrian Hunter		return current
1389*76099f98SAdrian Hunter
1390*76099f98SAdrian Hunter	def HasMoreRecords(self):
1391*76099f98SAdrian Hunter		return self.more
1392*76099f98SAdrian Hunter
1393*76099f98SAdrian Hunter# Branch window
1394*76099f98SAdrian Hunter
1395*76099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
1396*76099f98SAdrian Hunter
1397*76099f98SAdrian Hunter	def __init__(self, glb, event_id, name, where_clause, parent=None):
1398*76099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
1399*76099f98SAdrian Hunter
1400*76099f98SAdrian Hunter		model_name = "Branch Events " + str(event_id)
1401*76099f98SAdrian Hunter		if len(where_clause):
1402*76099f98SAdrian Hunter			model_name = where_clause + " " + model_name
1403*76099f98SAdrian Hunter
1404*76099f98SAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause))
1405*76099f98SAdrian Hunter
1406*76099f98SAdrian Hunter		self.view = QTreeView()
1407*76099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
1408*76099f98SAdrian Hunter		self.view.setModel(self.model)
1409*76099f98SAdrian Hunter
1410*76099f98SAdrian Hunter		self.ResizeColumnsToContents()
1411*76099f98SAdrian Hunter
1412*76099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
1413*76099f98SAdrian Hunter
1414*76099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
1415*76099f98SAdrian Hunter
1416*76099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
1417*76099f98SAdrian Hunter
1418*76099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
1419*76099f98SAdrian Hunter
1420*76099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
1421*76099f98SAdrian Hunter
1422*76099f98SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events")
1423*76099f98SAdrian Hunter
1424*76099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
1425*76099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
1426*76099f98SAdrian Hunter		# so implement a crude alternative
1427*76099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
1428*76099f98SAdrian Hunter		font = self.view.font()
1429*76099f98SAdrian Hunter		metrics = QFontMetrics(font)
1430*76099f98SAdrian Hunter		max = 0
1431*76099f98SAdrian Hunter		for row in xrange(n):
1432*76099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
1433*76099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
1434*76099f98SAdrian Hunter			max = len if len > max else max
1435*76099f98SAdrian Hunter		val = self.model.columnHeader(column)
1436*76099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
1437*76099f98SAdrian Hunter		max = len if len > max else max
1438*76099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
1439*76099f98SAdrian Hunter
1440*76099f98SAdrian Hunter	def ResizeColumnsToContents(self):
1441*76099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
1442*76099f98SAdrian Hunter		if n < 1:
1443*76099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
1444*76099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
1445*76099f98SAdrian Hunter			return
1446*76099f98SAdrian Hunter		columns = self.model.columnCount()
1447*76099f98SAdrian Hunter		for i in xrange(columns):
1448*76099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
1449*76099f98SAdrian Hunter
1450*76099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
1451*76099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
1452*76099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
1453*76099f98SAdrian Hunter		self.ResizeColumnsToContents()
1454*76099f98SAdrian Hunter
1455*76099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
1456*76099f98SAdrian Hunter		self.view.setFocus()
1457*76099f98SAdrian Hunter		self.find_bar.Busy()
1458*76099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
1459*76099f98SAdrian Hunter
1460*76099f98SAdrian Hunter	def FindDone(self, row):
1461*76099f98SAdrian Hunter		self.find_bar.Idle()
1462*76099f98SAdrian Hunter		if row >= 0:
1463*76099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
1464*76099f98SAdrian Hunter		else:
1465*76099f98SAdrian Hunter			self.find_bar.NotFound()
1466*76099f98SAdrian Hunter
1467*76099f98SAdrian Hunter# Event list
1468*76099f98SAdrian Hunter
1469*76099f98SAdrian Hunterdef GetEventList(db):
1470*76099f98SAdrian Hunter	events = []
1471*76099f98SAdrian Hunter	query = QSqlQuery(db)
1472*76099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
1473*76099f98SAdrian Hunter	while query.next():
1474*76099f98SAdrian Hunter		events.append(query.value(0))
1475*76099f98SAdrian Hunter	return events
1476*76099f98SAdrian Hunter
14778392b74bSAdrian Hunter# SQL data preparation
14788392b74bSAdrian Hunter
14798392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
14808392b74bSAdrian Hunter	data = []
14818392b74bSAdrian Hunter	for i in xrange(count):
14828392b74bSAdrian Hunter		data.append(query.value(i))
14838392b74bSAdrian Hunter	return data
14848392b74bSAdrian Hunter
14858392b74bSAdrian Hunter# SQL table data model item
14868392b74bSAdrian Hunter
14878392b74bSAdrian Hunterclass SQLTableItem():
14888392b74bSAdrian Hunter
14898392b74bSAdrian Hunter	def __init__(self, row, data):
14908392b74bSAdrian Hunter		self.row = row
14918392b74bSAdrian Hunter		self.data = data
14928392b74bSAdrian Hunter
14938392b74bSAdrian Hunter	def getData(self, column):
14948392b74bSAdrian Hunter		return self.data[column]
14958392b74bSAdrian Hunter
14968392b74bSAdrian Hunter# SQL table data model
14978392b74bSAdrian Hunter
14988392b74bSAdrian Hunterclass SQLTableModel(TableModel):
14998392b74bSAdrian Hunter
15008392b74bSAdrian Hunter	progress = Signal(object)
15018392b74bSAdrian Hunter
15028392b74bSAdrian Hunter	def __init__(self, glb, sql, column_count, parent=None):
15038392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
15048392b74bSAdrian Hunter		self.glb = glb
15058392b74bSAdrian Hunter		self.more = True
15068392b74bSAdrian Hunter		self.populated = 0
15078392b74bSAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample)
15088392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
15098392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
15108392b74bSAdrian Hunter
15118392b74bSAdrian Hunter	def DisplayData(self, item, index):
15128392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
15138392b74bSAdrian Hunter		return item.getData(index.column())
15148392b74bSAdrian Hunter
15158392b74bSAdrian Hunter	def AddSample(self, data):
15168392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
15178392b74bSAdrian Hunter		self.child_items.append(child)
15188392b74bSAdrian Hunter		self.populated += 1
15198392b74bSAdrian Hunter
15208392b74bSAdrian Hunter	def Update(self, fetched):
15218392b74bSAdrian Hunter		if not fetched:
15228392b74bSAdrian Hunter			self.more = False
15238392b74bSAdrian Hunter			self.progress.emit(0)
15248392b74bSAdrian Hunter		child_count = self.child_count
15258392b74bSAdrian Hunter		count = self.populated - child_count
15268392b74bSAdrian Hunter		if count > 0:
15278392b74bSAdrian Hunter			parent = QModelIndex()
15288392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
15298392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
15308392b74bSAdrian Hunter			self.child_count += count
15318392b74bSAdrian Hunter			self.endInsertRows()
15328392b74bSAdrian Hunter			self.progress.emit(self.child_count)
15338392b74bSAdrian Hunter
15348392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
15358392b74bSAdrian Hunter		current = self.child_count
15368392b74bSAdrian Hunter		if self.more:
15378392b74bSAdrian Hunter			self.fetcher.Fetch(count)
15388392b74bSAdrian Hunter		else:
15398392b74bSAdrian Hunter			self.progress.emit(0)
15408392b74bSAdrian Hunter		return current
15418392b74bSAdrian Hunter
15428392b74bSAdrian Hunter	def HasMoreRecords(self):
15438392b74bSAdrian Hunter		return self.more
15448392b74bSAdrian Hunter
15458392b74bSAdrian Hunter# SQL automatic table data model
15468392b74bSAdrian Hunter
15478392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
15488392b74bSAdrian Hunter
15498392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
15508392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
15518392b74bSAdrian Hunter		if table_name == "comm_threads_view":
15528392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
15538392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
15548392b74bSAdrian Hunter		self.column_headers = []
15558392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
15568392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
15578392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
15588392b74bSAdrian Hunter			while query.next():
15598392b74bSAdrian Hunter				self.column_headers.append(query.value(1))
15608392b74bSAdrian Hunter			if table_name == "sqlite_master":
15618392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
15628392b74bSAdrian Hunter		else:
15638392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
15648392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
15658392b74bSAdrian Hunter				select_table_name = table_name[19:]
15668392b74bSAdrian Hunter				schema = "information_schema"
15678392b74bSAdrian Hunter			else:
15688392b74bSAdrian Hunter				select_table_name = table_name
15698392b74bSAdrian Hunter				schema = "public"
15708392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
15718392b74bSAdrian Hunter			while query.next():
15728392b74bSAdrian Hunter				self.column_headers.append(query.value(0))
15738392b74bSAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent)
15748392b74bSAdrian Hunter
15758392b74bSAdrian Hunter	def columnCount(self, parent=None):
15768392b74bSAdrian Hunter		return len(self.column_headers)
15778392b74bSAdrian Hunter
15788392b74bSAdrian Hunter	def columnHeader(self, column):
15798392b74bSAdrian Hunter		return self.column_headers[column]
15808392b74bSAdrian Hunter
15818392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
15828392b74bSAdrian Hunter
15838392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
15848392b74bSAdrian Hunter
15858392b74bSAdrian Hunter	def __init__(self, parent=None):
15868392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
15878392b74bSAdrian Hunter
15888392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
15898392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
15908392b74bSAdrian Hunter		# so implement a crude alternative
15918392b74bSAdrian Hunter		font = self.view.font()
15928392b74bSAdrian Hunter		metrics = QFontMetrics(font)
15938392b74bSAdrian Hunter		max = 0
15948392b74bSAdrian Hunter		for row in xrange(n):
15958392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
15968392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
15978392b74bSAdrian Hunter			max = len if len > max else max
15988392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
15998392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
16008392b74bSAdrian Hunter		max = len if len > max else max
16018392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
16028392b74bSAdrian Hunter
16038392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
16048392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
16058392b74bSAdrian Hunter		if n < 1:
16068392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
16078392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
16088392b74bSAdrian Hunter			return
16098392b74bSAdrian Hunter		columns = self.data_model.columnCount()
16108392b74bSAdrian Hunter		for i in xrange(columns):
16118392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
16128392b74bSAdrian Hunter
16138392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
16148392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
16158392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
16168392b74bSAdrian Hunter		self.ResizeColumnsToContents()
16178392b74bSAdrian Hunter
16188392b74bSAdrian Hunter# Table window
16198392b74bSAdrian Hunter
16208392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
16218392b74bSAdrian Hunter
16228392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
16238392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
16248392b74bSAdrian Hunter
16258392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
16268392b74bSAdrian Hunter
16278392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
16288392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
16298392b74bSAdrian Hunter
16308392b74bSAdrian Hunter		self.view = QTableView()
16318392b74bSAdrian Hunter		self.view.setModel(self.model)
16328392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
16338392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
16348392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
16358392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
16368392b74bSAdrian Hunter
16378392b74bSAdrian Hunter		self.ResizeColumnsToContents()
16388392b74bSAdrian Hunter
16398392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
16408392b74bSAdrian Hunter
16418392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
16428392b74bSAdrian Hunter
16438392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
16448392b74bSAdrian Hunter
16458392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
16468392b74bSAdrian Hunter
16478392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
16488392b74bSAdrian Hunter
16498392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
16508392b74bSAdrian Hunter
16518392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
16528392b74bSAdrian Hunter		self.view.setFocus()
16538392b74bSAdrian Hunter		self.find_bar.Busy()
16548392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
16558392b74bSAdrian Hunter
16568392b74bSAdrian Hunter	def FindDone(self, row):
16578392b74bSAdrian Hunter		self.find_bar.Idle()
16588392b74bSAdrian Hunter		if row >= 0:
16598392b74bSAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
16608392b74bSAdrian Hunter		else:
16618392b74bSAdrian Hunter			self.find_bar.NotFound()
16628392b74bSAdrian Hunter
16638392b74bSAdrian Hunter# Table list
16648392b74bSAdrian Hunter
16658392b74bSAdrian Hunterdef GetTableList(glb):
16668392b74bSAdrian Hunter	tables = []
16678392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
16688392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
16698392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
16708392b74bSAdrian Hunter	else:
16718392b74bSAdrian 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")
16728392b74bSAdrian Hunter	while query.next():
16738392b74bSAdrian Hunter		tables.append(query.value(0))
16748392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
16758392b74bSAdrian Hunter		tables.append("sqlite_master")
16768392b74bSAdrian Hunter	else:
16778392b74bSAdrian Hunter		tables.append("information_schema.tables")
16788392b74bSAdrian Hunter		tables.append("information_schema.views")
16798392b74bSAdrian Hunter		tables.append("information_schema.columns")
16808392b74bSAdrian Hunter	return tables
16818392b74bSAdrian Hunter
16821beb5c7bSAdrian Hunter# Action Definition
16831beb5c7bSAdrian Hunter
16841beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
16851beb5c7bSAdrian Hunter	action = QAction(label, parent)
16861beb5c7bSAdrian Hunter	if shortcut != None:
16871beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
16881beb5c7bSAdrian Hunter	action.setStatusTip(tip)
16891beb5c7bSAdrian Hunter	action.triggered.connect(callback)
16901beb5c7bSAdrian Hunter	return action
16911beb5c7bSAdrian Hunter
16921beb5c7bSAdrian Hunter# Typical application actions
16931beb5c7bSAdrian Hunter
16941beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
16951beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
16961beb5c7bSAdrian Hunter
16971beb5c7bSAdrian Hunter# Typical MDI actions
16981beb5c7bSAdrian Hunter
16991beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
17001beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
17011beb5c7bSAdrian Hunter
17021beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
17031beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
17041beb5c7bSAdrian Hunter
17051beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
17061beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
17071beb5c7bSAdrian Hunter
17081beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
17091beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
17101beb5c7bSAdrian Hunter
17111beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
17121beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
17131beb5c7bSAdrian Hunter
17141beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
17151beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
17161beb5c7bSAdrian Hunter
17171beb5c7bSAdrian Hunter# Typical MDI window menu
17181beb5c7bSAdrian Hunter
17191beb5c7bSAdrian Hunterclass WindowMenu():
17201beb5c7bSAdrian Hunter
17211beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
17221beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
17231beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
17241beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
17251beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
17261beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
17271beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
17281beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
17291beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
17301beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
17311beb5c7bSAdrian Hunter
17321beb5c7bSAdrian Hunter	def Update(self):
17331beb5c7bSAdrian Hunter		self.window_menu.clear()
17341beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
17351beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
17361beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
17371beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
17381beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
17391beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
17401beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
17411beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
17421beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
17431beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
17441beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
17451beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
17461beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
17471beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
17481beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
17491beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
17501beb5c7bSAdrian Hunter		if sub_window_count == 0:
17511beb5c7bSAdrian Hunter			return
17521beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
17531beb5c7bSAdrian Hunter		nr = 1
17541beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
17551beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
17561beb5c7bSAdrian Hunter			if nr < 10:
17571beb5c7bSAdrian Hunter				label = "&" + label
17581beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
17591beb5c7bSAdrian Hunter			action.setCheckable(True)
17601beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
17611beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
17621beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
17631beb5c7bSAdrian Hunter			nr += 1
17641beb5c7bSAdrian Hunter
17651beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
17661beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
17671beb5c7bSAdrian Hunter
176882f68e28SAdrian Hunter# Font resize
176982f68e28SAdrian Hunter
177082f68e28SAdrian Hunterdef ResizeFont(widget, diff):
177182f68e28SAdrian Hunter	font = widget.font()
177282f68e28SAdrian Hunter	sz = font.pointSize()
177382f68e28SAdrian Hunter	font.setPointSize(sz + diff)
177482f68e28SAdrian Hunter	widget.setFont(font)
177582f68e28SAdrian Hunter
177682f68e28SAdrian Hunterdef ShrinkFont(widget):
177782f68e28SAdrian Hunter	ResizeFont(widget, -1)
177882f68e28SAdrian Hunter
177982f68e28SAdrian Hunterdef EnlargeFont(widget):
178082f68e28SAdrian Hunter	ResizeFont(widget, 1)
178182f68e28SAdrian Hunter
17821beb5c7bSAdrian Hunter# Unique name for sub-windows
17831beb5c7bSAdrian Hunter
17841beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
17851beb5c7bSAdrian Hunter	if nr > 1:
17861beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
17871beb5c7bSAdrian Hunter	return name
17881beb5c7bSAdrian Hunter
17891beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
17901beb5c7bSAdrian Hunter	nr = 1
17911beb5c7bSAdrian Hunter	while True:
17921beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
17931beb5c7bSAdrian Hunter		ok = True
17941beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
17951beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
17961beb5c7bSAdrian Hunter				ok = False
17971beb5c7bSAdrian Hunter				break
17981beb5c7bSAdrian Hunter		if ok:
17991beb5c7bSAdrian Hunter			return unique_name
18001beb5c7bSAdrian Hunter		nr += 1
18011beb5c7bSAdrian Hunter
18021beb5c7bSAdrian Hunter# Add a sub-window
18031beb5c7bSAdrian Hunter
18041beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
18051beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
18061beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
18071beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
18081beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
18091beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
18101beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
18111beb5c7bSAdrian Hunter	sub_window.name = unique_name
18121beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
18131beb5c7bSAdrian Hunter	sub_window.show()
18141beb5c7bSAdrian Hunter
1815031c2a00SAdrian Hunter# Main window
1816031c2a00SAdrian Hunter
1817031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
1818031c2a00SAdrian Hunter
1819031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
1820031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
1821031c2a00SAdrian Hunter
1822031c2a00SAdrian Hunter		self.glb = glb
1823031c2a00SAdrian Hunter
18241beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
1825031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
1826031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
1827031c2a00SAdrian Hunter
18281beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
18291beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
18301beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
1831031c2a00SAdrian Hunter
18321beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
1833031c2a00SAdrian Hunter
18341beb5c7bSAdrian Hunter		menu = self.menuBar()
1835031c2a00SAdrian Hunter
18361beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
18371beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
18381beb5c7bSAdrian Hunter
1839ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
1840ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
18418392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
184282f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
184382f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
1844ebd70c7dSAdrian Hunter
18451beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
18461beb5c7bSAdrian Hunter		reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
18471beb5c7bSAdrian Hunter
1848*76099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
1849*76099f98SAdrian Hunter
18508392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
18518392b74bSAdrian Hunter
18521beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
18531beb5c7bSAdrian Hunter
1854ebd70c7dSAdrian Hunter	def Find(self):
1855ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
1856ebd70c7dSAdrian Hunter		if win:
1857ebd70c7dSAdrian Hunter			try:
1858ebd70c7dSAdrian Hunter				win.find_bar.Activate()
1859ebd70c7dSAdrian Hunter			except:
1860ebd70c7dSAdrian Hunter				pass
1861ebd70c7dSAdrian Hunter
18628392b74bSAdrian Hunter	def FetchMoreRecords(self):
18638392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
18648392b74bSAdrian Hunter		if win:
18658392b74bSAdrian Hunter			try:
18668392b74bSAdrian Hunter				win.fetch_bar.Activate()
18678392b74bSAdrian Hunter			except:
18688392b74bSAdrian Hunter				pass
18698392b74bSAdrian Hunter
187082f68e28SAdrian Hunter	def ShrinkFont(self):
187182f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
187282f68e28SAdrian Hunter		ShrinkFont(win.view)
187382f68e28SAdrian Hunter
187482f68e28SAdrian Hunter	def EnlargeFont(self):
187582f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
187682f68e28SAdrian Hunter		EnlargeFont(win.view)
187782f68e28SAdrian Hunter
1878*76099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
1879*76099f98SAdrian Hunter		branches_events = 0
1880*76099f98SAdrian Hunter		for event in events:
1881*76099f98SAdrian Hunter			event = event.split(":")[0]
1882*76099f98SAdrian Hunter			if event == "branches":
1883*76099f98SAdrian Hunter				branches_events += 1
1884*76099f98SAdrian Hunter		dbid = 0
1885*76099f98SAdrian Hunter		for event in events:
1886*76099f98SAdrian Hunter			dbid += 1
1887*76099f98SAdrian Hunter			event = event.split(":")[0]
1888*76099f98SAdrian Hunter			if event == "branches":
1889*76099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
1890*76099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
1891*76099f98SAdrian Hunter
18928392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
18938392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
18948392b74bSAdrian Hunter		for table in tables:
18958392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
18968392b74bSAdrian Hunter
18971beb5c7bSAdrian Hunter	def NewCallGraph(self):
18981beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
1899031c2a00SAdrian Hunter
1900*76099f98SAdrian Hunter	def NewBranchView(self, event_id):
1901*76099f98SAdrian Hunter		BranchWindow(self.glb, event_id, "", "", self)
1902*76099f98SAdrian Hunter
19038392b74bSAdrian Hunter	def NewTableView(self, table_name):
19048392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
19058392b74bSAdrian Hunter
1906*76099f98SAdrian Hunter# XED Disassembler
1907*76099f98SAdrian Hunter
1908*76099f98SAdrian Hunterclass xed_state_t(Structure):
1909*76099f98SAdrian Hunter
1910*76099f98SAdrian Hunter	_fields_ = [
1911*76099f98SAdrian Hunter		("mode", c_int),
1912*76099f98SAdrian Hunter		("width", c_int)
1913*76099f98SAdrian Hunter	]
1914*76099f98SAdrian Hunter
1915*76099f98SAdrian Hunterclass XEDInstruction():
1916*76099f98SAdrian Hunter
1917*76099f98SAdrian Hunter	def __init__(self, libxed):
1918*76099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
1919*76099f98SAdrian Hunter		xedd_t = c_byte * 512
1920*76099f98SAdrian Hunter		self.xedd = xedd_t()
1921*76099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
1922*76099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
1923*76099f98SAdrian Hunter		self.state = xed_state_t()
1924*76099f98SAdrian Hunter		self.statep = addressof(self.state)
1925*76099f98SAdrian Hunter		# Buffer for disassembled instruction text
1926*76099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
1927*76099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
1928*76099f98SAdrian Hunter
1929*76099f98SAdrian Hunterclass LibXED():
1930*76099f98SAdrian Hunter
1931*76099f98SAdrian Hunter	def __init__(self):
1932*76099f98SAdrian Hunter		self.libxed = CDLL("libxed.so")
1933*76099f98SAdrian Hunter
1934*76099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
1935*76099f98SAdrian Hunter		self.xed_tables_init.restype = None
1936*76099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
1937*76099f98SAdrian Hunter
1938*76099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
1939*76099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
1940*76099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
1941*76099f98SAdrian Hunter
1942*76099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
1943*76099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
1944*76099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
1945*76099f98SAdrian Hunter
1946*76099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
1947*76099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
1948*76099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
1949*76099f98SAdrian Hunter
1950*76099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
1951*76099f98SAdrian Hunter		self.xed_decode.restype = c_int
1952*76099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
1953*76099f98SAdrian Hunter
1954*76099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
1955*76099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
1956*76099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
1957*76099f98SAdrian Hunter
1958*76099f98SAdrian Hunter		self.xed_tables_init()
1959*76099f98SAdrian Hunter
1960*76099f98SAdrian Hunter	def Instruction(self):
1961*76099f98SAdrian Hunter		return XEDInstruction(self)
1962*76099f98SAdrian Hunter
1963*76099f98SAdrian Hunter	def SetMode(self, inst, mode):
1964*76099f98SAdrian Hunter		if mode:
1965*76099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
1966*76099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
1967*76099f98SAdrian Hunter		else:
1968*76099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
1969*76099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
1970*76099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
1971*76099f98SAdrian Hunter
1972*76099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
1973*76099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
1974*76099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
1975*76099f98SAdrian Hunter		if err:
1976*76099f98SAdrian Hunter			return 0, ""
1977*76099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
1978*76099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
1979*76099f98SAdrian Hunter		if not ok:
1980*76099f98SAdrian Hunter			return 0, ""
1981*76099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
1982*76099f98SAdrian Hunter		# For now, assume the length is in byte 166
1983*76099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
1984*76099f98SAdrian Hunter
1985*76099f98SAdrian Hunterdef TryOpen(file_name):
1986*76099f98SAdrian Hunter	try:
1987*76099f98SAdrian Hunter		return open(file_name, "rb")
1988*76099f98SAdrian Hunter	except:
1989*76099f98SAdrian Hunter		return None
1990*76099f98SAdrian Hunter
1991*76099f98SAdrian Hunterdef Is64Bit(f):
1992*76099f98SAdrian Hunter	result = sizeof(c_void_p)
1993*76099f98SAdrian Hunter	# ELF support only
1994*76099f98SAdrian Hunter	pos = f.tell()
1995*76099f98SAdrian Hunter	f.seek(0)
1996*76099f98SAdrian Hunter	header = f.read(7)
1997*76099f98SAdrian Hunter	f.seek(pos)
1998*76099f98SAdrian Hunter	magic = header[0:4]
1999*76099f98SAdrian Hunter	eclass = ord(header[4])
2000*76099f98SAdrian Hunter	encoding = ord(header[5])
2001*76099f98SAdrian Hunter	version = ord(header[6])
2002*76099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
2003*76099f98SAdrian Hunter		result = True if eclass == 2 else False
2004*76099f98SAdrian Hunter	return result
2005*76099f98SAdrian Hunter
2006031c2a00SAdrian Hunter# Global data
2007031c2a00SAdrian Hunter
2008031c2a00SAdrian Hunterclass Glb():
2009031c2a00SAdrian Hunter
2010031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2011031c2a00SAdrian Hunter		self.dbref = dbref
2012031c2a00SAdrian Hunter		self.db = db
2013031c2a00SAdrian Hunter		self.dbname = dbname
2014*76099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
2015*76099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
2016*76099f98SAdrian Hunter		if self.buildid_dir:
2017*76099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
2018*76099f98SAdrian Hunter		else:
2019*76099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2020031c2a00SAdrian Hunter		self.app = None
2021031c2a00SAdrian Hunter		self.mainwindow = None
20228392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
2023*76099f98SAdrian Hunter		try:
2024*76099f98SAdrian Hunter			self.disassembler = LibXED()
2025*76099f98SAdrian Hunter			self.have_disassembler = True
2026*76099f98SAdrian Hunter		except:
2027*76099f98SAdrian Hunter			self.have_disassembler = False
2028*76099f98SAdrian Hunter
2029*76099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
2030*76099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
2031*76099f98SAdrian Hunter		return TryOpen(file_name)
2032*76099f98SAdrian Hunter
2033*76099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
2034*76099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
2035*76099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
2036*76099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
2037*76099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
2038*76099f98SAdrian Hunter			if f:
2039*76099f98SAdrian Hunter				return f
2040*76099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
2041*76099f98SAdrian Hunter			f = TryOpen(long_name)
2042*76099f98SAdrian Hunter			if f:
2043*76099f98SAdrian Hunter				return f
2044*76099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
2045*76099f98SAdrian Hunter		if f:
2046*76099f98SAdrian Hunter			return f
2047*76099f98SAdrian Hunter		return None
20488392b74bSAdrian Hunter
20498392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
20508392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
20518392b74bSAdrian Hunter
20528392b74bSAdrian Hunter	# Shutdown any background processes or threads
20538392b74bSAdrian Hunter	def ShutdownInstances(self):
20548392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
20558392b74bSAdrian Hunter			try:
20568392b74bSAdrian Hunter				x.Shutdown()
20578392b74bSAdrian Hunter			except:
20588392b74bSAdrian Hunter				pass
2059031c2a00SAdrian Hunter
2060031c2a00SAdrian Hunter# Database reference
2061031c2a00SAdrian Hunter
2062031c2a00SAdrian Hunterclass DBRef():
2063031c2a00SAdrian Hunter
2064031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2065031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2066031c2a00SAdrian Hunter		self.dbname = dbname
2067031c2a00SAdrian Hunter
2068031c2a00SAdrian Hunter	def Open(self, connection_name):
2069031c2a00SAdrian Hunter		dbname = self.dbname
2070031c2a00SAdrian Hunter		if self.is_sqlite3:
2071031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2072031c2a00SAdrian Hunter		else:
2073031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2074031c2a00SAdrian Hunter			opts = dbname.split()
2075031c2a00SAdrian Hunter			for opt in opts:
2076031c2a00SAdrian Hunter				if "=" in opt:
2077031c2a00SAdrian Hunter					opt = opt.split("=")
2078031c2a00SAdrian Hunter					if opt[0] == "hostname":
2079031c2a00SAdrian Hunter						db.setHostName(opt[1])
2080031c2a00SAdrian Hunter					elif opt[0] == "port":
2081031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2082031c2a00SAdrian Hunter					elif opt[0] == "username":
2083031c2a00SAdrian Hunter						db.setUserName(opt[1])
2084031c2a00SAdrian Hunter					elif opt[0] == "password":
2085031c2a00SAdrian Hunter						db.setPassword(opt[1])
2086031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2087031c2a00SAdrian Hunter						dbname = opt[1]
2088031c2a00SAdrian Hunter				else:
2089031c2a00SAdrian Hunter					dbname = opt
2090031c2a00SAdrian Hunter
2091031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2092031c2a00SAdrian Hunter		if not db.open():
2093031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2094031c2a00SAdrian Hunter		return db, dbname
2095031c2a00SAdrian Hunter
2096031c2a00SAdrian Hunter# Main
2097031c2a00SAdrian Hunter
2098031c2a00SAdrian Hunterdef Main():
2099031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
2100031c2a00SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>"
2101031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2102031c2a00SAdrian Hunter
2103031c2a00SAdrian Hunter	dbname = sys.argv[1]
2104031c2a00SAdrian Hunter
2105031c2a00SAdrian Hunter	is_sqlite3 = False
2106031c2a00SAdrian Hunter	try:
2107031c2a00SAdrian Hunter		f = open(dbname)
2108031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
2109031c2a00SAdrian Hunter			is_sqlite3 = True
2110031c2a00SAdrian Hunter		f.close()
2111031c2a00SAdrian Hunter	except:
2112031c2a00SAdrian Hunter		pass
2113031c2a00SAdrian Hunter
2114031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
2115031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
2116031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
2117031c2a00SAdrian Hunter	app = QApplication(sys.argv)
2118031c2a00SAdrian Hunter	glb.app = app
2119031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
2120031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
2121031c2a00SAdrian Hunter	mainwindow.show()
2122031c2a00SAdrian Hunter	err = app.exec_()
21238392b74bSAdrian Hunter	glb.ShutdownInstances()
2124031c2a00SAdrian Hunter	db.close()
2125031c2a00SAdrian Hunter	sys.exit(err)
2126031c2a00SAdrian Hunter
2127031c2a00SAdrian Hunterif __name__ == "__main__":
2128031c2a00SAdrian Hunter	Main()
2129