1b3a67546SAdrian Hunter#!/usr/bin/env python2
2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5031c2a00SAdrian Hunter
6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8031c2a00SAdrian Hunter# scripts for details.
9031c2a00SAdrian Hunter#
10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12031c2a00SAdrian Hunter#
13031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14031c2a00SAdrian Hunter#
15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17031c2a00SAdrian Hunter#
18031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19031c2a00SAdrian Hunter#
20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22031c2a00SAdrian Hunter# widths to suit will display something like:
23031c2a00SAdrian Hunter#
24031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26031c2a00SAdrian Hunter# v- ls
27031c2a00SAdrian Hunter#     v- 2638:2638
28031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38031c2a00SAdrian Hunter#
39031c2a00SAdrian Hunter# Points to note:
40031c2a00SAdrian Hunter#	The top level is a command name (comm)
41031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42031c2a00SAdrian Hunter#	Subsequent levels are functions
43031c2a00SAdrian Hunter#	'Count' is the number of calls
44031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45031c2a00SAdrian Hunter#	Percentages are relative to the level above
46031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47031c2a00SAdrian Hunter#       functions that it calls
48031c2a00SAdrian Hunter
4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
5076099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
5376099f98SAdrian Hunter# libxed.so:
5476099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
5576099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
5676099f98SAdrian Hunter#            cd xed
5776099f98SAdrian Hunter#            ./mfile.py --share
5876099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
5976099f98SAdrian Hunter#            sudo ldconfig
6076099f98SAdrian Hunter#
6176099f98SAdrian Hunter# Example report:
6276099f98SAdrian Hunter#
6376099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6476099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6576099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6676099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6776099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6876099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6976099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7076099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7176099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
7276099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7376099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
7476099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
7576099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
7676099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7776099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
7876099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7976099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8076099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
8176099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8276099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8376099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8476099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8576099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8676099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8776099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8876099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8976099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9076099f98SAdrian Hunter
91031c2a00SAdrian Hunterimport sys
921beb5c7bSAdrian Hunterimport weakref
931beb5c7bSAdrian Hunterimport threading
94ebd70c7dSAdrian Hunterimport string
958392b74bSAdrian Hunterimport cPickle
968392b74bSAdrian Hunterimport re
978392b74bSAdrian Hunterimport os
98031c2a00SAdrian Hunterfrom PySide.QtCore import *
99031c2a00SAdrian Hunterfrom PySide.QtGui import *
100031c2a00SAdrian Hunterfrom PySide.QtSql import *
101031c2a00SAdrian Hunterfrom decimal import *
1028392b74bSAdrian Hunterfrom ctypes import *
1038392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
104031c2a00SAdrian Hunter
105031c2a00SAdrian Hunter# Data formatting helpers
106031c2a00SAdrian Hunter
10776099f98SAdrian Hunterdef tohex(ip):
10876099f98SAdrian Hunter	if ip < 0:
10976099f98SAdrian Hunter		ip += 1 << 64
11076099f98SAdrian Hunter	return "%x" % ip
11176099f98SAdrian Hunter
11276099f98SAdrian Hunterdef offstr(offset):
11376099f98SAdrian Hunter	if offset:
11476099f98SAdrian Hunter		return "+0x%x" % offset
11576099f98SAdrian Hunter	return ""
11676099f98SAdrian Hunter
117031c2a00SAdrian Hunterdef dsoname(name):
118031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
119031c2a00SAdrian Hunter		return "[kernel]"
120031c2a00SAdrian Hunter	return name
121031c2a00SAdrian Hunter
122210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
123210cf1f9SAdrian Hunter	pos = s.find(sub)
124210cf1f9SAdrian Hunter	if pos < 0:
125210cf1f9SAdrian Hunter		return pos
126210cf1f9SAdrian Hunter	if n <= 1:
127210cf1f9SAdrian Hunter		return offs + pos
128210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
129210cf1f9SAdrian Hunter
130031c2a00SAdrian Hunter# Percent to one decimal place
131031c2a00SAdrian Hunter
132031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
133031c2a00SAdrian Hunter	if not d:
134031c2a00SAdrian Hunter		return "0.0"
135031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
136031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
137031c2a00SAdrian Hunter
138031c2a00SAdrian Hunter# Helper for queries that must not fail
139031c2a00SAdrian Hunter
140031c2a00SAdrian Hunterdef QueryExec(query, stmt):
141031c2a00SAdrian Hunter	ret = query.exec_(stmt)
142031c2a00SAdrian Hunter	if not ret:
143031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
144031c2a00SAdrian Hunter
145ebd70c7dSAdrian Hunter# Background thread
146ebd70c7dSAdrian Hunter
147ebd70c7dSAdrian Hunterclass Thread(QThread):
148ebd70c7dSAdrian Hunter
149ebd70c7dSAdrian Hunter	done = Signal(object)
150ebd70c7dSAdrian Hunter
151ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
152ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
153ebd70c7dSAdrian Hunter		self.task = task
154ebd70c7dSAdrian Hunter		self.param = param
155ebd70c7dSAdrian Hunter
156ebd70c7dSAdrian Hunter	def run(self):
157ebd70c7dSAdrian Hunter		while True:
158ebd70c7dSAdrian Hunter			if self.param is None:
159ebd70c7dSAdrian Hunter				done, result = self.task()
160ebd70c7dSAdrian Hunter			else:
161ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
162ebd70c7dSAdrian Hunter			self.done.emit(result)
163ebd70c7dSAdrian Hunter			if done:
164ebd70c7dSAdrian Hunter				break
165ebd70c7dSAdrian Hunter
166031c2a00SAdrian Hunter# Tree data model
167031c2a00SAdrian Hunter
168031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
169031c2a00SAdrian Hunter
170*a448ba23SAdrian Hunter	def __init__(self, glb, parent=None):
171031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
172*a448ba23SAdrian Hunter		self.glb = glb
173*a448ba23SAdrian Hunter		self.root = self.GetRoot()
174031c2a00SAdrian Hunter		self.last_row_read = 0
175031c2a00SAdrian Hunter
176031c2a00SAdrian Hunter	def Item(self, parent):
177031c2a00SAdrian Hunter		if parent.isValid():
178031c2a00SAdrian Hunter			return parent.internalPointer()
179031c2a00SAdrian Hunter		else:
180031c2a00SAdrian Hunter			return self.root
181031c2a00SAdrian Hunter
182031c2a00SAdrian Hunter	def rowCount(self, parent):
183031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
184031c2a00SAdrian Hunter		if result < 0:
185031c2a00SAdrian Hunter			result = 0
186031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
187031c2a00SAdrian Hunter		return result
188031c2a00SAdrian Hunter
189031c2a00SAdrian Hunter	def hasChildren(self, parent):
190031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
191031c2a00SAdrian Hunter
192031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
193031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
194031c2a00SAdrian Hunter			return self.columnAlignment(section)
195031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
196031c2a00SAdrian Hunter			return None
197031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
198031c2a00SAdrian Hunter			return None
199031c2a00SAdrian Hunter		return self.columnHeader(section)
200031c2a00SAdrian Hunter
201031c2a00SAdrian Hunter	def parent(self, child):
202031c2a00SAdrian Hunter		child_item = child.internalPointer()
203031c2a00SAdrian Hunter		if child_item is self.root:
204031c2a00SAdrian Hunter			return QModelIndex()
205031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
206031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
207031c2a00SAdrian Hunter
208031c2a00SAdrian Hunter	def index(self, row, column, parent):
209031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
210031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
211031c2a00SAdrian Hunter
212031c2a00SAdrian Hunter	def DisplayData(self, item, index):
213031c2a00SAdrian Hunter		return item.getData(index.column())
214031c2a00SAdrian Hunter
2158392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2168392b74bSAdrian Hunter		if row > self.last_row_read:
2178392b74bSAdrian Hunter			self.last_row_read = row
2188392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2198392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2208392b74bSAdrian Hunter
2218392b74bSAdrian Hunter	def columnAlignment(self, column):
2228392b74bSAdrian Hunter		return Qt.AlignLeft
2238392b74bSAdrian Hunter
2248392b74bSAdrian Hunter	def columnFont(self, column):
2258392b74bSAdrian Hunter		return None
2268392b74bSAdrian Hunter
2278392b74bSAdrian Hunter	def data(self, index, role):
2288392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2298392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2308392b74bSAdrian Hunter		if role == Qt.FontRole:
2318392b74bSAdrian Hunter			return self.columnFont(index.column())
2328392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2338392b74bSAdrian Hunter			return None
2348392b74bSAdrian Hunter		item = index.internalPointer()
2358392b74bSAdrian Hunter		return self.DisplayData(item, index)
2368392b74bSAdrian Hunter
2378392b74bSAdrian Hunter# Table data model
2388392b74bSAdrian Hunter
2398392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2408392b74bSAdrian Hunter
2418392b74bSAdrian Hunter	def __init__(self, parent=None):
2428392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2438392b74bSAdrian Hunter		self.child_count = 0
2448392b74bSAdrian Hunter		self.child_items = []
2458392b74bSAdrian Hunter		self.last_row_read = 0
2468392b74bSAdrian Hunter
2478392b74bSAdrian Hunter	def Item(self, parent):
2488392b74bSAdrian Hunter		if parent.isValid():
2498392b74bSAdrian Hunter			return parent.internalPointer()
2508392b74bSAdrian Hunter		else:
2518392b74bSAdrian Hunter			return self
2528392b74bSAdrian Hunter
2538392b74bSAdrian Hunter	def rowCount(self, parent):
2548392b74bSAdrian Hunter		return self.child_count
2558392b74bSAdrian Hunter
2568392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2578392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2588392b74bSAdrian Hunter			return self.columnAlignment(section)
2598392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2608392b74bSAdrian Hunter			return None
2618392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2628392b74bSAdrian Hunter			return None
2638392b74bSAdrian Hunter		return self.columnHeader(section)
2648392b74bSAdrian Hunter
2658392b74bSAdrian Hunter	def index(self, row, column, parent):
2668392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2678392b74bSAdrian Hunter
2688392b74bSAdrian Hunter	def DisplayData(self, item, index):
2698392b74bSAdrian Hunter		return item.getData(index.column())
2708392b74bSAdrian Hunter
2718392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2728392b74bSAdrian Hunter		if row > self.last_row_read:
2738392b74bSAdrian Hunter			self.last_row_read = row
2748392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2758392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2768392b74bSAdrian Hunter
277031c2a00SAdrian Hunter	def columnAlignment(self, column):
278031c2a00SAdrian Hunter		return Qt.AlignLeft
279031c2a00SAdrian Hunter
280031c2a00SAdrian Hunter	def columnFont(self, column):
281031c2a00SAdrian Hunter		return None
282031c2a00SAdrian Hunter
283031c2a00SAdrian Hunter	def data(self, index, role):
284031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
285031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
286031c2a00SAdrian Hunter		if role == Qt.FontRole:
287031c2a00SAdrian Hunter			return self.columnFont(index.column())
288031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
289031c2a00SAdrian Hunter			return None
290031c2a00SAdrian Hunter		item = index.internalPointer()
291031c2a00SAdrian Hunter		return self.DisplayData(item, index)
292031c2a00SAdrian Hunter
2931beb5c7bSAdrian Hunter# Model cache
2941beb5c7bSAdrian Hunter
2951beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
2961beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
2971beb5c7bSAdrian Hunter
2981beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
2991beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3001beb5c7bSAdrian Hunter	try:
3011beb5c7bSAdrian Hunter		model = model_cache[model_name]
3021beb5c7bSAdrian Hunter	except:
3031beb5c7bSAdrian Hunter		model = None
3041beb5c7bSAdrian Hunter	if model is None:
3051beb5c7bSAdrian Hunter		model = create_fn()
3061beb5c7bSAdrian Hunter		model_cache[model_name] = model
3071beb5c7bSAdrian Hunter	model_cache_lock.release()
3081beb5c7bSAdrian Hunter	return model
3091beb5c7bSAdrian Hunter
310ebd70c7dSAdrian Hunter# Find bar
311ebd70c7dSAdrian Hunter
312ebd70c7dSAdrian Hunterclass FindBar():
313ebd70c7dSAdrian Hunter
314ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
315ebd70c7dSAdrian Hunter		self.finder = finder
316ebd70c7dSAdrian Hunter		self.context = []
317ebd70c7dSAdrian Hunter		self.last_value = None
318ebd70c7dSAdrian Hunter		self.last_pattern = None
319ebd70c7dSAdrian Hunter
320ebd70c7dSAdrian Hunter		label = QLabel("Find:")
321ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
322ebd70c7dSAdrian Hunter
323ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
324ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
325ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
326ebd70c7dSAdrian Hunter
327ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
328ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
329ebd70c7dSAdrian Hunter		self.progress.hide()
330ebd70c7dSAdrian Hunter
331ebd70c7dSAdrian Hunter		if is_reg_expr:
332ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
333ebd70c7dSAdrian Hunter		else:
334ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
335ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
336ebd70c7dSAdrian Hunter
337ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
338ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
339ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
340ebd70c7dSAdrian Hunter
341ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
342ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
343ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
344ebd70c7dSAdrian Hunter
345ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
346ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
347ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
348ebd70c7dSAdrian Hunter
349ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
350ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
351ebd70c7dSAdrian Hunter
352ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
353ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
354ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
355ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
356ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
357ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
358ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
359ebd70c7dSAdrian Hunter
360ebd70c7dSAdrian Hunter		self.bar = QWidget()
361ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
362ebd70c7dSAdrian Hunter		self.bar.hide()
363ebd70c7dSAdrian Hunter
364ebd70c7dSAdrian Hunter	def Widget(self):
365ebd70c7dSAdrian Hunter		return self.bar
366ebd70c7dSAdrian Hunter
367ebd70c7dSAdrian Hunter	def Activate(self):
368ebd70c7dSAdrian Hunter		self.bar.show()
369ebd70c7dSAdrian Hunter		self.textbox.setFocus()
370ebd70c7dSAdrian Hunter
371ebd70c7dSAdrian Hunter	def Deactivate(self):
372ebd70c7dSAdrian Hunter		self.bar.hide()
373ebd70c7dSAdrian Hunter
374ebd70c7dSAdrian Hunter	def Busy(self):
375ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
376ebd70c7dSAdrian Hunter		self.pattern.hide()
377ebd70c7dSAdrian Hunter		self.next_button.hide()
378ebd70c7dSAdrian Hunter		self.prev_button.hide()
379ebd70c7dSAdrian Hunter		self.progress.show()
380ebd70c7dSAdrian Hunter
381ebd70c7dSAdrian Hunter	def Idle(self):
382ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
383ebd70c7dSAdrian Hunter		self.progress.hide()
384ebd70c7dSAdrian Hunter		self.pattern.show()
385ebd70c7dSAdrian Hunter		self.next_button.show()
386ebd70c7dSAdrian Hunter		self.prev_button.show()
387ebd70c7dSAdrian Hunter
388ebd70c7dSAdrian Hunter	def Find(self, direction):
389ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
390ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
391ebd70c7dSAdrian Hunter		self.last_value = value
392ebd70c7dSAdrian Hunter		self.last_pattern = pattern
393ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
394ebd70c7dSAdrian Hunter
395ebd70c7dSAdrian Hunter	def ValueChanged(self):
396ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
397ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
398ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
399ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
400ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
401ebd70c7dSAdrian Hunter		if data == None:
402ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
403ebd70c7dSAdrian Hunter		else:
404ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
405ebd70c7dSAdrian Hunter		self.Find(0)
406ebd70c7dSAdrian Hunter
407ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
408ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
409ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
410ebd70c7dSAdrian Hunter		if value != self.last_value:
411ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
412ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
413ebd70c7dSAdrian Hunter			if index < 0:
414ebd70c7dSAdrian Hunter				index = self.textbox.count()
415ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
416ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
417ebd70c7dSAdrian Hunter				return
418ebd70c7dSAdrian Hunter			else:
419ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
420ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
421ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
422ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
423ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
424ebd70c7dSAdrian Hunter		self.Find(direction)
425ebd70c7dSAdrian Hunter
426ebd70c7dSAdrian Hunter	def NotFound(self):
427ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
428ebd70c7dSAdrian Hunter
429031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
430031c2a00SAdrian Hunter
431031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
432031c2a00SAdrian Hunter
433031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
434031c2a00SAdrian Hunter		self.glb = glb
435031c2a00SAdrian Hunter		self.row = row
436031c2a00SAdrian Hunter		self.parent_item = parent_item
437031c2a00SAdrian Hunter		self.query_done = False;
438031c2a00SAdrian Hunter		self.child_count = 0
439031c2a00SAdrian Hunter		self.child_items = []
440031c2a00SAdrian Hunter
441031c2a00SAdrian Hunter	def getChildItem(self, row):
442031c2a00SAdrian Hunter		return self.child_items[row]
443031c2a00SAdrian Hunter
444031c2a00SAdrian Hunter	def getParentItem(self):
445031c2a00SAdrian Hunter		return self.parent_item
446031c2a00SAdrian Hunter
447031c2a00SAdrian Hunter	def getRow(self):
448031c2a00SAdrian Hunter		return self.row
449031c2a00SAdrian Hunter
450031c2a00SAdrian Hunter	def childCount(self):
451031c2a00SAdrian Hunter		if not self.query_done:
452031c2a00SAdrian Hunter			self.Select()
453031c2a00SAdrian Hunter			if not self.child_count:
454031c2a00SAdrian Hunter				return -1
455031c2a00SAdrian Hunter		return self.child_count
456031c2a00SAdrian Hunter
457031c2a00SAdrian Hunter	def hasChildren(self):
458031c2a00SAdrian Hunter		if not self.query_done:
459031c2a00SAdrian Hunter			return True
460031c2a00SAdrian Hunter		return self.child_count > 0
461031c2a00SAdrian Hunter
462031c2a00SAdrian Hunter	def getData(self, column):
463031c2a00SAdrian Hunter		return self.data[column]
464031c2a00SAdrian Hunter
465031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
466031c2a00SAdrian Hunter
467031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
468031c2a00SAdrian Hunter
469031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
470031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
471031c2a00SAdrian Hunter		self.comm_id = comm_id
472031c2a00SAdrian Hunter		self.thread_id = thread_id
473031c2a00SAdrian Hunter		self.call_path_id = call_path_id
474031c2a00SAdrian Hunter		self.branch_count = branch_count
475031c2a00SAdrian Hunter		self.time = time
476031c2a00SAdrian Hunter
477031c2a00SAdrian Hunter	def Select(self):
478031c2a00SAdrian Hunter		self.query_done = True;
479031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
480031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
481031c2a00SAdrian Hunter					" FROM calls"
482031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
483031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
484031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
485031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
486031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
487031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
488031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
489031c2a00SAdrian Hunter					" ORDER BY call_path_id")
490031c2a00SAdrian Hunter		while query.next():
491031c2a00SAdrian 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)
492031c2a00SAdrian Hunter			self.child_items.append(child_item)
493031c2a00SAdrian Hunter			self.child_count += 1
494031c2a00SAdrian Hunter
495031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
496031c2a00SAdrian Hunter
497031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
498031c2a00SAdrian Hunter
499031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
500031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
501031c2a00SAdrian Hunter		dso = dsoname(dso)
502031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
503031c2a00SAdrian Hunter		self.dbid = call_path_id
504031c2a00SAdrian Hunter
505031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
506031c2a00SAdrian Hunter
507031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
508031c2a00SAdrian Hunter
509031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
510031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
511031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
512031c2a00SAdrian Hunter		self.dbid = thread_id
513031c2a00SAdrian Hunter
514031c2a00SAdrian Hunter	def Select(self):
515031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
516031c2a00SAdrian Hunter		for child_item in self.child_items:
517031c2a00SAdrian Hunter			self.time += child_item.time
518031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
519031c2a00SAdrian Hunter		for child_item in self.child_items:
520031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
521031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
522031c2a00SAdrian Hunter
523031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
524031c2a00SAdrian Hunter
525031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
526031c2a00SAdrian Hunter
527031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
528031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
529031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
530031c2a00SAdrian Hunter		self.dbid = comm_id
531031c2a00SAdrian Hunter
532031c2a00SAdrian Hunter	def Select(self):
533031c2a00SAdrian Hunter		self.query_done = True;
534031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
535031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
536031c2a00SAdrian Hunter					" FROM comm_threads"
537031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
538031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
539031c2a00SAdrian Hunter		while query.next():
540031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
541031c2a00SAdrian Hunter			self.child_items.append(child_item)
542031c2a00SAdrian Hunter			self.child_count += 1
543031c2a00SAdrian Hunter
544031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
545031c2a00SAdrian Hunter
546031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
547031c2a00SAdrian Hunter
548031c2a00SAdrian Hunter	def __init__(self, glb):
549031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
550031c2a00SAdrian Hunter		self.dbid = 0
551031c2a00SAdrian Hunter		self.query_done = True;
552031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
553031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
554031c2a00SAdrian Hunter		while query.next():
555031c2a00SAdrian Hunter			if not query.value(0):
556031c2a00SAdrian Hunter				continue
557031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
558031c2a00SAdrian Hunter			self.child_items.append(child_item)
559031c2a00SAdrian Hunter			self.child_count += 1
560031c2a00SAdrian Hunter
561031c2a00SAdrian Hunter# Context-sensitive call graph data model
562031c2a00SAdrian Hunter
563031c2a00SAdrian Hunterclass CallGraphModel(TreeModel):
564031c2a00SAdrian Hunter
565031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
566*a448ba23SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
567*a448ba23SAdrian Hunter
568*a448ba23SAdrian Hunter	def GetRoot(self):
569*a448ba23SAdrian Hunter		return CallGraphRootItem(self.glb)
570031c2a00SAdrian Hunter
571031c2a00SAdrian Hunter	def columnCount(self, parent=None):
572031c2a00SAdrian Hunter		return 7
573031c2a00SAdrian Hunter
574031c2a00SAdrian Hunter	def columnHeader(self, column):
575031c2a00SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
576031c2a00SAdrian Hunter		return headers[column]
577031c2a00SAdrian Hunter
578031c2a00SAdrian Hunter	def columnAlignment(self, column):
579031c2a00SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
580031c2a00SAdrian Hunter		return alignment[column]
581031c2a00SAdrian Hunter
582ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
583ebd70c7dSAdrian Hunter		if pattern:
584ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
585ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
586ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
587ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
588ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
589ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
590ebd70c7dSAdrian Hunter				# Escape % and _
591ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
592ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
593ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
594ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
595ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
596ebd70c7dSAdrian Hunter			else:
597ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
598ebd70c7dSAdrian Hunter		else:
599ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
600ebd70c7dSAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
601ebd70c7dSAdrian Hunter						" FROM calls"
602ebd70c7dSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
603ebd70c7dSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
604ebd70c7dSAdrian Hunter						" WHERE symbols.name" + match +
605ebd70c7dSAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
606ebd70c7dSAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
607ebd70c7dSAdrian Hunter
608ebd70c7dSAdrian Hunter	def FindPath(self, query):
609ebd70c7dSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
610ebd70c7dSAdrian Hunter		# to open the tree at the right place.
611ebd70c7dSAdrian Hunter		ids = []
612ebd70c7dSAdrian Hunter		parent_id = query.value(0)
613ebd70c7dSAdrian Hunter		while parent_id:
614ebd70c7dSAdrian Hunter			ids.insert(0, parent_id)
615ebd70c7dSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
616ebd70c7dSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
617ebd70c7dSAdrian Hunter					" FROM call_paths"
618ebd70c7dSAdrian Hunter					" WHERE id = " + str(parent_id))
619ebd70c7dSAdrian Hunter			if not q2.next():
620ebd70c7dSAdrian Hunter				break
621ebd70c7dSAdrian Hunter			parent_id = q2.value(0)
622ebd70c7dSAdrian Hunter		# The call path root is not used
623ebd70c7dSAdrian Hunter		if ids[0] == 1:
624ebd70c7dSAdrian Hunter			del ids[0]
625ebd70c7dSAdrian Hunter		ids.insert(0, query.value(2))
626ebd70c7dSAdrian Hunter		ids.insert(0, query.value(1))
627ebd70c7dSAdrian Hunter		return ids
628ebd70c7dSAdrian Hunter
629ebd70c7dSAdrian Hunter	def Found(self, query, found):
630ebd70c7dSAdrian Hunter		if found:
631ebd70c7dSAdrian Hunter			return self.FindPath(query)
632ebd70c7dSAdrian Hunter		return []
633ebd70c7dSAdrian Hunter
634ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
635ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
636ebd70c7dSAdrian Hunter			found = query.first()
637ebd70c7dSAdrian Hunter		else:
638ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
639ebd70c7dSAdrian Hunter			found = query.next()
640ebd70c7dSAdrian Hunter		return self.Found(query, found)
641ebd70c7dSAdrian Hunter
642ebd70c7dSAdrian Hunter	def FindNext(self, query):
643ebd70c7dSAdrian Hunter		found = query.next()
644ebd70c7dSAdrian Hunter		if not found:
645ebd70c7dSAdrian Hunter			found = query.first()
646ebd70c7dSAdrian Hunter		return self.Found(query, found)
647ebd70c7dSAdrian Hunter
648ebd70c7dSAdrian Hunter	def FindPrev(self, query):
649ebd70c7dSAdrian Hunter		found = query.previous()
650ebd70c7dSAdrian Hunter		if not found:
651ebd70c7dSAdrian Hunter			found = query.last()
652ebd70c7dSAdrian Hunter		return self.Found(query, found)
653ebd70c7dSAdrian Hunter
654ebd70c7dSAdrian Hunter	def FindThread(self, c):
655ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
656ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
657ebd70c7dSAdrian Hunter		elif c.direction > 0:
658ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
659ebd70c7dSAdrian Hunter		else:
660ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
661ebd70c7dSAdrian Hunter		return (True, ids)
662ebd70c7dSAdrian Hunter
663ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
664ebd70c7dSAdrian Hunter		class Context():
665ebd70c7dSAdrian Hunter			def __init__(self, *x):
666ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
667ebd70c7dSAdrian Hunter			def Update(self, *x):
668ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
669ebd70c7dSAdrian Hunter		if len(context):
670ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
671ebd70c7dSAdrian Hunter		else:
672ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
673ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
674ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
675ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
676ebd70c7dSAdrian Hunter		thread.start()
677ebd70c7dSAdrian Hunter
678ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
679ebd70c7dSAdrian Hunter		callback(ids)
680ebd70c7dSAdrian Hunter
681ebd70c7dSAdrian Hunter# Vertical widget layout
682ebd70c7dSAdrian Hunter
683ebd70c7dSAdrian Hunterclass VBox():
684ebd70c7dSAdrian Hunter
685ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
686ebd70c7dSAdrian Hunter		self.vbox = QWidget()
687ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
688ebd70c7dSAdrian Hunter
689ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
690ebd70c7dSAdrian Hunter
691ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
692ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
693ebd70c7dSAdrian Hunter		if w3:
694ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
695ebd70c7dSAdrian Hunter
696ebd70c7dSAdrian Hunter	def Widget(self):
697ebd70c7dSAdrian Hunter		return self.vbox
698ebd70c7dSAdrian Hunter
699a731cc4cSAdrian Hunter# Tree window base
7001beb5c7bSAdrian Hunter
701a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
7021beb5c7bSAdrian Hunter
703a731cc4cSAdrian Hunter	def __init__(self, parent=None):
704a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
7051beb5c7bSAdrian Hunter
706a731cc4cSAdrian Hunter		self.model = None
707a731cc4cSAdrian Hunter		self.view = None
708a731cc4cSAdrian Hunter		self.find_bar = None
7091beb5c7bSAdrian Hunter
710ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
711ebd70c7dSAdrian Hunter		if not len(ids):
712ebd70c7dSAdrian Hunter			return False
713ebd70c7dSAdrian Hunter		parent = QModelIndex()
714ebd70c7dSAdrian Hunter		for dbid in ids:
715ebd70c7dSAdrian Hunter			found = False
716ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
717ebd70c7dSAdrian Hunter			for row in xrange(n):
718ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
719ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
720ebd70c7dSAdrian Hunter					found = True
721ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
722ebd70c7dSAdrian Hunter					parent = child
723ebd70c7dSAdrian Hunter					break
724ebd70c7dSAdrian Hunter			if not found:
725ebd70c7dSAdrian Hunter				break
726ebd70c7dSAdrian Hunter		return found
727ebd70c7dSAdrian Hunter
728ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
729ebd70c7dSAdrian Hunter		self.view.setFocus()
730ebd70c7dSAdrian Hunter		self.find_bar.Busy()
731ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
732ebd70c7dSAdrian Hunter
733ebd70c7dSAdrian Hunter	def FindDone(self, ids):
734ebd70c7dSAdrian Hunter		found = True
735ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
736ebd70c7dSAdrian Hunter			found = False
737ebd70c7dSAdrian Hunter		self.find_bar.Idle()
738ebd70c7dSAdrian Hunter		if not found:
739ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
740ebd70c7dSAdrian Hunter
741a731cc4cSAdrian Hunter
742a731cc4cSAdrian Hunter# Context-sensitive call graph window
743a731cc4cSAdrian Hunter
744a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
745a731cc4cSAdrian Hunter
746a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
747a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
748a731cc4cSAdrian Hunter
749a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
750a731cc4cSAdrian Hunter
751a731cc4cSAdrian Hunter		self.view = QTreeView()
752a731cc4cSAdrian Hunter		self.view.setModel(self.model)
753a731cc4cSAdrian Hunter
754a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
755a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
756a731cc4cSAdrian Hunter
757a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
758a731cc4cSAdrian Hunter
759a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
760a731cc4cSAdrian Hunter
761a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
762a731cc4cSAdrian Hunter
763a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
764a731cc4cSAdrian Hunter
7658392b74bSAdrian Hunter# Child data item  finder
7668392b74bSAdrian Hunter
7678392b74bSAdrian Hunterclass ChildDataItemFinder():
7688392b74bSAdrian Hunter
7698392b74bSAdrian Hunter	def __init__(self, root):
7708392b74bSAdrian Hunter		self.root = root
7718392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
7728392b74bSAdrian Hunter		self.rows = []
7738392b74bSAdrian Hunter		self.pos = 0
7748392b74bSAdrian Hunter
7758392b74bSAdrian Hunter	def FindSelect(self):
7768392b74bSAdrian Hunter		self.rows = []
7778392b74bSAdrian Hunter		if self.pattern:
7788392b74bSAdrian Hunter			pattern = re.compile(self.value)
7798392b74bSAdrian Hunter			for child in self.root.child_items:
7808392b74bSAdrian Hunter				for column_data in child.data:
7818392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
7828392b74bSAdrian Hunter						self.rows.append(child.row)
7838392b74bSAdrian Hunter						break
7848392b74bSAdrian Hunter		else:
7858392b74bSAdrian Hunter			for child in self.root.child_items:
7868392b74bSAdrian Hunter				for column_data in child.data:
7878392b74bSAdrian Hunter					if self.value in str(column_data):
7888392b74bSAdrian Hunter						self.rows.append(child.row)
7898392b74bSAdrian Hunter						break
7908392b74bSAdrian Hunter
7918392b74bSAdrian Hunter	def FindValue(self):
7928392b74bSAdrian Hunter		self.pos = 0
7938392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
7948392b74bSAdrian Hunter			self.FindSelect()
7958392b74bSAdrian Hunter		if not len(self.rows):
7968392b74bSAdrian Hunter			return -1
7978392b74bSAdrian Hunter		return self.rows[self.pos]
7988392b74bSAdrian Hunter
7998392b74bSAdrian Hunter	def FindThread(self):
8008392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
8018392b74bSAdrian Hunter			row = self.FindValue()
8028392b74bSAdrian Hunter		elif len(self.rows):
8038392b74bSAdrian Hunter			if self.direction > 0:
8048392b74bSAdrian Hunter				self.pos += 1
8058392b74bSAdrian Hunter				if self.pos >= len(self.rows):
8068392b74bSAdrian Hunter					self.pos = 0
8078392b74bSAdrian Hunter			else:
8088392b74bSAdrian Hunter				self.pos -= 1
8098392b74bSAdrian Hunter				if self.pos < 0:
8108392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
8118392b74bSAdrian Hunter			row = self.rows[self.pos]
8128392b74bSAdrian Hunter		else:
8138392b74bSAdrian Hunter			row = -1
8148392b74bSAdrian Hunter		return (True, row)
8158392b74bSAdrian Hunter
8168392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
8178392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
8188392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
8198392b74bSAdrian Hunter		thread = Thread(self.FindThread)
8208392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
8218392b74bSAdrian Hunter		thread.start()
8228392b74bSAdrian Hunter
8238392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
8248392b74bSAdrian Hunter		callback(row)
8258392b74bSAdrian Hunter
8268392b74bSAdrian Hunter# Number of database records to fetch in one go
8278392b74bSAdrian Hunter
8288392b74bSAdrian Hunterglb_chunk_sz = 10000
8298392b74bSAdrian Hunter
8308392b74bSAdrian Hunter# size of pickled integer big enough for record size
8318392b74bSAdrian Hunter
8328392b74bSAdrian Hunterglb_nsz = 8
8338392b74bSAdrian Hunter
8348392b74bSAdrian Hunter# Background process for SQL data fetcher
8358392b74bSAdrian Hunter
8368392b74bSAdrian Hunterclass SQLFetcherProcess():
8378392b74bSAdrian Hunter
8388392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
8398392b74bSAdrian Hunter		# Need a unique connection name
8408392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
8418392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
8428392b74bSAdrian Hunter		self.sql = sql
8438392b74bSAdrian Hunter		self.buffer = buffer
8448392b74bSAdrian Hunter		self.head = head
8458392b74bSAdrian Hunter		self.tail = tail
8468392b74bSAdrian Hunter		self.fetch_count = fetch_count
8478392b74bSAdrian Hunter		self.fetching_done = fetching_done
8488392b74bSAdrian Hunter		self.process_target = process_target
8498392b74bSAdrian Hunter		self.wait_event = wait_event
8508392b74bSAdrian Hunter		self.fetched_event = fetched_event
8518392b74bSAdrian Hunter		self.prep = prep
8528392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
8538392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
8548392b74bSAdrian Hunter		self.last_id = -1
8558392b74bSAdrian Hunter		self.fetched = 0
8568392b74bSAdrian Hunter		self.more = True
8578392b74bSAdrian Hunter		self.local_head = self.head.value
8588392b74bSAdrian Hunter		self.local_tail = self.tail.value
8598392b74bSAdrian Hunter
8608392b74bSAdrian Hunter	def Select(self):
8618392b74bSAdrian Hunter		if self.query_limit:
8628392b74bSAdrian Hunter			if self.query_limit == 1:
8638392b74bSAdrian Hunter				return
8648392b74bSAdrian Hunter			self.query_limit -= 1
8658392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
8668392b74bSAdrian Hunter		QueryExec(self.query, stmt)
8678392b74bSAdrian Hunter
8688392b74bSAdrian Hunter	def Next(self):
8698392b74bSAdrian Hunter		if not self.query.next():
8708392b74bSAdrian Hunter			self.Select()
8718392b74bSAdrian Hunter			if not self.query.next():
8728392b74bSAdrian Hunter				return None
8738392b74bSAdrian Hunter		self.last_id = self.query.value(0)
8748392b74bSAdrian Hunter		return self.prep(self.query)
8758392b74bSAdrian Hunter
8768392b74bSAdrian Hunter	def WaitForTarget(self):
8778392b74bSAdrian Hunter		while True:
8788392b74bSAdrian Hunter			self.wait_event.clear()
8798392b74bSAdrian Hunter			target = self.process_target.value
8808392b74bSAdrian Hunter			if target > self.fetched or target < 0:
8818392b74bSAdrian Hunter				break
8828392b74bSAdrian Hunter			self.wait_event.wait()
8838392b74bSAdrian Hunter		return target
8848392b74bSAdrian Hunter
8858392b74bSAdrian Hunter	def HasSpace(self, sz):
8868392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
8878392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
8888392b74bSAdrian Hunter			if space > sz:
8898392b74bSAdrian Hunter				return True
8908392b74bSAdrian Hunter			if space >= glb_nsz:
8918392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
8928392b74bSAdrian Hunter				nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
8938392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
8948392b74bSAdrian Hunter			self.local_head = 0
8958392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
8968392b74bSAdrian Hunter			return True
8978392b74bSAdrian Hunter		return False
8988392b74bSAdrian Hunter
8998392b74bSAdrian Hunter	def WaitForSpace(self, sz):
9008392b74bSAdrian Hunter		if self.HasSpace(sz):
9018392b74bSAdrian Hunter			return
9028392b74bSAdrian Hunter		while True:
9038392b74bSAdrian Hunter			self.wait_event.clear()
9048392b74bSAdrian Hunter			self.local_tail = self.tail.value
9058392b74bSAdrian Hunter			if self.HasSpace(sz):
9068392b74bSAdrian Hunter				return
9078392b74bSAdrian Hunter			self.wait_event.wait()
9088392b74bSAdrian Hunter
9098392b74bSAdrian Hunter	def AddToBuffer(self, obj):
9108392b74bSAdrian Hunter		d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
9118392b74bSAdrian Hunter		n = len(d)
9128392b74bSAdrian Hunter		nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
9138392b74bSAdrian Hunter		sz = n + glb_nsz
9148392b74bSAdrian Hunter		self.WaitForSpace(sz)
9158392b74bSAdrian Hunter		pos = self.local_head
9168392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
9178392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
9188392b74bSAdrian Hunter		self.local_head += sz
9198392b74bSAdrian Hunter
9208392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
9218392b74bSAdrian Hunter		fetched = 0
9228392b74bSAdrian Hunter		while batch_size > fetched:
9238392b74bSAdrian Hunter			obj = self.Next()
9248392b74bSAdrian Hunter			if obj is None:
9258392b74bSAdrian Hunter				self.more = False
9268392b74bSAdrian Hunter				break
9278392b74bSAdrian Hunter			self.AddToBuffer(obj)
9288392b74bSAdrian Hunter			fetched += 1
9298392b74bSAdrian Hunter		if fetched:
9308392b74bSAdrian Hunter			self.fetched += fetched
9318392b74bSAdrian Hunter			with self.fetch_count.get_lock():
9328392b74bSAdrian Hunter				self.fetch_count.value += fetched
9338392b74bSAdrian Hunter			self.head.value = self.local_head
9348392b74bSAdrian Hunter			self.fetched_event.set()
9358392b74bSAdrian Hunter
9368392b74bSAdrian Hunter	def Run(self):
9378392b74bSAdrian Hunter		while self.more:
9388392b74bSAdrian Hunter			target = self.WaitForTarget()
9398392b74bSAdrian Hunter			if target < 0:
9408392b74bSAdrian Hunter				break
9418392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
9428392b74bSAdrian Hunter			self.FetchBatch(batch_size)
9438392b74bSAdrian Hunter		self.fetching_done.value = True
9448392b74bSAdrian Hunter		self.fetched_event.set()
9458392b74bSAdrian Hunter
9468392b74bSAdrian Hunterdef SQLFetcherFn(*x):
9478392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
9488392b74bSAdrian Hunter	process.Run()
9498392b74bSAdrian Hunter
9508392b74bSAdrian Hunter# SQL data fetcher
9518392b74bSAdrian Hunter
9528392b74bSAdrian Hunterclass SQLFetcher(QObject):
9538392b74bSAdrian Hunter
9548392b74bSAdrian Hunter	done = Signal(object)
9558392b74bSAdrian Hunter
9568392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
9578392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
9588392b74bSAdrian Hunter		self.process_data = process_data
9598392b74bSAdrian Hunter		self.more = True
9608392b74bSAdrian Hunter		self.target = 0
9618392b74bSAdrian Hunter		self.last_target = 0
9628392b74bSAdrian Hunter		self.fetched = 0
9638392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
9648392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
9658392b74bSAdrian Hunter		self.head = Value(c_longlong)
9668392b74bSAdrian Hunter		self.tail = Value(c_longlong)
9678392b74bSAdrian Hunter		self.local_tail = 0
9688392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
9698392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
9708392b74bSAdrian Hunter		self.last_count = 0
9718392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
9728392b74bSAdrian Hunter		self.wait_event = Event()
9738392b74bSAdrian Hunter		self.fetched_event = Event()
9748392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
9758392b74bSAdrian 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))
9768392b74bSAdrian Hunter		self.process.start()
9778392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
9788392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
9798392b74bSAdrian Hunter		self.thread.start()
9808392b74bSAdrian Hunter
9818392b74bSAdrian Hunter	def Shutdown(self):
9828392b74bSAdrian Hunter		# Tell the thread and process to exit
9838392b74bSAdrian Hunter		self.process_target.value = -1
9848392b74bSAdrian Hunter		self.wait_event.set()
9858392b74bSAdrian Hunter		self.more = False
9868392b74bSAdrian Hunter		self.fetching_done.value = True
9878392b74bSAdrian Hunter		self.fetched_event.set()
9888392b74bSAdrian Hunter
9898392b74bSAdrian Hunter	def Thread(self):
9908392b74bSAdrian Hunter		if not self.more:
9918392b74bSAdrian Hunter			return True, 0
9928392b74bSAdrian Hunter		while True:
9938392b74bSAdrian Hunter			self.fetched_event.clear()
9948392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
9958392b74bSAdrian Hunter			if fetch_count != self.last_count:
9968392b74bSAdrian Hunter				break
9978392b74bSAdrian Hunter			if self.fetching_done.value:
9988392b74bSAdrian Hunter				self.more = False
9998392b74bSAdrian Hunter				return True, 0
10008392b74bSAdrian Hunter			self.fetched_event.wait()
10018392b74bSAdrian Hunter		count = fetch_count - self.last_count
10028392b74bSAdrian Hunter		self.last_count = fetch_count
10038392b74bSAdrian Hunter		self.fetched += count
10048392b74bSAdrian Hunter		return False, count
10058392b74bSAdrian Hunter
10068392b74bSAdrian Hunter	def Fetch(self, nr):
10078392b74bSAdrian Hunter		if not self.more:
10088392b74bSAdrian Hunter			# -1 inidcates there are no more
10098392b74bSAdrian Hunter			return -1
10108392b74bSAdrian Hunter		result = self.fetched
10118392b74bSAdrian Hunter		extra = result + nr - self.target
10128392b74bSAdrian Hunter		if extra > 0:
10138392b74bSAdrian Hunter			self.target += extra
10148392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
10158392b74bSAdrian Hunter			if self.process_target.value >= 0:
10168392b74bSAdrian Hunter				self.process_target.value = self.target
10178392b74bSAdrian Hunter			self.wait_event.set()
10188392b74bSAdrian Hunter		return result
10198392b74bSAdrian Hunter
10208392b74bSAdrian Hunter	def RemoveFromBuffer(self):
10218392b74bSAdrian Hunter		pos = self.local_tail
10228392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
10238392b74bSAdrian Hunter			pos = 0
10248392b74bSAdrian Hunter		n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
10258392b74bSAdrian Hunter		if n == 0:
10268392b74bSAdrian Hunter			pos = 0
10278392b74bSAdrian Hunter			n = cPickle.loads(self.buffer[0 : glb_nsz])
10288392b74bSAdrian Hunter		pos += glb_nsz
10298392b74bSAdrian Hunter		obj = cPickle.loads(self.buffer[pos : pos + n])
10308392b74bSAdrian Hunter		self.local_tail = pos + n
10318392b74bSAdrian Hunter		return obj
10328392b74bSAdrian Hunter
10338392b74bSAdrian Hunter	def ProcessData(self, count):
10348392b74bSAdrian Hunter		for i in xrange(count):
10358392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
10368392b74bSAdrian Hunter			self.process_data(obj)
10378392b74bSAdrian Hunter		self.tail.value = self.local_tail
10388392b74bSAdrian Hunter		self.wait_event.set()
10398392b74bSAdrian Hunter		self.done.emit(count)
10408392b74bSAdrian Hunter
10418392b74bSAdrian Hunter# Fetch more records bar
10428392b74bSAdrian Hunter
10438392b74bSAdrian Hunterclass FetchMoreRecordsBar():
10448392b74bSAdrian Hunter
10458392b74bSAdrian Hunter	def __init__(self, model, parent):
10468392b74bSAdrian Hunter		self.model = model
10478392b74bSAdrian Hunter
10488392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
10498392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10508392b74bSAdrian Hunter
10518392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
10528392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
10538392b74bSAdrian Hunter		self.fetch_count.setValue(10)
10548392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10558392b74bSAdrian Hunter
10568392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
10578392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10588392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
10598392b74bSAdrian Hunter
10608392b74bSAdrian Hunter		self.progress = QProgressBar()
10618392b74bSAdrian Hunter		self.progress.setRange(0, 100)
10628392b74bSAdrian Hunter		self.progress.hide()
10638392b74bSAdrian Hunter
10648392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
10658392b74bSAdrian Hunter		self.done_label.hide()
10668392b74bSAdrian Hunter
10678392b74bSAdrian Hunter		self.spacer = QLabel("")
10688392b74bSAdrian Hunter
10698392b74bSAdrian Hunter		self.close_button = QToolButton()
10708392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
10718392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
10728392b74bSAdrian Hunter
10738392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
10748392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
10778392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
10788392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
10798392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
10808392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
10818392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
10828392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
10838392b74bSAdrian Hunter
10848392b74bSAdrian Hunter		self.bar = QWidget()
10858392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
10868392b74bSAdrian Hunter		self.bar.show()
10878392b74bSAdrian Hunter
10888392b74bSAdrian Hunter		self.in_progress = False
10898392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
10908392b74bSAdrian Hunter
10918392b74bSAdrian Hunter		self.done = False
10928392b74bSAdrian Hunter
10938392b74bSAdrian Hunter		if not model.HasMoreRecords():
10948392b74bSAdrian Hunter			self.Done()
10958392b74bSAdrian Hunter
10968392b74bSAdrian Hunter	def Widget(self):
10978392b74bSAdrian Hunter		return self.bar
10988392b74bSAdrian Hunter
10998392b74bSAdrian Hunter	def Activate(self):
11008392b74bSAdrian Hunter		self.bar.show()
11018392b74bSAdrian Hunter		self.fetch.setFocus()
11028392b74bSAdrian Hunter
11038392b74bSAdrian Hunter	def Deactivate(self):
11048392b74bSAdrian Hunter		self.bar.hide()
11058392b74bSAdrian Hunter
11068392b74bSAdrian Hunter	def Enable(self, enable):
11078392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
11088392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
11098392b74bSAdrian Hunter
11108392b74bSAdrian Hunter	def Busy(self):
11118392b74bSAdrian Hunter		self.Enable(False)
11128392b74bSAdrian Hunter		self.fetch.hide()
11138392b74bSAdrian Hunter		self.spacer.hide()
11148392b74bSAdrian Hunter		self.progress.show()
11158392b74bSAdrian Hunter
11168392b74bSAdrian Hunter	def Idle(self):
11178392b74bSAdrian Hunter		self.in_progress = False
11188392b74bSAdrian Hunter		self.Enable(True)
11198392b74bSAdrian Hunter		self.progress.hide()
11208392b74bSAdrian Hunter		self.fetch.show()
11218392b74bSAdrian Hunter		self.spacer.show()
11228392b74bSAdrian Hunter
11238392b74bSAdrian Hunter	def Target(self):
11248392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
11258392b74bSAdrian Hunter
11268392b74bSAdrian Hunter	def Done(self):
11278392b74bSAdrian Hunter		self.done = True
11288392b74bSAdrian Hunter		self.Idle()
11298392b74bSAdrian Hunter		self.label.hide()
11308392b74bSAdrian Hunter		self.fetch_count.hide()
11318392b74bSAdrian Hunter		self.fetch.hide()
11328392b74bSAdrian Hunter		self.spacer.hide()
11338392b74bSAdrian Hunter		self.done_label.show()
11348392b74bSAdrian Hunter
11358392b74bSAdrian Hunter	def Progress(self, count):
11368392b74bSAdrian Hunter		if self.in_progress:
11378392b74bSAdrian Hunter			if count:
11388392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
11398392b74bSAdrian Hunter				if percent >= 100:
11408392b74bSAdrian Hunter					self.Idle()
11418392b74bSAdrian Hunter				else:
11428392b74bSAdrian Hunter					self.progress.setValue(percent)
11438392b74bSAdrian Hunter		if not count:
11448392b74bSAdrian Hunter			# Count value of zero means no more records
11458392b74bSAdrian Hunter			self.Done()
11468392b74bSAdrian Hunter
11478392b74bSAdrian Hunter	def FetchMoreRecords(self):
11488392b74bSAdrian Hunter		if self.done:
11498392b74bSAdrian Hunter			return
11508392b74bSAdrian Hunter		self.progress.setValue(0)
11518392b74bSAdrian Hunter		self.Busy()
11528392b74bSAdrian Hunter		self.in_progress = True
11538392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
11548392b74bSAdrian Hunter
115576099f98SAdrian Hunter# Brance data model level two item
115676099f98SAdrian Hunter
115776099f98SAdrian Hunterclass BranchLevelTwoItem():
115876099f98SAdrian Hunter
115976099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
116076099f98SAdrian Hunter		self.row = row
116176099f98SAdrian Hunter		self.parent_item = parent_item
116276099f98SAdrian Hunter		self.data = [""] * 8
116376099f98SAdrian Hunter		self.data[7] = text
116476099f98SAdrian Hunter		self.level = 2
116576099f98SAdrian Hunter
116676099f98SAdrian Hunter	def getParentItem(self):
116776099f98SAdrian Hunter		return self.parent_item
116876099f98SAdrian Hunter
116976099f98SAdrian Hunter	def getRow(self):
117076099f98SAdrian Hunter		return self.row
117176099f98SAdrian Hunter
117276099f98SAdrian Hunter	def childCount(self):
117376099f98SAdrian Hunter		return 0
117476099f98SAdrian Hunter
117576099f98SAdrian Hunter	def hasChildren(self):
117676099f98SAdrian Hunter		return False
117776099f98SAdrian Hunter
117876099f98SAdrian Hunter	def getData(self, column):
117976099f98SAdrian Hunter		return self.data[column]
118076099f98SAdrian Hunter
118176099f98SAdrian Hunter# Brance data model level one item
118276099f98SAdrian Hunter
118376099f98SAdrian Hunterclass BranchLevelOneItem():
118476099f98SAdrian Hunter
118576099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
118676099f98SAdrian Hunter		self.glb = glb
118776099f98SAdrian Hunter		self.row = row
118876099f98SAdrian Hunter		self.parent_item = parent_item
118976099f98SAdrian Hunter		self.child_count = 0
119076099f98SAdrian Hunter		self.child_items = []
119176099f98SAdrian Hunter		self.data = data[1:]
119276099f98SAdrian Hunter		self.dbid = data[0]
119376099f98SAdrian Hunter		self.level = 1
119476099f98SAdrian Hunter		self.query_done = False
119576099f98SAdrian Hunter
119676099f98SAdrian Hunter	def getChildItem(self, row):
119776099f98SAdrian Hunter		return self.child_items[row]
119876099f98SAdrian Hunter
119976099f98SAdrian Hunter	def getParentItem(self):
120076099f98SAdrian Hunter		return self.parent_item
120176099f98SAdrian Hunter
120276099f98SAdrian Hunter	def getRow(self):
120376099f98SAdrian Hunter		return self.row
120476099f98SAdrian Hunter
120576099f98SAdrian Hunter	def Select(self):
120676099f98SAdrian Hunter		self.query_done = True
120776099f98SAdrian Hunter
120876099f98SAdrian Hunter		if not self.glb.have_disassembler:
120976099f98SAdrian Hunter			return
121076099f98SAdrian Hunter
121176099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
121276099f98SAdrian Hunter
121376099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
121476099f98SAdrian Hunter				  " FROM samples"
121576099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
121676099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
121776099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
121876099f98SAdrian Hunter		if not query.next():
121976099f98SAdrian Hunter			return
122076099f98SAdrian Hunter		cpu = query.value(0)
122176099f98SAdrian Hunter		dso = query.value(1)
122276099f98SAdrian Hunter		sym = query.value(2)
122376099f98SAdrian Hunter		if dso == 0 or sym == 0:
122476099f98SAdrian Hunter			return
122576099f98SAdrian Hunter		off = query.value(3)
122676099f98SAdrian Hunter		short_name = query.value(4)
122776099f98SAdrian Hunter		long_name = query.value(5)
122876099f98SAdrian Hunter		build_id = query.value(6)
122976099f98SAdrian Hunter		sym_start = query.value(7)
123076099f98SAdrian Hunter		ip = query.value(8)
123176099f98SAdrian Hunter
123276099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
123376099f98SAdrian Hunter				  " FROM samples"
123476099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
123576099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
123676099f98SAdrian Hunter				  " ORDER BY samples.id"
123776099f98SAdrian Hunter				  " LIMIT 1")
123876099f98SAdrian Hunter		if not query.next():
123976099f98SAdrian Hunter			return
124076099f98SAdrian Hunter		if query.value(0) != dso:
124176099f98SAdrian Hunter			# Cannot disassemble from one dso to another
124276099f98SAdrian Hunter			return
124376099f98SAdrian Hunter		bsym = query.value(1)
124476099f98SAdrian Hunter		boff = query.value(2)
124576099f98SAdrian Hunter		bsym_start = query.value(3)
124676099f98SAdrian Hunter		if bsym == 0:
124776099f98SAdrian Hunter			return
124876099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
124976099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
125076099f98SAdrian Hunter			return
125176099f98SAdrian Hunter
125276099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
125376099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
125476099f98SAdrian Hunter		if not f:
125576099f98SAdrian Hunter			return
125676099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
125776099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
125876099f98SAdrian Hunter
125976099f98SAdrian Hunter		buf_sz = tot + 16
126076099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
126176099f98SAdrian Hunter		f.seek(sym_start + off)
126276099f98SAdrian Hunter		buf.value = f.read(buf_sz)
126376099f98SAdrian Hunter		buf_ptr = addressof(buf)
126476099f98SAdrian Hunter		i = 0
126576099f98SAdrian Hunter		while tot > 0:
126676099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
126776099f98SAdrian Hunter			if cnt:
126876099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
126976099f98SAdrian Hunter				for k in xrange(cnt):
127076099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
127176099f98SAdrian Hunter					i += 1
127276099f98SAdrian Hunter				while k < 15:
127376099f98SAdrian Hunter					byte_str += "   "
127476099f98SAdrian Hunter					k += 1
127576099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
127676099f98SAdrian Hunter				self.child_count += 1
127776099f98SAdrian Hunter			else:
127876099f98SAdrian Hunter				return
127976099f98SAdrian Hunter			buf_ptr += cnt
128076099f98SAdrian Hunter			tot -= cnt
128176099f98SAdrian Hunter			buf_sz -= cnt
128276099f98SAdrian Hunter			ip += cnt
128376099f98SAdrian Hunter
128476099f98SAdrian Hunter	def childCount(self):
128576099f98SAdrian Hunter		if not self.query_done:
128676099f98SAdrian Hunter			self.Select()
128776099f98SAdrian Hunter			if not self.child_count:
128876099f98SAdrian Hunter				return -1
128976099f98SAdrian Hunter		return self.child_count
129076099f98SAdrian Hunter
129176099f98SAdrian Hunter	def hasChildren(self):
129276099f98SAdrian Hunter		if not self.query_done:
129376099f98SAdrian Hunter			return True
129476099f98SAdrian Hunter		return self.child_count > 0
129576099f98SAdrian Hunter
129676099f98SAdrian Hunter	def getData(self, column):
129776099f98SAdrian Hunter		return self.data[column]
129876099f98SAdrian Hunter
129976099f98SAdrian Hunter# Brance data model root item
130076099f98SAdrian Hunter
130176099f98SAdrian Hunterclass BranchRootItem():
130276099f98SAdrian Hunter
130376099f98SAdrian Hunter	def __init__(self):
130476099f98SAdrian Hunter		self.child_count = 0
130576099f98SAdrian Hunter		self.child_items = []
130676099f98SAdrian Hunter		self.level = 0
130776099f98SAdrian Hunter
130876099f98SAdrian Hunter	def getChildItem(self, row):
130976099f98SAdrian Hunter		return self.child_items[row]
131076099f98SAdrian Hunter
131176099f98SAdrian Hunter	def getParentItem(self):
131276099f98SAdrian Hunter		return None
131376099f98SAdrian Hunter
131476099f98SAdrian Hunter	def getRow(self):
131576099f98SAdrian Hunter		return 0
131676099f98SAdrian Hunter
131776099f98SAdrian Hunter	def childCount(self):
131876099f98SAdrian Hunter		return self.child_count
131976099f98SAdrian Hunter
132076099f98SAdrian Hunter	def hasChildren(self):
132176099f98SAdrian Hunter		return self.child_count > 0
132276099f98SAdrian Hunter
132376099f98SAdrian Hunter	def getData(self, column):
132476099f98SAdrian Hunter		return ""
132576099f98SAdrian Hunter
132676099f98SAdrian Hunter# Branch data preparation
132776099f98SAdrian Hunter
132876099f98SAdrian Hunterdef BranchDataPrep(query):
132976099f98SAdrian Hunter	data = []
133076099f98SAdrian Hunter	for i in xrange(0, 8):
133176099f98SAdrian Hunter		data.append(query.value(i))
133276099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
133376099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
133476099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
133576099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
133676099f98SAdrian Hunter	return data
133776099f98SAdrian Hunter
133876099f98SAdrian Hunter# Branch data model
133976099f98SAdrian Hunter
134076099f98SAdrian Hunterclass BranchModel(TreeModel):
134176099f98SAdrian Hunter
134276099f98SAdrian Hunter	progress = Signal(object)
134376099f98SAdrian Hunter
134476099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
1345*a448ba23SAdrian Hunter		super(BranchModel, self).__init__(glb, parent)
134676099f98SAdrian Hunter		self.event_id = event_id
134776099f98SAdrian Hunter		self.more = True
134876099f98SAdrian Hunter		self.populated = 0
134976099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
135076099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
135176099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
135276099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
135376099f98SAdrian Hunter			" FROM samples"
135476099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
135576099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
135676099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
135776099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
135876099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
135976099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
136076099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
136176099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
136276099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
136376099f98SAdrian Hunter			" ORDER BY samples.id"
136476099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
136576099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
136676099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
136776099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
136876099f98SAdrian Hunter
1369*a448ba23SAdrian Hunter	def GetRoot(self):
1370*a448ba23SAdrian Hunter		return BranchRootItem()
1371*a448ba23SAdrian Hunter
137276099f98SAdrian Hunter	def columnCount(self, parent=None):
137376099f98SAdrian Hunter		return 8
137476099f98SAdrian Hunter
137576099f98SAdrian Hunter	def columnHeader(self, column):
137676099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
137776099f98SAdrian Hunter
137876099f98SAdrian Hunter	def columnFont(self, column):
137976099f98SAdrian Hunter		if column != 7:
138076099f98SAdrian Hunter			return None
138176099f98SAdrian Hunter		return QFont("Monospace")
138276099f98SAdrian Hunter
138376099f98SAdrian Hunter	def DisplayData(self, item, index):
138476099f98SAdrian Hunter		if item.level == 1:
138576099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
138676099f98SAdrian Hunter		return item.getData(index.column())
138776099f98SAdrian Hunter
138876099f98SAdrian Hunter	def AddSample(self, data):
138976099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
139076099f98SAdrian Hunter		self.root.child_items.append(child)
139176099f98SAdrian Hunter		self.populated += 1
139276099f98SAdrian Hunter
139376099f98SAdrian Hunter	def Update(self, fetched):
139476099f98SAdrian Hunter		if not fetched:
139576099f98SAdrian Hunter			self.more = False
139676099f98SAdrian Hunter			self.progress.emit(0)
139776099f98SAdrian Hunter		child_count = self.root.child_count
139876099f98SAdrian Hunter		count = self.populated - child_count
139976099f98SAdrian Hunter		if count > 0:
140076099f98SAdrian Hunter			parent = QModelIndex()
140176099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
140276099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
140376099f98SAdrian Hunter			self.root.child_count += count
140476099f98SAdrian Hunter			self.endInsertRows()
140576099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
140676099f98SAdrian Hunter
140776099f98SAdrian Hunter	def FetchMoreRecords(self, count):
140876099f98SAdrian Hunter		current = self.root.child_count
140976099f98SAdrian Hunter		if self.more:
141076099f98SAdrian Hunter			self.fetcher.Fetch(count)
141176099f98SAdrian Hunter		else:
141276099f98SAdrian Hunter			self.progress.emit(0)
141376099f98SAdrian Hunter		return current
141476099f98SAdrian Hunter
141576099f98SAdrian Hunter	def HasMoreRecords(self):
141676099f98SAdrian Hunter		return self.more
141776099f98SAdrian Hunter
14180bf0947aSAdrian Hunter# Report Variables
14190bf0947aSAdrian Hunter
14200bf0947aSAdrian Hunterclass ReportVars():
14210bf0947aSAdrian Hunter
1422cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1423947cc38dSAdrian Hunter		self.name = name
14240bf0947aSAdrian Hunter		self.where_clause = where_clause
1425cd358012SAdrian Hunter		self.limit = limit
14260bf0947aSAdrian Hunter
14270bf0947aSAdrian Hunter	def UniqueId(self):
1428cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
14290bf0947aSAdrian Hunter
143076099f98SAdrian Hunter# Branch window
143176099f98SAdrian Hunter
143276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
143376099f98SAdrian Hunter
1434947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
143576099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
143676099f98SAdrian Hunter
14370bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
143876099f98SAdrian Hunter
14390bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
144076099f98SAdrian Hunter
144176099f98SAdrian Hunter		self.view = QTreeView()
144276099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
144376099f98SAdrian Hunter		self.view.setModel(self.model)
144476099f98SAdrian Hunter
144576099f98SAdrian Hunter		self.ResizeColumnsToContents()
144676099f98SAdrian Hunter
144776099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
144876099f98SAdrian Hunter
144976099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
145076099f98SAdrian Hunter
145176099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
145276099f98SAdrian Hunter
145376099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
145476099f98SAdrian Hunter
145576099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
145676099f98SAdrian Hunter
1457947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
145876099f98SAdrian Hunter
145976099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
146076099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
146176099f98SAdrian Hunter		# so implement a crude alternative
146276099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
146376099f98SAdrian Hunter		font = self.view.font()
146476099f98SAdrian Hunter		metrics = QFontMetrics(font)
146576099f98SAdrian Hunter		max = 0
146676099f98SAdrian Hunter		for row in xrange(n):
146776099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
146876099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
146976099f98SAdrian Hunter			max = len if len > max else max
147076099f98SAdrian Hunter		val = self.model.columnHeader(column)
147176099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
147276099f98SAdrian Hunter		max = len if len > max else max
147376099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
147476099f98SAdrian Hunter
147576099f98SAdrian Hunter	def ResizeColumnsToContents(self):
147676099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
147776099f98SAdrian Hunter		if n < 1:
147876099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
147976099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
148076099f98SAdrian Hunter			return
148176099f98SAdrian Hunter		columns = self.model.columnCount()
148276099f98SAdrian Hunter		for i in xrange(columns):
148376099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
148476099f98SAdrian Hunter
148576099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
148676099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
148776099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
148876099f98SAdrian Hunter		self.ResizeColumnsToContents()
148976099f98SAdrian Hunter
149076099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
149176099f98SAdrian Hunter		self.view.setFocus()
149276099f98SAdrian Hunter		self.find_bar.Busy()
149376099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
149476099f98SAdrian Hunter
149576099f98SAdrian Hunter	def FindDone(self, row):
149676099f98SAdrian Hunter		self.find_bar.Idle()
149776099f98SAdrian Hunter		if row >= 0:
149876099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
149976099f98SAdrian Hunter		else:
150076099f98SAdrian Hunter			self.find_bar.NotFound()
150176099f98SAdrian Hunter
15021c3ca1b3SAdrian Hunter# Line edit data item
15031c3ca1b3SAdrian Hunter
15041c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
15051c3ca1b3SAdrian Hunter
1506cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
15071c3ca1b3SAdrian Hunter		self.glb = glb
15081c3ca1b3SAdrian Hunter		self.label = label
15091c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
15101c3ca1b3SAdrian Hunter		self.parent = parent
15111c3ca1b3SAdrian Hunter		self.id = id
15121c3ca1b3SAdrian Hunter
1513cd358012SAdrian Hunter		self.value = default
15141c3ca1b3SAdrian Hunter
1515cd358012SAdrian Hunter		self.widget = QLineEdit(default)
15161c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
15171c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
15181c3ca1b3SAdrian Hunter		self.red = False
15191c3ca1b3SAdrian Hunter		self.error = ""
15201c3ca1b3SAdrian Hunter		self.validated = True
15211c3ca1b3SAdrian Hunter
15221c3ca1b3SAdrian Hunter		if placeholder_text:
15231c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
15241c3ca1b3SAdrian Hunter
15251c3ca1b3SAdrian Hunter	def TurnTextRed(self):
15261c3ca1b3SAdrian Hunter		if not self.red:
15271c3ca1b3SAdrian Hunter			palette = QPalette()
15281c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
15291c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
15301c3ca1b3SAdrian Hunter			self.red = True
15311c3ca1b3SAdrian Hunter
15321c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
15331c3ca1b3SAdrian Hunter		if self.red:
15341c3ca1b3SAdrian Hunter			palette = QPalette()
15351c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
15361c3ca1b3SAdrian Hunter			self.red = False
15371c3ca1b3SAdrian Hunter
15381c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
15391c3ca1b3SAdrian Hunter		self.value = ""
15401c3ca1b3SAdrian Hunter		self.TurnTextRed()
15411c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
15421c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
15431c3ca1b3SAdrian Hunter
15441c3ca1b3SAdrian Hunter	def Invalidate(self):
15451c3ca1b3SAdrian Hunter		self.validated = False
15461c3ca1b3SAdrian Hunter
15471c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
15481c3ca1b3SAdrian Hunter		self.value = input_string.strip()
15491c3ca1b3SAdrian Hunter
15501c3ca1b3SAdrian Hunter	def Validate(self):
15511c3ca1b3SAdrian Hunter		self.validated = True
15521c3ca1b3SAdrian Hunter		self.error = ""
15531c3ca1b3SAdrian Hunter		self.TurnTextNormal()
15541c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
15551c3ca1b3SAdrian Hunter		input_string = self.widget.text()
15561c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
15571c3ca1b3SAdrian Hunter			self.value = ""
15581c3ca1b3SAdrian Hunter			return
15591c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
15601c3ca1b3SAdrian Hunter
15611c3ca1b3SAdrian Hunter	def IsValid(self):
15621c3ca1b3SAdrian Hunter		if not self.validated:
15631c3ca1b3SAdrian Hunter			self.Validate()
15641c3ca1b3SAdrian Hunter		if len(self.error):
15651c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
15661c3ca1b3SAdrian Hunter			return False
15671c3ca1b3SAdrian Hunter		return True
15681c3ca1b3SAdrian Hunter
15691c3ca1b3SAdrian Hunter	def IsNumber(self, value):
15701c3ca1b3SAdrian Hunter		try:
15711c3ca1b3SAdrian Hunter			x = int(value)
15721c3ca1b3SAdrian Hunter		except:
15731c3ca1b3SAdrian Hunter			x = 0
15741c3ca1b3SAdrian Hunter		return str(x) == value
15751c3ca1b3SAdrian Hunter
15761c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
15771c3ca1b3SAdrian Hunter
15781c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
15791c3ca1b3SAdrian Hunter
15801c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
15811c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
15821c3ca1b3SAdrian Hunter
15831c3ca1b3SAdrian Hunter		self.column_name = column_name
15841c3ca1b3SAdrian Hunter
15851c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
15861c3ca1b3SAdrian Hunter		singles = []
15871c3ca1b3SAdrian Hunter		ranges = []
15881c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
15891c3ca1b3SAdrian Hunter			if "-" in value:
15901c3ca1b3SAdrian Hunter				vrange = value.split("-")
15911c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
15921c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
15931c3ca1b3SAdrian Hunter				ranges.append(vrange)
15941c3ca1b3SAdrian Hunter			else:
15951c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
15961c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
15971c3ca1b3SAdrian Hunter				singles.append(value)
15981c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
15991c3ca1b3SAdrian Hunter		if len(singles):
16001c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
16011c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
16021c3ca1b3SAdrian Hunter
1603cd358012SAdrian Hunter# Positive integer dialog data item
1604cd358012SAdrian Hunter
1605cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
1606cd358012SAdrian Hunter
1607cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1608cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1609cd358012SAdrian Hunter
1610cd358012SAdrian Hunter	def DoValidate(self, input_string):
1611cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
1612cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1613cd358012SAdrian Hunter		value = int(input_string.strip())
1614cd358012SAdrian Hunter		if value <= 0:
1615cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1616cd358012SAdrian Hunter		self.value = str(value)
1617cd358012SAdrian Hunter
16181c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
16191c3ca1b3SAdrian Hunter
16201c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
16211c3ca1b3SAdrian Hunter
16221c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
16231c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
16241c3ca1b3SAdrian Hunter
16251c3ca1b3SAdrian Hunter		self.table_name = table_name
16261c3ca1b3SAdrian Hunter		self.match_column = match_column
16271c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
16281c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
16291c3ca1b3SAdrian Hunter
16301c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
16311c3ca1b3SAdrian Hunter		ids = []
16321c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
16331c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
16341c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
16351c3ca1b3SAdrian Hunter		if ret:
16361c3ca1b3SAdrian Hunter			while query.next():
16371c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
16381c3ca1b3SAdrian Hunter		return ids
16391c3ca1b3SAdrian Hunter
16401c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
16411c3ca1b3SAdrian Hunter		all_ids = []
16421c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
16431c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
16441c3ca1b3SAdrian Hunter			if len(ids):
16451c3ca1b3SAdrian Hunter				all_ids.extend(ids)
16461c3ca1b3SAdrian Hunter			else:
16471c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
16481c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
16491c3ca1b3SAdrian Hunter		if self.column_name2:
16501c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
16511c3ca1b3SAdrian Hunter
16521c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
16531c3ca1b3SAdrian Hunter
16541c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
16551c3ca1b3SAdrian Hunter
16561c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
16571c3ca1b3SAdrian Hunter		self.column_name = column_name
16581c3ca1b3SAdrian Hunter
16591c3ca1b3SAdrian Hunter		self.last_id = 0
16601c3ca1b3SAdrian Hunter		self.first_time = 0
16611c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
16621c3ca1b3SAdrian Hunter
16631c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
16641c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
16651c3ca1b3SAdrian Hunter		if query.next():
16661c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
16671c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
16681c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
16691c3ca1b3SAdrian Hunter		if query.next():
16701c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
16711c3ca1b3SAdrian Hunter		if placeholder_text:
16721c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
16731c3ca1b3SAdrian Hunter
16741c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
16751c3ca1b3SAdrian Hunter
16761c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
16771c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
16781c3ca1b3SAdrian Hunter		if query.next():
16791c3ca1b3SAdrian Hunter			return True, int(query.value(0))
16801c3ca1b3SAdrian Hunter		else:
16811c3ca1b3SAdrian Hunter			return False, 0
16821c3ca1b3SAdrian Hunter
16831c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
16841c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
16851c3ca1b3SAdrian Hunter		while True:
16861c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
16871c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
16881c3ca1b3SAdrian Hunter			if not query.next():
16891c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
16901c3ca1b3SAdrian Hunter				if not ok:
16911c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
16921c3ca1b3SAdrian Hunter					if not ok:
16931c3ca1b3SAdrian Hunter						return str(higher_id)
16941c3ca1b3SAdrian Hunter				next_id = dbid
16951c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
16961c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
16971c3ca1b3SAdrian Hunter			if get_floor:
16981c3ca1b3SAdrian Hunter				if target_time > next_time:
16991c3ca1b3SAdrian Hunter					lower_id = next_id
17001c3ca1b3SAdrian Hunter				else:
17011c3ca1b3SAdrian Hunter					higher_id = next_id
17021c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
17031c3ca1b3SAdrian Hunter					return str(higher_id)
17041c3ca1b3SAdrian Hunter			else:
17051c3ca1b3SAdrian Hunter				if target_time >= next_time:
17061c3ca1b3SAdrian Hunter					lower_id = next_id
17071c3ca1b3SAdrian Hunter				else:
17081c3ca1b3SAdrian Hunter					higher_id = next_id
17091c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
17101c3ca1b3SAdrian Hunter					return str(lower_id)
17111c3ca1b3SAdrian Hunter
17121c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
17131c3ca1b3SAdrian Hunter		mult = 1
17141c3ca1b3SAdrian Hunter		suffix = val[-2:]
17151c3ca1b3SAdrian Hunter		if suffix == "ms":
17161c3ca1b3SAdrian Hunter			mult = 1000000
17171c3ca1b3SAdrian Hunter		elif suffix == "us":
17181c3ca1b3SAdrian Hunter			mult = 1000
17191c3ca1b3SAdrian Hunter		elif suffix == "ns":
17201c3ca1b3SAdrian Hunter			mult = 1
17211c3ca1b3SAdrian Hunter		else:
17221c3ca1b3SAdrian Hunter			return val
17231c3ca1b3SAdrian Hunter		val = val[:-2].strip()
17241c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
17251c3ca1b3SAdrian Hunter			return val
17261c3ca1b3SAdrian Hunter		val = int(val) * mult
17271c3ca1b3SAdrian Hunter		if val >= 0:
17281c3ca1b3SAdrian Hunter			val += self.first_time
17291c3ca1b3SAdrian Hunter		else:
17301c3ca1b3SAdrian Hunter			val += self.last_time
17311c3ca1b3SAdrian Hunter		return str(val)
17321c3ca1b3SAdrian Hunter
17331c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
17341c3ca1b3SAdrian Hunter		if vrange[0] == "":
17351c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
17361c3ca1b3SAdrian Hunter		if vrange[1] == "":
17371c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
17381c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
17391c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
17401c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
17411c3ca1b3SAdrian Hunter			return False
17421c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
17431c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
17441c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
17451c3ca1b3SAdrian Hunter			return False
17461c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
17471c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
17481c3ca1b3SAdrian Hunter		return True
17491c3ca1b3SAdrian Hunter
17501c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
17511c3ca1b3SAdrian Hunter		n = value.count("-")
17521c3ca1b3SAdrian Hunter		if n == 1:
17531c3ca1b3SAdrian Hunter			pass
17541c3ca1b3SAdrian Hunter		elif n == 2:
17551c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
17561c3ca1b3SAdrian Hunter				n = 1
17571c3ca1b3SAdrian Hunter		elif n == 3:
17581c3ca1b3SAdrian Hunter			n = 2
17591c3ca1b3SAdrian Hunter		else:
17601c3ca1b3SAdrian Hunter			return False
17611c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
17621c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
17631c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
17641c3ca1b3SAdrian Hunter			ranges.append(vrange)
17651c3ca1b3SAdrian Hunter			return True
17661c3ca1b3SAdrian Hunter		return False
17671c3ca1b3SAdrian Hunter
17681c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
17691c3ca1b3SAdrian Hunter		ranges = []
17701c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
17711c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
17721c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
17731c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
17741c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
17751c3ca1b3SAdrian Hunter
17760924cd68SAdrian Hunter# Report Dialog Base
1777210cf1f9SAdrian Hunter
17780924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
1779210cf1f9SAdrian Hunter
17800924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
17810924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
1782210cf1f9SAdrian Hunter
1783210cf1f9SAdrian Hunter		self.glb = glb
1784210cf1f9SAdrian Hunter
17850bf0947aSAdrian Hunter		self.report_vars = ReportVars()
1786210cf1f9SAdrian Hunter
17870924cd68SAdrian Hunter		self.setWindowTitle(title)
1788210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
1789210cf1f9SAdrian Hunter
17901c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
1791210cf1f9SAdrian Hunter
17920924cd68SAdrian Hunter		self.partial = partial
17930924cd68SAdrian Hunter
1794210cf1f9SAdrian Hunter		self.grid = QGridLayout()
1795210cf1f9SAdrian Hunter
1796210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
1797210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1798210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
1799210cf1f9SAdrian Hunter
1800210cf1f9SAdrian Hunter		self.status = QLabel()
1801210cf1f9SAdrian Hunter
1802210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
1803210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
1804210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
1805210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1806210cf1f9SAdrian Hunter
1807210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
1808210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
1809210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1810210cf1f9SAdrian Hunter
1811210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
1812210cf1f9SAdrian Hunter		#self.hbox.addStretch()
1813210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
1814210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
1815210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
1816210cf1f9SAdrian Hunter
1817210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
1818210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
1819210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
1820210cf1f9SAdrian Hunter
1821210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
1822210cf1f9SAdrian Hunter
1823210cf1f9SAdrian Hunter	def Ok(self):
18240bf0947aSAdrian Hunter		vars = self.report_vars
18251c3ca1b3SAdrian Hunter		for d in self.data_items:
18261c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
18271c3ca1b3SAdrian Hunter				vars.name = d.value
1828947cc38dSAdrian Hunter		if not vars.name:
1829210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
1830210cf1f9SAdrian Hunter			return
1831210cf1f9SAdrian Hunter		for d in self.data_items:
1832210cf1f9SAdrian Hunter			if not d.IsValid():
1833210cf1f9SAdrian Hunter				return
1834210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
1835cd358012SAdrian Hunter			if d.id == "LIMIT":
1836cd358012SAdrian Hunter				vars.limit = d.value
1837cd358012SAdrian Hunter			elif len(d.value):
18380bf0947aSAdrian Hunter				if len(vars.where_clause):
18390bf0947aSAdrian Hunter					vars.where_clause += " AND "
18400bf0947aSAdrian Hunter				vars.where_clause += d.value
18410bf0947aSAdrian Hunter		if len(vars.where_clause):
18420924cd68SAdrian Hunter			if self.partial:
18430bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
1844210cf1f9SAdrian Hunter			else:
18450bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
1846210cf1f9SAdrian Hunter		self.accept()
1847210cf1f9SAdrian Hunter
1848210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
1849210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
1850210cf1f9SAdrian Hunter
1851210cf1f9SAdrian Hunter	def ClearMessage(self):
1852210cf1f9SAdrian Hunter		self.status.setText("")
1853210cf1f9SAdrian Hunter
18540924cd68SAdrian Hunter# Selected branch report creation dialog
18550924cd68SAdrian Hunter
18560924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
18570924cd68SAdrian Hunter
18580924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
18590924cd68SAdrian Hunter		title = "Selected Branches"
18601c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
18611c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
18621c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
18631c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
18641c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
18651c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
18661c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p),
18671c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
18681c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
18690924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
18700924cd68SAdrian Hunter
187176099f98SAdrian Hunter# Event list
187276099f98SAdrian Hunter
187376099f98SAdrian Hunterdef GetEventList(db):
187476099f98SAdrian Hunter	events = []
187576099f98SAdrian Hunter	query = QSqlQuery(db)
187676099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
187776099f98SAdrian Hunter	while query.next():
187876099f98SAdrian Hunter		events.append(query.value(0))
187976099f98SAdrian Hunter	return events
188076099f98SAdrian Hunter
1881655cb952SAdrian Hunter# Is a table selectable
1882655cb952SAdrian Hunter
1883655cb952SAdrian Hunterdef IsSelectable(db, table):
1884655cb952SAdrian Hunter	query = QSqlQuery(db)
1885655cb952SAdrian Hunter	try:
1886655cb952SAdrian Hunter		QueryExec(query, "SELECT * FROM " + table + " LIMIT 1")
1887655cb952SAdrian Hunter	except:
1888655cb952SAdrian Hunter		return False
1889655cb952SAdrian Hunter	return True
1890655cb952SAdrian Hunter
18918392b74bSAdrian Hunter# SQL data preparation
18928392b74bSAdrian Hunter
18938392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
18948392b74bSAdrian Hunter	data = []
18958392b74bSAdrian Hunter	for i in xrange(count):
18968392b74bSAdrian Hunter		data.append(query.value(i))
18978392b74bSAdrian Hunter	return data
18988392b74bSAdrian Hunter
18998392b74bSAdrian Hunter# SQL table data model item
19008392b74bSAdrian Hunter
19018392b74bSAdrian Hunterclass SQLTableItem():
19028392b74bSAdrian Hunter
19038392b74bSAdrian Hunter	def __init__(self, row, data):
19048392b74bSAdrian Hunter		self.row = row
19058392b74bSAdrian Hunter		self.data = data
19068392b74bSAdrian Hunter
19078392b74bSAdrian Hunter	def getData(self, column):
19088392b74bSAdrian Hunter		return self.data[column]
19098392b74bSAdrian Hunter
19108392b74bSAdrian Hunter# SQL table data model
19118392b74bSAdrian Hunter
19128392b74bSAdrian Hunterclass SQLTableModel(TableModel):
19138392b74bSAdrian Hunter
19148392b74bSAdrian Hunter	progress = Signal(object)
19158392b74bSAdrian Hunter
19168c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
19178392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
19188392b74bSAdrian Hunter		self.glb = glb
19198392b74bSAdrian Hunter		self.more = True
19208392b74bSAdrian Hunter		self.populated = 0
19218c90fef9SAdrian Hunter		self.column_headers = column_headers
19228c90fef9SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample)
19238392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
19248392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
19258392b74bSAdrian Hunter
19268392b74bSAdrian Hunter	def DisplayData(self, item, index):
19278392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
19288392b74bSAdrian Hunter		return item.getData(index.column())
19298392b74bSAdrian Hunter
19308392b74bSAdrian Hunter	def AddSample(self, data):
19318392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
19328392b74bSAdrian Hunter		self.child_items.append(child)
19338392b74bSAdrian Hunter		self.populated += 1
19348392b74bSAdrian Hunter
19358392b74bSAdrian Hunter	def Update(self, fetched):
19368392b74bSAdrian Hunter		if not fetched:
19378392b74bSAdrian Hunter			self.more = False
19388392b74bSAdrian Hunter			self.progress.emit(0)
19398392b74bSAdrian Hunter		child_count = self.child_count
19408392b74bSAdrian Hunter		count = self.populated - child_count
19418392b74bSAdrian Hunter		if count > 0:
19428392b74bSAdrian Hunter			parent = QModelIndex()
19438392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
19448392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
19458392b74bSAdrian Hunter			self.child_count += count
19468392b74bSAdrian Hunter			self.endInsertRows()
19478392b74bSAdrian Hunter			self.progress.emit(self.child_count)
19488392b74bSAdrian Hunter
19498392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
19508392b74bSAdrian Hunter		current = self.child_count
19518392b74bSAdrian Hunter		if self.more:
19528392b74bSAdrian Hunter			self.fetcher.Fetch(count)
19538392b74bSAdrian Hunter		else:
19548392b74bSAdrian Hunter			self.progress.emit(0)
19558392b74bSAdrian Hunter		return current
19568392b74bSAdrian Hunter
19578392b74bSAdrian Hunter	def HasMoreRecords(self):
19588392b74bSAdrian Hunter		return self.more
19598392b74bSAdrian Hunter
19608c90fef9SAdrian Hunter	def columnCount(self, parent=None):
19618c90fef9SAdrian Hunter		return len(self.column_headers)
19628c90fef9SAdrian Hunter
19638c90fef9SAdrian Hunter	def columnHeader(self, column):
19648c90fef9SAdrian Hunter		return self.column_headers[column]
19658c90fef9SAdrian Hunter
19668392b74bSAdrian Hunter# SQL automatic table data model
19678392b74bSAdrian Hunter
19688392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
19698392b74bSAdrian Hunter
19708392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
19718392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
19728392b74bSAdrian Hunter		if table_name == "comm_threads_view":
19738392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
19748392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
19758c90fef9SAdrian Hunter		column_headers = []
19768392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
19778392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
19788392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
19798392b74bSAdrian Hunter			while query.next():
19808c90fef9SAdrian Hunter				column_headers.append(query.value(1))
19818392b74bSAdrian Hunter			if table_name == "sqlite_master":
19828392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
19838392b74bSAdrian Hunter		else:
19848392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
19858392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
19868392b74bSAdrian Hunter				select_table_name = table_name[19:]
19878392b74bSAdrian Hunter				schema = "information_schema"
19888392b74bSAdrian Hunter			else:
19898392b74bSAdrian Hunter				select_table_name = table_name
19908392b74bSAdrian Hunter				schema = "public"
19918392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
19928392b74bSAdrian Hunter			while query.next():
19938c90fef9SAdrian Hunter				column_headers.append(query.value(0))
19948c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
19958392b74bSAdrian Hunter
19968392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
19978392b74bSAdrian Hunter
19988392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
19998392b74bSAdrian Hunter
20008392b74bSAdrian Hunter	def __init__(self, parent=None):
20018392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
20028392b74bSAdrian Hunter
20038392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
20048392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
20058392b74bSAdrian Hunter		# so implement a crude alternative
20068392b74bSAdrian Hunter		font = self.view.font()
20078392b74bSAdrian Hunter		metrics = QFontMetrics(font)
20088392b74bSAdrian Hunter		max = 0
20098392b74bSAdrian Hunter		for row in xrange(n):
20108392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
20118392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
20128392b74bSAdrian Hunter			max = len if len > max else max
20138392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
20148392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
20158392b74bSAdrian Hunter		max = len if len > max else max
20168392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
20178392b74bSAdrian Hunter
20188392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
20198392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
20208392b74bSAdrian Hunter		if n < 1:
20218392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
20228392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
20238392b74bSAdrian Hunter			return
20248392b74bSAdrian Hunter		columns = self.data_model.columnCount()
20258392b74bSAdrian Hunter		for i in xrange(columns):
20268392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
20278392b74bSAdrian Hunter
20288392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
20298392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
20308392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
20318392b74bSAdrian Hunter		self.ResizeColumnsToContents()
20328392b74bSAdrian Hunter
20338392b74bSAdrian Hunter# Table window
20348392b74bSAdrian Hunter
20358392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
20368392b74bSAdrian Hunter
20378392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
20388392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
20398392b74bSAdrian Hunter
20408392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
20418392b74bSAdrian Hunter
20428392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
20438392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
20448392b74bSAdrian Hunter
20458392b74bSAdrian Hunter		self.view = QTableView()
20468392b74bSAdrian Hunter		self.view.setModel(self.model)
20478392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
20488392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
20498392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
20508392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
20518392b74bSAdrian Hunter
20528392b74bSAdrian Hunter		self.ResizeColumnsToContents()
20538392b74bSAdrian Hunter
20548392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
20558392b74bSAdrian Hunter
20568392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
20578392b74bSAdrian Hunter
20588392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
20598392b74bSAdrian Hunter
20608392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
20618392b74bSAdrian Hunter
20628392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
20638392b74bSAdrian Hunter
20648392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
20658392b74bSAdrian Hunter
20668392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
20678392b74bSAdrian Hunter		self.view.setFocus()
20688392b74bSAdrian Hunter		self.find_bar.Busy()
20698392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
20708392b74bSAdrian Hunter
20718392b74bSAdrian Hunter	def FindDone(self, row):
20728392b74bSAdrian Hunter		self.find_bar.Idle()
20738392b74bSAdrian Hunter		if row >= 0:
207435fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
20758392b74bSAdrian Hunter		else:
20768392b74bSAdrian Hunter			self.find_bar.NotFound()
20778392b74bSAdrian Hunter
20788392b74bSAdrian Hunter# Table list
20798392b74bSAdrian Hunter
20808392b74bSAdrian Hunterdef GetTableList(glb):
20818392b74bSAdrian Hunter	tables = []
20828392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
20838392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
20848392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
20858392b74bSAdrian Hunter	else:
20868392b74bSAdrian 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")
20878392b74bSAdrian Hunter	while query.next():
20888392b74bSAdrian Hunter		tables.append(query.value(0))
20898392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
20908392b74bSAdrian Hunter		tables.append("sqlite_master")
20918392b74bSAdrian Hunter	else:
20928392b74bSAdrian Hunter		tables.append("information_schema.tables")
20938392b74bSAdrian Hunter		tables.append("information_schema.views")
20948392b74bSAdrian Hunter		tables.append("information_schema.columns")
20958392b74bSAdrian Hunter	return tables
20968392b74bSAdrian Hunter
2097cd358012SAdrian Hunter# Top Calls data model
2098cd358012SAdrian Hunter
2099cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2100cd358012SAdrian Hunter
2101cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2102cd358012SAdrian Hunter		text = ""
2103cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2104cd358012SAdrian Hunter			text = "::text"
2105cd358012SAdrian Hunter		limit = ""
2106cd358012SAdrian Hunter		if len(report_vars.limit):
2107cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2108cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2109cd358012SAdrian Hunter			" CASE"
2110cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2111cd358012SAdrian Hunter			" ELSE short_name"
2112cd358012SAdrian Hunter			" END AS dso,"
2113cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2114cd358012SAdrian Hunter			" CASE"
2115cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2116cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2117cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2118cd358012SAdrian Hunter			" ELSE ''" + text +
2119cd358012SAdrian Hunter			" END AS flags"
2120cd358012SAdrian Hunter			" FROM calls"
2121cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2122cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2123cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2124cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2125cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2126cd358012SAdrian Hunter			report_vars.where_clause +
2127cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2128cd358012SAdrian Hunter			limit
2129cd358012SAdrian Hunter			)
2130cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2131cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2132cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2133cd358012SAdrian Hunter
2134cd358012SAdrian Hunter	def columnAlignment(self, column):
2135cd358012SAdrian Hunter		return self.alignment[column]
2136cd358012SAdrian Hunter
2137cd358012SAdrian Hunter# Top Calls report creation dialog
2138cd358012SAdrian Hunter
2139cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2140cd358012SAdrian Hunter
2141cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2142cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2143cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2144cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2145cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2146cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2147cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2148cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2149cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2150cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2151cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2152cd358012SAdrian Hunter
2153cd358012SAdrian Hunter# Top Calls window
2154cd358012SAdrian Hunter
2155cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2156cd358012SAdrian Hunter
2157cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2158cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2159cd358012SAdrian Hunter
2160cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2161cd358012SAdrian Hunter		self.model = self.data_model
2162cd358012SAdrian Hunter
2163cd358012SAdrian Hunter		self.view = QTableView()
2164cd358012SAdrian Hunter		self.view.setModel(self.model)
2165cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2166cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
2167cd358012SAdrian Hunter
2168cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2169cd358012SAdrian Hunter
2170cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2171cd358012SAdrian Hunter
2172cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2173cd358012SAdrian Hunter
2174cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2175cd358012SAdrian Hunter
2176cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2177cd358012SAdrian Hunter
2178cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2179cd358012SAdrian Hunter
2180cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2181cd358012SAdrian Hunter
2182cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2183cd358012SAdrian Hunter		self.view.setFocus()
2184cd358012SAdrian Hunter		self.find_bar.Busy()
2185cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2186cd358012SAdrian Hunter
2187cd358012SAdrian Hunter	def FindDone(self, row):
2188cd358012SAdrian Hunter		self.find_bar.Idle()
2189cd358012SAdrian Hunter		if row >= 0:
2190cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2191cd358012SAdrian Hunter		else:
2192cd358012SAdrian Hunter			self.find_bar.NotFound()
2193cd358012SAdrian Hunter
21941beb5c7bSAdrian Hunter# Action Definition
21951beb5c7bSAdrian Hunter
21961beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
21971beb5c7bSAdrian Hunter	action = QAction(label, parent)
21981beb5c7bSAdrian Hunter	if shortcut != None:
21991beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
22001beb5c7bSAdrian Hunter	action.setStatusTip(tip)
22011beb5c7bSAdrian Hunter	action.triggered.connect(callback)
22021beb5c7bSAdrian Hunter	return action
22031beb5c7bSAdrian Hunter
22041beb5c7bSAdrian Hunter# Typical application actions
22051beb5c7bSAdrian Hunter
22061beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
22071beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
22081beb5c7bSAdrian Hunter
22091beb5c7bSAdrian Hunter# Typical MDI actions
22101beb5c7bSAdrian Hunter
22111beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
22121beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
22131beb5c7bSAdrian Hunter
22141beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
22151beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
22161beb5c7bSAdrian Hunter
22171beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
22181beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
22191beb5c7bSAdrian Hunter
22201beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
22211beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
22221beb5c7bSAdrian Hunter
22231beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
22241beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
22251beb5c7bSAdrian Hunter
22261beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
22271beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
22281beb5c7bSAdrian Hunter
22291beb5c7bSAdrian Hunter# Typical MDI window menu
22301beb5c7bSAdrian Hunter
22311beb5c7bSAdrian Hunterclass WindowMenu():
22321beb5c7bSAdrian Hunter
22331beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
22341beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
22351beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
22361beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
22371beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
22381beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
22391beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
22401beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
22411beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
22421beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
22431beb5c7bSAdrian Hunter
22441beb5c7bSAdrian Hunter	def Update(self):
22451beb5c7bSAdrian Hunter		self.window_menu.clear()
22461beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
22471beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
22481beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
22491beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
22501beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
22511beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
22521beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
22531beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
22541beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
22551beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
22561beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
22571beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
22581beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
22591beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
22601beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
22611beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
22621beb5c7bSAdrian Hunter		if sub_window_count == 0:
22631beb5c7bSAdrian Hunter			return
22641beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
22651beb5c7bSAdrian Hunter		nr = 1
22661beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
22671beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
22681beb5c7bSAdrian Hunter			if nr < 10:
22691beb5c7bSAdrian Hunter				label = "&" + label
22701beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
22711beb5c7bSAdrian Hunter			action.setCheckable(True)
22721beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
22731beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
22741beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
22751beb5c7bSAdrian Hunter			nr += 1
22761beb5c7bSAdrian Hunter
22771beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
22781beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
22791beb5c7bSAdrian Hunter
228065b24292SAdrian Hunter# Help text
228165b24292SAdrian Hunter
228265b24292SAdrian Hunterglb_help_text = """
228365b24292SAdrian Hunter<h1>Contents</h1>
228465b24292SAdrian Hunter<style>
228565b24292SAdrian Hunterp.c1 {
228665b24292SAdrian Hunter    text-indent: 40px;
228765b24292SAdrian Hunter}
228865b24292SAdrian Hunterp.c2 {
228965b24292SAdrian Hunter    text-indent: 80px;
229065b24292SAdrian Hunter}
229165b24292SAdrian Hunter}
229265b24292SAdrian Hunter</style>
229365b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
229465b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
229565b24292SAdrian Hunter<p class=c2><a href=#allbranches>1.2 All branches</a></p>
229665b24292SAdrian Hunter<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
2297cd358012SAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p>
229865b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
229965b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
230065b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
230165b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
230265b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
230365b24292SAdrian Hunterwidths to suit will display something like:
230465b24292SAdrian Hunter<pre>
230565b24292SAdrian Hunter                                         Call Graph: pt_example
230665b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
230765b24292SAdrian Hunterv- ls
230865b24292SAdrian Hunter    v- 2638:2638
230965b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
231065b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
231165b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
231265b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
231365b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
231465b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
231565b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
231665b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
231765b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
231865b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
231965b24292SAdrian Hunter</pre>
232065b24292SAdrian Hunter<h3>Points to note:</h3>
232165b24292SAdrian Hunter<ul>
232265b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
232365b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
232465b24292SAdrian Hunter<li>Subsequent levels are functions</li>
232565b24292SAdrian Hunter<li>'Count' is the number of calls</li>
232665b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
232765b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
232865b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
232965b24292SAdrian Hunter</ul>
233065b24292SAdrian Hunter<h3>Find</h3>
233165b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
233265b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
233365b24292SAdrian Hunter<h2 id=allbranches>1.2 All branches</h2>
233465b24292SAdrian HunterThe All branches report displays all branches in chronological order.
233565b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
233665b24292SAdrian Hunter<h3>Disassembly</h3>
233765b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
233865b24292SAdrian Hunter<ol>
233965b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
234065b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
234165b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
234265b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
234365b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
234465b24292SAdrian Hunter</ol>
234565b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
234665b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
234765b24292SAdrian Hunter<pre>
234865b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
234965b24292SAdrian Huntergit clone https://github.com/intelxed/xed
235065b24292SAdrian Huntercd xed
235165b24292SAdrian Hunter./mfile.py --share
235265b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
235365b24292SAdrian Huntersudo ldconfig
235465b24292SAdrian Hunter</pre>
235565b24292SAdrian Hunter<h3>Find</h3>
235665b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
235765b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
235865b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
235965b24292SAdrian Hunter<h2 id=selectedbranches>1.3 Selected branches</h2>
236065b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
236165b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
236265b24292SAdrian Hunter<h3>1.3.1 Time ranges</h3>
236365b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
236465b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
236565b24292SAdrian Hunter<pre>
236665b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
236765b24292SAdrian Hunter	100us-200us		From 100us to 200us
236865b24292SAdrian Hunter	10ms-			From 10ms to the end
236965b24292SAdrian Hunter	-100ns			The first 100ns
237065b24292SAdrian Hunter	-10ms-			The last 10ms
237165b24292SAdrian Hunter</pre>
237265b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2373cd358012SAdrian Hunter<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2>
2374cd358012SAdrian HunterThe Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
2375cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2376cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
237765b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
237865b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
237965b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
238065b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
238165b24292SAdrian Hunterbut that can be slow for large tables.
238265b24292SAdrian Hunter<p>There are also tables of database meta-information.
238365b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
238465b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
238565b24292SAdrian Hunter<h3>Find</h3>
238665b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
238765b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
238865b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
238935fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
239035fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
239165b24292SAdrian Hunter"""
239265b24292SAdrian Hunter
239365b24292SAdrian Hunter# Help window
239465b24292SAdrian Hunter
239565b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
239665b24292SAdrian Hunter
239765b24292SAdrian Hunter	def __init__(self, glb, parent=None):
239865b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
239965b24292SAdrian Hunter
240065b24292SAdrian Hunter		self.text = QTextBrowser()
240165b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
240265b24292SAdrian Hunter		self.text.setReadOnly(True)
240365b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
240465b24292SAdrian Hunter
240565b24292SAdrian Hunter		self.setWidget(self.text)
240665b24292SAdrian Hunter
240765b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
240865b24292SAdrian Hunter
240965b24292SAdrian Hunter# Main window that only displays the help text
241065b24292SAdrian Hunter
241165b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
241265b24292SAdrian Hunter
241365b24292SAdrian Hunter	def __init__(self, parent=None):
241465b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
241565b24292SAdrian Hunter
241665b24292SAdrian Hunter		self.setMinimumSize(200, 100)
241765b24292SAdrian Hunter		self.resize(800, 600)
241865b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
241965b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
242065b24292SAdrian Hunter
242165b24292SAdrian Hunter		self.text = QTextBrowser()
242265b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
242365b24292SAdrian Hunter		self.text.setReadOnly(True)
242465b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
242565b24292SAdrian Hunter
242665b24292SAdrian Hunter		self.setCentralWidget(self.text)
242765b24292SAdrian Hunter
242882f68e28SAdrian Hunter# Font resize
242982f68e28SAdrian Hunter
243082f68e28SAdrian Hunterdef ResizeFont(widget, diff):
243182f68e28SAdrian Hunter	font = widget.font()
243282f68e28SAdrian Hunter	sz = font.pointSize()
243382f68e28SAdrian Hunter	font.setPointSize(sz + diff)
243482f68e28SAdrian Hunter	widget.setFont(font)
243582f68e28SAdrian Hunter
243682f68e28SAdrian Hunterdef ShrinkFont(widget):
243782f68e28SAdrian Hunter	ResizeFont(widget, -1)
243882f68e28SAdrian Hunter
243982f68e28SAdrian Hunterdef EnlargeFont(widget):
244082f68e28SAdrian Hunter	ResizeFont(widget, 1)
244182f68e28SAdrian Hunter
24421beb5c7bSAdrian Hunter# Unique name for sub-windows
24431beb5c7bSAdrian Hunter
24441beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
24451beb5c7bSAdrian Hunter	if nr > 1:
24461beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
24471beb5c7bSAdrian Hunter	return name
24481beb5c7bSAdrian Hunter
24491beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
24501beb5c7bSAdrian Hunter	nr = 1
24511beb5c7bSAdrian Hunter	while True:
24521beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
24531beb5c7bSAdrian Hunter		ok = True
24541beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
24551beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
24561beb5c7bSAdrian Hunter				ok = False
24571beb5c7bSAdrian Hunter				break
24581beb5c7bSAdrian Hunter		if ok:
24591beb5c7bSAdrian Hunter			return unique_name
24601beb5c7bSAdrian Hunter		nr += 1
24611beb5c7bSAdrian Hunter
24621beb5c7bSAdrian Hunter# Add a sub-window
24631beb5c7bSAdrian Hunter
24641beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
24651beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
24661beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
24671beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
24681beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
24691beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
24701beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
24711beb5c7bSAdrian Hunter	sub_window.name = unique_name
24721beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
24731beb5c7bSAdrian Hunter	sub_window.show()
24741beb5c7bSAdrian Hunter
2475031c2a00SAdrian Hunter# Main window
2476031c2a00SAdrian Hunter
2477031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
2478031c2a00SAdrian Hunter
2479031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
2480031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
2481031c2a00SAdrian Hunter
2482031c2a00SAdrian Hunter		self.glb = glb
2483031c2a00SAdrian Hunter
24841beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
2485031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
2486031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
2487031c2a00SAdrian Hunter
24881beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
24891beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
24901beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
2491031c2a00SAdrian Hunter
24921beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
2493031c2a00SAdrian Hunter
24941beb5c7bSAdrian Hunter		menu = self.menuBar()
2495031c2a00SAdrian Hunter
24961beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
24971beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
24981beb5c7bSAdrian Hunter
2499ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
2500ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
25018392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
250282f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
250382f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2504ebd70c7dSAdrian Hunter
25051beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
2506655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
25071beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
25081beb5c7bSAdrian Hunter
250976099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
251076099f98SAdrian Hunter
2511cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
2512cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
2513cd358012SAdrian Hunter
25148392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
25158392b74bSAdrian Hunter
25161beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
25171beb5c7bSAdrian Hunter
251865b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
251965b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
252065b24292SAdrian Hunter
2521ebd70c7dSAdrian Hunter	def Find(self):
2522ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
2523ebd70c7dSAdrian Hunter		if win:
2524ebd70c7dSAdrian Hunter			try:
2525ebd70c7dSAdrian Hunter				win.find_bar.Activate()
2526ebd70c7dSAdrian Hunter			except:
2527ebd70c7dSAdrian Hunter				pass
2528ebd70c7dSAdrian Hunter
25298392b74bSAdrian Hunter	def FetchMoreRecords(self):
25308392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
25318392b74bSAdrian Hunter		if win:
25328392b74bSAdrian Hunter			try:
25338392b74bSAdrian Hunter				win.fetch_bar.Activate()
25348392b74bSAdrian Hunter			except:
25358392b74bSAdrian Hunter				pass
25368392b74bSAdrian Hunter
253782f68e28SAdrian Hunter	def ShrinkFont(self):
253882f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
253982f68e28SAdrian Hunter		ShrinkFont(win.view)
254082f68e28SAdrian Hunter
254182f68e28SAdrian Hunter	def EnlargeFont(self):
254282f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
254382f68e28SAdrian Hunter		EnlargeFont(win.view)
254482f68e28SAdrian Hunter
254576099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
254676099f98SAdrian Hunter		branches_events = 0
254776099f98SAdrian Hunter		for event in events:
254876099f98SAdrian Hunter			event = event.split(":")[0]
254976099f98SAdrian Hunter			if event == "branches":
255076099f98SAdrian Hunter				branches_events += 1
255176099f98SAdrian Hunter		dbid = 0
255276099f98SAdrian Hunter		for event in events:
255376099f98SAdrian Hunter			dbid += 1
255476099f98SAdrian Hunter			event = event.split(":")[0]
255576099f98SAdrian Hunter			if event == "branches":
255676099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
255776099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2558210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2559210cf1f9SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
256076099f98SAdrian Hunter
25618392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
25628392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
25638392b74bSAdrian Hunter		for table in tables:
25648392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
25658392b74bSAdrian Hunter
25661beb5c7bSAdrian Hunter	def NewCallGraph(self):
25671beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
2568031c2a00SAdrian Hunter
2569cd358012SAdrian Hunter	def NewTopCalls(self):
2570cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
2571cd358012SAdrian Hunter		ret = dialog.exec_()
2572cd358012SAdrian Hunter		if ret:
2573cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
2574cd358012SAdrian Hunter
257576099f98SAdrian Hunter	def NewBranchView(self, event_id):
2576947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
257776099f98SAdrian Hunter
2578210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
2579210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
2580210cf1f9SAdrian Hunter		ret = dialog.exec_()
2581210cf1f9SAdrian Hunter		if ret:
2582947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
2583210cf1f9SAdrian Hunter
25848392b74bSAdrian Hunter	def NewTableView(self, table_name):
25858392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
25868392b74bSAdrian Hunter
258765b24292SAdrian Hunter	def Help(self):
258865b24292SAdrian Hunter		HelpWindow(self.glb, self)
258965b24292SAdrian Hunter
259076099f98SAdrian Hunter# XED Disassembler
259176099f98SAdrian Hunter
259276099f98SAdrian Hunterclass xed_state_t(Structure):
259376099f98SAdrian Hunter
259476099f98SAdrian Hunter	_fields_ = [
259576099f98SAdrian Hunter		("mode", c_int),
259676099f98SAdrian Hunter		("width", c_int)
259776099f98SAdrian Hunter	]
259876099f98SAdrian Hunter
259976099f98SAdrian Hunterclass XEDInstruction():
260076099f98SAdrian Hunter
260176099f98SAdrian Hunter	def __init__(self, libxed):
260276099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
260376099f98SAdrian Hunter		xedd_t = c_byte * 512
260476099f98SAdrian Hunter		self.xedd = xedd_t()
260576099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
260676099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
260776099f98SAdrian Hunter		self.state = xed_state_t()
260876099f98SAdrian Hunter		self.statep = addressof(self.state)
260976099f98SAdrian Hunter		# Buffer for disassembled instruction text
261076099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
261176099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
261276099f98SAdrian Hunter
261376099f98SAdrian Hunterclass LibXED():
261476099f98SAdrian Hunter
261576099f98SAdrian Hunter	def __init__(self):
26165ed4419dSAdrian Hunter		try:
261776099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
26185ed4419dSAdrian Hunter		except:
26195ed4419dSAdrian Hunter			self.libxed = None
26205ed4419dSAdrian Hunter		if not self.libxed:
26215ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
262276099f98SAdrian Hunter
262376099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
262476099f98SAdrian Hunter		self.xed_tables_init.restype = None
262576099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
262676099f98SAdrian Hunter
262776099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
262876099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
262976099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
263076099f98SAdrian Hunter
263176099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
263276099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
263376099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
263476099f98SAdrian Hunter
263576099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
263676099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
263776099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
263876099f98SAdrian Hunter
263976099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
264076099f98SAdrian Hunter		self.xed_decode.restype = c_int
264176099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
264276099f98SAdrian Hunter
264376099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
264476099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
264576099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
264676099f98SAdrian Hunter
264776099f98SAdrian Hunter		self.xed_tables_init()
264876099f98SAdrian Hunter
264976099f98SAdrian Hunter	def Instruction(self):
265076099f98SAdrian Hunter		return XEDInstruction(self)
265176099f98SAdrian Hunter
265276099f98SAdrian Hunter	def SetMode(self, inst, mode):
265376099f98SAdrian Hunter		if mode:
265476099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
265576099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
265676099f98SAdrian Hunter		else:
265776099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
265876099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
265976099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
266076099f98SAdrian Hunter
266176099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
266276099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
266376099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
266476099f98SAdrian Hunter		if err:
266576099f98SAdrian Hunter			return 0, ""
266676099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
266776099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
266876099f98SAdrian Hunter		if not ok:
266976099f98SAdrian Hunter			return 0, ""
267076099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
267176099f98SAdrian Hunter		# For now, assume the length is in byte 166
267276099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
267376099f98SAdrian Hunter
267476099f98SAdrian Hunterdef TryOpen(file_name):
267576099f98SAdrian Hunter	try:
267676099f98SAdrian Hunter		return open(file_name, "rb")
267776099f98SAdrian Hunter	except:
267876099f98SAdrian Hunter		return None
267976099f98SAdrian Hunter
268076099f98SAdrian Hunterdef Is64Bit(f):
268176099f98SAdrian Hunter	result = sizeof(c_void_p)
268276099f98SAdrian Hunter	# ELF support only
268376099f98SAdrian Hunter	pos = f.tell()
268476099f98SAdrian Hunter	f.seek(0)
268576099f98SAdrian Hunter	header = f.read(7)
268676099f98SAdrian Hunter	f.seek(pos)
268776099f98SAdrian Hunter	magic = header[0:4]
268876099f98SAdrian Hunter	eclass = ord(header[4])
268976099f98SAdrian Hunter	encoding = ord(header[5])
269076099f98SAdrian Hunter	version = ord(header[6])
269176099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
269276099f98SAdrian Hunter		result = True if eclass == 2 else False
269376099f98SAdrian Hunter	return result
269476099f98SAdrian Hunter
2695031c2a00SAdrian Hunter# Global data
2696031c2a00SAdrian Hunter
2697031c2a00SAdrian Hunterclass Glb():
2698031c2a00SAdrian Hunter
2699031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2700031c2a00SAdrian Hunter		self.dbref = dbref
2701031c2a00SAdrian Hunter		self.db = db
2702031c2a00SAdrian Hunter		self.dbname = dbname
270376099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
270476099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
270576099f98SAdrian Hunter		if self.buildid_dir:
270676099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
270776099f98SAdrian Hunter		else:
270876099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2709031c2a00SAdrian Hunter		self.app = None
2710031c2a00SAdrian Hunter		self.mainwindow = None
27118392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
271276099f98SAdrian Hunter		try:
271376099f98SAdrian Hunter			self.disassembler = LibXED()
271476099f98SAdrian Hunter			self.have_disassembler = True
271576099f98SAdrian Hunter		except:
271676099f98SAdrian Hunter			self.have_disassembler = False
271776099f98SAdrian Hunter
271876099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
271976099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
272076099f98SAdrian Hunter		return TryOpen(file_name)
272176099f98SAdrian Hunter
272276099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
272376099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
272476099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
272576099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
272676099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
272776099f98SAdrian Hunter			if f:
272876099f98SAdrian Hunter				return f
272976099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
273076099f98SAdrian Hunter			f = TryOpen(long_name)
273176099f98SAdrian Hunter			if f:
273276099f98SAdrian Hunter				return f
273376099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
273476099f98SAdrian Hunter		if f:
273576099f98SAdrian Hunter			return f
273676099f98SAdrian Hunter		return None
27378392b74bSAdrian Hunter
27388392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
27398392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
27408392b74bSAdrian Hunter
27418392b74bSAdrian Hunter	# Shutdown any background processes or threads
27428392b74bSAdrian Hunter	def ShutdownInstances(self):
27438392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
27448392b74bSAdrian Hunter			try:
27458392b74bSAdrian Hunter				x.Shutdown()
27468392b74bSAdrian Hunter			except:
27478392b74bSAdrian Hunter				pass
2748031c2a00SAdrian Hunter
2749031c2a00SAdrian Hunter# Database reference
2750031c2a00SAdrian Hunter
2751031c2a00SAdrian Hunterclass DBRef():
2752031c2a00SAdrian Hunter
2753031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2754031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2755031c2a00SAdrian Hunter		self.dbname = dbname
2756031c2a00SAdrian Hunter
2757031c2a00SAdrian Hunter	def Open(self, connection_name):
2758031c2a00SAdrian Hunter		dbname = self.dbname
2759031c2a00SAdrian Hunter		if self.is_sqlite3:
2760031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2761031c2a00SAdrian Hunter		else:
2762031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2763031c2a00SAdrian Hunter			opts = dbname.split()
2764031c2a00SAdrian Hunter			for opt in opts:
2765031c2a00SAdrian Hunter				if "=" in opt:
2766031c2a00SAdrian Hunter					opt = opt.split("=")
2767031c2a00SAdrian Hunter					if opt[0] == "hostname":
2768031c2a00SAdrian Hunter						db.setHostName(opt[1])
2769031c2a00SAdrian Hunter					elif opt[0] == "port":
2770031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2771031c2a00SAdrian Hunter					elif opt[0] == "username":
2772031c2a00SAdrian Hunter						db.setUserName(opt[1])
2773031c2a00SAdrian Hunter					elif opt[0] == "password":
2774031c2a00SAdrian Hunter						db.setPassword(opt[1])
2775031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2776031c2a00SAdrian Hunter						dbname = opt[1]
2777031c2a00SAdrian Hunter				else:
2778031c2a00SAdrian Hunter					dbname = opt
2779031c2a00SAdrian Hunter
2780031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2781031c2a00SAdrian Hunter		if not db.open():
2782031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2783031c2a00SAdrian Hunter		return db, dbname
2784031c2a00SAdrian Hunter
2785031c2a00SAdrian Hunter# Main
2786031c2a00SAdrian Hunter
2787031c2a00SAdrian Hunterdef Main():
2788031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
278965b24292SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}"
2790031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2791031c2a00SAdrian Hunter
2792031c2a00SAdrian Hunter	dbname = sys.argv[1]
279365b24292SAdrian Hunter	if dbname == "--help-only":
279465b24292SAdrian Hunter		app = QApplication(sys.argv)
279565b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
279665b24292SAdrian Hunter		mainwindow.show()
279765b24292SAdrian Hunter		err = app.exec_()
279865b24292SAdrian Hunter		sys.exit(err)
2799031c2a00SAdrian Hunter
2800031c2a00SAdrian Hunter	is_sqlite3 = False
2801031c2a00SAdrian Hunter	try:
2802031c2a00SAdrian Hunter		f = open(dbname)
2803031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
2804031c2a00SAdrian Hunter			is_sqlite3 = True
2805031c2a00SAdrian Hunter		f.close()
2806031c2a00SAdrian Hunter	except:
2807031c2a00SAdrian Hunter		pass
2808031c2a00SAdrian Hunter
2809031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
2810031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
2811031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
2812031c2a00SAdrian Hunter	app = QApplication(sys.argv)
2813031c2a00SAdrian Hunter	glb.app = app
2814031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
2815031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
2816031c2a00SAdrian Hunter	mainwindow.show()
2817031c2a00SAdrian Hunter	err = app.exec_()
28188392b74bSAdrian Hunter	glb.ShutdownInstances()
2819031c2a00SAdrian Hunter	db.close()
2820031c2a00SAdrian Hunter	sys.exit(err)
2821031c2a00SAdrian Hunter
2822031c2a00SAdrian Hunterif __name__ == "__main__":
2823031c2a00SAdrian Hunter	Main()
2824