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
170031c2a00SAdrian Hunter	def __init__(self, root, parent=None):
171031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
172031c2a00SAdrian Hunter		self.root = root
173031c2a00SAdrian Hunter		self.last_row_read = 0
174031c2a00SAdrian Hunter
175031c2a00SAdrian Hunter	def Item(self, parent):
176031c2a00SAdrian Hunter		if parent.isValid():
177031c2a00SAdrian Hunter			return parent.internalPointer()
178031c2a00SAdrian Hunter		else:
179031c2a00SAdrian Hunter			return self.root
180031c2a00SAdrian Hunter
181031c2a00SAdrian Hunter	def rowCount(self, parent):
182031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
183031c2a00SAdrian Hunter		if result < 0:
184031c2a00SAdrian Hunter			result = 0
185031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
186031c2a00SAdrian Hunter		return result
187031c2a00SAdrian Hunter
188031c2a00SAdrian Hunter	def hasChildren(self, parent):
189031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
190031c2a00SAdrian Hunter
191031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
192031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
193031c2a00SAdrian Hunter			return self.columnAlignment(section)
194031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
195031c2a00SAdrian Hunter			return None
196031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
197031c2a00SAdrian Hunter			return None
198031c2a00SAdrian Hunter		return self.columnHeader(section)
199031c2a00SAdrian Hunter
200031c2a00SAdrian Hunter	def parent(self, child):
201031c2a00SAdrian Hunter		child_item = child.internalPointer()
202031c2a00SAdrian Hunter		if child_item is self.root:
203031c2a00SAdrian Hunter			return QModelIndex()
204031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
205031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
206031c2a00SAdrian Hunter
207031c2a00SAdrian Hunter	def index(self, row, column, parent):
208031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
209031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
210031c2a00SAdrian Hunter
211031c2a00SAdrian Hunter	def DisplayData(self, item, index):
212031c2a00SAdrian Hunter		return item.getData(index.column())
213031c2a00SAdrian Hunter
2148392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2158392b74bSAdrian Hunter		if row > self.last_row_read:
2168392b74bSAdrian Hunter			self.last_row_read = row
2178392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2188392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2198392b74bSAdrian Hunter
2208392b74bSAdrian Hunter	def columnAlignment(self, column):
2218392b74bSAdrian Hunter		return Qt.AlignLeft
2228392b74bSAdrian Hunter
2238392b74bSAdrian Hunter	def columnFont(self, column):
2248392b74bSAdrian Hunter		return None
2258392b74bSAdrian Hunter
2268392b74bSAdrian Hunter	def data(self, index, role):
2278392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2288392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2298392b74bSAdrian Hunter		if role == Qt.FontRole:
2308392b74bSAdrian Hunter			return self.columnFont(index.column())
2318392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2328392b74bSAdrian Hunter			return None
2338392b74bSAdrian Hunter		item = index.internalPointer()
2348392b74bSAdrian Hunter		return self.DisplayData(item, index)
2358392b74bSAdrian Hunter
2368392b74bSAdrian Hunter# Table data model
2378392b74bSAdrian Hunter
2388392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2398392b74bSAdrian Hunter
2408392b74bSAdrian Hunter	def __init__(self, parent=None):
2418392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2428392b74bSAdrian Hunter		self.child_count = 0
2438392b74bSAdrian Hunter		self.child_items = []
2448392b74bSAdrian Hunter		self.last_row_read = 0
2458392b74bSAdrian Hunter
2468392b74bSAdrian Hunter	def Item(self, parent):
2478392b74bSAdrian Hunter		if parent.isValid():
2488392b74bSAdrian Hunter			return parent.internalPointer()
2498392b74bSAdrian Hunter		else:
2508392b74bSAdrian Hunter			return self
2518392b74bSAdrian Hunter
2528392b74bSAdrian Hunter	def rowCount(self, parent):
2538392b74bSAdrian Hunter		return self.child_count
2548392b74bSAdrian Hunter
2558392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2568392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2578392b74bSAdrian Hunter			return self.columnAlignment(section)
2588392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2598392b74bSAdrian Hunter			return None
2608392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2618392b74bSAdrian Hunter			return None
2628392b74bSAdrian Hunter		return self.columnHeader(section)
2638392b74bSAdrian Hunter
2648392b74bSAdrian Hunter	def index(self, row, column, parent):
2658392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2668392b74bSAdrian Hunter
2678392b74bSAdrian Hunter	def DisplayData(self, item, index):
2688392b74bSAdrian Hunter		return item.getData(index.column())
2698392b74bSAdrian Hunter
2708392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2718392b74bSAdrian Hunter		if row > self.last_row_read:
2728392b74bSAdrian Hunter			self.last_row_read = row
2738392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2748392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2758392b74bSAdrian Hunter
276031c2a00SAdrian Hunter	def columnAlignment(self, column):
277031c2a00SAdrian Hunter		return Qt.AlignLeft
278031c2a00SAdrian Hunter
279031c2a00SAdrian Hunter	def columnFont(self, column):
280031c2a00SAdrian Hunter		return None
281031c2a00SAdrian Hunter
282031c2a00SAdrian Hunter	def data(self, index, role):
283031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
284031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
285031c2a00SAdrian Hunter		if role == Qt.FontRole:
286031c2a00SAdrian Hunter			return self.columnFont(index.column())
287031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
288031c2a00SAdrian Hunter			return None
289031c2a00SAdrian Hunter		item = index.internalPointer()
290031c2a00SAdrian Hunter		return self.DisplayData(item, index)
291031c2a00SAdrian Hunter
2921beb5c7bSAdrian Hunter# Model cache
2931beb5c7bSAdrian Hunter
2941beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
2951beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
2961beb5c7bSAdrian Hunter
2971beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
2981beb5c7bSAdrian Hunter	model_cache_lock.acquire()
2991beb5c7bSAdrian Hunter	try:
3001beb5c7bSAdrian Hunter		model = model_cache[model_name]
3011beb5c7bSAdrian Hunter	except:
3021beb5c7bSAdrian Hunter		model = None
3031beb5c7bSAdrian Hunter	if model is None:
3041beb5c7bSAdrian Hunter		model = create_fn()
3051beb5c7bSAdrian Hunter		model_cache[model_name] = model
3061beb5c7bSAdrian Hunter	model_cache_lock.release()
3071beb5c7bSAdrian Hunter	return model
3081beb5c7bSAdrian Hunter
309ebd70c7dSAdrian Hunter# Find bar
310ebd70c7dSAdrian Hunter
311ebd70c7dSAdrian Hunterclass FindBar():
312ebd70c7dSAdrian Hunter
313ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
314ebd70c7dSAdrian Hunter		self.finder = finder
315ebd70c7dSAdrian Hunter		self.context = []
316ebd70c7dSAdrian Hunter		self.last_value = None
317ebd70c7dSAdrian Hunter		self.last_pattern = None
318ebd70c7dSAdrian Hunter
319ebd70c7dSAdrian Hunter		label = QLabel("Find:")
320ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
321ebd70c7dSAdrian Hunter
322ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
323ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
324ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
325ebd70c7dSAdrian Hunter
326ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
327ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
328ebd70c7dSAdrian Hunter		self.progress.hide()
329ebd70c7dSAdrian Hunter
330ebd70c7dSAdrian Hunter		if is_reg_expr:
331ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
332ebd70c7dSAdrian Hunter		else:
333ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
334ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
335ebd70c7dSAdrian Hunter
336ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
337ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
338ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
339ebd70c7dSAdrian Hunter
340ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
341ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
342ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
343ebd70c7dSAdrian Hunter
344ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
345ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
346ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
347ebd70c7dSAdrian Hunter
348ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
349ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
350ebd70c7dSAdrian Hunter
351ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
352ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
353ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
354ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
355ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
356ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
357ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
358ebd70c7dSAdrian Hunter
359ebd70c7dSAdrian Hunter		self.bar = QWidget()
360ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
361ebd70c7dSAdrian Hunter		self.bar.hide()
362ebd70c7dSAdrian Hunter
363ebd70c7dSAdrian Hunter	def Widget(self):
364ebd70c7dSAdrian Hunter		return self.bar
365ebd70c7dSAdrian Hunter
366ebd70c7dSAdrian Hunter	def Activate(self):
367ebd70c7dSAdrian Hunter		self.bar.show()
368ebd70c7dSAdrian Hunter		self.textbox.setFocus()
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter	def Deactivate(self):
371ebd70c7dSAdrian Hunter		self.bar.hide()
372ebd70c7dSAdrian Hunter
373ebd70c7dSAdrian Hunter	def Busy(self):
374ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
375ebd70c7dSAdrian Hunter		self.pattern.hide()
376ebd70c7dSAdrian Hunter		self.next_button.hide()
377ebd70c7dSAdrian Hunter		self.prev_button.hide()
378ebd70c7dSAdrian Hunter		self.progress.show()
379ebd70c7dSAdrian Hunter
380ebd70c7dSAdrian Hunter	def Idle(self):
381ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
382ebd70c7dSAdrian Hunter		self.progress.hide()
383ebd70c7dSAdrian Hunter		self.pattern.show()
384ebd70c7dSAdrian Hunter		self.next_button.show()
385ebd70c7dSAdrian Hunter		self.prev_button.show()
386ebd70c7dSAdrian Hunter
387ebd70c7dSAdrian Hunter	def Find(self, direction):
388ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
389ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
390ebd70c7dSAdrian Hunter		self.last_value = value
391ebd70c7dSAdrian Hunter		self.last_pattern = pattern
392ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
393ebd70c7dSAdrian Hunter
394ebd70c7dSAdrian Hunter	def ValueChanged(self):
395ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
396ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
397ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
398ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
399ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
400ebd70c7dSAdrian Hunter		if data == None:
401ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
402ebd70c7dSAdrian Hunter		else:
403ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
404ebd70c7dSAdrian Hunter		self.Find(0)
405ebd70c7dSAdrian Hunter
406ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
407ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
408ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
409ebd70c7dSAdrian Hunter		if value != self.last_value:
410ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
411ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
412ebd70c7dSAdrian Hunter			if index < 0:
413ebd70c7dSAdrian Hunter				index = self.textbox.count()
414ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
415ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
416ebd70c7dSAdrian Hunter				return
417ebd70c7dSAdrian Hunter			else:
418ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
419ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
420ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
421ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
422ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
423ebd70c7dSAdrian Hunter		self.Find(direction)
424ebd70c7dSAdrian Hunter
425ebd70c7dSAdrian Hunter	def NotFound(self):
426ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
427ebd70c7dSAdrian Hunter
428031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
429031c2a00SAdrian Hunter
430031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
431031c2a00SAdrian Hunter
432031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
433031c2a00SAdrian Hunter		self.glb = glb
434031c2a00SAdrian Hunter		self.row = row
435031c2a00SAdrian Hunter		self.parent_item = parent_item
436031c2a00SAdrian Hunter		self.query_done = False;
437031c2a00SAdrian Hunter		self.child_count = 0
438031c2a00SAdrian Hunter		self.child_items = []
439031c2a00SAdrian Hunter
440031c2a00SAdrian Hunter	def getChildItem(self, row):
441031c2a00SAdrian Hunter		return self.child_items[row]
442031c2a00SAdrian Hunter
443031c2a00SAdrian Hunter	def getParentItem(self):
444031c2a00SAdrian Hunter		return self.parent_item
445031c2a00SAdrian Hunter
446031c2a00SAdrian Hunter	def getRow(self):
447031c2a00SAdrian Hunter		return self.row
448031c2a00SAdrian Hunter
449031c2a00SAdrian Hunter	def childCount(self):
450031c2a00SAdrian Hunter		if not self.query_done:
451031c2a00SAdrian Hunter			self.Select()
452031c2a00SAdrian Hunter			if not self.child_count:
453031c2a00SAdrian Hunter				return -1
454031c2a00SAdrian Hunter		return self.child_count
455031c2a00SAdrian Hunter
456031c2a00SAdrian Hunter	def hasChildren(self):
457031c2a00SAdrian Hunter		if not self.query_done:
458031c2a00SAdrian Hunter			return True
459031c2a00SAdrian Hunter		return self.child_count > 0
460031c2a00SAdrian Hunter
461031c2a00SAdrian Hunter	def getData(self, column):
462031c2a00SAdrian Hunter		return self.data[column]
463031c2a00SAdrian Hunter
464031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
465031c2a00SAdrian Hunter
466031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
467031c2a00SAdrian Hunter
468031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
469031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
470031c2a00SAdrian Hunter		self.comm_id = comm_id
471031c2a00SAdrian Hunter		self.thread_id = thread_id
472031c2a00SAdrian Hunter		self.call_path_id = call_path_id
473031c2a00SAdrian Hunter		self.branch_count = branch_count
474031c2a00SAdrian Hunter		self.time = time
475031c2a00SAdrian Hunter
476031c2a00SAdrian Hunter	def Select(self):
477031c2a00SAdrian Hunter		self.query_done = True;
478031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
479031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
480031c2a00SAdrian Hunter					" FROM calls"
481031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
482031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
483031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
484031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
485031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
486031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
487031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
488031c2a00SAdrian Hunter					" ORDER BY call_path_id")
489031c2a00SAdrian Hunter		while query.next():
490031c2a00SAdrian Hunter			child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
491031c2a00SAdrian Hunter			self.child_items.append(child_item)
492031c2a00SAdrian Hunter			self.child_count += 1
493031c2a00SAdrian Hunter
494031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
497031c2a00SAdrian Hunter
498031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
499031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
500031c2a00SAdrian Hunter		dso = dsoname(dso)
501031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
502031c2a00SAdrian Hunter		self.dbid = call_path_id
503031c2a00SAdrian Hunter
504031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
505031c2a00SAdrian Hunter
506031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
507031c2a00SAdrian Hunter
508031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
509031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
510031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
511031c2a00SAdrian Hunter		self.dbid = thread_id
512031c2a00SAdrian Hunter
513031c2a00SAdrian Hunter	def Select(self):
514031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
515031c2a00SAdrian Hunter		for child_item in self.child_items:
516031c2a00SAdrian Hunter			self.time += child_item.time
517031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
518031c2a00SAdrian Hunter		for child_item in self.child_items:
519031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
520031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
521031c2a00SAdrian Hunter
522031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
523031c2a00SAdrian Hunter
524031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
525031c2a00SAdrian Hunter
526031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
527031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
528031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
529031c2a00SAdrian Hunter		self.dbid = comm_id
530031c2a00SAdrian Hunter
531031c2a00SAdrian Hunter	def Select(self):
532031c2a00SAdrian Hunter		self.query_done = True;
533031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
534031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
535031c2a00SAdrian Hunter					" FROM comm_threads"
536031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
537031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
538031c2a00SAdrian Hunter		while query.next():
539031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
540031c2a00SAdrian Hunter			self.child_items.append(child_item)
541031c2a00SAdrian Hunter			self.child_count += 1
542031c2a00SAdrian Hunter
543031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
544031c2a00SAdrian Hunter
545031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
546031c2a00SAdrian Hunter
547031c2a00SAdrian Hunter	def __init__(self, glb):
548031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
549031c2a00SAdrian Hunter		self.dbid = 0
550031c2a00SAdrian Hunter		self.query_done = True;
551031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
552031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
553031c2a00SAdrian Hunter		while query.next():
554031c2a00SAdrian Hunter			if not query.value(0):
555031c2a00SAdrian Hunter				continue
556031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
557031c2a00SAdrian Hunter			self.child_items.append(child_item)
558031c2a00SAdrian Hunter			self.child_count += 1
559031c2a00SAdrian Hunter
560031c2a00SAdrian Hunter# Context-sensitive call graph data model
561031c2a00SAdrian Hunter
562031c2a00SAdrian Hunterclass CallGraphModel(TreeModel):
563031c2a00SAdrian Hunter
564031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
565031c2a00SAdrian Hunter		super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
566031c2a00SAdrian Hunter		self.glb = glb
567031c2a00SAdrian Hunter
568031c2a00SAdrian Hunter	def columnCount(self, parent=None):
569031c2a00SAdrian Hunter		return 7
570031c2a00SAdrian Hunter
571031c2a00SAdrian Hunter	def columnHeader(self, column):
572031c2a00SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
573031c2a00SAdrian Hunter		return headers[column]
574031c2a00SAdrian Hunter
575031c2a00SAdrian Hunter	def columnAlignment(self, column):
576031c2a00SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
577031c2a00SAdrian Hunter		return alignment[column]
578031c2a00SAdrian Hunter
579ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
580ebd70c7dSAdrian Hunter		if pattern:
581ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
582ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
583ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
584ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
585ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
586ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
587ebd70c7dSAdrian Hunter				# Escape % and _
588ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
589ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
590ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
591ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
592ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
593ebd70c7dSAdrian Hunter			else:
594ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
595ebd70c7dSAdrian Hunter		else:
596ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
597ebd70c7dSAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
598ebd70c7dSAdrian Hunter						" FROM calls"
599ebd70c7dSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
600ebd70c7dSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
601ebd70c7dSAdrian Hunter						" WHERE symbols.name" + match +
602ebd70c7dSAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
603ebd70c7dSAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
604ebd70c7dSAdrian Hunter
605ebd70c7dSAdrian Hunter	def FindPath(self, query):
606ebd70c7dSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
607ebd70c7dSAdrian Hunter		# to open the tree at the right place.
608ebd70c7dSAdrian Hunter		ids = []
609ebd70c7dSAdrian Hunter		parent_id = query.value(0)
610ebd70c7dSAdrian Hunter		while parent_id:
611ebd70c7dSAdrian Hunter			ids.insert(0, parent_id)
612ebd70c7dSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
613ebd70c7dSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
614ebd70c7dSAdrian Hunter					" FROM call_paths"
615ebd70c7dSAdrian Hunter					" WHERE id = " + str(parent_id))
616ebd70c7dSAdrian Hunter			if not q2.next():
617ebd70c7dSAdrian Hunter				break
618ebd70c7dSAdrian Hunter			parent_id = q2.value(0)
619ebd70c7dSAdrian Hunter		# The call path root is not used
620ebd70c7dSAdrian Hunter		if ids[0] == 1:
621ebd70c7dSAdrian Hunter			del ids[0]
622ebd70c7dSAdrian Hunter		ids.insert(0, query.value(2))
623ebd70c7dSAdrian Hunter		ids.insert(0, query.value(1))
624ebd70c7dSAdrian Hunter		return ids
625ebd70c7dSAdrian Hunter
626ebd70c7dSAdrian Hunter	def Found(self, query, found):
627ebd70c7dSAdrian Hunter		if found:
628ebd70c7dSAdrian Hunter			return self.FindPath(query)
629ebd70c7dSAdrian Hunter		return []
630ebd70c7dSAdrian Hunter
631ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
632ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
633ebd70c7dSAdrian Hunter			found = query.first()
634ebd70c7dSAdrian Hunter		else:
635ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
636ebd70c7dSAdrian Hunter			found = query.next()
637ebd70c7dSAdrian Hunter		return self.Found(query, found)
638ebd70c7dSAdrian Hunter
639ebd70c7dSAdrian Hunter	def FindNext(self, query):
640ebd70c7dSAdrian Hunter		found = query.next()
641ebd70c7dSAdrian Hunter		if not found:
642ebd70c7dSAdrian Hunter			found = query.first()
643ebd70c7dSAdrian Hunter		return self.Found(query, found)
644ebd70c7dSAdrian Hunter
645ebd70c7dSAdrian Hunter	def FindPrev(self, query):
646ebd70c7dSAdrian Hunter		found = query.previous()
647ebd70c7dSAdrian Hunter		if not found:
648ebd70c7dSAdrian Hunter			found = query.last()
649ebd70c7dSAdrian Hunter		return self.Found(query, found)
650ebd70c7dSAdrian Hunter
651ebd70c7dSAdrian Hunter	def FindThread(self, c):
652ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
653ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
654ebd70c7dSAdrian Hunter		elif c.direction > 0:
655ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
656ebd70c7dSAdrian Hunter		else:
657ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
658ebd70c7dSAdrian Hunter		return (True, ids)
659ebd70c7dSAdrian Hunter
660ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
661ebd70c7dSAdrian Hunter		class Context():
662ebd70c7dSAdrian Hunter			def __init__(self, *x):
663ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
664ebd70c7dSAdrian Hunter			def Update(self, *x):
665ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
666ebd70c7dSAdrian Hunter		if len(context):
667ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
668ebd70c7dSAdrian Hunter		else:
669ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
670ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
671ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
672ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
673ebd70c7dSAdrian Hunter		thread.start()
674ebd70c7dSAdrian Hunter
675ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
676ebd70c7dSAdrian Hunter		callback(ids)
677ebd70c7dSAdrian Hunter
678ebd70c7dSAdrian Hunter# Vertical widget layout
679ebd70c7dSAdrian Hunter
680ebd70c7dSAdrian Hunterclass VBox():
681ebd70c7dSAdrian Hunter
682ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
683ebd70c7dSAdrian Hunter		self.vbox = QWidget()
684ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
685ebd70c7dSAdrian Hunter
686ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
687ebd70c7dSAdrian Hunter
688ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
689ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
690ebd70c7dSAdrian Hunter		if w3:
691ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
692ebd70c7dSAdrian Hunter
693ebd70c7dSAdrian Hunter	def Widget(self):
694ebd70c7dSAdrian Hunter		return self.vbox
695ebd70c7dSAdrian Hunter
6961beb5c7bSAdrian Hunter# Context-sensitive call graph window
6971beb5c7bSAdrian Hunter
6981beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow):
6991beb5c7bSAdrian Hunter
7001beb5c7bSAdrian Hunter	def __init__(self, glb, parent=None):
7011beb5c7bSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
7021beb5c7bSAdrian Hunter
7031beb5c7bSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
7041beb5c7bSAdrian Hunter
7051beb5c7bSAdrian Hunter		self.view = QTreeView()
7061beb5c7bSAdrian Hunter		self.view.setModel(self.model)
7071beb5c7bSAdrian Hunter
7081beb5c7bSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
7091beb5c7bSAdrian Hunter			self.view.setColumnWidth(c, w)
7101beb5c7bSAdrian Hunter
711ebd70c7dSAdrian Hunter		self.find_bar = FindBar(self, self)
712ebd70c7dSAdrian Hunter
713ebd70c7dSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
714ebd70c7dSAdrian Hunter
715ebd70c7dSAdrian Hunter		self.setWidget(self.vbox.Widget())
7161beb5c7bSAdrian Hunter
7171beb5c7bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
7181beb5c7bSAdrian Hunter
719ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
720ebd70c7dSAdrian Hunter		if not len(ids):
721ebd70c7dSAdrian Hunter			return False
722ebd70c7dSAdrian Hunter		parent = QModelIndex()
723ebd70c7dSAdrian Hunter		for dbid in ids:
724ebd70c7dSAdrian Hunter			found = False
725ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
726ebd70c7dSAdrian Hunter			for row in xrange(n):
727ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
728ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
729ebd70c7dSAdrian Hunter					found = True
730ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
731ebd70c7dSAdrian Hunter					parent = child
732ebd70c7dSAdrian Hunter					break
733ebd70c7dSAdrian Hunter			if not found:
734ebd70c7dSAdrian Hunter				break
735ebd70c7dSAdrian Hunter		return found
736ebd70c7dSAdrian Hunter
737ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
738ebd70c7dSAdrian Hunter		self.view.setFocus()
739ebd70c7dSAdrian Hunter		self.find_bar.Busy()
740ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
741ebd70c7dSAdrian Hunter
742ebd70c7dSAdrian Hunter	def FindDone(self, ids):
743ebd70c7dSAdrian Hunter		found = True
744ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
745ebd70c7dSAdrian Hunter			found = False
746ebd70c7dSAdrian Hunter		self.find_bar.Idle()
747ebd70c7dSAdrian Hunter		if not found:
748ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
749ebd70c7dSAdrian Hunter
7508392b74bSAdrian Hunter# Child data item  finder
7518392b74bSAdrian Hunter
7528392b74bSAdrian Hunterclass ChildDataItemFinder():
7538392b74bSAdrian Hunter
7548392b74bSAdrian Hunter	def __init__(self, root):
7558392b74bSAdrian Hunter		self.root = root
7568392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
7578392b74bSAdrian Hunter		self.rows = []
7588392b74bSAdrian Hunter		self.pos = 0
7598392b74bSAdrian Hunter
7608392b74bSAdrian Hunter	def FindSelect(self):
7618392b74bSAdrian Hunter		self.rows = []
7628392b74bSAdrian Hunter		if self.pattern:
7638392b74bSAdrian Hunter			pattern = re.compile(self.value)
7648392b74bSAdrian Hunter			for child in self.root.child_items:
7658392b74bSAdrian Hunter				for column_data in child.data:
7668392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
7678392b74bSAdrian Hunter						self.rows.append(child.row)
7688392b74bSAdrian Hunter						break
7698392b74bSAdrian Hunter		else:
7708392b74bSAdrian Hunter			for child in self.root.child_items:
7718392b74bSAdrian Hunter				for column_data in child.data:
7728392b74bSAdrian Hunter					if self.value in str(column_data):
7738392b74bSAdrian Hunter						self.rows.append(child.row)
7748392b74bSAdrian Hunter						break
7758392b74bSAdrian Hunter
7768392b74bSAdrian Hunter	def FindValue(self):
7778392b74bSAdrian Hunter		self.pos = 0
7788392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
7798392b74bSAdrian Hunter			self.FindSelect()
7808392b74bSAdrian Hunter		if not len(self.rows):
7818392b74bSAdrian Hunter			return -1
7828392b74bSAdrian Hunter		return self.rows[self.pos]
7838392b74bSAdrian Hunter
7848392b74bSAdrian Hunter	def FindThread(self):
7858392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
7868392b74bSAdrian Hunter			row = self.FindValue()
7878392b74bSAdrian Hunter		elif len(self.rows):
7888392b74bSAdrian Hunter			if self.direction > 0:
7898392b74bSAdrian Hunter				self.pos += 1
7908392b74bSAdrian Hunter				if self.pos >= len(self.rows):
7918392b74bSAdrian Hunter					self.pos = 0
7928392b74bSAdrian Hunter			else:
7938392b74bSAdrian Hunter				self.pos -= 1
7948392b74bSAdrian Hunter				if self.pos < 0:
7958392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
7968392b74bSAdrian Hunter			row = self.rows[self.pos]
7978392b74bSAdrian Hunter		else:
7988392b74bSAdrian Hunter			row = -1
7998392b74bSAdrian Hunter		return (True, row)
8008392b74bSAdrian Hunter
8018392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
8028392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
8038392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
8048392b74bSAdrian Hunter		thread = Thread(self.FindThread)
8058392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
8068392b74bSAdrian Hunter		thread.start()
8078392b74bSAdrian Hunter
8088392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
8098392b74bSAdrian Hunter		callback(row)
8108392b74bSAdrian Hunter
8118392b74bSAdrian Hunter# Number of database records to fetch in one go
8128392b74bSAdrian Hunter
8138392b74bSAdrian Hunterglb_chunk_sz = 10000
8148392b74bSAdrian Hunter
8158392b74bSAdrian Hunter# size of pickled integer big enough for record size
8168392b74bSAdrian Hunter
8178392b74bSAdrian Hunterglb_nsz = 8
8188392b74bSAdrian Hunter
8198392b74bSAdrian Hunter# Background process for SQL data fetcher
8208392b74bSAdrian Hunter
8218392b74bSAdrian Hunterclass SQLFetcherProcess():
8228392b74bSAdrian Hunter
8238392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
8248392b74bSAdrian Hunter		# Need a unique connection name
8258392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
8268392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
8278392b74bSAdrian Hunter		self.sql = sql
8288392b74bSAdrian Hunter		self.buffer = buffer
8298392b74bSAdrian Hunter		self.head = head
8308392b74bSAdrian Hunter		self.tail = tail
8318392b74bSAdrian Hunter		self.fetch_count = fetch_count
8328392b74bSAdrian Hunter		self.fetching_done = fetching_done
8338392b74bSAdrian Hunter		self.process_target = process_target
8348392b74bSAdrian Hunter		self.wait_event = wait_event
8358392b74bSAdrian Hunter		self.fetched_event = fetched_event
8368392b74bSAdrian Hunter		self.prep = prep
8378392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
8388392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
8398392b74bSAdrian Hunter		self.last_id = -1
8408392b74bSAdrian Hunter		self.fetched = 0
8418392b74bSAdrian Hunter		self.more = True
8428392b74bSAdrian Hunter		self.local_head = self.head.value
8438392b74bSAdrian Hunter		self.local_tail = self.tail.value
8448392b74bSAdrian Hunter
8458392b74bSAdrian Hunter	def Select(self):
8468392b74bSAdrian Hunter		if self.query_limit:
8478392b74bSAdrian Hunter			if self.query_limit == 1:
8488392b74bSAdrian Hunter				return
8498392b74bSAdrian Hunter			self.query_limit -= 1
8508392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
8518392b74bSAdrian Hunter		QueryExec(self.query, stmt)
8528392b74bSAdrian Hunter
8538392b74bSAdrian Hunter	def Next(self):
8548392b74bSAdrian Hunter		if not self.query.next():
8558392b74bSAdrian Hunter			self.Select()
8568392b74bSAdrian Hunter			if not self.query.next():
8578392b74bSAdrian Hunter				return None
8588392b74bSAdrian Hunter		self.last_id = self.query.value(0)
8598392b74bSAdrian Hunter		return self.prep(self.query)
8608392b74bSAdrian Hunter
8618392b74bSAdrian Hunter	def WaitForTarget(self):
8628392b74bSAdrian Hunter		while True:
8638392b74bSAdrian Hunter			self.wait_event.clear()
8648392b74bSAdrian Hunter			target = self.process_target.value
8658392b74bSAdrian Hunter			if target > self.fetched or target < 0:
8668392b74bSAdrian Hunter				break
8678392b74bSAdrian Hunter			self.wait_event.wait()
8688392b74bSAdrian Hunter		return target
8698392b74bSAdrian Hunter
8708392b74bSAdrian Hunter	def HasSpace(self, sz):
8718392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
8728392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
8738392b74bSAdrian Hunter			if space > sz:
8748392b74bSAdrian Hunter				return True
8758392b74bSAdrian Hunter			if space >= glb_nsz:
8768392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
8778392b74bSAdrian Hunter				nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
8788392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
8798392b74bSAdrian Hunter			self.local_head = 0
8808392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
8818392b74bSAdrian Hunter			return True
8828392b74bSAdrian Hunter		return False
8838392b74bSAdrian Hunter
8848392b74bSAdrian Hunter	def WaitForSpace(self, sz):
8858392b74bSAdrian Hunter		if self.HasSpace(sz):
8868392b74bSAdrian Hunter			return
8878392b74bSAdrian Hunter		while True:
8888392b74bSAdrian Hunter			self.wait_event.clear()
8898392b74bSAdrian Hunter			self.local_tail = self.tail.value
8908392b74bSAdrian Hunter			if self.HasSpace(sz):
8918392b74bSAdrian Hunter				return
8928392b74bSAdrian Hunter			self.wait_event.wait()
8938392b74bSAdrian Hunter
8948392b74bSAdrian Hunter	def AddToBuffer(self, obj):
8958392b74bSAdrian Hunter		d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
8968392b74bSAdrian Hunter		n = len(d)
8978392b74bSAdrian Hunter		nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
8988392b74bSAdrian Hunter		sz = n + glb_nsz
8998392b74bSAdrian Hunter		self.WaitForSpace(sz)
9008392b74bSAdrian Hunter		pos = self.local_head
9018392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
9028392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
9038392b74bSAdrian Hunter		self.local_head += sz
9048392b74bSAdrian Hunter
9058392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
9068392b74bSAdrian Hunter		fetched = 0
9078392b74bSAdrian Hunter		while batch_size > fetched:
9088392b74bSAdrian Hunter			obj = self.Next()
9098392b74bSAdrian Hunter			if obj is None:
9108392b74bSAdrian Hunter				self.more = False
9118392b74bSAdrian Hunter				break
9128392b74bSAdrian Hunter			self.AddToBuffer(obj)
9138392b74bSAdrian Hunter			fetched += 1
9148392b74bSAdrian Hunter		if fetched:
9158392b74bSAdrian Hunter			self.fetched += fetched
9168392b74bSAdrian Hunter			with self.fetch_count.get_lock():
9178392b74bSAdrian Hunter				self.fetch_count.value += fetched
9188392b74bSAdrian Hunter			self.head.value = self.local_head
9198392b74bSAdrian Hunter			self.fetched_event.set()
9208392b74bSAdrian Hunter
9218392b74bSAdrian Hunter	def Run(self):
9228392b74bSAdrian Hunter		while self.more:
9238392b74bSAdrian Hunter			target = self.WaitForTarget()
9248392b74bSAdrian Hunter			if target < 0:
9258392b74bSAdrian Hunter				break
9268392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
9278392b74bSAdrian Hunter			self.FetchBatch(batch_size)
9288392b74bSAdrian Hunter		self.fetching_done.value = True
9298392b74bSAdrian Hunter		self.fetched_event.set()
9308392b74bSAdrian Hunter
9318392b74bSAdrian Hunterdef SQLFetcherFn(*x):
9328392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
9338392b74bSAdrian Hunter	process.Run()
9348392b74bSAdrian Hunter
9358392b74bSAdrian Hunter# SQL data fetcher
9368392b74bSAdrian Hunter
9378392b74bSAdrian Hunterclass SQLFetcher(QObject):
9388392b74bSAdrian Hunter
9398392b74bSAdrian Hunter	done = Signal(object)
9408392b74bSAdrian Hunter
9418392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
9428392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
9438392b74bSAdrian Hunter		self.process_data = process_data
9448392b74bSAdrian Hunter		self.more = True
9458392b74bSAdrian Hunter		self.target = 0
9468392b74bSAdrian Hunter		self.last_target = 0
9478392b74bSAdrian Hunter		self.fetched = 0
9488392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
9498392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
9508392b74bSAdrian Hunter		self.head = Value(c_longlong)
9518392b74bSAdrian Hunter		self.tail = Value(c_longlong)
9528392b74bSAdrian Hunter		self.local_tail = 0
9538392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
9548392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
9558392b74bSAdrian Hunter		self.last_count = 0
9568392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
9578392b74bSAdrian Hunter		self.wait_event = Event()
9588392b74bSAdrian Hunter		self.fetched_event = Event()
9598392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
9608392b74bSAdrian Hunter		self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep))
9618392b74bSAdrian Hunter		self.process.start()
9628392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
9638392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
9648392b74bSAdrian Hunter		self.thread.start()
9658392b74bSAdrian Hunter
9668392b74bSAdrian Hunter	def Shutdown(self):
9678392b74bSAdrian Hunter		# Tell the thread and process to exit
9688392b74bSAdrian Hunter		self.process_target.value = -1
9698392b74bSAdrian Hunter		self.wait_event.set()
9708392b74bSAdrian Hunter		self.more = False
9718392b74bSAdrian Hunter		self.fetching_done.value = True
9728392b74bSAdrian Hunter		self.fetched_event.set()
9738392b74bSAdrian Hunter
9748392b74bSAdrian Hunter	def Thread(self):
9758392b74bSAdrian Hunter		if not self.more:
9768392b74bSAdrian Hunter			return True, 0
9778392b74bSAdrian Hunter		while True:
9788392b74bSAdrian Hunter			self.fetched_event.clear()
9798392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
9808392b74bSAdrian Hunter			if fetch_count != self.last_count:
9818392b74bSAdrian Hunter				break
9828392b74bSAdrian Hunter			if self.fetching_done.value:
9838392b74bSAdrian Hunter				self.more = False
9848392b74bSAdrian Hunter				return True, 0
9858392b74bSAdrian Hunter			self.fetched_event.wait()
9868392b74bSAdrian Hunter		count = fetch_count - self.last_count
9878392b74bSAdrian Hunter		self.last_count = fetch_count
9888392b74bSAdrian Hunter		self.fetched += count
9898392b74bSAdrian Hunter		return False, count
9908392b74bSAdrian Hunter
9918392b74bSAdrian Hunter	def Fetch(self, nr):
9928392b74bSAdrian Hunter		if not self.more:
9938392b74bSAdrian Hunter			# -1 inidcates there are no more
9948392b74bSAdrian Hunter			return -1
9958392b74bSAdrian Hunter		result = self.fetched
9968392b74bSAdrian Hunter		extra = result + nr - self.target
9978392b74bSAdrian Hunter		if extra > 0:
9988392b74bSAdrian Hunter			self.target += extra
9998392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
10008392b74bSAdrian Hunter			if self.process_target.value >= 0:
10018392b74bSAdrian Hunter				self.process_target.value = self.target
10028392b74bSAdrian Hunter			self.wait_event.set()
10038392b74bSAdrian Hunter		return result
10048392b74bSAdrian Hunter
10058392b74bSAdrian Hunter	def RemoveFromBuffer(self):
10068392b74bSAdrian Hunter		pos = self.local_tail
10078392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
10088392b74bSAdrian Hunter			pos = 0
10098392b74bSAdrian Hunter		n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
10108392b74bSAdrian Hunter		if n == 0:
10118392b74bSAdrian Hunter			pos = 0
10128392b74bSAdrian Hunter			n = cPickle.loads(self.buffer[0 : glb_nsz])
10138392b74bSAdrian Hunter		pos += glb_nsz
10148392b74bSAdrian Hunter		obj = cPickle.loads(self.buffer[pos : pos + n])
10158392b74bSAdrian Hunter		self.local_tail = pos + n
10168392b74bSAdrian Hunter		return obj
10178392b74bSAdrian Hunter
10188392b74bSAdrian Hunter	def ProcessData(self, count):
10198392b74bSAdrian Hunter		for i in xrange(count):
10208392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
10218392b74bSAdrian Hunter			self.process_data(obj)
10228392b74bSAdrian Hunter		self.tail.value = self.local_tail
10238392b74bSAdrian Hunter		self.wait_event.set()
10248392b74bSAdrian Hunter		self.done.emit(count)
10258392b74bSAdrian Hunter
10268392b74bSAdrian Hunter# Fetch more records bar
10278392b74bSAdrian Hunter
10288392b74bSAdrian Hunterclass FetchMoreRecordsBar():
10298392b74bSAdrian Hunter
10308392b74bSAdrian Hunter	def __init__(self, model, parent):
10318392b74bSAdrian Hunter		self.model = model
10328392b74bSAdrian Hunter
10338392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
10348392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10358392b74bSAdrian Hunter
10368392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
10378392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
10388392b74bSAdrian Hunter		self.fetch_count.setValue(10)
10398392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10408392b74bSAdrian Hunter
10418392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
10428392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
10438392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
10448392b74bSAdrian Hunter
10458392b74bSAdrian Hunter		self.progress = QProgressBar()
10468392b74bSAdrian Hunter		self.progress.setRange(0, 100)
10478392b74bSAdrian Hunter		self.progress.hide()
10488392b74bSAdrian Hunter
10498392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
10508392b74bSAdrian Hunter		self.done_label.hide()
10518392b74bSAdrian Hunter
10528392b74bSAdrian Hunter		self.spacer = QLabel("")
10538392b74bSAdrian Hunter
10548392b74bSAdrian Hunter		self.close_button = QToolButton()
10558392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
10568392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
10578392b74bSAdrian Hunter
10588392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
10598392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
10608392b74bSAdrian Hunter
10618392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
10628392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
10638392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
10648392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
10658392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
10668392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
10678392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
10688392b74bSAdrian Hunter
10698392b74bSAdrian Hunter		self.bar = QWidget()
10708392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
10718392b74bSAdrian Hunter		self.bar.show()
10728392b74bSAdrian Hunter
10738392b74bSAdrian Hunter		self.in_progress = False
10748392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter		self.done = False
10778392b74bSAdrian Hunter
10788392b74bSAdrian Hunter		if not model.HasMoreRecords():
10798392b74bSAdrian Hunter			self.Done()
10808392b74bSAdrian Hunter
10818392b74bSAdrian Hunter	def Widget(self):
10828392b74bSAdrian Hunter		return self.bar
10838392b74bSAdrian Hunter
10848392b74bSAdrian Hunter	def Activate(self):
10858392b74bSAdrian Hunter		self.bar.show()
10868392b74bSAdrian Hunter		self.fetch.setFocus()
10878392b74bSAdrian Hunter
10888392b74bSAdrian Hunter	def Deactivate(self):
10898392b74bSAdrian Hunter		self.bar.hide()
10908392b74bSAdrian Hunter
10918392b74bSAdrian Hunter	def Enable(self, enable):
10928392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
10938392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
10948392b74bSAdrian Hunter
10958392b74bSAdrian Hunter	def Busy(self):
10968392b74bSAdrian Hunter		self.Enable(False)
10978392b74bSAdrian Hunter		self.fetch.hide()
10988392b74bSAdrian Hunter		self.spacer.hide()
10998392b74bSAdrian Hunter		self.progress.show()
11008392b74bSAdrian Hunter
11018392b74bSAdrian Hunter	def Idle(self):
11028392b74bSAdrian Hunter		self.in_progress = False
11038392b74bSAdrian Hunter		self.Enable(True)
11048392b74bSAdrian Hunter		self.progress.hide()
11058392b74bSAdrian Hunter		self.fetch.show()
11068392b74bSAdrian Hunter		self.spacer.show()
11078392b74bSAdrian Hunter
11088392b74bSAdrian Hunter	def Target(self):
11098392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
11108392b74bSAdrian Hunter
11118392b74bSAdrian Hunter	def Done(self):
11128392b74bSAdrian Hunter		self.done = True
11138392b74bSAdrian Hunter		self.Idle()
11148392b74bSAdrian Hunter		self.label.hide()
11158392b74bSAdrian Hunter		self.fetch_count.hide()
11168392b74bSAdrian Hunter		self.fetch.hide()
11178392b74bSAdrian Hunter		self.spacer.hide()
11188392b74bSAdrian Hunter		self.done_label.show()
11198392b74bSAdrian Hunter
11208392b74bSAdrian Hunter	def Progress(self, count):
11218392b74bSAdrian Hunter		if self.in_progress:
11228392b74bSAdrian Hunter			if count:
11238392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
11248392b74bSAdrian Hunter				if percent >= 100:
11258392b74bSAdrian Hunter					self.Idle()
11268392b74bSAdrian Hunter				else:
11278392b74bSAdrian Hunter					self.progress.setValue(percent)
11288392b74bSAdrian Hunter		if not count:
11298392b74bSAdrian Hunter			# Count value of zero means no more records
11308392b74bSAdrian Hunter			self.Done()
11318392b74bSAdrian Hunter
11328392b74bSAdrian Hunter	def FetchMoreRecords(self):
11338392b74bSAdrian Hunter		if self.done:
11348392b74bSAdrian Hunter			return
11358392b74bSAdrian Hunter		self.progress.setValue(0)
11368392b74bSAdrian Hunter		self.Busy()
11378392b74bSAdrian Hunter		self.in_progress = True
11388392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
11398392b74bSAdrian Hunter
114076099f98SAdrian Hunter# Brance data model level two item
114176099f98SAdrian Hunter
114276099f98SAdrian Hunterclass BranchLevelTwoItem():
114376099f98SAdrian Hunter
114476099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
114576099f98SAdrian Hunter		self.row = row
114676099f98SAdrian Hunter		self.parent_item = parent_item
114776099f98SAdrian Hunter		self.data = [""] * 8
114876099f98SAdrian Hunter		self.data[7] = text
114976099f98SAdrian Hunter		self.level = 2
115076099f98SAdrian Hunter
115176099f98SAdrian Hunter	def getParentItem(self):
115276099f98SAdrian Hunter		return self.parent_item
115376099f98SAdrian Hunter
115476099f98SAdrian Hunter	def getRow(self):
115576099f98SAdrian Hunter		return self.row
115676099f98SAdrian Hunter
115776099f98SAdrian Hunter	def childCount(self):
115876099f98SAdrian Hunter		return 0
115976099f98SAdrian Hunter
116076099f98SAdrian Hunter	def hasChildren(self):
116176099f98SAdrian Hunter		return False
116276099f98SAdrian Hunter
116376099f98SAdrian Hunter	def getData(self, column):
116476099f98SAdrian Hunter		return self.data[column]
116576099f98SAdrian Hunter
116676099f98SAdrian Hunter# Brance data model level one item
116776099f98SAdrian Hunter
116876099f98SAdrian Hunterclass BranchLevelOneItem():
116976099f98SAdrian Hunter
117076099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
117176099f98SAdrian Hunter		self.glb = glb
117276099f98SAdrian Hunter		self.row = row
117376099f98SAdrian Hunter		self.parent_item = parent_item
117476099f98SAdrian Hunter		self.child_count = 0
117576099f98SAdrian Hunter		self.child_items = []
117676099f98SAdrian Hunter		self.data = data[1:]
117776099f98SAdrian Hunter		self.dbid = data[0]
117876099f98SAdrian Hunter		self.level = 1
117976099f98SAdrian Hunter		self.query_done = False
118076099f98SAdrian Hunter
118176099f98SAdrian Hunter	def getChildItem(self, row):
118276099f98SAdrian Hunter		return self.child_items[row]
118376099f98SAdrian Hunter
118476099f98SAdrian Hunter	def getParentItem(self):
118576099f98SAdrian Hunter		return self.parent_item
118676099f98SAdrian Hunter
118776099f98SAdrian Hunter	def getRow(self):
118876099f98SAdrian Hunter		return self.row
118976099f98SAdrian Hunter
119076099f98SAdrian Hunter	def Select(self):
119176099f98SAdrian Hunter		self.query_done = True
119276099f98SAdrian Hunter
119376099f98SAdrian Hunter		if not self.glb.have_disassembler:
119476099f98SAdrian Hunter			return
119576099f98SAdrian Hunter
119676099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
119776099f98SAdrian Hunter
119876099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
119976099f98SAdrian Hunter				  " FROM samples"
120076099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
120176099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
120276099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
120376099f98SAdrian Hunter		if not query.next():
120476099f98SAdrian Hunter			return
120576099f98SAdrian Hunter		cpu = query.value(0)
120676099f98SAdrian Hunter		dso = query.value(1)
120776099f98SAdrian Hunter		sym = query.value(2)
120876099f98SAdrian Hunter		if dso == 0 or sym == 0:
120976099f98SAdrian Hunter			return
121076099f98SAdrian Hunter		off = query.value(3)
121176099f98SAdrian Hunter		short_name = query.value(4)
121276099f98SAdrian Hunter		long_name = query.value(5)
121376099f98SAdrian Hunter		build_id = query.value(6)
121476099f98SAdrian Hunter		sym_start = query.value(7)
121576099f98SAdrian Hunter		ip = query.value(8)
121676099f98SAdrian Hunter
121776099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
121876099f98SAdrian Hunter				  " FROM samples"
121976099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
122076099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
122176099f98SAdrian Hunter				  " ORDER BY samples.id"
122276099f98SAdrian Hunter				  " LIMIT 1")
122376099f98SAdrian Hunter		if not query.next():
122476099f98SAdrian Hunter			return
122576099f98SAdrian Hunter		if query.value(0) != dso:
122676099f98SAdrian Hunter			# Cannot disassemble from one dso to another
122776099f98SAdrian Hunter			return
122876099f98SAdrian Hunter		bsym = query.value(1)
122976099f98SAdrian Hunter		boff = query.value(2)
123076099f98SAdrian Hunter		bsym_start = query.value(3)
123176099f98SAdrian Hunter		if bsym == 0:
123276099f98SAdrian Hunter			return
123376099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
123476099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
123576099f98SAdrian Hunter			return
123676099f98SAdrian Hunter
123776099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
123876099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
123976099f98SAdrian Hunter		if not f:
124076099f98SAdrian Hunter			return
124176099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
124276099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
124376099f98SAdrian Hunter
124476099f98SAdrian Hunter		buf_sz = tot + 16
124576099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
124676099f98SAdrian Hunter		f.seek(sym_start + off)
124776099f98SAdrian Hunter		buf.value = f.read(buf_sz)
124876099f98SAdrian Hunter		buf_ptr = addressof(buf)
124976099f98SAdrian Hunter		i = 0
125076099f98SAdrian Hunter		while tot > 0:
125176099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
125276099f98SAdrian Hunter			if cnt:
125376099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
125476099f98SAdrian Hunter				for k in xrange(cnt):
125576099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
125676099f98SAdrian Hunter					i += 1
125776099f98SAdrian Hunter				while k < 15:
125876099f98SAdrian Hunter					byte_str += "   "
125976099f98SAdrian Hunter					k += 1
126076099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
126176099f98SAdrian Hunter				self.child_count += 1
126276099f98SAdrian Hunter			else:
126376099f98SAdrian Hunter				return
126476099f98SAdrian Hunter			buf_ptr += cnt
126576099f98SAdrian Hunter			tot -= cnt
126676099f98SAdrian Hunter			buf_sz -= cnt
126776099f98SAdrian Hunter			ip += cnt
126876099f98SAdrian Hunter
126976099f98SAdrian Hunter	def childCount(self):
127076099f98SAdrian Hunter		if not self.query_done:
127176099f98SAdrian Hunter			self.Select()
127276099f98SAdrian Hunter			if not self.child_count:
127376099f98SAdrian Hunter				return -1
127476099f98SAdrian Hunter		return self.child_count
127576099f98SAdrian Hunter
127676099f98SAdrian Hunter	def hasChildren(self):
127776099f98SAdrian Hunter		if not self.query_done:
127876099f98SAdrian Hunter			return True
127976099f98SAdrian Hunter		return self.child_count > 0
128076099f98SAdrian Hunter
128176099f98SAdrian Hunter	def getData(self, column):
128276099f98SAdrian Hunter		return self.data[column]
128376099f98SAdrian Hunter
128476099f98SAdrian Hunter# Brance data model root item
128576099f98SAdrian Hunter
128676099f98SAdrian Hunterclass BranchRootItem():
128776099f98SAdrian Hunter
128876099f98SAdrian Hunter	def __init__(self):
128976099f98SAdrian Hunter		self.child_count = 0
129076099f98SAdrian Hunter		self.child_items = []
129176099f98SAdrian Hunter		self.level = 0
129276099f98SAdrian Hunter
129376099f98SAdrian Hunter	def getChildItem(self, row):
129476099f98SAdrian Hunter		return self.child_items[row]
129576099f98SAdrian Hunter
129676099f98SAdrian Hunter	def getParentItem(self):
129776099f98SAdrian Hunter		return None
129876099f98SAdrian Hunter
129976099f98SAdrian Hunter	def getRow(self):
130076099f98SAdrian Hunter		return 0
130176099f98SAdrian Hunter
130276099f98SAdrian Hunter	def childCount(self):
130376099f98SAdrian Hunter		return self.child_count
130476099f98SAdrian Hunter
130576099f98SAdrian Hunter	def hasChildren(self):
130676099f98SAdrian Hunter		return self.child_count > 0
130776099f98SAdrian Hunter
130876099f98SAdrian Hunter	def getData(self, column):
130976099f98SAdrian Hunter		return ""
131076099f98SAdrian Hunter
131176099f98SAdrian Hunter# Branch data preparation
131276099f98SAdrian Hunter
131376099f98SAdrian Hunterdef BranchDataPrep(query):
131476099f98SAdrian Hunter	data = []
131576099f98SAdrian Hunter	for i in xrange(0, 8):
131676099f98SAdrian Hunter		data.append(query.value(i))
131776099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
131876099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
131976099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
132076099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
132176099f98SAdrian Hunter	return data
132276099f98SAdrian Hunter
132376099f98SAdrian Hunter# Branch data model
132476099f98SAdrian Hunter
132576099f98SAdrian Hunterclass BranchModel(TreeModel):
132676099f98SAdrian Hunter
132776099f98SAdrian Hunter	progress = Signal(object)
132876099f98SAdrian Hunter
132976099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
133076099f98SAdrian Hunter		super(BranchModel, self).__init__(BranchRootItem(), parent)
133176099f98SAdrian Hunter		self.glb = glb
133276099f98SAdrian Hunter		self.event_id = event_id
133376099f98SAdrian Hunter		self.more = True
133476099f98SAdrian Hunter		self.populated = 0
133576099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
133676099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
133776099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
133876099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
133976099f98SAdrian Hunter			" FROM samples"
134076099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
134176099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
134276099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
134376099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
134476099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
134576099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
134676099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
134776099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
134876099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
134976099f98SAdrian Hunter			" ORDER BY samples.id"
135076099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
135176099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
135276099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
135376099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
135476099f98SAdrian Hunter
135576099f98SAdrian Hunter	def columnCount(self, parent=None):
135676099f98SAdrian Hunter		return 8
135776099f98SAdrian Hunter
135876099f98SAdrian Hunter	def columnHeader(self, column):
135976099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
136076099f98SAdrian Hunter
136176099f98SAdrian Hunter	def columnFont(self, column):
136276099f98SAdrian Hunter		if column != 7:
136376099f98SAdrian Hunter			return None
136476099f98SAdrian Hunter		return QFont("Monospace")
136576099f98SAdrian Hunter
136676099f98SAdrian Hunter	def DisplayData(self, item, index):
136776099f98SAdrian Hunter		if item.level == 1:
136876099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
136976099f98SAdrian Hunter		return item.getData(index.column())
137076099f98SAdrian Hunter
137176099f98SAdrian Hunter	def AddSample(self, data):
137276099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
137376099f98SAdrian Hunter		self.root.child_items.append(child)
137476099f98SAdrian Hunter		self.populated += 1
137576099f98SAdrian Hunter
137676099f98SAdrian Hunter	def Update(self, fetched):
137776099f98SAdrian Hunter		if not fetched:
137876099f98SAdrian Hunter			self.more = False
137976099f98SAdrian Hunter			self.progress.emit(0)
138076099f98SAdrian Hunter		child_count = self.root.child_count
138176099f98SAdrian Hunter		count = self.populated - child_count
138276099f98SAdrian Hunter		if count > 0:
138376099f98SAdrian Hunter			parent = QModelIndex()
138476099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
138576099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
138676099f98SAdrian Hunter			self.root.child_count += count
138776099f98SAdrian Hunter			self.endInsertRows()
138876099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
138976099f98SAdrian Hunter
139076099f98SAdrian Hunter	def FetchMoreRecords(self, count):
139176099f98SAdrian Hunter		current = self.root.child_count
139276099f98SAdrian Hunter		if self.more:
139376099f98SAdrian Hunter			self.fetcher.Fetch(count)
139476099f98SAdrian Hunter		else:
139576099f98SAdrian Hunter			self.progress.emit(0)
139676099f98SAdrian Hunter		return current
139776099f98SAdrian Hunter
139876099f98SAdrian Hunter	def HasMoreRecords(self):
139976099f98SAdrian Hunter		return self.more
140076099f98SAdrian Hunter
14010bf0947aSAdrian Hunter# Report Variables
14020bf0947aSAdrian Hunter
14030bf0947aSAdrian Hunterclass ReportVars():
14040bf0947aSAdrian Hunter
1405947cc38dSAdrian Hunter	def __init__(self, name = "", where_clause = ""):
1406947cc38dSAdrian Hunter		self.name = name
14070bf0947aSAdrian Hunter		self.where_clause = where_clause
14080bf0947aSAdrian Hunter
14090bf0947aSAdrian Hunter	def UniqueId(self):
14100bf0947aSAdrian Hunter		return str(self.where_clause)
14110bf0947aSAdrian Hunter
141276099f98SAdrian Hunter# Branch window
141376099f98SAdrian Hunter
141476099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
141576099f98SAdrian Hunter
1416947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
141776099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
141876099f98SAdrian Hunter
14190bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
142076099f98SAdrian Hunter
14210bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
142276099f98SAdrian Hunter
142376099f98SAdrian Hunter		self.view = QTreeView()
142476099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
142576099f98SAdrian Hunter		self.view.setModel(self.model)
142676099f98SAdrian Hunter
142776099f98SAdrian Hunter		self.ResizeColumnsToContents()
142876099f98SAdrian Hunter
142976099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
143076099f98SAdrian Hunter
143176099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
143276099f98SAdrian Hunter
143376099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
143476099f98SAdrian Hunter
143576099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
143676099f98SAdrian Hunter
143776099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
143876099f98SAdrian Hunter
1439947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
144076099f98SAdrian Hunter
144176099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
144276099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
144376099f98SAdrian Hunter		# so implement a crude alternative
144476099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
144576099f98SAdrian Hunter		font = self.view.font()
144676099f98SAdrian Hunter		metrics = QFontMetrics(font)
144776099f98SAdrian Hunter		max = 0
144876099f98SAdrian Hunter		for row in xrange(n):
144976099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
145076099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
145176099f98SAdrian Hunter			max = len if len > max else max
145276099f98SAdrian Hunter		val = self.model.columnHeader(column)
145376099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
145476099f98SAdrian Hunter		max = len if len > max else max
145576099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
145676099f98SAdrian Hunter
145776099f98SAdrian Hunter	def ResizeColumnsToContents(self):
145876099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
145976099f98SAdrian Hunter		if n < 1:
146076099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
146176099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
146276099f98SAdrian Hunter			return
146376099f98SAdrian Hunter		columns = self.model.columnCount()
146476099f98SAdrian Hunter		for i in xrange(columns):
146576099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
146676099f98SAdrian Hunter
146776099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
146876099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
146976099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
147076099f98SAdrian Hunter		self.ResizeColumnsToContents()
147176099f98SAdrian Hunter
147276099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
147376099f98SAdrian Hunter		self.view.setFocus()
147476099f98SAdrian Hunter		self.find_bar.Busy()
147576099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
147676099f98SAdrian Hunter
147776099f98SAdrian Hunter	def FindDone(self, row):
147876099f98SAdrian Hunter		self.find_bar.Idle()
147976099f98SAdrian Hunter		if row >= 0:
148076099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
148176099f98SAdrian Hunter		else:
148276099f98SAdrian Hunter			self.find_bar.NotFound()
148376099f98SAdrian Hunter
1484210cf1f9SAdrian Hunter# Dialog data item converted and validated using a SQL table
1485210cf1f9SAdrian Hunter
1486210cf1f9SAdrian Hunterclass SQLTableDialogDataItem():
1487210cf1f9SAdrian Hunter
1488210cf1f9SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
1489210cf1f9SAdrian Hunter		self.glb = glb
1490210cf1f9SAdrian Hunter		self.label = label
1491210cf1f9SAdrian Hunter		self.placeholder_text = placeholder_text
1492210cf1f9SAdrian Hunter		self.table_name = table_name
1493210cf1f9SAdrian Hunter		self.match_column = match_column
1494210cf1f9SAdrian Hunter		self.column_name1 = column_name1
1495210cf1f9SAdrian Hunter		self.column_name2 = column_name2
1496210cf1f9SAdrian Hunter		self.parent = parent
1497210cf1f9SAdrian Hunter
1498210cf1f9SAdrian Hunter		self.value = ""
1499210cf1f9SAdrian Hunter
1500210cf1f9SAdrian Hunter		self.widget = QLineEdit()
1501210cf1f9SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
1502210cf1f9SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
1503210cf1f9SAdrian Hunter		self.red = False
1504210cf1f9SAdrian Hunter		self.error = ""
1505210cf1f9SAdrian Hunter		self.validated = True
1506210cf1f9SAdrian Hunter
1507210cf1f9SAdrian Hunter		self.last_id = 0
1508210cf1f9SAdrian Hunter		self.first_time = 0
1509210cf1f9SAdrian Hunter		self.last_time = 2 ** 64
1510210cf1f9SAdrian Hunter		if self.table_name == "<timeranges>":
1511210cf1f9SAdrian Hunter			query = QSqlQuery(self.glb.db)
1512210cf1f9SAdrian Hunter			QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1513210cf1f9SAdrian Hunter			if query.next():
1514210cf1f9SAdrian Hunter				self.last_id = int(query.value(0))
1515210cf1f9SAdrian Hunter				self.last_time = int(query.value(1))
1516210cf1f9SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1517210cf1f9SAdrian Hunter			if query.next():
1518210cf1f9SAdrian Hunter				self.first_time = int(query.value(0))
1519210cf1f9SAdrian Hunter			if placeholder_text:
1520210cf1f9SAdrian Hunter				placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1521210cf1f9SAdrian Hunter
1522210cf1f9SAdrian Hunter		if placeholder_text:
1523210cf1f9SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
1524210cf1f9SAdrian Hunter
1525210cf1f9SAdrian Hunter	def ValueToIds(self, value):
1526210cf1f9SAdrian Hunter		ids = []
1527210cf1f9SAdrian Hunter		query = QSqlQuery(self.glb.db)
1528210cf1f9SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
1529210cf1f9SAdrian Hunter		ret = query.exec_(stmt)
1530210cf1f9SAdrian Hunter		if ret:
1531210cf1f9SAdrian Hunter			while query.next():
1532210cf1f9SAdrian Hunter				ids.append(str(query.value(0)))
1533210cf1f9SAdrian Hunter		return ids
1534210cf1f9SAdrian Hunter
1535210cf1f9SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
1536210cf1f9SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
1537210cf1f9SAdrian Hunter		if query.next():
1538210cf1f9SAdrian Hunter			return True, int(query.value(0))
1539210cf1f9SAdrian Hunter		else:
1540210cf1f9SAdrian Hunter			return False, 0
1541210cf1f9SAdrian Hunter
1542210cf1f9SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
1543210cf1f9SAdrian Hunter		query = QSqlQuery(self.glb.db)
1544210cf1f9SAdrian Hunter		while True:
1545210cf1f9SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
1546210cf1f9SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1547210cf1f9SAdrian Hunter			if not query.next():
1548210cf1f9SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
1549210cf1f9SAdrian Hunter				if not ok:
1550210cf1f9SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
1551210cf1f9SAdrian Hunter					if not ok:
1552210cf1f9SAdrian Hunter						return str(higher_id)
1553210cf1f9SAdrian Hunter				next_id = dbid
1554210cf1f9SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1555210cf1f9SAdrian Hunter			next_time = int(query.value(0))
1556210cf1f9SAdrian Hunter			if get_floor:
1557210cf1f9SAdrian Hunter				if target_time > next_time:
1558210cf1f9SAdrian Hunter					lower_id = next_id
1559210cf1f9SAdrian Hunter				else:
1560210cf1f9SAdrian Hunter					higher_id = next_id
1561210cf1f9SAdrian Hunter				if higher_id <= lower_id + 1:
1562210cf1f9SAdrian Hunter					return str(higher_id)
1563210cf1f9SAdrian Hunter			else:
1564210cf1f9SAdrian Hunter				if target_time >= next_time:
1565210cf1f9SAdrian Hunter					lower_id = next_id
1566210cf1f9SAdrian Hunter				else:
1567210cf1f9SAdrian Hunter					higher_id = next_id
1568210cf1f9SAdrian Hunter				if higher_id <= lower_id + 1:
1569210cf1f9SAdrian Hunter					return str(lower_id)
1570210cf1f9SAdrian Hunter
1571210cf1f9SAdrian Hunter	def ConvertRelativeTime(self, val):
1572210cf1f9SAdrian Hunter		mult = 1
1573210cf1f9SAdrian Hunter		suffix = val[-2:]
1574210cf1f9SAdrian Hunter		if suffix == "ms":
1575210cf1f9SAdrian Hunter			mult = 1000000
1576210cf1f9SAdrian Hunter		elif suffix == "us":
1577210cf1f9SAdrian Hunter			mult = 1000
1578210cf1f9SAdrian Hunter		elif suffix == "ns":
1579210cf1f9SAdrian Hunter			mult = 1
1580210cf1f9SAdrian Hunter		else:
1581210cf1f9SAdrian Hunter			return val
1582210cf1f9SAdrian Hunter		val = val[:-2].strip()
1583210cf1f9SAdrian Hunter		if not self.IsNumber(val):
1584210cf1f9SAdrian Hunter			return val
1585210cf1f9SAdrian Hunter		val = int(val) * mult
1586210cf1f9SAdrian Hunter		if val >= 0:
1587210cf1f9SAdrian Hunter			val += self.first_time
1588210cf1f9SAdrian Hunter		else:
1589210cf1f9SAdrian Hunter			val += self.last_time
1590210cf1f9SAdrian Hunter		return str(val)
1591210cf1f9SAdrian Hunter
1592210cf1f9SAdrian Hunter	def ConvertTimeRange(self, vrange):
1593210cf1f9SAdrian Hunter		if vrange[0] == "":
1594210cf1f9SAdrian Hunter			vrange[0] = str(self.first_time)
1595210cf1f9SAdrian Hunter		if vrange[1] == "":
1596210cf1f9SAdrian Hunter			vrange[1] = str(self.last_time)
1597210cf1f9SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
1598210cf1f9SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
1599210cf1f9SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1600210cf1f9SAdrian Hunter			return False
1601210cf1f9SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
1602210cf1f9SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
1603210cf1f9SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
1604210cf1f9SAdrian Hunter			return False
1605210cf1f9SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
1606210cf1f9SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
1607210cf1f9SAdrian Hunter		return True
1608210cf1f9SAdrian Hunter
1609210cf1f9SAdrian Hunter	def AddTimeRange(self, value, ranges):
1610210cf1f9SAdrian Hunter		n = value.count("-")
1611210cf1f9SAdrian Hunter		if n == 1:
1612210cf1f9SAdrian Hunter			pass
1613210cf1f9SAdrian Hunter		elif n == 2:
1614210cf1f9SAdrian Hunter			if value.split("-")[1].strip() == "":
1615210cf1f9SAdrian Hunter				n = 1
1616210cf1f9SAdrian Hunter		elif n == 3:
1617210cf1f9SAdrian Hunter			n = 2
1618210cf1f9SAdrian Hunter		else:
1619210cf1f9SAdrian Hunter			return False
1620210cf1f9SAdrian Hunter		pos = findnth(value, "-", n)
1621210cf1f9SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
1622210cf1f9SAdrian Hunter		if self.ConvertTimeRange(vrange):
1623210cf1f9SAdrian Hunter			ranges.append(vrange)
1624210cf1f9SAdrian Hunter			return True
1625210cf1f9SAdrian Hunter		return False
1626210cf1f9SAdrian Hunter
1627210cf1f9SAdrian Hunter	def InvalidValue(self, value):
1628210cf1f9SAdrian Hunter		self.value = ""
1629210cf1f9SAdrian Hunter		palette = QPalette()
1630210cf1f9SAdrian Hunter		palette.setColor(QPalette.Text,Qt.red)
1631210cf1f9SAdrian Hunter		self.widget.setPalette(palette)
1632210cf1f9SAdrian Hunter		self.red = True
1633210cf1f9SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
1634210cf1f9SAdrian Hunter		self.parent.ShowMessage(self.error)
1635210cf1f9SAdrian Hunter
1636210cf1f9SAdrian Hunter	def IsNumber(self, value):
1637210cf1f9SAdrian Hunter		try:
1638210cf1f9SAdrian Hunter			x = int(value)
1639210cf1f9SAdrian Hunter		except:
1640210cf1f9SAdrian Hunter			x = 0
1641210cf1f9SAdrian Hunter		return str(x) == value
1642210cf1f9SAdrian Hunter
1643210cf1f9SAdrian Hunter	def Invalidate(self):
1644210cf1f9SAdrian Hunter		self.validated = False
1645210cf1f9SAdrian Hunter
1646210cf1f9SAdrian Hunter	def Validate(self):
1647210cf1f9SAdrian Hunter		input_string = self.widget.text()
1648210cf1f9SAdrian Hunter		self.validated = True
1649210cf1f9SAdrian Hunter		if self.red:
1650210cf1f9SAdrian Hunter			palette = QPalette()
1651210cf1f9SAdrian Hunter			self.widget.setPalette(palette)
1652210cf1f9SAdrian Hunter			self.red = False
1653210cf1f9SAdrian Hunter		if not len(input_string.strip()):
1654210cf1f9SAdrian Hunter			self.error = ""
1655210cf1f9SAdrian Hunter			self.value = ""
1656210cf1f9SAdrian Hunter			return
1657210cf1f9SAdrian Hunter		if self.table_name == "<timeranges>":
1658210cf1f9SAdrian Hunter			ranges = []
1659210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1660210cf1f9SAdrian Hunter				if not self.AddTimeRange(value, ranges):
1661210cf1f9SAdrian Hunter					return self.InvalidValue(value)
1662210cf1f9SAdrian Hunter			ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1663210cf1f9SAdrian Hunter			self.value = " OR ".join(ranges)
1664210cf1f9SAdrian Hunter		elif self.table_name == "<ranges>":
1665210cf1f9SAdrian Hunter			singles = []
1666210cf1f9SAdrian Hunter			ranges = []
1667210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1668210cf1f9SAdrian Hunter				if "-" in value:
1669210cf1f9SAdrian Hunter					vrange = value.split("-")
1670210cf1f9SAdrian Hunter					if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1671210cf1f9SAdrian Hunter						return self.InvalidValue(value)
1672210cf1f9SAdrian Hunter					ranges.append(vrange)
1673210cf1f9SAdrian Hunter				else:
1674210cf1f9SAdrian Hunter					if not self.IsNumber(value):
1675210cf1f9SAdrian Hunter						return self.InvalidValue(value)
1676210cf1f9SAdrian Hunter					singles.append(value)
1677210cf1f9SAdrian Hunter			ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1678210cf1f9SAdrian Hunter			if len(singles):
1679210cf1f9SAdrian Hunter				ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")")
1680210cf1f9SAdrian Hunter			self.value = " OR ".join(ranges)
1681210cf1f9SAdrian Hunter		elif self.table_name:
1682210cf1f9SAdrian Hunter			all_ids = []
1683210cf1f9SAdrian Hunter			for value in [x.strip() for x in input_string.split(",")]:
1684210cf1f9SAdrian Hunter				ids = self.ValueToIds(value)
1685210cf1f9SAdrian Hunter				if len(ids):
1686210cf1f9SAdrian Hunter					all_ids.extend(ids)
1687210cf1f9SAdrian Hunter				else:
1688210cf1f9SAdrian Hunter					return self.InvalidValue(value)
1689210cf1f9SAdrian Hunter			self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1690210cf1f9SAdrian Hunter			if self.column_name2:
1691210cf1f9SAdrian Hunter				self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1692210cf1f9SAdrian Hunter		else:
1693210cf1f9SAdrian Hunter			self.value = input_string.strip()
1694210cf1f9SAdrian Hunter		self.error = ""
1695210cf1f9SAdrian Hunter		self.parent.ClearMessage()
1696210cf1f9SAdrian Hunter
1697210cf1f9SAdrian Hunter	def IsValid(self):
1698210cf1f9SAdrian Hunter		if not self.validated:
1699210cf1f9SAdrian Hunter			self.Validate()
1700210cf1f9SAdrian Hunter		if len(self.error):
1701210cf1f9SAdrian Hunter			self.parent.ShowMessage(self.error)
1702210cf1f9SAdrian Hunter			return False
1703210cf1f9SAdrian Hunter		return True
1704210cf1f9SAdrian Hunter
1705*1c3ca1b3SAdrian Hunter# Line edit data item
1706*1c3ca1b3SAdrian Hunter
1707*1c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
1708*1c3ca1b3SAdrian Hunter
1709*1c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = ""):
1710*1c3ca1b3SAdrian Hunter		self.glb = glb
1711*1c3ca1b3SAdrian Hunter		self.label = label
1712*1c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
1713*1c3ca1b3SAdrian Hunter		self.parent = parent
1714*1c3ca1b3SAdrian Hunter		self.id = id
1715*1c3ca1b3SAdrian Hunter
1716*1c3ca1b3SAdrian Hunter		self.value = ""
1717*1c3ca1b3SAdrian Hunter
1718*1c3ca1b3SAdrian Hunter		self.widget = QLineEdit()
1719*1c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
1720*1c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
1721*1c3ca1b3SAdrian Hunter		self.red = False
1722*1c3ca1b3SAdrian Hunter		self.error = ""
1723*1c3ca1b3SAdrian Hunter		self.validated = True
1724*1c3ca1b3SAdrian Hunter
1725*1c3ca1b3SAdrian Hunter		if placeholder_text:
1726*1c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
1727*1c3ca1b3SAdrian Hunter
1728*1c3ca1b3SAdrian Hunter	def TurnTextRed(self):
1729*1c3ca1b3SAdrian Hunter		if not self.red:
1730*1c3ca1b3SAdrian Hunter			palette = QPalette()
1731*1c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
1732*1c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
1733*1c3ca1b3SAdrian Hunter			self.red = True
1734*1c3ca1b3SAdrian Hunter
1735*1c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
1736*1c3ca1b3SAdrian Hunter		if self.red:
1737*1c3ca1b3SAdrian Hunter			palette = QPalette()
1738*1c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
1739*1c3ca1b3SAdrian Hunter			self.red = False
1740*1c3ca1b3SAdrian Hunter
1741*1c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
1742*1c3ca1b3SAdrian Hunter		self.value = ""
1743*1c3ca1b3SAdrian Hunter		self.TurnTextRed()
1744*1c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
1745*1c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
1746*1c3ca1b3SAdrian Hunter
1747*1c3ca1b3SAdrian Hunter	def Invalidate(self):
1748*1c3ca1b3SAdrian Hunter		self.validated = False
1749*1c3ca1b3SAdrian Hunter
1750*1c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
1751*1c3ca1b3SAdrian Hunter		self.value = input_string.strip()
1752*1c3ca1b3SAdrian Hunter
1753*1c3ca1b3SAdrian Hunter	def Validate(self):
1754*1c3ca1b3SAdrian Hunter		self.validated = True
1755*1c3ca1b3SAdrian Hunter		self.error = ""
1756*1c3ca1b3SAdrian Hunter		self.TurnTextNormal()
1757*1c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
1758*1c3ca1b3SAdrian Hunter		input_string = self.widget.text()
1759*1c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
1760*1c3ca1b3SAdrian Hunter			self.value = ""
1761*1c3ca1b3SAdrian Hunter			return
1762*1c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
1763*1c3ca1b3SAdrian Hunter
1764*1c3ca1b3SAdrian Hunter	def IsValid(self):
1765*1c3ca1b3SAdrian Hunter		if not self.validated:
1766*1c3ca1b3SAdrian Hunter			self.Validate()
1767*1c3ca1b3SAdrian Hunter		if len(self.error):
1768*1c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
1769*1c3ca1b3SAdrian Hunter			return False
1770*1c3ca1b3SAdrian Hunter		return True
1771*1c3ca1b3SAdrian Hunter
1772*1c3ca1b3SAdrian Hunter	def IsNumber(self, value):
1773*1c3ca1b3SAdrian Hunter		try:
1774*1c3ca1b3SAdrian Hunter			x = int(value)
1775*1c3ca1b3SAdrian Hunter		except:
1776*1c3ca1b3SAdrian Hunter			x = 0
1777*1c3ca1b3SAdrian Hunter		return str(x) == value
1778*1c3ca1b3SAdrian Hunter
1779*1c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
1780*1c3ca1b3SAdrian Hunter
1781*1c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
1782*1c3ca1b3SAdrian Hunter
1783*1c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
1784*1c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
1785*1c3ca1b3SAdrian Hunter
1786*1c3ca1b3SAdrian Hunter		self.column_name = column_name
1787*1c3ca1b3SAdrian Hunter
1788*1c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
1789*1c3ca1b3SAdrian Hunter		singles = []
1790*1c3ca1b3SAdrian Hunter		ranges = []
1791*1c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
1792*1c3ca1b3SAdrian Hunter			if "-" in value:
1793*1c3ca1b3SAdrian Hunter				vrange = value.split("-")
1794*1c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1795*1c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
1796*1c3ca1b3SAdrian Hunter				ranges.append(vrange)
1797*1c3ca1b3SAdrian Hunter			else:
1798*1c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
1799*1c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
1800*1c3ca1b3SAdrian Hunter				singles.append(value)
1801*1c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
1802*1c3ca1b3SAdrian Hunter		if len(singles):
1803*1c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
1804*1c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
1805*1c3ca1b3SAdrian Hunter
1806*1c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
1807*1c3ca1b3SAdrian Hunter
1808*1c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
1809*1c3ca1b3SAdrian Hunter
1810*1c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
1811*1c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
1812*1c3ca1b3SAdrian Hunter
1813*1c3ca1b3SAdrian Hunter		self.table_name = table_name
1814*1c3ca1b3SAdrian Hunter		self.match_column = match_column
1815*1c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
1816*1c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
1817*1c3ca1b3SAdrian Hunter
1818*1c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
1819*1c3ca1b3SAdrian Hunter		ids = []
1820*1c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
1821*1c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
1822*1c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
1823*1c3ca1b3SAdrian Hunter		if ret:
1824*1c3ca1b3SAdrian Hunter			while query.next():
1825*1c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
1826*1c3ca1b3SAdrian Hunter		return ids
1827*1c3ca1b3SAdrian Hunter
1828*1c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
1829*1c3ca1b3SAdrian Hunter		all_ids = []
1830*1c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
1831*1c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
1832*1c3ca1b3SAdrian Hunter			if len(ids):
1833*1c3ca1b3SAdrian Hunter				all_ids.extend(ids)
1834*1c3ca1b3SAdrian Hunter			else:
1835*1c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
1836*1c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1837*1c3ca1b3SAdrian Hunter		if self.column_name2:
1838*1c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1839*1c3ca1b3SAdrian Hunter
1840*1c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
1841*1c3ca1b3SAdrian Hunter
1842*1c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
1843*1c3ca1b3SAdrian Hunter
1844*1c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
1845*1c3ca1b3SAdrian Hunter		self.column_name = column_name
1846*1c3ca1b3SAdrian Hunter
1847*1c3ca1b3SAdrian Hunter		self.last_id = 0
1848*1c3ca1b3SAdrian Hunter		self.first_time = 0
1849*1c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
1850*1c3ca1b3SAdrian Hunter
1851*1c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
1852*1c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1853*1c3ca1b3SAdrian Hunter		if query.next():
1854*1c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
1855*1c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
1856*1c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1857*1c3ca1b3SAdrian Hunter		if query.next():
1858*1c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
1859*1c3ca1b3SAdrian Hunter		if placeholder_text:
1860*1c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1861*1c3ca1b3SAdrian Hunter
1862*1c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
1863*1c3ca1b3SAdrian Hunter
1864*1c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
1865*1c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
1866*1c3ca1b3SAdrian Hunter		if query.next():
1867*1c3ca1b3SAdrian Hunter			return True, int(query.value(0))
1868*1c3ca1b3SAdrian Hunter		else:
1869*1c3ca1b3SAdrian Hunter			return False, 0
1870*1c3ca1b3SAdrian Hunter
1871*1c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
1872*1c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
1873*1c3ca1b3SAdrian Hunter		while True:
1874*1c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
1875*1c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1876*1c3ca1b3SAdrian Hunter			if not query.next():
1877*1c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
1878*1c3ca1b3SAdrian Hunter				if not ok:
1879*1c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
1880*1c3ca1b3SAdrian Hunter					if not ok:
1881*1c3ca1b3SAdrian Hunter						return str(higher_id)
1882*1c3ca1b3SAdrian Hunter				next_id = dbid
1883*1c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1884*1c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
1885*1c3ca1b3SAdrian Hunter			if get_floor:
1886*1c3ca1b3SAdrian Hunter				if target_time > next_time:
1887*1c3ca1b3SAdrian Hunter					lower_id = next_id
1888*1c3ca1b3SAdrian Hunter				else:
1889*1c3ca1b3SAdrian Hunter					higher_id = next_id
1890*1c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
1891*1c3ca1b3SAdrian Hunter					return str(higher_id)
1892*1c3ca1b3SAdrian Hunter			else:
1893*1c3ca1b3SAdrian Hunter				if target_time >= next_time:
1894*1c3ca1b3SAdrian Hunter					lower_id = next_id
1895*1c3ca1b3SAdrian Hunter				else:
1896*1c3ca1b3SAdrian Hunter					higher_id = next_id
1897*1c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
1898*1c3ca1b3SAdrian Hunter					return str(lower_id)
1899*1c3ca1b3SAdrian Hunter
1900*1c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
1901*1c3ca1b3SAdrian Hunter		mult = 1
1902*1c3ca1b3SAdrian Hunter		suffix = val[-2:]
1903*1c3ca1b3SAdrian Hunter		if suffix == "ms":
1904*1c3ca1b3SAdrian Hunter			mult = 1000000
1905*1c3ca1b3SAdrian Hunter		elif suffix == "us":
1906*1c3ca1b3SAdrian Hunter			mult = 1000
1907*1c3ca1b3SAdrian Hunter		elif suffix == "ns":
1908*1c3ca1b3SAdrian Hunter			mult = 1
1909*1c3ca1b3SAdrian Hunter		else:
1910*1c3ca1b3SAdrian Hunter			return val
1911*1c3ca1b3SAdrian Hunter		val = val[:-2].strip()
1912*1c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
1913*1c3ca1b3SAdrian Hunter			return val
1914*1c3ca1b3SAdrian Hunter		val = int(val) * mult
1915*1c3ca1b3SAdrian Hunter		if val >= 0:
1916*1c3ca1b3SAdrian Hunter			val += self.first_time
1917*1c3ca1b3SAdrian Hunter		else:
1918*1c3ca1b3SAdrian Hunter			val += self.last_time
1919*1c3ca1b3SAdrian Hunter		return str(val)
1920*1c3ca1b3SAdrian Hunter
1921*1c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
1922*1c3ca1b3SAdrian Hunter		if vrange[0] == "":
1923*1c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
1924*1c3ca1b3SAdrian Hunter		if vrange[1] == "":
1925*1c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
1926*1c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
1927*1c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
1928*1c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1929*1c3ca1b3SAdrian Hunter			return False
1930*1c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
1931*1c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
1932*1c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
1933*1c3ca1b3SAdrian Hunter			return False
1934*1c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
1935*1c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
1936*1c3ca1b3SAdrian Hunter		return True
1937*1c3ca1b3SAdrian Hunter
1938*1c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
1939*1c3ca1b3SAdrian Hunter		n = value.count("-")
1940*1c3ca1b3SAdrian Hunter		if n == 1:
1941*1c3ca1b3SAdrian Hunter			pass
1942*1c3ca1b3SAdrian Hunter		elif n == 2:
1943*1c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
1944*1c3ca1b3SAdrian Hunter				n = 1
1945*1c3ca1b3SAdrian Hunter		elif n == 3:
1946*1c3ca1b3SAdrian Hunter			n = 2
1947*1c3ca1b3SAdrian Hunter		else:
1948*1c3ca1b3SAdrian Hunter			return False
1949*1c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
1950*1c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
1951*1c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
1952*1c3ca1b3SAdrian Hunter			ranges.append(vrange)
1953*1c3ca1b3SAdrian Hunter			return True
1954*1c3ca1b3SAdrian Hunter		return False
1955*1c3ca1b3SAdrian Hunter
1956*1c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
1957*1c3ca1b3SAdrian Hunter		ranges = []
1958*1c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
1959*1c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
1960*1c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
1961*1c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
1962*1c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
1963*1c3ca1b3SAdrian Hunter
19640924cd68SAdrian Hunter# Report Dialog Base
1965210cf1f9SAdrian Hunter
19660924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
1967210cf1f9SAdrian Hunter
19680924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
19690924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
1970210cf1f9SAdrian Hunter
1971210cf1f9SAdrian Hunter		self.glb = glb
1972210cf1f9SAdrian Hunter
19730bf0947aSAdrian Hunter		self.report_vars = ReportVars()
1974210cf1f9SAdrian Hunter
19750924cd68SAdrian Hunter		self.setWindowTitle(title)
1976210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
1977210cf1f9SAdrian Hunter
1978*1c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
1979210cf1f9SAdrian Hunter
19800924cd68SAdrian Hunter		self.partial = partial
19810924cd68SAdrian Hunter
1982210cf1f9SAdrian Hunter		self.grid = QGridLayout()
1983210cf1f9SAdrian Hunter
1984210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
1985210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1986210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
1987210cf1f9SAdrian Hunter
1988210cf1f9SAdrian Hunter		self.status = QLabel()
1989210cf1f9SAdrian Hunter
1990210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
1991210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
1992210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
1993210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1994210cf1f9SAdrian Hunter
1995210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
1996210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
1997210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1998210cf1f9SAdrian Hunter
1999210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
2000210cf1f9SAdrian Hunter		#self.hbox.addStretch()
2001210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
2002210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
2003210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
2004210cf1f9SAdrian Hunter
2005210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
2006210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
2007210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
2008210cf1f9SAdrian Hunter
2009210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
2010210cf1f9SAdrian Hunter
2011210cf1f9SAdrian Hunter	def Ok(self):
20120bf0947aSAdrian Hunter		vars = self.report_vars
2013*1c3ca1b3SAdrian Hunter		for d in self.data_items:
2014*1c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
2015*1c3ca1b3SAdrian Hunter				vars.name = d.value
2016947cc38dSAdrian Hunter		if not vars.name:
2017210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2018210cf1f9SAdrian Hunter			return
2019210cf1f9SAdrian Hunter		for d in self.data_items:
2020210cf1f9SAdrian Hunter			if not d.IsValid():
2021210cf1f9SAdrian Hunter				return
2022210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2023210cf1f9SAdrian Hunter			if len(d.value):
20240bf0947aSAdrian Hunter				if len(vars.where_clause):
20250bf0947aSAdrian Hunter					vars.where_clause += " AND "
20260bf0947aSAdrian Hunter				vars.where_clause += d.value
20270bf0947aSAdrian Hunter		if len(vars.where_clause):
20280924cd68SAdrian Hunter			if self.partial:
20290bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2030210cf1f9SAdrian Hunter			else:
20310bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
20320924cd68SAdrian Hunter		else:
2033210cf1f9SAdrian Hunter			self.ShowMessage("No selection")
2034210cf1f9SAdrian Hunter			return
2035210cf1f9SAdrian Hunter		self.accept()
2036210cf1f9SAdrian Hunter
2037210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2038210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2039210cf1f9SAdrian Hunter
2040210cf1f9SAdrian Hunter	def ClearMessage(self):
2041210cf1f9SAdrian Hunter		self.status.setText("")
2042210cf1f9SAdrian Hunter
20430924cd68SAdrian Hunter# Selected branch report creation dialog
20440924cd68SAdrian Hunter
20450924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
20460924cd68SAdrian Hunter
20470924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
20480924cd68SAdrian Hunter		title = "Selected Branches"
2049*1c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2050*1c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
2051*1c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
2052*1c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
2053*1c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2054*1c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2055*1c3ca1b3SAdrian 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),
2056*1c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
2057*1c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
20580924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
20590924cd68SAdrian Hunter
206076099f98SAdrian Hunter# Event list
206176099f98SAdrian Hunter
206276099f98SAdrian Hunterdef GetEventList(db):
206376099f98SAdrian Hunter	events = []
206476099f98SAdrian Hunter	query = QSqlQuery(db)
206576099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
206676099f98SAdrian Hunter	while query.next():
206776099f98SAdrian Hunter		events.append(query.value(0))
206876099f98SAdrian Hunter	return events
206976099f98SAdrian Hunter
2070655cb952SAdrian Hunter# Is a table selectable
2071655cb952SAdrian Hunter
2072655cb952SAdrian Hunterdef IsSelectable(db, table):
2073655cb952SAdrian Hunter	query = QSqlQuery(db)
2074655cb952SAdrian Hunter	try:
2075655cb952SAdrian Hunter		QueryExec(query, "SELECT * FROM " + table + " LIMIT 1")
2076655cb952SAdrian Hunter	except:
2077655cb952SAdrian Hunter		return False
2078655cb952SAdrian Hunter	return True
2079655cb952SAdrian Hunter
20808392b74bSAdrian Hunter# SQL data preparation
20818392b74bSAdrian Hunter
20828392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
20838392b74bSAdrian Hunter	data = []
20848392b74bSAdrian Hunter	for i in xrange(count):
20858392b74bSAdrian Hunter		data.append(query.value(i))
20868392b74bSAdrian Hunter	return data
20878392b74bSAdrian Hunter
20888392b74bSAdrian Hunter# SQL table data model item
20898392b74bSAdrian Hunter
20908392b74bSAdrian Hunterclass SQLTableItem():
20918392b74bSAdrian Hunter
20928392b74bSAdrian Hunter	def __init__(self, row, data):
20938392b74bSAdrian Hunter		self.row = row
20948392b74bSAdrian Hunter		self.data = data
20958392b74bSAdrian Hunter
20968392b74bSAdrian Hunter	def getData(self, column):
20978392b74bSAdrian Hunter		return self.data[column]
20988392b74bSAdrian Hunter
20998392b74bSAdrian Hunter# SQL table data model
21008392b74bSAdrian Hunter
21018392b74bSAdrian Hunterclass SQLTableModel(TableModel):
21028392b74bSAdrian Hunter
21038392b74bSAdrian Hunter	progress = Signal(object)
21048392b74bSAdrian Hunter
21058c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
21068392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
21078392b74bSAdrian Hunter		self.glb = glb
21088392b74bSAdrian Hunter		self.more = True
21098392b74bSAdrian Hunter		self.populated = 0
21108c90fef9SAdrian Hunter		self.column_headers = column_headers
21118c90fef9SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample)
21128392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
21138392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
21148392b74bSAdrian Hunter
21158392b74bSAdrian Hunter	def DisplayData(self, item, index):
21168392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
21178392b74bSAdrian Hunter		return item.getData(index.column())
21188392b74bSAdrian Hunter
21198392b74bSAdrian Hunter	def AddSample(self, data):
21208392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
21218392b74bSAdrian Hunter		self.child_items.append(child)
21228392b74bSAdrian Hunter		self.populated += 1
21238392b74bSAdrian Hunter
21248392b74bSAdrian Hunter	def Update(self, fetched):
21258392b74bSAdrian Hunter		if not fetched:
21268392b74bSAdrian Hunter			self.more = False
21278392b74bSAdrian Hunter			self.progress.emit(0)
21288392b74bSAdrian Hunter		child_count = self.child_count
21298392b74bSAdrian Hunter		count = self.populated - child_count
21308392b74bSAdrian Hunter		if count > 0:
21318392b74bSAdrian Hunter			parent = QModelIndex()
21328392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
21338392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
21348392b74bSAdrian Hunter			self.child_count += count
21358392b74bSAdrian Hunter			self.endInsertRows()
21368392b74bSAdrian Hunter			self.progress.emit(self.child_count)
21378392b74bSAdrian Hunter
21388392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
21398392b74bSAdrian Hunter		current = self.child_count
21408392b74bSAdrian Hunter		if self.more:
21418392b74bSAdrian Hunter			self.fetcher.Fetch(count)
21428392b74bSAdrian Hunter		else:
21438392b74bSAdrian Hunter			self.progress.emit(0)
21448392b74bSAdrian Hunter		return current
21458392b74bSAdrian Hunter
21468392b74bSAdrian Hunter	def HasMoreRecords(self):
21478392b74bSAdrian Hunter		return self.more
21488392b74bSAdrian Hunter
21498c90fef9SAdrian Hunter	def columnCount(self, parent=None):
21508c90fef9SAdrian Hunter		return len(self.column_headers)
21518c90fef9SAdrian Hunter
21528c90fef9SAdrian Hunter	def columnHeader(self, column):
21538c90fef9SAdrian Hunter		return self.column_headers[column]
21548c90fef9SAdrian Hunter
21558392b74bSAdrian Hunter# SQL automatic table data model
21568392b74bSAdrian Hunter
21578392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
21588392b74bSAdrian Hunter
21598392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
21608392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
21618392b74bSAdrian Hunter		if table_name == "comm_threads_view":
21628392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
21638392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
21648c90fef9SAdrian Hunter		column_headers = []
21658392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
21668392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
21678392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
21688392b74bSAdrian Hunter			while query.next():
21698c90fef9SAdrian Hunter				column_headers.append(query.value(1))
21708392b74bSAdrian Hunter			if table_name == "sqlite_master":
21718392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21728392b74bSAdrian Hunter		else:
21738392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
21748392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21758392b74bSAdrian Hunter				select_table_name = table_name[19:]
21768392b74bSAdrian Hunter				schema = "information_schema"
21778392b74bSAdrian Hunter			else:
21788392b74bSAdrian Hunter				select_table_name = table_name
21798392b74bSAdrian Hunter				schema = "public"
21808392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
21818392b74bSAdrian Hunter			while query.next():
21828c90fef9SAdrian Hunter				column_headers.append(query.value(0))
21838c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
21848392b74bSAdrian Hunter
21858392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
21868392b74bSAdrian Hunter
21878392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
21888392b74bSAdrian Hunter
21898392b74bSAdrian Hunter	def __init__(self, parent=None):
21908392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
21918392b74bSAdrian Hunter
21928392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
21938392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
21948392b74bSAdrian Hunter		# so implement a crude alternative
21958392b74bSAdrian Hunter		font = self.view.font()
21968392b74bSAdrian Hunter		metrics = QFontMetrics(font)
21978392b74bSAdrian Hunter		max = 0
21988392b74bSAdrian Hunter		for row in xrange(n):
21998392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
22008392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
22018392b74bSAdrian Hunter			max = len if len > max else max
22028392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
22038392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
22048392b74bSAdrian Hunter		max = len if len > max else max
22058392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
22068392b74bSAdrian Hunter
22078392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
22088392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
22098392b74bSAdrian Hunter		if n < 1:
22108392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
22118392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
22128392b74bSAdrian Hunter			return
22138392b74bSAdrian Hunter		columns = self.data_model.columnCount()
22148392b74bSAdrian Hunter		for i in xrange(columns):
22158392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
22168392b74bSAdrian Hunter
22178392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
22188392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
22198392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
22208392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22218392b74bSAdrian Hunter
22228392b74bSAdrian Hunter# Table window
22238392b74bSAdrian Hunter
22248392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
22258392b74bSAdrian Hunter
22268392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
22278392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
22288392b74bSAdrian Hunter
22298392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
22308392b74bSAdrian Hunter
22318392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
22328392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
22338392b74bSAdrian Hunter
22348392b74bSAdrian Hunter		self.view = QTableView()
22358392b74bSAdrian Hunter		self.view.setModel(self.model)
22368392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
22378392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
22388392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
22398392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
22408392b74bSAdrian Hunter
22418392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22428392b74bSAdrian Hunter
22438392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
22448392b74bSAdrian Hunter
22458392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
22468392b74bSAdrian Hunter
22478392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
22488392b74bSAdrian Hunter
22498392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
22508392b74bSAdrian Hunter
22518392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
22528392b74bSAdrian Hunter
22538392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
22548392b74bSAdrian Hunter
22558392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
22568392b74bSAdrian Hunter		self.view.setFocus()
22578392b74bSAdrian Hunter		self.find_bar.Busy()
22588392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
22598392b74bSAdrian Hunter
22608392b74bSAdrian Hunter	def FindDone(self, row):
22618392b74bSAdrian Hunter		self.find_bar.Idle()
22628392b74bSAdrian Hunter		if row >= 0:
226335fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
22648392b74bSAdrian Hunter		else:
22658392b74bSAdrian Hunter			self.find_bar.NotFound()
22668392b74bSAdrian Hunter
22678392b74bSAdrian Hunter# Table list
22688392b74bSAdrian Hunter
22698392b74bSAdrian Hunterdef GetTableList(glb):
22708392b74bSAdrian Hunter	tables = []
22718392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
22728392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22738392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
22748392b74bSAdrian Hunter	else:
22758392b74bSAdrian 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")
22768392b74bSAdrian Hunter	while query.next():
22778392b74bSAdrian Hunter		tables.append(query.value(0))
22788392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22798392b74bSAdrian Hunter		tables.append("sqlite_master")
22808392b74bSAdrian Hunter	else:
22818392b74bSAdrian Hunter		tables.append("information_schema.tables")
22828392b74bSAdrian Hunter		tables.append("information_schema.views")
22838392b74bSAdrian Hunter		tables.append("information_schema.columns")
22848392b74bSAdrian Hunter	return tables
22858392b74bSAdrian Hunter
22861beb5c7bSAdrian Hunter# Action Definition
22871beb5c7bSAdrian Hunter
22881beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
22891beb5c7bSAdrian Hunter	action = QAction(label, parent)
22901beb5c7bSAdrian Hunter	if shortcut != None:
22911beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
22921beb5c7bSAdrian Hunter	action.setStatusTip(tip)
22931beb5c7bSAdrian Hunter	action.triggered.connect(callback)
22941beb5c7bSAdrian Hunter	return action
22951beb5c7bSAdrian Hunter
22961beb5c7bSAdrian Hunter# Typical application actions
22971beb5c7bSAdrian Hunter
22981beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
22991beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
23001beb5c7bSAdrian Hunter
23011beb5c7bSAdrian Hunter# Typical MDI actions
23021beb5c7bSAdrian Hunter
23031beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
23041beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
23051beb5c7bSAdrian Hunter
23061beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
23071beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
23081beb5c7bSAdrian Hunter
23091beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
23101beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
23111beb5c7bSAdrian Hunter
23121beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
23131beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
23141beb5c7bSAdrian Hunter
23151beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
23161beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
23171beb5c7bSAdrian Hunter
23181beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
23191beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
23201beb5c7bSAdrian Hunter
23211beb5c7bSAdrian Hunter# Typical MDI window menu
23221beb5c7bSAdrian Hunter
23231beb5c7bSAdrian Hunterclass WindowMenu():
23241beb5c7bSAdrian Hunter
23251beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
23261beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
23271beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
23281beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
23291beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
23301beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
23311beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
23321beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
23331beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
23341beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
23351beb5c7bSAdrian Hunter
23361beb5c7bSAdrian Hunter	def Update(self):
23371beb5c7bSAdrian Hunter		self.window_menu.clear()
23381beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
23391beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
23401beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
23411beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
23421beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
23431beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
23441beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
23451beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
23461beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
23471beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
23481beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
23491beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
23501beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
23511beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
23521beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
23531beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
23541beb5c7bSAdrian Hunter		if sub_window_count == 0:
23551beb5c7bSAdrian Hunter			return
23561beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
23571beb5c7bSAdrian Hunter		nr = 1
23581beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
23591beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
23601beb5c7bSAdrian Hunter			if nr < 10:
23611beb5c7bSAdrian Hunter				label = "&" + label
23621beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
23631beb5c7bSAdrian Hunter			action.setCheckable(True)
23641beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
23651beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
23661beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
23671beb5c7bSAdrian Hunter			nr += 1
23681beb5c7bSAdrian Hunter
23691beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
23701beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
23711beb5c7bSAdrian Hunter
237265b24292SAdrian Hunter# Help text
237365b24292SAdrian Hunter
237465b24292SAdrian Hunterglb_help_text = """
237565b24292SAdrian Hunter<h1>Contents</h1>
237665b24292SAdrian Hunter<style>
237765b24292SAdrian Hunterp.c1 {
237865b24292SAdrian Hunter    text-indent: 40px;
237965b24292SAdrian Hunter}
238065b24292SAdrian Hunterp.c2 {
238165b24292SAdrian Hunter    text-indent: 80px;
238265b24292SAdrian Hunter}
238365b24292SAdrian Hunter}
238465b24292SAdrian Hunter</style>
238565b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
238665b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
238765b24292SAdrian Hunter<p class=c2><a href=#allbranches>1.2 All branches</a></p>
238865b24292SAdrian Hunter<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
238965b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
239065b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
239165b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
239265b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
239365b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
239465b24292SAdrian Hunterwidths to suit will display something like:
239565b24292SAdrian Hunter<pre>
239665b24292SAdrian Hunter                                         Call Graph: pt_example
239765b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
239865b24292SAdrian Hunterv- ls
239965b24292SAdrian Hunter    v- 2638:2638
240065b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
240165b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
240265b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
240365b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
240465b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
240565b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
240665b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
240765b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
240865b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
240965b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
241065b24292SAdrian Hunter</pre>
241165b24292SAdrian Hunter<h3>Points to note:</h3>
241265b24292SAdrian Hunter<ul>
241365b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
241465b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
241565b24292SAdrian Hunter<li>Subsequent levels are functions</li>
241665b24292SAdrian Hunter<li>'Count' is the number of calls</li>
241765b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
241865b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
241965b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
242065b24292SAdrian Hunter</ul>
242165b24292SAdrian Hunter<h3>Find</h3>
242265b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
242365b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
242465b24292SAdrian Hunter<h2 id=allbranches>1.2 All branches</h2>
242565b24292SAdrian HunterThe All branches report displays all branches in chronological order.
242665b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
242765b24292SAdrian Hunter<h3>Disassembly</h3>
242865b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
242965b24292SAdrian Hunter<ol>
243065b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
243165b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
243265b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
243365b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
243465b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
243565b24292SAdrian Hunter</ol>
243665b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
243765b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
243865b24292SAdrian Hunter<pre>
243965b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
244065b24292SAdrian Huntergit clone https://github.com/intelxed/xed
244165b24292SAdrian Huntercd xed
244265b24292SAdrian Hunter./mfile.py --share
244365b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
244465b24292SAdrian Huntersudo ldconfig
244565b24292SAdrian Hunter</pre>
244665b24292SAdrian Hunter<h3>Find</h3>
244765b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
244865b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
244965b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
245065b24292SAdrian Hunter<h2 id=selectedbranches>1.3 Selected branches</h2>
245165b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
245265b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
245365b24292SAdrian Hunter<h3>1.3.1 Time ranges</h3>
245465b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
245565b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
245665b24292SAdrian Hunter<pre>
245765b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
245865b24292SAdrian Hunter	100us-200us		From 100us to 200us
245965b24292SAdrian Hunter	10ms-			From 10ms to the end
246065b24292SAdrian Hunter	-100ns			The first 100ns
246165b24292SAdrian Hunter	-10ms-			The last 10ms
246265b24292SAdrian Hunter</pre>
246365b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
246465b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
246565b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
246665b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
246765b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
246865b24292SAdrian Hunterbut that can be slow for large tables.
246965b24292SAdrian Hunter<p>There are also tables of database meta-information.
247065b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
247165b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
247265b24292SAdrian Hunter<h3>Find</h3>
247365b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
247465b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
247565b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
247635fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
247735fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
247865b24292SAdrian Hunter"""
247965b24292SAdrian Hunter
248065b24292SAdrian Hunter# Help window
248165b24292SAdrian Hunter
248265b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
248365b24292SAdrian Hunter
248465b24292SAdrian Hunter	def __init__(self, glb, parent=None):
248565b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
248665b24292SAdrian Hunter
248765b24292SAdrian Hunter		self.text = QTextBrowser()
248865b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
248965b24292SAdrian Hunter		self.text.setReadOnly(True)
249065b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
249165b24292SAdrian Hunter
249265b24292SAdrian Hunter		self.setWidget(self.text)
249365b24292SAdrian Hunter
249465b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
249565b24292SAdrian Hunter
249665b24292SAdrian Hunter# Main window that only displays the help text
249765b24292SAdrian Hunter
249865b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
249965b24292SAdrian Hunter
250065b24292SAdrian Hunter	def __init__(self, parent=None):
250165b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
250265b24292SAdrian Hunter
250365b24292SAdrian Hunter		self.setMinimumSize(200, 100)
250465b24292SAdrian Hunter		self.resize(800, 600)
250565b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
250665b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
250765b24292SAdrian Hunter
250865b24292SAdrian Hunter		self.text = QTextBrowser()
250965b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
251065b24292SAdrian Hunter		self.text.setReadOnly(True)
251165b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
251265b24292SAdrian Hunter
251365b24292SAdrian Hunter		self.setCentralWidget(self.text)
251465b24292SAdrian Hunter
251582f68e28SAdrian Hunter# Font resize
251682f68e28SAdrian Hunter
251782f68e28SAdrian Hunterdef ResizeFont(widget, diff):
251882f68e28SAdrian Hunter	font = widget.font()
251982f68e28SAdrian Hunter	sz = font.pointSize()
252082f68e28SAdrian Hunter	font.setPointSize(sz + diff)
252182f68e28SAdrian Hunter	widget.setFont(font)
252282f68e28SAdrian Hunter
252382f68e28SAdrian Hunterdef ShrinkFont(widget):
252482f68e28SAdrian Hunter	ResizeFont(widget, -1)
252582f68e28SAdrian Hunter
252682f68e28SAdrian Hunterdef EnlargeFont(widget):
252782f68e28SAdrian Hunter	ResizeFont(widget, 1)
252882f68e28SAdrian Hunter
25291beb5c7bSAdrian Hunter# Unique name for sub-windows
25301beb5c7bSAdrian Hunter
25311beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
25321beb5c7bSAdrian Hunter	if nr > 1:
25331beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
25341beb5c7bSAdrian Hunter	return name
25351beb5c7bSAdrian Hunter
25361beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
25371beb5c7bSAdrian Hunter	nr = 1
25381beb5c7bSAdrian Hunter	while True:
25391beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
25401beb5c7bSAdrian Hunter		ok = True
25411beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
25421beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
25431beb5c7bSAdrian Hunter				ok = False
25441beb5c7bSAdrian Hunter				break
25451beb5c7bSAdrian Hunter		if ok:
25461beb5c7bSAdrian Hunter			return unique_name
25471beb5c7bSAdrian Hunter		nr += 1
25481beb5c7bSAdrian Hunter
25491beb5c7bSAdrian Hunter# Add a sub-window
25501beb5c7bSAdrian Hunter
25511beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
25521beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
25531beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
25541beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
25551beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
25561beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
25571beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
25581beb5c7bSAdrian Hunter	sub_window.name = unique_name
25591beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
25601beb5c7bSAdrian Hunter	sub_window.show()
25611beb5c7bSAdrian Hunter
2562031c2a00SAdrian Hunter# Main window
2563031c2a00SAdrian Hunter
2564031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
2565031c2a00SAdrian Hunter
2566031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
2567031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
2568031c2a00SAdrian Hunter
2569031c2a00SAdrian Hunter		self.glb = glb
2570031c2a00SAdrian Hunter
25711beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
2572031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
2573031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
2574031c2a00SAdrian Hunter
25751beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
25761beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
25771beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
2578031c2a00SAdrian Hunter
25791beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
2580031c2a00SAdrian Hunter
25811beb5c7bSAdrian Hunter		menu = self.menuBar()
2582031c2a00SAdrian Hunter
25831beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
25841beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
25851beb5c7bSAdrian Hunter
2586ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
2587ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
25888392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
258982f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
259082f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2591ebd70c7dSAdrian Hunter
25921beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
2593655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
25941beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
25951beb5c7bSAdrian Hunter
259676099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
259776099f98SAdrian Hunter
25988392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
25998392b74bSAdrian Hunter
26001beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
26011beb5c7bSAdrian Hunter
260265b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
260365b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
260465b24292SAdrian Hunter
2605ebd70c7dSAdrian Hunter	def Find(self):
2606ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
2607ebd70c7dSAdrian Hunter		if win:
2608ebd70c7dSAdrian Hunter			try:
2609ebd70c7dSAdrian Hunter				win.find_bar.Activate()
2610ebd70c7dSAdrian Hunter			except:
2611ebd70c7dSAdrian Hunter				pass
2612ebd70c7dSAdrian Hunter
26138392b74bSAdrian Hunter	def FetchMoreRecords(self):
26148392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
26158392b74bSAdrian Hunter		if win:
26168392b74bSAdrian Hunter			try:
26178392b74bSAdrian Hunter				win.fetch_bar.Activate()
26188392b74bSAdrian Hunter			except:
26198392b74bSAdrian Hunter				pass
26208392b74bSAdrian Hunter
262182f68e28SAdrian Hunter	def ShrinkFont(self):
262282f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
262382f68e28SAdrian Hunter		ShrinkFont(win.view)
262482f68e28SAdrian Hunter
262582f68e28SAdrian Hunter	def EnlargeFont(self):
262682f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
262782f68e28SAdrian Hunter		EnlargeFont(win.view)
262882f68e28SAdrian Hunter
262976099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
263076099f98SAdrian Hunter		branches_events = 0
263176099f98SAdrian Hunter		for event in events:
263276099f98SAdrian Hunter			event = event.split(":")[0]
263376099f98SAdrian Hunter			if event == "branches":
263476099f98SAdrian Hunter				branches_events += 1
263576099f98SAdrian Hunter		dbid = 0
263676099f98SAdrian Hunter		for event in events:
263776099f98SAdrian Hunter			dbid += 1
263876099f98SAdrian Hunter			event = event.split(":")[0]
263976099f98SAdrian Hunter			if event == "branches":
264076099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
264176099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2642210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2643210cf1f9SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
264476099f98SAdrian Hunter
26458392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
26468392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
26478392b74bSAdrian Hunter		for table in tables:
26488392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
26498392b74bSAdrian Hunter
26501beb5c7bSAdrian Hunter	def NewCallGraph(self):
26511beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
2652031c2a00SAdrian Hunter
265376099f98SAdrian Hunter	def NewBranchView(self, event_id):
2654947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
265576099f98SAdrian Hunter
2656210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
2657210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
2658210cf1f9SAdrian Hunter		ret = dialog.exec_()
2659210cf1f9SAdrian Hunter		if ret:
2660947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
2661210cf1f9SAdrian Hunter
26628392b74bSAdrian Hunter	def NewTableView(self, table_name):
26638392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
26648392b74bSAdrian Hunter
266565b24292SAdrian Hunter	def Help(self):
266665b24292SAdrian Hunter		HelpWindow(self.glb, self)
266765b24292SAdrian Hunter
266876099f98SAdrian Hunter# XED Disassembler
266976099f98SAdrian Hunter
267076099f98SAdrian Hunterclass xed_state_t(Structure):
267176099f98SAdrian Hunter
267276099f98SAdrian Hunter	_fields_ = [
267376099f98SAdrian Hunter		("mode", c_int),
267476099f98SAdrian Hunter		("width", c_int)
267576099f98SAdrian Hunter	]
267676099f98SAdrian Hunter
267776099f98SAdrian Hunterclass XEDInstruction():
267876099f98SAdrian Hunter
267976099f98SAdrian Hunter	def __init__(self, libxed):
268076099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
268176099f98SAdrian Hunter		xedd_t = c_byte * 512
268276099f98SAdrian Hunter		self.xedd = xedd_t()
268376099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
268476099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
268576099f98SAdrian Hunter		self.state = xed_state_t()
268676099f98SAdrian Hunter		self.statep = addressof(self.state)
268776099f98SAdrian Hunter		# Buffer for disassembled instruction text
268876099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
268976099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
269076099f98SAdrian Hunter
269176099f98SAdrian Hunterclass LibXED():
269276099f98SAdrian Hunter
269376099f98SAdrian Hunter	def __init__(self):
26945ed4419dSAdrian Hunter		try:
269576099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
26965ed4419dSAdrian Hunter		except:
26975ed4419dSAdrian Hunter			self.libxed = None
26985ed4419dSAdrian Hunter		if not self.libxed:
26995ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
270076099f98SAdrian Hunter
270176099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
270276099f98SAdrian Hunter		self.xed_tables_init.restype = None
270376099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
270476099f98SAdrian Hunter
270576099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
270676099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
270776099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
270876099f98SAdrian Hunter
270976099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
271076099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
271176099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
271276099f98SAdrian Hunter
271376099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
271476099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
271576099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
271676099f98SAdrian Hunter
271776099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
271876099f98SAdrian Hunter		self.xed_decode.restype = c_int
271976099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
272076099f98SAdrian Hunter
272176099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
272276099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
272376099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
272476099f98SAdrian Hunter
272576099f98SAdrian Hunter		self.xed_tables_init()
272676099f98SAdrian Hunter
272776099f98SAdrian Hunter	def Instruction(self):
272876099f98SAdrian Hunter		return XEDInstruction(self)
272976099f98SAdrian Hunter
273076099f98SAdrian Hunter	def SetMode(self, inst, mode):
273176099f98SAdrian Hunter		if mode:
273276099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
273376099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
273476099f98SAdrian Hunter		else:
273576099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
273676099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
273776099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
273876099f98SAdrian Hunter
273976099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
274076099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
274176099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
274276099f98SAdrian Hunter		if err:
274376099f98SAdrian Hunter			return 0, ""
274476099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
274576099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
274676099f98SAdrian Hunter		if not ok:
274776099f98SAdrian Hunter			return 0, ""
274876099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
274976099f98SAdrian Hunter		# For now, assume the length is in byte 166
275076099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
275176099f98SAdrian Hunter
275276099f98SAdrian Hunterdef TryOpen(file_name):
275376099f98SAdrian Hunter	try:
275476099f98SAdrian Hunter		return open(file_name, "rb")
275576099f98SAdrian Hunter	except:
275676099f98SAdrian Hunter		return None
275776099f98SAdrian Hunter
275876099f98SAdrian Hunterdef Is64Bit(f):
275976099f98SAdrian Hunter	result = sizeof(c_void_p)
276076099f98SAdrian Hunter	# ELF support only
276176099f98SAdrian Hunter	pos = f.tell()
276276099f98SAdrian Hunter	f.seek(0)
276376099f98SAdrian Hunter	header = f.read(7)
276476099f98SAdrian Hunter	f.seek(pos)
276576099f98SAdrian Hunter	magic = header[0:4]
276676099f98SAdrian Hunter	eclass = ord(header[4])
276776099f98SAdrian Hunter	encoding = ord(header[5])
276876099f98SAdrian Hunter	version = ord(header[6])
276976099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
277076099f98SAdrian Hunter		result = True if eclass == 2 else False
277176099f98SAdrian Hunter	return result
277276099f98SAdrian Hunter
2773031c2a00SAdrian Hunter# Global data
2774031c2a00SAdrian Hunter
2775031c2a00SAdrian Hunterclass Glb():
2776031c2a00SAdrian Hunter
2777031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2778031c2a00SAdrian Hunter		self.dbref = dbref
2779031c2a00SAdrian Hunter		self.db = db
2780031c2a00SAdrian Hunter		self.dbname = dbname
278176099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
278276099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
278376099f98SAdrian Hunter		if self.buildid_dir:
278476099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
278576099f98SAdrian Hunter		else:
278676099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2787031c2a00SAdrian Hunter		self.app = None
2788031c2a00SAdrian Hunter		self.mainwindow = None
27898392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
279076099f98SAdrian Hunter		try:
279176099f98SAdrian Hunter			self.disassembler = LibXED()
279276099f98SAdrian Hunter			self.have_disassembler = True
279376099f98SAdrian Hunter		except:
279476099f98SAdrian Hunter			self.have_disassembler = False
279576099f98SAdrian Hunter
279676099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
279776099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
279876099f98SAdrian Hunter		return TryOpen(file_name)
279976099f98SAdrian Hunter
280076099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
280176099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
280276099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
280376099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
280476099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
280576099f98SAdrian Hunter			if f:
280676099f98SAdrian Hunter				return f
280776099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
280876099f98SAdrian Hunter			f = TryOpen(long_name)
280976099f98SAdrian Hunter			if f:
281076099f98SAdrian Hunter				return f
281176099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
281276099f98SAdrian Hunter		if f:
281376099f98SAdrian Hunter			return f
281476099f98SAdrian Hunter		return None
28158392b74bSAdrian Hunter
28168392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
28178392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
28188392b74bSAdrian Hunter
28198392b74bSAdrian Hunter	# Shutdown any background processes or threads
28208392b74bSAdrian Hunter	def ShutdownInstances(self):
28218392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
28228392b74bSAdrian Hunter			try:
28238392b74bSAdrian Hunter				x.Shutdown()
28248392b74bSAdrian Hunter			except:
28258392b74bSAdrian Hunter				pass
2826031c2a00SAdrian Hunter
2827031c2a00SAdrian Hunter# Database reference
2828031c2a00SAdrian Hunter
2829031c2a00SAdrian Hunterclass DBRef():
2830031c2a00SAdrian Hunter
2831031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2832031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2833031c2a00SAdrian Hunter		self.dbname = dbname
2834031c2a00SAdrian Hunter
2835031c2a00SAdrian Hunter	def Open(self, connection_name):
2836031c2a00SAdrian Hunter		dbname = self.dbname
2837031c2a00SAdrian Hunter		if self.is_sqlite3:
2838031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2839031c2a00SAdrian Hunter		else:
2840031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2841031c2a00SAdrian Hunter			opts = dbname.split()
2842031c2a00SAdrian Hunter			for opt in opts:
2843031c2a00SAdrian Hunter				if "=" in opt:
2844031c2a00SAdrian Hunter					opt = opt.split("=")
2845031c2a00SAdrian Hunter					if opt[0] == "hostname":
2846031c2a00SAdrian Hunter						db.setHostName(opt[1])
2847031c2a00SAdrian Hunter					elif opt[0] == "port":
2848031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2849031c2a00SAdrian Hunter					elif opt[0] == "username":
2850031c2a00SAdrian Hunter						db.setUserName(opt[1])
2851031c2a00SAdrian Hunter					elif opt[0] == "password":
2852031c2a00SAdrian Hunter						db.setPassword(opt[1])
2853031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2854031c2a00SAdrian Hunter						dbname = opt[1]
2855031c2a00SAdrian Hunter				else:
2856031c2a00SAdrian Hunter					dbname = opt
2857031c2a00SAdrian Hunter
2858031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2859031c2a00SAdrian Hunter		if not db.open():
2860031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2861031c2a00SAdrian Hunter		return db, dbname
2862031c2a00SAdrian Hunter
2863031c2a00SAdrian Hunter# Main
2864031c2a00SAdrian Hunter
2865031c2a00SAdrian Hunterdef Main():
2866031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
286765b24292SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}"
2868031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2869031c2a00SAdrian Hunter
2870031c2a00SAdrian Hunter	dbname = sys.argv[1]
287165b24292SAdrian Hunter	if dbname == "--help-only":
287265b24292SAdrian Hunter		app = QApplication(sys.argv)
287365b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
287465b24292SAdrian Hunter		mainwindow.show()
287565b24292SAdrian Hunter		err = app.exec_()
287665b24292SAdrian Hunter		sys.exit(err)
2877031c2a00SAdrian Hunter
2878031c2a00SAdrian Hunter	is_sqlite3 = False
2879031c2a00SAdrian Hunter	try:
2880031c2a00SAdrian Hunter		f = open(dbname)
2881031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
2882031c2a00SAdrian Hunter			is_sqlite3 = True
2883031c2a00SAdrian Hunter		f.close()
2884031c2a00SAdrian Hunter	except:
2885031c2a00SAdrian Hunter		pass
2886031c2a00SAdrian Hunter
2887031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
2888031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
2889031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
2890031c2a00SAdrian Hunter	app = QApplication(sys.argv)
2891031c2a00SAdrian Hunter	glb.app = app
2892031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
2893031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
2894031c2a00SAdrian Hunter	mainwindow.show()
2895031c2a00SAdrian Hunter	err = app.exec_()
28968392b74bSAdrian Hunter	glb.ShutdownInstances()
2897031c2a00SAdrian Hunter	db.close()
2898031c2a00SAdrian Hunter	sys.exit(err)
2899031c2a00SAdrian Hunter
2900031c2a00SAdrian Hunterif __name__ == "__main__":
2901031c2a00SAdrian Hunter	Main()
2902