1c6aba1bfSAdrian Hunter#!/usr/bin/env python
2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5031c2a00SAdrian Hunter
6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8031c2a00SAdrian Hunter# scripts for details.
9031c2a00SAdrian Hunter#
10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12031c2a00SAdrian Hunter#
13031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14031c2a00SAdrian Hunter#
15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17031c2a00SAdrian Hunter#
18031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19031c2a00SAdrian Hunter#
20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22031c2a00SAdrian Hunter# widths to suit will display something like:
23031c2a00SAdrian Hunter#
24031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26031c2a00SAdrian Hunter# v- ls
27031c2a00SAdrian Hunter#     v- 2638:2638
28031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38031c2a00SAdrian Hunter#
39031c2a00SAdrian Hunter# Points to note:
40031c2a00SAdrian Hunter#	The top level is a command name (comm)
41031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42031c2a00SAdrian Hunter#	Subsequent levels are functions
43031c2a00SAdrian Hunter#	'Count' is the number of calls
44031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45031c2a00SAdrian Hunter#	Percentages are relative to the level above
46031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47031c2a00SAdrian Hunter#       functions that it calls
48031c2a00SAdrian Hunter
4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
5076099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
5376099f98SAdrian Hunter# libxed.so:
5476099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
5576099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
5676099f98SAdrian Hunter#            cd xed
5776099f98SAdrian Hunter#            ./mfile.py --share
5876099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
5976099f98SAdrian Hunter#            sudo ldconfig
6076099f98SAdrian Hunter#
6176099f98SAdrian Hunter# Example report:
6276099f98SAdrian Hunter#
6376099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6476099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6576099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6676099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6776099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6876099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6976099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7076099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7176099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
7276099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7376099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
7476099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
7576099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
7676099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7776099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
7876099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7976099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8076099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
8176099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8276099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8376099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8476099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8576099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8676099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8776099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8876099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8976099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9076099f98SAdrian Hunter
91beda0e72STony Jonesfrom __future__ import print_function
92beda0e72STony Jones
93031c2a00SAdrian Hunterimport sys
941ed7f47fSAdrian Hunterimport argparse
951beb5c7bSAdrian Hunterimport weakref
961beb5c7bSAdrian Hunterimport threading
97ebd70c7dSAdrian Hunterimport string
98beda0e72STony Jonestry:
99beda0e72STony Jones	# Python2
100beda0e72STony Jones	import cPickle as pickle
101beda0e72STony Jones	# size of pickled integer big enough for record size
102beda0e72STony Jones	glb_nsz = 8
103beda0e72STony Jonesexcept ImportError:
104beda0e72STony Jones	import pickle
105beda0e72STony Jones	glb_nsz = 16
1068392b74bSAdrian Hunterimport re
1078392b74bSAdrian Hunterimport os
108df8ea22aSAdrian Hunter
109df8ea22aSAdrian Hunterpyside_version_1 = True
110df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv:
111df8ea22aSAdrian Hunter	try:
112df8ea22aSAdrian Hunter		from PySide2.QtCore import *
113df8ea22aSAdrian Hunter		from PySide2.QtGui import *
114df8ea22aSAdrian Hunter		from PySide2.QtSql import *
115df8ea22aSAdrian Hunter		from PySide2.QtWidgets import *
116df8ea22aSAdrian Hunter		pyside_version_1 = False
117df8ea22aSAdrian Hunter	except:
118df8ea22aSAdrian Hunter		pass
119df8ea22aSAdrian Hunter
120df8ea22aSAdrian Hunterif pyside_version_1:
121031c2a00SAdrian Hunter	from PySide.QtCore import *
122031c2a00SAdrian Hunter	from PySide.QtGui import *
123031c2a00SAdrian Hunter	from PySide.QtSql import *
124df8ea22aSAdrian Hunter
125031c2a00SAdrian Hunterfrom decimal import *
1268392b74bSAdrian Hunterfrom ctypes import *
1278392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
128031c2a00SAdrian Hunter
129beda0e72STony Jones# xrange is range in Python3
130beda0e72STony Jonestry:
131beda0e72STony Jones	xrange
132beda0e72STony Jonesexcept NameError:
133beda0e72STony Jones	xrange = range
134beda0e72STony Jones
135beda0e72STony Jonesdef printerr(*args, **keyword_args):
136beda0e72STony Jones	print(*args, file=sys.stderr, **keyword_args)
137beda0e72STony Jones
138031c2a00SAdrian Hunter# Data formatting helpers
139031c2a00SAdrian Hunter
14076099f98SAdrian Hunterdef tohex(ip):
14176099f98SAdrian Hunter	if ip < 0:
14276099f98SAdrian Hunter		ip += 1 << 64
14376099f98SAdrian Hunter	return "%x" % ip
14476099f98SAdrian Hunter
14576099f98SAdrian Hunterdef offstr(offset):
14676099f98SAdrian Hunter	if offset:
14776099f98SAdrian Hunter		return "+0x%x" % offset
14876099f98SAdrian Hunter	return ""
14976099f98SAdrian Hunter
150031c2a00SAdrian Hunterdef dsoname(name):
151031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
152031c2a00SAdrian Hunter		return "[kernel]"
153031c2a00SAdrian Hunter	return name
154031c2a00SAdrian Hunter
155210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
156210cf1f9SAdrian Hunter	pos = s.find(sub)
157210cf1f9SAdrian Hunter	if pos < 0:
158210cf1f9SAdrian Hunter		return pos
159210cf1f9SAdrian Hunter	if n <= 1:
160210cf1f9SAdrian Hunter		return offs + pos
161210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
162210cf1f9SAdrian Hunter
163031c2a00SAdrian Hunter# Percent to one decimal place
164031c2a00SAdrian Hunter
165031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
166031c2a00SAdrian Hunter	if not d:
167031c2a00SAdrian Hunter		return "0.0"
168031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
169031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
170031c2a00SAdrian Hunter
171031c2a00SAdrian Hunter# Helper for queries that must not fail
172031c2a00SAdrian Hunter
173031c2a00SAdrian Hunterdef QueryExec(query, stmt):
174031c2a00SAdrian Hunter	ret = query.exec_(stmt)
175031c2a00SAdrian Hunter	if not ret:
176031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
177031c2a00SAdrian Hunter
178ebd70c7dSAdrian Hunter# Background thread
179ebd70c7dSAdrian Hunter
180ebd70c7dSAdrian Hunterclass Thread(QThread):
181ebd70c7dSAdrian Hunter
182ebd70c7dSAdrian Hunter	done = Signal(object)
183ebd70c7dSAdrian Hunter
184ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
185ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
186ebd70c7dSAdrian Hunter		self.task = task
187ebd70c7dSAdrian Hunter		self.param = param
188ebd70c7dSAdrian Hunter
189ebd70c7dSAdrian Hunter	def run(self):
190ebd70c7dSAdrian Hunter		while True:
191ebd70c7dSAdrian Hunter			if self.param is None:
192ebd70c7dSAdrian Hunter				done, result = self.task()
193ebd70c7dSAdrian Hunter			else:
194ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
195ebd70c7dSAdrian Hunter			self.done.emit(result)
196ebd70c7dSAdrian Hunter			if done:
197ebd70c7dSAdrian Hunter				break
198ebd70c7dSAdrian Hunter
199031c2a00SAdrian Hunter# Tree data model
200031c2a00SAdrian Hunter
201031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
202031c2a00SAdrian Hunter
2034a0979d4SAdrian Hunter	def __init__(self, glb, params, parent=None):
204031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
205a448ba23SAdrian Hunter		self.glb = glb
2064a0979d4SAdrian Hunter		self.params = params
207a448ba23SAdrian Hunter		self.root = self.GetRoot()
208031c2a00SAdrian Hunter		self.last_row_read = 0
209031c2a00SAdrian Hunter
210031c2a00SAdrian Hunter	def Item(self, parent):
211031c2a00SAdrian Hunter		if parent.isValid():
212031c2a00SAdrian Hunter			return parent.internalPointer()
213031c2a00SAdrian Hunter		else:
214031c2a00SAdrian Hunter			return self.root
215031c2a00SAdrian Hunter
216031c2a00SAdrian Hunter	def rowCount(self, parent):
217031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
218031c2a00SAdrian Hunter		if result < 0:
219031c2a00SAdrian Hunter			result = 0
220031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
221031c2a00SAdrian Hunter		return result
222031c2a00SAdrian Hunter
223031c2a00SAdrian Hunter	def hasChildren(self, parent):
224031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
225031c2a00SAdrian Hunter
226031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
227031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
228031c2a00SAdrian Hunter			return self.columnAlignment(section)
229031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
230031c2a00SAdrian Hunter			return None
231031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
232031c2a00SAdrian Hunter			return None
233031c2a00SAdrian Hunter		return self.columnHeader(section)
234031c2a00SAdrian Hunter
235031c2a00SAdrian Hunter	def parent(self, child):
236031c2a00SAdrian Hunter		child_item = child.internalPointer()
237031c2a00SAdrian Hunter		if child_item is self.root:
238031c2a00SAdrian Hunter			return QModelIndex()
239031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
240031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
241031c2a00SAdrian Hunter
242031c2a00SAdrian Hunter	def index(self, row, column, parent):
243031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
244031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
245031c2a00SAdrian Hunter
246031c2a00SAdrian Hunter	def DisplayData(self, item, index):
247031c2a00SAdrian Hunter		return item.getData(index.column())
248031c2a00SAdrian Hunter
2498392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2508392b74bSAdrian Hunter		if row > self.last_row_read:
2518392b74bSAdrian Hunter			self.last_row_read = row
2528392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2538392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2548392b74bSAdrian Hunter
2558392b74bSAdrian Hunter	def columnAlignment(self, column):
2568392b74bSAdrian Hunter		return Qt.AlignLeft
2578392b74bSAdrian Hunter
2588392b74bSAdrian Hunter	def columnFont(self, column):
2598392b74bSAdrian Hunter		return None
2608392b74bSAdrian Hunter
2618392b74bSAdrian Hunter	def data(self, index, role):
2628392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2638392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2648392b74bSAdrian Hunter		if role == Qt.FontRole:
2658392b74bSAdrian Hunter			return self.columnFont(index.column())
2668392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2678392b74bSAdrian Hunter			return None
2688392b74bSAdrian Hunter		item = index.internalPointer()
2698392b74bSAdrian Hunter		return self.DisplayData(item, index)
2708392b74bSAdrian Hunter
2718392b74bSAdrian Hunter# Table data model
2728392b74bSAdrian Hunter
2738392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2748392b74bSAdrian Hunter
2758392b74bSAdrian Hunter	def __init__(self, parent=None):
2768392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2778392b74bSAdrian Hunter		self.child_count = 0
2788392b74bSAdrian Hunter		self.child_items = []
2798392b74bSAdrian Hunter		self.last_row_read = 0
2808392b74bSAdrian Hunter
2818392b74bSAdrian Hunter	def Item(self, parent):
2828392b74bSAdrian Hunter		if parent.isValid():
2838392b74bSAdrian Hunter			return parent.internalPointer()
2848392b74bSAdrian Hunter		else:
2858392b74bSAdrian Hunter			return self
2868392b74bSAdrian Hunter
2878392b74bSAdrian Hunter	def rowCount(self, parent):
2888392b74bSAdrian Hunter		return self.child_count
2898392b74bSAdrian Hunter
2908392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2918392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2928392b74bSAdrian Hunter			return self.columnAlignment(section)
2938392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2948392b74bSAdrian Hunter			return None
2958392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2968392b74bSAdrian Hunter			return None
2978392b74bSAdrian Hunter		return self.columnHeader(section)
2988392b74bSAdrian Hunter
2998392b74bSAdrian Hunter	def index(self, row, column, parent):
3008392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
3018392b74bSAdrian Hunter
3028392b74bSAdrian Hunter	def DisplayData(self, item, index):
3038392b74bSAdrian Hunter		return item.getData(index.column())
3048392b74bSAdrian Hunter
3058392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
3068392b74bSAdrian Hunter		if row > self.last_row_read:
3078392b74bSAdrian Hunter			self.last_row_read = row
3088392b74bSAdrian Hunter			if row + 10 >= self.child_count:
3098392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
3108392b74bSAdrian Hunter
311031c2a00SAdrian Hunter	def columnAlignment(self, column):
312031c2a00SAdrian Hunter		return Qt.AlignLeft
313031c2a00SAdrian Hunter
314031c2a00SAdrian Hunter	def columnFont(self, column):
315031c2a00SAdrian Hunter		return None
316031c2a00SAdrian Hunter
317031c2a00SAdrian Hunter	def data(self, index, role):
318031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
319031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
320031c2a00SAdrian Hunter		if role == Qt.FontRole:
321031c2a00SAdrian Hunter			return self.columnFont(index.column())
322031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
323031c2a00SAdrian Hunter			return None
324031c2a00SAdrian Hunter		item = index.internalPointer()
325031c2a00SAdrian Hunter		return self.DisplayData(item, index)
326031c2a00SAdrian Hunter
3271beb5c7bSAdrian Hunter# Model cache
3281beb5c7bSAdrian Hunter
3291beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
3301beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
3311beb5c7bSAdrian Hunter
3321beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
3331beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3341beb5c7bSAdrian Hunter	try:
3351beb5c7bSAdrian Hunter		model = model_cache[model_name]
3361beb5c7bSAdrian Hunter	except:
3371beb5c7bSAdrian Hunter		model = None
3381beb5c7bSAdrian Hunter	if model is None:
3391beb5c7bSAdrian Hunter		model = create_fn()
3401beb5c7bSAdrian Hunter		model_cache[model_name] = model
3411beb5c7bSAdrian Hunter	model_cache_lock.release()
3421beb5c7bSAdrian Hunter	return model
3431beb5c7bSAdrian Hunter
344181ea40aSAdrian Hunterdef LookupModel(model_name):
345181ea40aSAdrian Hunter	model_cache_lock.acquire()
346181ea40aSAdrian Hunter	try:
347181ea40aSAdrian Hunter		model = model_cache[model_name]
348181ea40aSAdrian Hunter	except:
349181ea40aSAdrian Hunter		model = None
350181ea40aSAdrian Hunter	model_cache_lock.release()
351181ea40aSAdrian Hunter	return model
352181ea40aSAdrian Hunter
353ebd70c7dSAdrian Hunter# Find bar
354ebd70c7dSAdrian Hunter
355ebd70c7dSAdrian Hunterclass FindBar():
356ebd70c7dSAdrian Hunter
357ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
358ebd70c7dSAdrian Hunter		self.finder = finder
359ebd70c7dSAdrian Hunter		self.context = []
360ebd70c7dSAdrian Hunter		self.last_value = None
361ebd70c7dSAdrian Hunter		self.last_pattern = None
362ebd70c7dSAdrian Hunter
363ebd70c7dSAdrian Hunter		label = QLabel("Find:")
364ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
365ebd70c7dSAdrian Hunter
366ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
367ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
368ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
371ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
372ebd70c7dSAdrian Hunter		self.progress.hide()
373ebd70c7dSAdrian Hunter
374ebd70c7dSAdrian Hunter		if is_reg_expr:
375ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
376ebd70c7dSAdrian Hunter		else:
377ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
378ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
379ebd70c7dSAdrian Hunter
380ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
381ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
382ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
383ebd70c7dSAdrian Hunter
384ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
385ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
386ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
387ebd70c7dSAdrian Hunter
388ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
389ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
390ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
391ebd70c7dSAdrian Hunter
392ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
393ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
394ebd70c7dSAdrian Hunter
395ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
396ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
397ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
398ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
399ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
400ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
401ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
402ebd70c7dSAdrian Hunter
403ebd70c7dSAdrian Hunter		self.bar = QWidget()
40426688729SAdrian Hunter		self.bar.setLayout(self.hbox)
405ebd70c7dSAdrian Hunter		self.bar.hide()
406ebd70c7dSAdrian Hunter
407ebd70c7dSAdrian Hunter	def Widget(self):
408ebd70c7dSAdrian Hunter		return self.bar
409ebd70c7dSAdrian Hunter
410ebd70c7dSAdrian Hunter	def Activate(self):
411ebd70c7dSAdrian Hunter		self.bar.show()
41280b3fb64SAdrian Hunter		self.textbox.lineEdit().selectAll()
413ebd70c7dSAdrian Hunter		self.textbox.setFocus()
414ebd70c7dSAdrian Hunter
415ebd70c7dSAdrian Hunter	def Deactivate(self):
416ebd70c7dSAdrian Hunter		self.bar.hide()
417ebd70c7dSAdrian Hunter
418ebd70c7dSAdrian Hunter	def Busy(self):
419ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
420ebd70c7dSAdrian Hunter		self.pattern.hide()
421ebd70c7dSAdrian Hunter		self.next_button.hide()
422ebd70c7dSAdrian Hunter		self.prev_button.hide()
423ebd70c7dSAdrian Hunter		self.progress.show()
424ebd70c7dSAdrian Hunter
425ebd70c7dSAdrian Hunter	def Idle(self):
426ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
427ebd70c7dSAdrian Hunter		self.progress.hide()
428ebd70c7dSAdrian Hunter		self.pattern.show()
429ebd70c7dSAdrian Hunter		self.next_button.show()
430ebd70c7dSAdrian Hunter		self.prev_button.show()
431ebd70c7dSAdrian Hunter
432ebd70c7dSAdrian Hunter	def Find(self, direction):
433ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
434ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
435ebd70c7dSAdrian Hunter		self.last_value = value
436ebd70c7dSAdrian Hunter		self.last_pattern = pattern
437ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
438ebd70c7dSAdrian Hunter
439ebd70c7dSAdrian Hunter	def ValueChanged(self):
440ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
441ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
442ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
443ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
444ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
445ebd70c7dSAdrian Hunter		if data == None:
446ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
447ebd70c7dSAdrian Hunter		else:
448ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
449ebd70c7dSAdrian Hunter		self.Find(0)
450ebd70c7dSAdrian Hunter
451ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
452ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
453ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
454ebd70c7dSAdrian Hunter		if value != self.last_value:
455ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
456ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
457ebd70c7dSAdrian Hunter			if index < 0:
458ebd70c7dSAdrian Hunter				index = self.textbox.count()
459ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
460ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
461ebd70c7dSAdrian Hunter				return
462ebd70c7dSAdrian Hunter			else:
463ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
464ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
465ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
466ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
467ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
468ebd70c7dSAdrian Hunter		self.Find(direction)
469ebd70c7dSAdrian Hunter
470ebd70c7dSAdrian Hunter	def NotFound(self):
471ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
472ebd70c7dSAdrian Hunter
473031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
474031c2a00SAdrian Hunter
475031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
476031c2a00SAdrian Hunter
4774a0979d4SAdrian Hunter	def __init__(self, glb, params, row, parent_item):
478031c2a00SAdrian Hunter		self.glb = glb
4794a0979d4SAdrian Hunter		self.params = params
480031c2a00SAdrian Hunter		self.row = row
481031c2a00SAdrian Hunter		self.parent_item = parent_item
48226688729SAdrian Hunter		self.query_done = False
483031c2a00SAdrian Hunter		self.child_count = 0
484031c2a00SAdrian Hunter		self.child_items = []
4853ac641f4SAdrian Hunter		if parent_item:
4863ac641f4SAdrian Hunter			self.level = parent_item.level + 1
4873ac641f4SAdrian Hunter		else:
4883ac641f4SAdrian Hunter			self.level = 0
489031c2a00SAdrian Hunter
490031c2a00SAdrian Hunter	def getChildItem(self, row):
491031c2a00SAdrian Hunter		return self.child_items[row]
492031c2a00SAdrian Hunter
493031c2a00SAdrian Hunter	def getParentItem(self):
494031c2a00SAdrian Hunter		return self.parent_item
495031c2a00SAdrian Hunter
496031c2a00SAdrian Hunter	def getRow(self):
497031c2a00SAdrian Hunter		return self.row
498031c2a00SAdrian Hunter
499031c2a00SAdrian Hunter	def childCount(self):
500031c2a00SAdrian Hunter		if not self.query_done:
501031c2a00SAdrian Hunter			self.Select()
502031c2a00SAdrian Hunter			if not self.child_count:
503031c2a00SAdrian Hunter				return -1
504031c2a00SAdrian Hunter		return self.child_count
505031c2a00SAdrian Hunter
506031c2a00SAdrian Hunter	def hasChildren(self):
507031c2a00SAdrian Hunter		if not self.query_done:
508031c2a00SAdrian Hunter			return True
509031c2a00SAdrian Hunter		return self.child_count > 0
510031c2a00SAdrian Hunter
511031c2a00SAdrian Hunter	def getData(self, column):
512031c2a00SAdrian Hunter		return self.data[column]
513031c2a00SAdrian Hunter
514031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
515031c2a00SAdrian Hunter
516031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
517031c2a00SAdrian Hunter
51838a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
5194a0979d4SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
520031c2a00SAdrian Hunter		self.comm_id = comm_id
521031c2a00SAdrian Hunter		self.thread_id = thread_id
522031c2a00SAdrian Hunter		self.call_path_id = call_path_id
52338a846d4SAdrian Hunter		self.insn_cnt = insn_cnt
52438a846d4SAdrian Hunter		self.cyc_cnt = cyc_cnt
525031c2a00SAdrian Hunter		self.branch_count = branch_count
526031c2a00SAdrian Hunter		self.time = time
527031c2a00SAdrian Hunter
528031c2a00SAdrian Hunter	def Select(self):
52926688729SAdrian Hunter		self.query_done = True
530031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
53138a846d4SAdrian Hunter		if self.params.have_ipc:
53238a846d4SAdrian Hunter			ipc_str = ", SUM(insn_count), SUM(cyc_count)"
53338a846d4SAdrian Hunter		else:
53438a846d4SAdrian Hunter			ipc_str = ""
53538a846d4SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
536031c2a00SAdrian Hunter					" FROM calls"
537031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
538031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
539031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
540031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
541031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
542031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
543031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
544031c2a00SAdrian Hunter					" ORDER BY call_path_id")
545031c2a00SAdrian Hunter		while query.next():
54638a846d4SAdrian Hunter			if self.params.have_ipc:
54738a846d4SAdrian Hunter				insn_cnt = int(query.value(5))
54838a846d4SAdrian Hunter				cyc_cnt = int(query.value(6))
54938a846d4SAdrian Hunter				branch_count = int(query.value(7))
55038a846d4SAdrian Hunter			else:
55138a846d4SAdrian Hunter				insn_cnt = 0
55238a846d4SAdrian Hunter				cyc_cnt = 0
55338a846d4SAdrian Hunter				branch_count = int(query.value(5))
55438a846d4SAdrian Hunter			child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
555031c2a00SAdrian Hunter			self.child_items.append(child_item)
556031c2a00SAdrian Hunter			self.child_count += 1
557031c2a00SAdrian Hunter
558031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
559031c2a00SAdrian Hunter
560031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
561031c2a00SAdrian Hunter
56238a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
56338a846d4SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
564031c2a00SAdrian Hunter		dso = dsoname(dso)
56538a846d4SAdrian Hunter		if self.params.have_ipc:
56638a846d4SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
56738a846d4SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
56838a846d4SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
56938a846d4SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
57038a846d4SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
57138a846d4SAdrian Hunter		else:
572031c2a00SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
573031c2a00SAdrian Hunter		self.dbid = call_path_id
574031c2a00SAdrian Hunter
575031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
576031c2a00SAdrian Hunter
577031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
578031c2a00SAdrian Hunter
5794a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
58038a846d4SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
58138a846d4SAdrian Hunter		if self.params.have_ipc:
58238a846d4SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
58338a846d4SAdrian Hunter		else:
584031c2a00SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
585031c2a00SAdrian Hunter		self.dbid = thread_id
586031c2a00SAdrian Hunter
587031c2a00SAdrian Hunter	def Select(self):
588031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
589031c2a00SAdrian Hunter		for child_item in self.child_items:
590031c2a00SAdrian Hunter			self.time += child_item.time
59138a846d4SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
59238a846d4SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
593031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
594031c2a00SAdrian Hunter		for child_item in self.child_items:
595031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
59638a846d4SAdrian Hunter			if self.params.have_ipc:
59738a846d4SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
59838a846d4SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
59938a846d4SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
60038a846d4SAdrian Hunter			else:
601031c2a00SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
602031c2a00SAdrian Hunter
603031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
604031c2a00SAdrian Hunter
605031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
606031c2a00SAdrian Hunter
6074a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
6084a0979d4SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
60938a846d4SAdrian Hunter		if self.params.have_ipc:
61038a846d4SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
61138a846d4SAdrian Hunter		else:
612031c2a00SAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
613031c2a00SAdrian Hunter		self.dbid = comm_id
614031c2a00SAdrian Hunter
615031c2a00SAdrian Hunter	def Select(self):
61626688729SAdrian Hunter		self.query_done = True
617031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
618031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
619031c2a00SAdrian Hunter					" FROM comm_threads"
620031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
621031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
622031c2a00SAdrian Hunter		while query.next():
6234a0979d4SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
624031c2a00SAdrian Hunter			self.child_items.append(child_item)
625031c2a00SAdrian Hunter			self.child_count += 1
626031c2a00SAdrian Hunter
627031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
628031c2a00SAdrian Hunter
629031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
630031c2a00SAdrian Hunter
6314a0979d4SAdrian Hunter	def __init__(self, glb, params):
6324a0979d4SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, params, 0, None)
633031c2a00SAdrian Hunter		self.dbid = 0
63426688729SAdrian Hunter		self.query_done = True
63526c11206SAdrian Hunter		if_has_calls = ""
63626c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
63726c11206SAdrian Hunter			if_has_calls = " WHERE has_calls = TRUE"
638031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
63926c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
640031c2a00SAdrian Hunter		while query.next():
641031c2a00SAdrian Hunter			if not query.value(0):
642031c2a00SAdrian Hunter				continue
6434a0979d4SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
644031c2a00SAdrian Hunter			self.child_items.append(child_item)
645031c2a00SAdrian Hunter			self.child_count += 1
646031c2a00SAdrian Hunter
6474a0979d4SAdrian Hunter# Call graph model parameters
6484a0979d4SAdrian Hunter
6494a0979d4SAdrian Hunterclass CallGraphModelParams():
6504a0979d4SAdrian Hunter
6514a0979d4SAdrian Hunter	def __init__(self, glb, parent=None):
6524a0979d4SAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
6534a0979d4SAdrian Hunter
654254c0d82SAdrian Hunter# Context-sensitive call graph data model base
655031c2a00SAdrian Hunter
656254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
657031c2a00SAdrian Hunter
658031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
6594a0979d4SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
660031c2a00SAdrian Hunter
661ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
662ebd70c7dSAdrian Hunter		if pattern:
663ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
664ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
665ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
666ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
667ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
668ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
669ebd70c7dSAdrian Hunter				# Escape % and _
670ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
671ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
672ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
673ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
674ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
675ebd70c7dSAdrian Hunter			else:
676ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
677ebd70c7dSAdrian Hunter		else:
678ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
679254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
680ebd70c7dSAdrian Hunter
681ebd70c7dSAdrian Hunter	def Found(self, query, found):
682ebd70c7dSAdrian Hunter		if found:
683ebd70c7dSAdrian Hunter			return self.FindPath(query)
684ebd70c7dSAdrian Hunter		return []
685ebd70c7dSAdrian Hunter
686ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
687ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
688ebd70c7dSAdrian Hunter			found = query.first()
689ebd70c7dSAdrian Hunter		else:
690ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
691ebd70c7dSAdrian Hunter			found = query.next()
692ebd70c7dSAdrian Hunter		return self.Found(query, found)
693ebd70c7dSAdrian Hunter
694ebd70c7dSAdrian Hunter	def FindNext(self, query):
695ebd70c7dSAdrian Hunter		found = query.next()
696ebd70c7dSAdrian Hunter		if not found:
697ebd70c7dSAdrian Hunter			found = query.first()
698ebd70c7dSAdrian Hunter		return self.Found(query, found)
699ebd70c7dSAdrian Hunter
700ebd70c7dSAdrian Hunter	def FindPrev(self, query):
701ebd70c7dSAdrian Hunter		found = query.previous()
702ebd70c7dSAdrian Hunter		if not found:
703ebd70c7dSAdrian Hunter			found = query.last()
704ebd70c7dSAdrian Hunter		return self.Found(query, found)
705ebd70c7dSAdrian Hunter
706ebd70c7dSAdrian Hunter	def FindThread(self, c):
707ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
708ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
709ebd70c7dSAdrian Hunter		elif c.direction > 0:
710ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
711ebd70c7dSAdrian Hunter		else:
712ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
713ebd70c7dSAdrian Hunter		return (True, ids)
714ebd70c7dSAdrian Hunter
715ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
716ebd70c7dSAdrian Hunter		class Context():
717ebd70c7dSAdrian Hunter			def __init__(self, *x):
718ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
719ebd70c7dSAdrian Hunter			def Update(self, *x):
720ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
721ebd70c7dSAdrian Hunter		if len(context):
722ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
723ebd70c7dSAdrian Hunter		else:
724ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
725ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
726ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
727ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
728ebd70c7dSAdrian Hunter		thread.start()
729ebd70c7dSAdrian Hunter
730ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
731ebd70c7dSAdrian Hunter		callback(ids)
732ebd70c7dSAdrian Hunter
733254c0d82SAdrian Hunter# Context-sensitive call graph data model
734254c0d82SAdrian Hunter
735254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
736254c0d82SAdrian Hunter
737254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
738254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
739254c0d82SAdrian Hunter
740254c0d82SAdrian Hunter	def GetRoot(self):
7414a0979d4SAdrian Hunter		return CallGraphRootItem(self.glb, self.params)
742254c0d82SAdrian Hunter
743254c0d82SAdrian Hunter	def columnCount(self, parent=None):
74438a846d4SAdrian Hunter		if self.params.have_ipc:
74538a846d4SAdrian Hunter			return 12
74638a846d4SAdrian Hunter		else:
747254c0d82SAdrian Hunter			return 7
748254c0d82SAdrian Hunter
749254c0d82SAdrian Hunter	def columnHeader(self, column):
75038a846d4SAdrian Hunter		if self.params.have_ipc:
75138a846d4SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
75238a846d4SAdrian Hunter		else:
753254c0d82SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
754254c0d82SAdrian Hunter		return headers[column]
755254c0d82SAdrian Hunter
756254c0d82SAdrian Hunter	def columnAlignment(self, column):
75738a846d4SAdrian Hunter		if self.params.have_ipc:
75838a846d4SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
75938a846d4SAdrian Hunter		else:
760254c0d82SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
761254c0d82SAdrian Hunter		return alignment[column]
762254c0d82SAdrian Hunter
763254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
764254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
765254c0d82SAdrian Hunter						" FROM calls"
766254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
767254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
768254c0d82SAdrian Hunter						" WHERE symbols.name" + match +
769254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
770254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
771254c0d82SAdrian Hunter
772254c0d82SAdrian Hunter	def FindPath(self, query):
773254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
774254c0d82SAdrian Hunter		# to open the tree at the right place.
775254c0d82SAdrian Hunter		ids = []
776254c0d82SAdrian Hunter		parent_id = query.value(0)
777254c0d82SAdrian Hunter		while parent_id:
778254c0d82SAdrian Hunter			ids.insert(0, parent_id)
779254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
780254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
781254c0d82SAdrian Hunter					" FROM call_paths"
782254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
783254c0d82SAdrian Hunter			if not q2.next():
784254c0d82SAdrian Hunter				break
785254c0d82SAdrian Hunter			parent_id = q2.value(0)
786254c0d82SAdrian Hunter		# The call path root is not used
787254c0d82SAdrian Hunter		if ids[0] == 1:
788254c0d82SAdrian Hunter			del ids[0]
789254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
790254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
791254c0d82SAdrian Hunter		return ids
792254c0d82SAdrian Hunter
793ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
794ae8b887cSAdrian Hunter
795ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
796ae8b887cSAdrian Hunter
797b3b66079SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
7984a0979d4SAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
799ae8b887cSAdrian Hunter		self.comm_id = comm_id
800ae8b887cSAdrian Hunter		self.thread_id = thread_id
801ae8b887cSAdrian Hunter		self.calls_id = calls_id
802b3b66079SAdrian Hunter		self.insn_cnt = insn_cnt
803b3b66079SAdrian Hunter		self.cyc_cnt = cyc_cnt
804ae8b887cSAdrian Hunter		self.branch_count = branch_count
805ae8b887cSAdrian Hunter		self.time = time
806ae8b887cSAdrian Hunter
807ae8b887cSAdrian Hunter	def Select(self):
80826688729SAdrian Hunter		self.query_done = True
809ae8b887cSAdrian Hunter		if self.calls_id == 0:
810ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
811ae8b887cSAdrian Hunter		else:
812ae8b887cSAdrian Hunter			comm_thread = ""
813b3b66079SAdrian Hunter		if self.params.have_ipc:
814b3b66079SAdrian Hunter			ipc_str = ", insn_count, cyc_count"
815b3b66079SAdrian Hunter		else:
816b3b66079SAdrian Hunter			ipc_str = ""
817ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
818b3b66079SAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
819ae8b887cSAdrian Hunter					" FROM calls"
820ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
821ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
822ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
823ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
824ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
825ae8b887cSAdrian Hunter		while query.next():
826b3b66079SAdrian Hunter			if self.params.have_ipc:
827b3b66079SAdrian Hunter				insn_cnt = int(query.value(5))
828b3b66079SAdrian Hunter				cyc_cnt = int(query.value(6))
829b3b66079SAdrian Hunter				branch_count = int(query.value(7))
830b3b66079SAdrian Hunter			else:
831b3b66079SAdrian Hunter				insn_cnt = 0
832b3b66079SAdrian Hunter				cyc_cnt = 0
833b3b66079SAdrian Hunter				branch_count = int(query.value(5))
834b3b66079SAdrian Hunter			child_item = CallTreeLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
835ae8b887cSAdrian Hunter			self.child_items.append(child_item)
836ae8b887cSAdrian Hunter			self.child_count += 1
837ae8b887cSAdrian Hunter
838ae8b887cSAdrian Hunter# Call tree data model level three item
839ae8b887cSAdrian Hunter
840ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
841ae8b887cSAdrian Hunter
842b3b66079SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
843b3b66079SAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
844ae8b887cSAdrian Hunter		dso = dsoname(dso)
845b3b66079SAdrian Hunter		if self.params.have_ipc:
846b3b66079SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
847b3b66079SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
848b3b66079SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
849b3b66079SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
850b3b66079SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
851b3b66079SAdrian Hunter		else:
852ae8b887cSAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
853ae8b887cSAdrian Hunter		self.dbid = calls_id
854ae8b887cSAdrian Hunter
855ae8b887cSAdrian Hunter# Call tree data model level two item
856ae8b887cSAdrian Hunter
857ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
858ae8b887cSAdrian Hunter
8594a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
860b3b66079SAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item)
861b3b66079SAdrian Hunter		if self.params.have_ipc:
862b3b66079SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
863b3b66079SAdrian Hunter		else:
864ae8b887cSAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
865ae8b887cSAdrian Hunter		self.dbid = thread_id
866ae8b887cSAdrian Hunter
867ae8b887cSAdrian Hunter	def Select(self):
868ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
869ae8b887cSAdrian Hunter		for child_item in self.child_items:
870ae8b887cSAdrian Hunter			self.time += child_item.time
871b3b66079SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
872b3b66079SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
873ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
874ae8b887cSAdrian Hunter		for child_item in self.child_items:
875ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
876b3b66079SAdrian Hunter			if self.params.have_ipc:
877b3b66079SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
878b3b66079SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
879b3b66079SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
880b3b66079SAdrian Hunter			else:
881ae8b887cSAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
882ae8b887cSAdrian Hunter
883ae8b887cSAdrian Hunter# Call tree data model level one item
884ae8b887cSAdrian Hunter
885ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
886ae8b887cSAdrian Hunter
8874a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
8884a0979d4SAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
889b3b66079SAdrian Hunter		if self.params.have_ipc:
890b3b66079SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
891b3b66079SAdrian Hunter		else:
892ae8b887cSAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
893ae8b887cSAdrian Hunter		self.dbid = comm_id
894ae8b887cSAdrian Hunter
895ae8b887cSAdrian Hunter	def Select(self):
89626688729SAdrian Hunter		self.query_done = True
897ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
898ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
899ae8b887cSAdrian Hunter					" FROM comm_threads"
900ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
901ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
902ae8b887cSAdrian Hunter		while query.next():
9034a0979d4SAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
904ae8b887cSAdrian Hunter			self.child_items.append(child_item)
905ae8b887cSAdrian Hunter			self.child_count += 1
906ae8b887cSAdrian Hunter
907ae8b887cSAdrian Hunter# Call tree data model root item
908ae8b887cSAdrian Hunter
909ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
910ae8b887cSAdrian Hunter
9114a0979d4SAdrian Hunter	def __init__(self, glb, params):
9124a0979d4SAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, params, 0, None)
913ae8b887cSAdrian Hunter		self.dbid = 0
91426688729SAdrian Hunter		self.query_done = True
91526c11206SAdrian Hunter		if_has_calls = ""
91626c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
91726c11206SAdrian Hunter			if_has_calls = " WHERE has_calls = TRUE"
918ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
91926c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
920ae8b887cSAdrian Hunter		while query.next():
921ae8b887cSAdrian Hunter			if not query.value(0):
922ae8b887cSAdrian Hunter				continue
9234a0979d4SAdrian Hunter			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
924ae8b887cSAdrian Hunter			self.child_items.append(child_item)
925ae8b887cSAdrian Hunter			self.child_count += 1
926ae8b887cSAdrian Hunter
927ae8b887cSAdrian Hunter# Call Tree data model
928ae8b887cSAdrian Hunter
929ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
930ae8b887cSAdrian Hunter
931ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
932ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
933ae8b887cSAdrian Hunter
934ae8b887cSAdrian Hunter	def GetRoot(self):
9354a0979d4SAdrian Hunter		return CallTreeRootItem(self.glb, self.params)
936ae8b887cSAdrian Hunter
937ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
938b3b66079SAdrian Hunter		if self.params.have_ipc:
939b3b66079SAdrian Hunter			return 12
940b3b66079SAdrian Hunter		else:
941ae8b887cSAdrian Hunter			return 7
942ae8b887cSAdrian Hunter
943ae8b887cSAdrian Hunter	def columnHeader(self, column):
944b3b66079SAdrian Hunter		if self.params.have_ipc:
945b3b66079SAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
946b3b66079SAdrian Hunter		else:
947ae8b887cSAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
948ae8b887cSAdrian Hunter		return headers[column]
949ae8b887cSAdrian Hunter
950ae8b887cSAdrian Hunter	def columnAlignment(self, column):
951b3b66079SAdrian Hunter		if self.params.have_ipc:
952b3b66079SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
953b3b66079SAdrian Hunter		else:
954ae8b887cSAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
955ae8b887cSAdrian Hunter		return alignment[column]
956ae8b887cSAdrian Hunter
957ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
958ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
959ae8b887cSAdrian Hunter						" FROM calls"
960ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
961ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
962ae8b887cSAdrian Hunter						" WHERE symbols.name" + match +
963ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
964ae8b887cSAdrian Hunter
965ae8b887cSAdrian Hunter	def FindPath(self, query):
966ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
967ae8b887cSAdrian Hunter		# to open the tree at the right place.
968ae8b887cSAdrian Hunter		ids = []
969ae8b887cSAdrian Hunter		parent_id = query.value(0)
970ae8b887cSAdrian Hunter		while parent_id:
971ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
972ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
973ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
974ae8b887cSAdrian Hunter					" FROM calls"
975ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
976ae8b887cSAdrian Hunter			if not q2.next():
977ae8b887cSAdrian Hunter				break
978ae8b887cSAdrian Hunter			parent_id = q2.value(0)
979ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
980ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
981ae8b887cSAdrian Hunter		return ids
982ae8b887cSAdrian Hunter
983*42c303ffSAdrian Hunter# Vertical layout
984*42c303ffSAdrian Hunter
985*42c303ffSAdrian Hunterclass HBoxLayout(QHBoxLayout):
986*42c303ffSAdrian Hunter
987*42c303ffSAdrian Hunter	def __init__(self, *children):
988*42c303ffSAdrian Hunter		super(HBoxLayout, self).__init__()
989*42c303ffSAdrian Hunter
990*42c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
991*42c303ffSAdrian Hunter		for child in children:
992*42c303ffSAdrian Hunter			if child.isWidgetType():
993*42c303ffSAdrian Hunter				self.layout().addWidget(child)
994*42c303ffSAdrian Hunter			else:
995*42c303ffSAdrian Hunter				self.layout().addLayout(child)
996*42c303ffSAdrian Hunter
997*42c303ffSAdrian Hunter# Horizontal layout
998*42c303ffSAdrian Hunter
999*42c303ffSAdrian Hunterclass VBoxLayout(QVBoxLayout):
1000*42c303ffSAdrian Hunter
1001*42c303ffSAdrian Hunter	def __init__(self, *children):
1002*42c303ffSAdrian Hunter		super(VBoxLayout, self).__init__()
1003*42c303ffSAdrian Hunter
1004*42c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
1005*42c303ffSAdrian Hunter		for child in children:
1006*42c303ffSAdrian Hunter			if child.isWidgetType():
1007*42c303ffSAdrian Hunter				self.layout().addWidget(child)
1008*42c303ffSAdrian Hunter			else:
1009*42c303ffSAdrian Hunter				self.layout().addLayout(child)
1010*42c303ffSAdrian Hunter
1011*42c303ffSAdrian Hunter# Vertical layout widget
1012ebd70c7dSAdrian Hunter
1013ebd70c7dSAdrian Hunterclass VBox():
1014ebd70c7dSAdrian Hunter
1015*42c303ffSAdrian Hunter	def __init__(self, *children):
1016ebd70c7dSAdrian Hunter		self.vbox = QWidget()
1017*42c303ffSAdrian Hunter		self.vbox.setLayout(VBoxLayout(*children))
1018ebd70c7dSAdrian Hunter
1019ebd70c7dSAdrian Hunter	def Widget(self):
1020ebd70c7dSAdrian Hunter		return self.vbox
1021ebd70c7dSAdrian Hunter
1022a731cc4cSAdrian Hunter# Tree window base
10231beb5c7bSAdrian Hunter
1024a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
10251beb5c7bSAdrian Hunter
1026a731cc4cSAdrian Hunter	def __init__(self, parent=None):
1027a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
10281beb5c7bSAdrian Hunter
1029a731cc4cSAdrian Hunter		self.model = None
1030a731cc4cSAdrian Hunter		self.find_bar = None
10311beb5c7bSAdrian Hunter
1032be6e7471SAdrian Hunter		self.view = QTreeView()
103396c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
103496c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
1035be6e7471SAdrian Hunter
10369bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
10379bc4e4bfSAdrian Hunter
1038ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
1039ebd70c7dSAdrian Hunter		if not len(ids):
1040ebd70c7dSAdrian Hunter			return False
1041ebd70c7dSAdrian Hunter		parent = QModelIndex()
1042ebd70c7dSAdrian Hunter		for dbid in ids:
1043ebd70c7dSAdrian Hunter			found = False
1044ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
1045ebd70c7dSAdrian Hunter			for row in xrange(n):
1046ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
1047ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
1048ebd70c7dSAdrian Hunter					found = True
1049ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
1050ebd70c7dSAdrian Hunter					parent = child
1051ebd70c7dSAdrian Hunter					break
1052ebd70c7dSAdrian Hunter			if not found:
1053ebd70c7dSAdrian Hunter				break
1054ebd70c7dSAdrian Hunter		return found
1055ebd70c7dSAdrian Hunter
1056ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
1057ebd70c7dSAdrian Hunter		self.view.setFocus()
1058ebd70c7dSAdrian Hunter		self.find_bar.Busy()
1059ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
1060ebd70c7dSAdrian Hunter
1061ebd70c7dSAdrian Hunter	def FindDone(self, ids):
1062ebd70c7dSAdrian Hunter		found = True
1063ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
1064ebd70c7dSAdrian Hunter			found = False
1065ebd70c7dSAdrian Hunter		self.find_bar.Idle()
1066ebd70c7dSAdrian Hunter		if not found:
1067ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
1068ebd70c7dSAdrian Hunter
1069a731cc4cSAdrian Hunter
1070a731cc4cSAdrian Hunter# Context-sensitive call graph window
1071a731cc4cSAdrian Hunter
1072a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
1073a731cc4cSAdrian Hunter
1074a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
1075a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
1076a731cc4cSAdrian Hunter
1077a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
1078a731cc4cSAdrian Hunter
1079a731cc4cSAdrian Hunter		self.view.setModel(self.model)
1080a731cc4cSAdrian Hunter
1081a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
1082a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
1083a731cc4cSAdrian Hunter
1084a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
1085a731cc4cSAdrian Hunter
1086a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1087a731cc4cSAdrian Hunter
1088a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1089a731cc4cSAdrian Hunter
1090a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
1091a731cc4cSAdrian Hunter
1092ae8b887cSAdrian Hunter# Call tree window
1093ae8b887cSAdrian Hunter
1094ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
1095ae8b887cSAdrian Hunter
1096ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
1097ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
1098ae8b887cSAdrian Hunter
1099ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
1100ae8b887cSAdrian Hunter
1101ae8b887cSAdrian Hunter		self.view.setModel(self.model)
1102ae8b887cSAdrian Hunter
1103ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
1104ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
1105ae8b887cSAdrian Hunter
1106ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
1107ae8b887cSAdrian Hunter
1108ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1109ae8b887cSAdrian Hunter
1110ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1111ae8b887cSAdrian Hunter
1112ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
1113ae8b887cSAdrian Hunter
11148392b74bSAdrian Hunter# Child data item  finder
11158392b74bSAdrian Hunter
11168392b74bSAdrian Hunterclass ChildDataItemFinder():
11178392b74bSAdrian Hunter
11188392b74bSAdrian Hunter	def __init__(self, root):
11198392b74bSAdrian Hunter		self.root = root
11208392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
11218392b74bSAdrian Hunter		self.rows = []
11228392b74bSAdrian Hunter		self.pos = 0
11238392b74bSAdrian Hunter
11248392b74bSAdrian Hunter	def FindSelect(self):
11258392b74bSAdrian Hunter		self.rows = []
11268392b74bSAdrian Hunter		if self.pattern:
11278392b74bSAdrian Hunter			pattern = re.compile(self.value)
11288392b74bSAdrian Hunter			for child in self.root.child_items:
11298392b74bSAdrian Hunter				for column_data in child.data:
11308392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
11318392b74bSAdrian Hunter						self.rows.append(child.row)
11328392b74bSAdrian Hunter						break
11338392b74bSAdrian Hunter		else:
11348392b74bSAdrian Hunter			for child in self.root.child_items:
11358392b74bSAdrian Hunter				for column_data in child.data:
11368392b74bSAdrian Hunter					if self.value in str(column_data):
11378392b74bSAdrian Hunter						self.rows.append(child.row)
11388392b74bSAdrian Hunter						break
11398392b74bSAdrian Hunter
11408392b74bSAdrian Hunter	def FindValue(self):
11418392b74bSAdrian Hunter		self.pos = 0
11428392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
11438392b74bSAdrian Hunter			self.FindSelect()
11448392b74bSAdrian Hunter		if not len(self.rows):
11458392b74bSAdrian Hunter			return -1
11468392b74bSAdrian Hunter		return self.rows[self.pos]
11478392b74bSAdrian Hunter
11488392b74bSAdrian Hunter	def FindThread(self):
11498392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
11508392b74bSAdrian Hunter			row = self.FindValue()
11518392b74bSAdrian Hunter		elif len(self.rows):
11528392b74bSAdrian Hunter			if self.direction > 0:
11538392b74bSAdrian Hunter				self.pos += 1
11548392b74bSAdrian Hunter				if self.pos >= len(self.rows):
11558392b74bSAdrian Hunter					self.pos = 0
11568392b74bSAdrian Hunter			else:
11578392b74bSAdrian Hunter				self.pos -= 1
11588392b74bSAdrian Hunter				if self.pos < 0:
11598392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
11608392b74bSAdrian Hunter			row = self.rows[self.pos]
11618392b74bSAdrian Hunter		else:
11628392b74bSAdrian Hunter			row = -1
11638392b74bSAdrian Hunter		return (True, row)
11648392b74bSAdrian Hunter
11658392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
11668392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
11678392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
11688392b74bSAdrian Hunter		thread = Thread(self.FindThread)
11698392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
11708392b74bSAdrian Hunter		thread.start()
11718392b74bSAdrian Hunter
11728392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
11738392b74bSAdrian Hunter		callback(row)
11748392b74bSAdrian Hunter
11758392b74bSAdrian Hunter# Number of database records to fetch in one go
11768392b74bSAdrian Hunter
11778392b74bSAdrian Hunterglb_chunk_sz = 10000
11788392b74bSAdrian Hunter
11798392b74bSAdrian Hunter# Background process for SQL data fetcher
11808392b74bSAdrian Hunter
11818392b74bSAdrian Hunterclass SQLFetcherProcess():
11828392b74bSAdrian Hunter
11838392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
11848392b74bSAdrian Hunter		# Need a unique connection name
11858392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
11868392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
11878392b74bSAdrian Hunter		self.sql = sql
11888392b74bSAdrian Hunter		self.buffer = buffer
11898392b74bSAdrian Hunter		self.head = head
11908392b74bSAdrian Hunter		self.tail = tail
11918392b74bSAdrian Hunter		self.fetch_count = fetch_count
11928392b74bSAdrian Hunter		self.fetching_done = fetching_done
11938392b74bSAdrian Hunter		self.process_target = process_target
11948392b74bSAdrian Hunter		self.wait_event = wait_event
11958392b74bSAdrian Hunter		self.fetched_event = fetched_event
11968392b74bSAdrian Hunter		self.prep = prep
11978392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
11988392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
11998392b74bSAdrian Hunter		self.last_id = -1
12008392b74bSAdrian Hunter		self.fetched = 0
12018392b74bSAdrian Hunter		self.more = True
12028392b74bSAdrian Hunter		self.local_head = self.head.value
12038392b74bSAdrian Hunter		self.local_tail = self.tail.value
12048392b74bSAdrian Hunter
12058392b74bSAdrian Hunter	def Select(self):
12068392b74bSAdrian Hunter		if self.query_limit:
12078392b74bSAdrian Hunter			if self.query_limit == 1:
12088392b74bSAdrian Hunter				return
12098392b74bSAdrian Hunter			self.query_limit -= 1
12108392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
12118392b74bSAdrian Hunter		QueryExec(self.query, stmt)
12128392b74bSAdrian Hunter
12138392b74bSAdrian Hunter	def Next(self):
12148392b74bSAdrian Hunter		if not self.query.next():
12158392b74bSAdrian Hunter			self.Select()
12168392b74bSAdrian Hunter			if not self.query.next():
12178392b74bSAdrian Hunter				return None
12188392b74bSAdrian Hunter		self.last_id = self.query.value(0)
12198392b74bSAdrian Hunter		return self.prep(self.query)
12208392b74bSAdrian Hunter
12218392b74bSAdrian Hunter	def WaitForTarget(self):
12228392b74bSAdrian Hunter		while True:
12238392b74bSAdrian Hunter			self.wait_event.clear()
12248392b74bSAdrian Hunter			target = self.process_target.value
12258392b74bSAdrian Hunter			if target > self.fetched or target < 0:
12268392b74bSAdrian Hunter				break
12278392b74bSAdrian Hunter			self.wait_event.wait()
12288392b74bSAdrian Hunter		return target
12298392b74bSAdrian Hunter
12308392b74bSAdrian Hunter	def HasSpace(self, sz):
12318392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
12328392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
12338392b74bSAdrian Hunter			if space > sz:
12348392b74bSAdrian Hunter				return True
12358392b74bSAdrian Hunter			if space >= glb_nsz:
12368392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
1237beda0e72STony Jones				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
12388392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
12398392b74bSAdrian Hunter			self.local_head = 0
12408392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
12418392b74bSAdrian Hunter			return True
12428392b74bSAdrian Hunter		return False
12438392b74bSAdrian Hunter
12448392b74bSAdrian Hunter	def WaitForSpace(self, sz):
12458392b74bSAdrian Hunter		if self.HasSpace(sz):
12468392b74bSAdrian Hunter			return
12478392b74bSAdrian Hunter		while True:
12488392b74bSAdrian Hunter			self.wait_event.clear()
12498392b74bSAdrian Hunter			self.local_tail = self.tail.value
12508392b74bSAdrian Hunter			if self.HasSpace(sz):
12518392b74bSAdrian Hunter				return
12528392b74bSAdrian Hunter			self.wait_event.wait()
12538392b74bSAdrian Hunter
12548392b74bSAdrian Hunter	def AddToBuffer(self, obj):
1255beda0e72STony Jones		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
12568392b74bSAdrian Hunter		n = len(d)
1257beda0e72STony Jones		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
12588392b74bSAdrian Hunter		sz = n + glb_nsz
12598392b74bSAdrian Hunter		self.WaitForSpace(sz)
12608392b74bSAdrian Hunter		pos = self.local_head
12618392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
12628392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
12638392b74bSAdrian Hunter		self.local_head += sz
12648392b74bSAdrian Hunter
12658392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
12668392b74bSAdrian Hunter		fetched = 0
12678392b74bSAdrian Hunter		while batch_size > fetched:
12688392b74bSAdrian Hunter			obj = self.Next()
12698392b74bSAdrian Hunter			if obj is None:
12708392b74bSAdrian Hunter				self.more = False
12718392b74bSAdrian Hunter				break
12728392b74bSAdrian Hunter			self.AddToBuffer(obj)
12738392b74bSAdrian Hunter			fetched += 1
12748392b74bSAdrian Hunter		if fetched:
12758392b74bSAdrian Hunter			self.fetched += fetched
12768392b74bSAdrian Hunter			with self.fetch_count.get_lock():
12778392b74bSAdrian Hunter				self.fetch_count.value += fetched
12788392b74bSAdrian Hunter			self.head.value = self.local_head
12798392b74bSAdrian Hunter			self.fetched_event.set()
12808392b74bSAdrian Hunter
12818392b74bSAdrian Hunter	def Run(self):
12828392b74bSAdrian Hunter		while self.more:
12838392b74bSAdrian Hunter			target = self.WaitForTarget()
12848392b74bSAdrian Hunter			if target < 0:
12858392b74bSAdrian Hunter				break
12868392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
12878392b74bSAdrian Hunter			self.FetchBatch(batch_size)
12888392b74bSAdrian Hunter		self.fetching_done.value = True
12898392b74bSAdrian Hunter		self.fetched_event.set()
12908392b74bSAdrian Hunter
12918392b74bSAdrian Hunterdef SQLFetcherFn(*x):
12928392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
12938392b74bSAdrian Hunter	process.Run()
12948392b74bSAdrian Hunter
12958392b74bSAdrian Hunter# SQL data fetcher
12968392b74bSAdrian Hunter
12978392b74bSAdrian Hunterclass SQLFetcher(QObject):
12988392b74bSAdrian Hunter
12998392b74bSAdrian Hunter	done = Signal(object)
13008392b74bSAdrian Hunter
13018392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
13028392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
13038392b74bSAdrian Hunter		self.process_data = process_data
13048392b74bSAdrian Hunter		self.more = True
13058392b74bSAdrian Hunter		self.target = 0
13068392b74bSAdrian Hunter		self.last_target = 0
13078392b74bSAdrian Hunter		self.fetched = 0
13088392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
13098392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
13108392b74bSAdrian Hunter		self.head = Value(c_longlong)
13118392b74bSAdrian Hunter		self.tail = Value(c_longlong)
13128392b74bSAdrian Hunter		self.local_tail = 0
13138392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
13148392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
13158392b74bSAdrian Hunter		self.last_count = 0
13168392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
13178392b74bSAdrian Hunter		self.wait_event = Event()
13188392b74bSAdrian Hunter		self.fetched_event = Event()
13198392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
13208392b74bSAdrian 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))
13218392b74bSAdrian Hunter		self.process.start()
13228392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
13238392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
13248392b74bSAdrian Hunter		self.thread.start()
13258392b74bSAdrian Hunter
13268392b74bSAdrian Hunter	def Shutdown(self):
13278392b74bSAdrian Hunter		# Tell the thread and process to exit
13288392b74bSAdrian Hunter		self.process_target.value = -1
13298392b74bSAdrian Hunter		self.wait_event.set()
13308392b74bSAdrian Hunter		self.more = False
13318392b74bSAdrian Hunter		self.fetching_done.value = True
13328392b74bSAdrian Hunter		self.fetched_event.set()
13338392b74bSAdrian Hunter
13348392b74bSAdrian Hunter	def Thread(self):
13358392b74bSAdrian Hunter		if not self.more:
13368392b74bSAdrian Hunter			return True, 0
13378392b74bSAdrian Hunter		while True:
13388392b74bSAdrian Hunter			self.fetched_event.clear()
13398392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
13408392b74bSAdrian Hunter			if fetch_count != self.last_count:
13418392b74bSAdrian Hunter				break
13428392b74bSAdrian Hunter			if self.fetching_done.value:
13438392b74bSAdrian Hunter				self.more = False
13448392b74bSAdrian Hunter				return True, 0
13458392b74bSAdrian Hunter			self.fetched_event.wait()
13468392b74bSAdrian Hunter		count = fetch_count - self.last_count
13478392b74bSAdrian Hunter		self.last_count = fetch_count
13488392b74bSAdrian Hunter		self.fetched += count
13498392b74bSAdrian Hunter		return False, count
13508392b74bSAdrian Hunter
13518392b74bSAdrian Hunter	def Fetch(self, nr):
13528392b74bSAdrian Hunter		if not self.more:
13538392b74bSAdrian Hunter			# -1 inidcates there are no more
13548392b74bSAdrian Hunter			return -1
13558392b74bSAdrian Hunter		result = self.fetched
13568392b74bSAdrian Hunter		extra = result + nr - self.target
13578392b74bSAdrian Hunter		if extra > 0:
13588392b74bSAdrian Hunter			self.target += extra
13598392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
13608392b74bSAdrian Hunter			if self.process_target.value >= 0:
13618392b74bSAdrian Hunter				self.process_target.value = self.target
13628392b74bSAdrian Hunter			self.wait_event.set()
13638392b74bSAdrian Hunter		return result
13648392b74bSAdrian Hunter
13658392b74bSAdrian Hunter	def RemoveFromBuffer(self):
13668392b74bSAdrian Hunter		pos = self.local_tail
13678392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
13688392b74bSAdrian Hunter			pos = 0
1369beda0e72STony Jones		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
13708392b74bSAdrian Hunter		if n == 0:
13718392b74bSAdrian Hunter			pos = 0
1372beda0e72STony Jones			n = pickle.loads(self.buffer[0 : glb_nsz])
13738392b74bSAdrian Hunter		pos += glb_nsz
1374beda0e72STony Jones		obj = pickle.loads(self.buffer[pos : pos + n])
13758392b74bSAdrian Hunter		self.local_tail = pos + n
13768392b74bSAdrian Hunter		return obj
13778392b74bSAdrian Hunter
13788392b74bSAdrian Hunter	def ProcessData(self, count):
13798392b74bSAdrian Hunter		for i in xrange(count):
13808392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
13818392b74bSAdrian Hunter			self.process_data(obj)
13828392b74bSAdrian Hunter		self.tail.value = self.local_tail
13838392b74bSAdrian Hunter		self.wait_event.set()
13848392b74bSAdrian Hunter		self.done.emit(count)
13858392b74bSAdrian Hunter
13868392b74bSAdrian Hunter# Fetch more records bar
13878392b74bSAdrian Hunter
13888392b74bSAdrian Hunterclass FetchMoreRecordsBar():
13898392b74bSAdrian Hunter
13908392b74bSAdrian Hunter	def __init__(self, model, parent):
13918392b74bSAdrian Hunter		self.model = model
13928392b74bSAdrian Hunter
13938392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
13948392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
13958392b74bSAdrian Hunter
13968392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
13978392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
13988392b74bSAdrian Hunter		self.fetch_count.setValue(10)
13998392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
14008392b74bSAdrian Hunter
14018392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
14028392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
14038392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
14048392b74bSAdrian Hunter
14058392b74bSAdrian Hunter		self.progress = QProgressBar()
14068392b74bSAdrian Hunter		self.progress.setRange(0, 100)
14078392b74bSAdrian Hunter		self.progress.hide()
14088392b74bSAdrian Hunter
14098392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
14108392b74bSAdrian Hunter		self.done_label.hide()
14118392b74bSAdrian Hunter
14128392b74bSAdrian Hunter		self.spacer = QLabel("")
14138392b74bSAdrian Hunter
14148392b74bSAdrian Hunter		self.close_button = QToolButton()
14158392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
14168392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
14178392b74bSAdrian Hunter
14188392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
14198392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
14208392b74bSAdrian Hunter
14218392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
14228392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
14238392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
14248392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
14258392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
14268392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
14278392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
14288392b74bSAdrian Hunter
14298392b74bSAdrian Hunter		self.bar = QWidget()
143026688729SAdrian Hunter		self.bar.setLayout(self.hbox)
14318392b74bSAdrian Hunter		self.bar.show()
14328392b74bSAdrian Hunter
14338392b74bSAdrian Hunter		self.in_progress = False
14348392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
14358392b74bSAdrian Hunter
14368392b74bSAdrian Hunter		self.done = False
14378392b74bSAdrian Hunter
14388392b74bSAdrian Hunter		if not model.HasMoreRecords():
14398392b74bSAdrian Hunter			self.Done()
14408392b74bSAdrian Hunter
14418392b74bSAdrian Hunter	def Widget(self):
14428392b74bSAdrian Hunter		return self.bar
14438392b74bSAdrian Hunter
14448392b74bSAdrian Hunter	def Activate(self):
14458392b74bSAdrian Hunter		self.bar.show()
14468392b74bSAdrian Hunter		self.fetch.setFocus()
14478392b74bSAdrian Hunter
14488392b74bSAdrian Hunter	def Deactivate(self):
14498392b74bSAdrian Hunter		self.bar.hide()
14508392b74bSAdrian Hunter
14518392b74bSAdrian Hunter	def Enable(self, enable):
14528392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
14538392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
14548392b74bSAdrian Hunter
14558392b74bSAdrian Hunter	def Busy(self):
14568392b74bSAdrian Hunter		self.Enable(False)
14578392b74bSAdrian Hunter		self.fetch.hide()
14588392b74bSAdrian Hunter		self.spacer.hide()
14598392b74bSAdrian Hunter		self.progress.show()
14608392b74bSAdrian Hunter
14618392b74bSAdrian Hunter	def Idle(self):
14628392b74bSAdrian Hunter		self.in_progress = False
14638392b74bSAdrian Hunter		self.Enable(True)
14648392b74bSAdrian Hunter		self.progress.hide()
14658392b74bSAdrian Hunter		self.fetch.show()
14668392b74bSAdrian Hunter		self.spacer.show()
14678392b74bSAdrian Hunter
14688392b74bSAdrian Hunter	def Target(self):
14698392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
14708392b74bSAdrian Hunter
14718392b74bSAdrian Hunter	def Done(self):
14728392b74bSAdrian Hunter		self.done = True
14738392b74bSAdrian Hunter		self.Idle()
14748392b74bSAdrian Hunter		self.label.hide()
14758392b74bSAdrian Hunter		self.fetch_count.hide()
14768392b74bSAdrian Hunter		self.fetch.hide()
14778392b74bSAdrian Hunter		self.spacer.hide()
14788392b74bSAdrian Hunter		self.done_label.show()
14798392b74bSAdrian Hunter
14808392b74bSAdrian Hunter	def Progress(self, count):
14818392b74bSAdrian Hunter		if self.in_progress:
14828392b74bSAdrian Hunter			if count:
14838392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
14848392b74bSAdrian Hunter				if percent >= 100:
14858392b74bSAdrian Hunter					self.Idle()
14868392b74bSAdrian Hunter				else:
14878392b74bSAdrian Hunter					self.progress.setValue(percent)
14888392b74bSAdrian Hunter		if not count:
14898392b74bSAdrian Hunter			# Count value of zero means no more records
14908392b74bSAdrian Hunter			self.Done()
14918392b74bSAdrian Hunter
14928392b74bSAdrian Hunter	def FetchMoreRecords(self):
14938392b74bSAdrian Hunter		if self.done:
14948392b74bSAdrian Hunter			return
14958392b74bSAdrian Hunter		self.progress.setValue(0)
14968392b74bSAdrian Hunter		self.Busy()
14978392b74bSAdrian Hunter		self.in_progress = True
14988392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
14998392b74bSAdrian Hunter
150076099f98SAdrian Hunter# Brance data model level two item
150176099f98SAdrian Hunter
150276099f98SAdrian Hunterclass BranchLevelTwoItem():
150376099f98SAdrian Hunter
1504530e22fdSAdrian Hunter	def __init__(self, row, col, text, parent_item):
150576099f98SAdrian Hunter		self.row = row
150676099f98SAdrian Hunter		self.parent_item = parent_item
1507530e22fdSAdrian Hunter		self.data = [""] * (col + 1)
1508530e22fdSAdrian Hunter		self.data[col] = text
150976099f98SAdrian Hunter		self.level = 2
151076099f98SAdrian Hunter
151176099f98SAdrian Hunter	def getParentItem(self):
151276099f98SAdrian Hunter		return self.parent_item
151376099f98SAdrian Hunter
151476099f98SAdrian Hunter	def getRow(self):
151576099f98SAdrian Hunter		return self.row
151676099f98SAdrian Hunter
151776099f98SAdrian Hunter	def childCount(self):
151876099f98SAdrian Hunter		return 0
151976099f98SAdrian Hunter
152076099f98SAdrian Hunter	def hasChildren(self):
152176099f98SAdrian Hunter		return False
152276099f98SAdrian Hunter
152376099f98SAdrian Hunter	def getData(self, column):
152476099f98SAdrian Hunter		return self.data[column]
152576099f98SAdrian Hunter
152676099f98SAdrian Hunter# Brance data model level one item
152776099f98SAdrian Hunter
152876099f98SAdrian Hunterclass BranchLevelOneItem():
152976099f98SAdrian Hunter
153076099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
153176099f98SAdrian Hunter		self.glb = glb
153276099f98SAdrian Hunter		self.row = row
153376099f98SAdrian Hunter		self.parent_item = parent_item
153476099f98SAdrian Hunter		self.child_count = 0
153576099f98SAdrian Hunter		self.child_items = []
153676099f98SAdrian Hunter		self.data = data[1:]
153776099f98SAdrian Hunter		self.dbid = data[0]
153876099f98SAdrian Hunter		self.level = 1
153976099f98SAdrian Hunter		self.query_done = False
1540530e22fdSAdrian Hunter		self.br_col = len(self.data) - 1
154176099f98SAdrian Hunter
154276099f98SAdrian Hunter	def getChildItem(self, row):
154376099f98SAdrian Hunter		return self.child_items[row]
154476099f98SAdrian Hunter
154576099f98SAdrian Hunter	def getParentItem(self):
154676099f98SAdrian Hunter		return self.parent_item
154776099f98SAdrian Hunter
154876099f98SAdrian Hunter	def getRow(self):
154976099f98SAdrian Hunter		return self.row
155076099f98SAdrian Hunter
155176099f98SAdrian Hunter	def Select(self):
155276099f98SAdrian Hunter		self.query_done = True
155376099f98SAdrian Hunter
155476099f98SAdrian Hunter		if not self.glb.have_disassembler:
155576099f98SAdrian Hunter			return
155676099f98SAdrian Hunter
155776099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
155876099f98SAdrian Hunter
155976099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
156076099f98SAdrian Hunter				  " FROM samples"
156176099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
156276099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
156376099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
156476099f98SAdrian Hunter		if not query.next():
156576099f98SAdrian Hunter			return
156676099f98SAdrian Hunter		cpu = query.value(0)
156776099f98SAdrian Hunter		dso = query.value(1)
156876099f98SAdrian Hunter		sym = query.value(2)
156976099f98SAdrian Hunter		if dso == 0 or sym == 0:
157076099f98SAdrian Hunter			return
157176099f98SAdrian Hunter		off = query.value(3)
157276099f98SAdrian Hunter		short_name = query.value(4)
157376099f98SAdrian Hunter		long_name = query.value(5)
157476099f98SAdrian Hunter		build_id = query.value(6)
157576099f98SAdrian Hunter		sym_start = query.value(7)
157676099f98SAdrian Hunter		ip = query.value(8)
157776099f98SAdrian Hunter
157876099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
157976099f98SAdrian Hunter				  " FROM samples"
158076099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
158176099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
158276099f98SAdrian Hunter				  " ORDER BY samples.id"
158376099f98SAdrian Hunter				  " LIMIT 1")
158476099f98SAdrian Hunter		if not query.next():
158576099f98SAdrian Hunter			return
158676099f98SAdrian Hunter		if query.value(0) != dso:
158776099f98SAdrian Hunter			# Cannot disassemble from one dso to another
158876099f98SAdrian Hunter			return
158976099f98SAdrian Hunter		bsym = query.value(1)
159076099f98SAdrian Hunter		boff = query.value(2)
159176099f98SAdrian Hunter		bsym_start = query.value(3)
159276099f98SAdrian Hunter		if bsym == 0:
159376099f98SAdrian Hunter			return
159476099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
159576099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
159676099f98SAdrian Hunter			return
159776099f98SAdrian Hunter
159876099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
159976099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
160076099f98SAdrian Hunter		if not f:
160176099f98SAdrian Hunter			return
160276099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
160376099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
160476099f98SAdrian Hunter
160576099f98SAdrian Hunter		buf_sz = tot + 16
160676099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
160776099f98SAdrian Hunter		f.seek(sym_start + off)
160876099f98SAdrian Hunter		buf.value = f.read(buf_sz)
160976099f98SAdrian Hunter		buf_ptr = addressof(buf)
161076099f98SAdrian Hunter		i = 0
161176099f98SAdrian Hunter		while tot > 0:
161276099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
161376099f98SAdrian Hunter			if cnt:
161476099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
161576099f98SAdrian Hunter				for k in xrange(cnt):
161676099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
161776099f98SAdrian Hunter					i += 1
161876099f98SAdrian Hunter				while k < 15:
161976099f98SAdrian Hunter					byte_str += "   "
162076099f98SAdrian Hunter					k += 1
1621530e22fdSAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
162276099f98SAdrian Hunter				self.child_count += 1
162376099f98SAdrian Hunter			else:
162476099f98SAdrian Hunter				return
162576099f98SAdrian Hunter			buf_ptr += cnt
162676099f98SAdrian Hunter			tot -= cnt
162776099f98SAdrian Hunter			buf_sz -= cnt
162876099f98SAdrian Hunter			ip += cnt
162976099f98SAdrian Hunter
163076099f98SAdrian Hunter	def childCount(self):
163176099f98SAdrian Hunter		if not self.query_done:
163276099f98SAdrian Hunter			self.Select()
163376099f98SAdrian Hunter			if not self.child_count:
163476099f98SAdrian Hunter				return -1
163576099f98SAdrian Hunter		return self.child_count
163676099f98SAdrian Hunter
163776099f98SAdrian Hunter	def hasChildren(self):
163876099f98SAdrian Hunter		if not self.query_done:
163976099f98SAdrian Hunter			return True
164076099f98SAdrian Hunter		return self.child_count > 0
164176099f98SAdrian Hunter
164276099f98SAdrian Hunter	def getData(self, column):
164376099f98SAdrian Hunter		return self.data[column]
164476099f98SAdrian Hunter
164576099f98SAdrian Hunter# Brance data model root item
164676099f98SAdrian Hunter
164776099f98SAdrian Hunterclass BranchRootItem():
164876099f98SAdrian Hunter
164976099f98SAdrian Hunter	def __init__(self):
165076099f98SAdrian Hunter		self.child_count = 0
165176099f98SAdrian Hunter		self.child_items = []
165276099f98SAdrian Hunter		self.level = 0
165376099f98SAdrian Hunter
165476099f98SAdrian Hunter	def getChildItem(self, row):
165576099f98SAdrian Hunter		return self.child_items[row]
165676099f98SAdrian Hunter
165776099f98SAdrian Hunter	def getParentItem(self):
165876099f98SAdrian Hunter		return None
165976099f98SAdrian Hunter
166076099f98SAdrian Hunter	def getRow(self):
166176099f98SAdrian Hunter		return 0
166276099f98SAdrian Hunter
166376099f98SAdrian Hunter	def childCount(self):
166476099f98SAdrian Hunter		return self.child_count
166576099f98SAdrian Hunter
166676099f98SAdrian Hunter	def hasChildren(self):
166776099f98SAdrian Hunter		return self.child_count > 0
166876099f98SAdrian Hunter
166976099f98SAdrian Hunter	def getData(self, column):
167076099f98SAdrian Hunter		return ""
167176099f98SAdrian Hunter
1672530e22fdSAdrian Hunter# Calculate instructions per cycle
1673530e22fdSAdrian Hunter
1674530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt):
1675530e22fdSAdrian Hunter	if cyc_cnt and insn_cnt:
1676530e22fdSAdrian Hunter		ipc = Decimal(float(insn_cnt) / cyc_cnt)
1677530e22fdSAdrian Hunter		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
1678530e22fdSAdrian Hunter	else:
1679530e22fdSAdrian Hunter		ipc = "0"
1680530e22fdSAdrian Hunter	return ipc
1681530e22fdSAdrian Hunter
168276099f98SAdrian Hunter# Branch data preparation
168376099f98SAdrian Hunter
1684530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data):
1685530e22fdSAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
1686530e22fdSAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
1687530e22fdSAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
1688530e22fdSAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
1689530e22fdSAdrian Hunter
1690530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data):
1691530e22fdSAdrian Hunter	insn_cnt = query.value(16)
1692530e22fdSAdrian Hunter	cyc_cnt = query.value(17)
1693530e22fdSAdrian Hunter	ipc = CalcIPC(cyc_cnt, insn_cnt)
1694530e22fdSAdrian Hunter	data.append(insn_cnt)
1695530e22fdSAdrian Hunter	data.append(cyc_cnt)
1696530e22fdSAdrian Hunter	data.append(ipc)
1697530e22fdSAdrian Hunter
169876099f98SAdrian Hunterdef BranchDataPrep(query):
169976099f98SAdrian Hunter	data = []
170076099f98SAdrian Hunter	for i in xrange(0, 8):
170176099f98SAdrian Hunter		data.append(query.value(i))
1702530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
170376099f98SAdrian Hunter	return data
170476099f98SAdrian Hunter
17058453c936SAdrian Hunterdef BranchDataPrepWA(query):
17068453c936SAdrian Hunter	data = []
17078453c936SAdrian Hunter	data.append(query.value(0))
17088453c936SAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
17098453c936SAdrian Hunter	data.append("{:>19}".format(query.value(1)))
17108453c936SAdrian Hunter	for i in xrange(2, 8):
17118453c936SAdrian Hunter		data.append(query.value(i))
1712530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1713530e22fdSAdrian Hunter	return data
1714530e22fdSAdrian Hunter
1715530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query):
1716530e22fdSAdrian Hunter	data = []
1717530e22fdSAdrian Hunter	for i in xrange(0, 8):
1718530e22fdSAdrian Hunter		data.append(query.value(i))
1719530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1720530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
1721530e22fdSAdrian Hunter	return data
1722530e22fdSAdrian Hunter
1723530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query):
1724530e22fdSAdrian Hunter	data = []
1725530e22fdSAdrian Hunter	data.append(query.value(0))
1726530e22fdSAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
1727530e22fdSAdrian Hunter	data.append("{:>19}".format(query.value(1)))
1728530e22fdSAdrian Hunter	for i in xrange(2, 8):
1729530e22fdSAdrian Hunter		data.append(query.value(i))
1730530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
1731530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
17328453c936SAdrian Hunter	return data
17338453c936SAdrian Hunter
173476099f98SAdrian Hunter# Branch data model
173576099f98SAdrian Hunter
173676099f98SAdrian Hunterclass BranchModel(TreeModel):
173776099f98SAdrian Hunter
173876099f98SAdrian Hunter	progress = Signal(object)
173976099f98SAdrian Hunter
174076099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
17414a0979d4SAdrian Hunter		super(BranchModel, self).__init__(glb, None, parent)
174276099f98SAdrian Hunter		self.event_id = event_id
174376099f98SAdrian Hunter		self.more = True
174476099f98SAdrian Hunter		self.populated = 0
1745530e22fdSAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
1746530e22fdSAdrian Hunter		if self.have_ipc:
1747530e22fdSAdrian Hunter			select_ipc = ", insn_count, cyc_count"
1748530e22fdSAdrian Hunter			prep_fn = BranchDataWithIPCPrep
1749530e22fdSAdrian Hunter			prep_wa_fn = BranchDataWithIPCPrepWA
1750530e22fdSAdrian Hunter		else:
1751530e22fdSAdrian Hunter			select_ipc = ""
1752530e22fdSAdrian Hunter			prep_fn = BranchDataPrep
1753530e22fdSAdrian Hunter			prep_wa_fn = BranchDataPrepWA
175476099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
175576099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
175676099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
175776099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
1758530e22fdSAdrian Hunter			+ select_ipc +
175976099f98SAdrian Hunter			" FROM samples"
176076099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
176176099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
176276099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
176376099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
176476099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
176576099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
176676099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
176776099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
176876099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
176976099f98SAdrian Hunter			" ORDER BY samples.id"
177076099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
17718453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
1772530e22fdSAdrian Hunter			prep = prep_fn
17738453c936SAdrian Hunter		else:
1774530e22fdSAdrian Hunter			prep = prep_wa_fn
17758453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
177676099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
177776099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
177876099f98SAdrian Hunter
1779a448ba23SAdrian Hunter	def GetRoot(self):
1780a448ba23SAdrian Hunter		return BranchRootItem()
1781a448ba23SAdrian Hunter
178276099f98SAdrian Hunter	def columnCount(self, parent=None):
1783530e22fdSAdrian Hunter		if self.have_ipc:
1784530e22fdSAdrian Hunter			return 11
1785530e22fdSAdrian Hunter		else:
178676099f98SAdrian Hunter			return 8
178776099f98SAdrian Hunter
178876099f98SAdrian Hunter	def columnHeader(self, column):
1789530e22fdSAdrian Hunter		if self.have_ipc:
1790530e22fdSAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
1791530e22fdSAdrian Hunter		else:
179276099f98SAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
179376099f98SAdrian Hunter
179476099f98SAdrian Hunter	def columnFont(self, column):
1795530e22fdSAdrian Hunter		if self.have_ipc:
1796530e22fdSAdrian Hunter			br_col = 10
1797530e22fdSAdrian Hunter		else:
1798530e22fdSAdrian Hunter			br_col = 7
1799530e22fdSAdrian Hunter		if column != br_col:
180076099f98SAdrian Hunter			return None
180176099f98SAdrian Hunter		return QFont("Monospace")
180276099f98SAdrian Hunter
180376099f98SAdrian Hunter	def DisplayData(self, item, index):
180476099f98SAdrian Hunter		if item.level == 1:
180576099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
180676099f98SAdrian Hunter		return item.getData(index.column())
180776099f98SAdrian Hunter
180876099f98SAdrian Hunter	def AddSample(self, data):
180976099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
181076099f98SAdrian Hunter		self.root.child_items.append(child)
181176099f98SAdrian Hunter		self.populated += 1
181276099f98SAdrian Hunter
181376099f98SAdrian Hunter	def Update(self, fetched):
181476099f98SAdrian Hunter		if not fetched:
181576099f98SAdrian Hunter			self.more = False
181676099f98SAdrian Hunter			self.progress.emit(0)
181776099f98SAdrian Hunter		child_count = self.root.child_count
181876099f98SAdrian Hunter		count = self.populated - child_count
181976099f98SAdrian Hunter		if count > 0:
182076099f98SAdrian Hunter			parent = QModelIndex()
182176099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
182276099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
182376099f98SAdrian Hunter			self.root.child_count += count
182476099f98SAdrian Hunter			self.endInsertRows()
182576099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
182676099f98SAdrian Hunter
182776099f98SAdrian Hunter	def FetchMoreRecords(self, count):
182876099f98SAdrian Hunter		current = self.root.child_count
182976099f98SAdrian Hunter		if self.more:
183076099f98SAdrian Hunter			self.fetcher.Fetch(count)
183176099f98SAdrian Hunter		else:
183276099f98SAdrian Hunter			self.progress.emit(0)
183376099f98SAdrian Hunter		return current
183476099f98SAdrian Hunter
183576099f98SAdrian Hunter	def HasMoreRecords(self):
183676099f98SAdrian Hunter		return self.more
183776099f98SAdrian Hunter
18380bf0947aSAdrian Hunter# Report Variables
18390bf0947aSAdrian Hunter
18400bf0947aSAdrian Hunterclass ReportVars():
18410bf0947aSAdrian Hunter
1842cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1843947cc38dSAdrian Hunter		self.name = name
18440bf0947aSAdrian Hunter		self.where_clause = where_clause
1845cd358012SAdrian Hunter		self.limit = limit
18460bf0947aSAdrian Hunter
18470bf0947aSAdrian Hunter	def UniqueId(self):
1848cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
18490bf0947aSAdrian Hunter
185076099f98SAdrian Hunter# Branch window
185176099f98SAdrian Hunter
185276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
185376099f98SAdrian Hunter
1854947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
185576099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
185676099f98SAdrian Hunter
18570bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
185876099f98SAdrian Hunter
18590bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
186076099f98SAdrian Hunter
186176099f98SAdrian Hunter		self.view = QTreeView()
186276099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
186396c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
186496c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
186576099f98SAdrian Hunter		self.view.setModel(self.model)
186676099f98SAdrian Hunter
186776099f98SAdrian Hunter		self.ResizeColumnsToContents()
186876099f98SAdrian Hunter
18699bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
18709bc4e4bfSAdrian Hunter
187176099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
187276099f98SAdrian Hunter
187376099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
187476099f98SAdrian Hunter
187576099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
187676099f98SAdrian Hunter
187776099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
187876099f98SAdrian Hunter
187976099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
188076099f98SAdrian Hunter
1881947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
188276099f98SAdrian Hunter
188376099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
188476099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
188576099f98SAdrian Hunter		# so implement a crude alternative
188676099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
188776099f98SAdrian Hunter		font = self.view.font()
188876099f98SAdrian Hunter		metrics = QFontMetrics(font)
188976099f98SAdrian Hunter		max = 0
189076099f98SAdrian Hunter		for row in xrange(n):
189176099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
189276099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
189376099f98SAdrian Hunter			max = len if len > max else max
189476099f98SAdrian Hunter		val = self.model.columnHeader(column)
189576099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
189676099f98SAdrian Hunter		max = len if len > max else max
189776099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
189876099f98SAdrian Hunter
189976099f98SAdrian Hunter	def ResizeColumnsToContents(self):
190076099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
190176099f98SAdrian Hunter		if n < 1:
190276099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
190376099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
190476099f98SAdrian Hunter			return
190576099f98SAdrian Hunter		columns = self.model.columnCount()
190676099f98SAdrian Hunter		for i in xrange(columns):
190776099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
190876099f98SAdrian Hunter
190976099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
191076099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
191176099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
191276099f98SAdrian Hunter		self.ResizeColumnsToContents()
191376099f98SAdrian Hunter
191476099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
191576099f98SAdrian Hunter		self.view.setFocus()
191676099f98SAdrian Hunter		self.find_bar.Busy()
191776099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
191876099f98SAdrian Hunter
191976099f98SAdrian Hunter	def FindDone(self, row):
192076099f98SAdrian Hunter		self.find_bar.Idle()
192176099f98SAdrian Hunter		if row >= 0:
192276099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
192376099f98SAdrian Hunter		else:
192476099f98SAdrian Hunter			self.find_bar.NotFound()
192576099f98SAdrian Hunter
19261c3ca1b3SAdrian Hunter# Line edit data item
19271c3ca1b3SAdrian Hunter
19281c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
19291c3ca1b3SAdrian Hunter
1930cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
19311c3ca1b3SAdrian Hunter		self.glb = glb
19321c3ca1b3SAdrian Hunter		self.label = label
19331c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
19341c3ca1b3SAdrian Hunter		self.parent = parent
19351c3ca1b3SAdrian Hunter		self.id = id
19361c3ca1b3SAdrian Hunter
1937cd358012SAdrian Hunter		self.value = default
19381c3ca1b3SAdrian Hunter
1939cd358012SAdrian Hunter		self.widget = QLineEdit(default)
19401c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
19411c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
19421c3ca1b3SAdrian Hunter		self.red = False
19431c3ca1b3SAdrian Hunter		self.error = ""
19441c3ca1b3SAdrian Hunter		self.validated = True
19451c3ca1b3SAdrian Hunter
19461c3ca1b3SAdrian Hunter		if placeholder_text:
19471c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
19481c3ca1b3SAdrian Hunter
19491c3ca1b3SAdrian Hunter	def TurnTextRed(self):
19501c3ca1b3SAdrian Hunter		if not self.red:
19511c3ca1b3SAdrian Hunter			palette = QPalette()
19521c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
19531c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
19541c3ca1b3SAdrian Hunter			self.red = True
19551c3ca1b3SAdrian Hunter
19561c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
19571c3ca1b3SAdrian Hunter		if self.red:
19581c3ca1b3SAdrian Hunter			palette = QPalette()
19591c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
19601c3ca1b3SAdrian Hunter			self.red = False
19611c3ca1b3SAdrian Hunter
19621c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
19631c3ca1b3SAdrian Hunter		self.value = ""
19641c3ca1b3SAdrian Hunter		self.TurnTextRed()
19651c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
19661c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
19671c3ca1b3SAdrian Hunter
19681c3ca1b3SAdrian Hunter	def Invalidate(self):
19691c3ca1b3SAdrian Hunter		self.validated = False
19701c3ca1b3SAdrian Hunter
19711c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
19721c3ca1b3SAdrian Hunter		self.value = input_string.strip()
19731c3ca1b3SAdrian Hunter
19741c3ca1b3SAdrian Hunter	def Validate(self):
19751c3ca1b3SAdrian Hunter		self.validated = True
19761c3ca1b3SAdrian Hunter		self.error = ""
19771c3ca1b3SAdrian Hunter		self.TurnTextNormal()
19781c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
19791c3ca1b3SAdrian Hunter		input_string = self.widget.text()
19801c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
19811c3ca1b3SAdrian Hunter			self.value = ""
19821c3ca1b3SAdrian Hunter			return
19831c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
19841c3ca1b3SAdrian Hunter
19851c3ca1b3SAdrian Hunter	def IsValid(self):
19861c3ca1b3SAdrian Hunter		if not self.validated:
19871c3ca1b3SAdrian Hunter			self.Validate()
19881c3ca1b3SAdrian Hunter		if len(self.error):
19891c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
19901c3ca1b3SAdrian Hunter			return False
19911c3ca1b3SAdrian Hunter		return True
19921c3ca1b3SAdrian Hunter
19931c3ca1b3SAdrian Hunter	def IsNumber(self, value):
19941c3ca1b3SAdrian Hunter		try:
19951c3ca1b3SAdrian Hunter			x = int(value)
19961c3ca1b3SAdrian Hunter		except:
19971c3ca1b3SAdrian Hunter			x = 0
19981c3ca1b3SAdrian Hunter		return str(x) == value
19991c3ca1b3SAdrian Hunter
20001c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
20011c3ca1b3SAdrian Hunter
20021c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
20031c3ca1b3SAdrian Hunter
20041c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
20051c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
20061c3ca1b3SAdrian Hunter
20071c3ca1b3SAdrian Hunter		self.column_name = column_name
20081c3ca1b3SAdrian Hunter
20091c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
20101c3ca1b3SAdrian Hunter		singles = []
20111c3ca1b3SAdrian Hunter		ranges = []
20121c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
20131c3ca1b3SAdrian Hunter			if "-" in value:
20141c3ca1b3SAdrian Hunter				vrange = value.split("-")
20151c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
20161c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
20171c3ca1b3SAdrian Hunter				ranges.append(vrange)
20181c3ca1b3SAdrian Hunter			else:
20191c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
20201c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
20211c3ca1b3SAdrian Hunter				singles.append(value)
20221c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
20231c3ca1b3SAdrian Hunter		if len(singles):
20241c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
20251c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
20261c3ca1b3SAdrian Hunter
2027cd358012SAdrian Hunter# Positive integer dialog data item
2028cd358012SAdrian Hunter
2029cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
2030cd358012SAdrian Hunter
2031cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
2032cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
2033cd358012SAdrian Hunter
2034cd358012SAdrian Hunter	def DoValidate(self, input_string):
2035cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
2036cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2037cd358012SAdrian Hunter		value = int(input_string.strip())
2038cd358012SAdrian Hunter		if value <= 0:
2039cd358012SAdrian Hunter			return self.InvalidValue(input_string)
2040cd358012SAdrian Hunter		self.value = str(value)
2041cd358012SAdrian Hunter
20421c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
20431c3ca1b3SAdrian Hunter
20441c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
20451c3ca1b3SAdrian Hunter
20461c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
20471c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
20481c3ca1b3SAdrian Hunter
20491c3ca1b3SAdrian Hunter		self.table_name = table_name
20501c3ca1b3SAdrian Hunter		self.match_column = match_column
20511c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
20521c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
20531c3ca1b3SAdrian Hunter
20541c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
20551c3ca1b3SAdrian Hunter		ids = []
20561c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
20571c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
20581c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
20591c3ca1b3SAdrian Hunter		if ret:
20601c3ca1b3SAdrian Hunter			while query.next():
20611c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
20621c3ca1b3SAdrian Hunter		return ids
20631c3ca1b3SAdrian Hunter
20641c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
20651c3ca1b3SAdrian Hunter		all_ids = []
20661c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
20671c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
20681c3ca1b3SAdrian Hunter			if len(ids):
20691c3ca1b3SAdrian Hunter				all_ids.extend(ids)
20701c3ca1b3SAdrian Hunter			else:
20711c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
20721c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
20731c3ca1b3SAdrian Hunter		if self.column_name2:
20741c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
20751c3ca1b3SAdrian Hunter
20761c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
20771c3ca1b3SAdrian Hunter
20781c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
20791c3ca1b3SAdrian Hunter
20801c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
20811c3ca1b3SAdrian Hunter		self.column_name = column_name
20821c3ca1b3SAdrian Hunter
20831c3ca1b3SAdrian Hunter		self.last_id = 0
20841c3ca1b3SAdrian Hunter		self.first_time = 0
20851c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
20861c3ca1b3SAdrian Hunter
20871c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
20881c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
20891c3ca1b3SAdrian Hunter		if query.next():
20901c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
20911c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
20921c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
20931c3ca1b3SAdrian Hunter		if query.next():
20941c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
20951c3ca1b3SAdrian Hunter		if placeholder_text:
20961c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
20971c3ca1b3SAdrian Hunter
20981c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
20991c3ca1b3SAdrian Hunter
21001c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
21011c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
21021c3ca1b3SAdrian Hunter		if query.next():
21031c3ca1b3SAdrian Hunter			return True, int(query.value(0))
21041c3ca1b3SAdrian Hunter		else:
21051c3ca1b3SAdrian Hunter			return False, 0
21061c3ca1b3SAdrian Hunter
21071c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
21081c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
21091c3ca1b3SAdrian Hunter		while True:
21101c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
21111c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
21121c3ca1b3SAdrian Hunter			if not query.next():
21131c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
21141c3ca1b3SAdrian Hunter				if not ok:
21151c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
21161c3ca1b3SAdrian Hunter					if not ok:
21171c3ca1b3SAdrian Hunter						return str(higher_id)
21181c3ca1b3SAdrian Hunter				next_id = dbid
21191c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
21201c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
21211c3ca1b3SAdrian Hunter			if get_floor:
21221c3ca1b3SAdrian Hunter				if target_time > next_time:
21231c3ca1b3SAdrian Hunter					lower_id = next_id
21241c3ca1b3SAdrian Hunter				else:
21251c3ca1b3SAdrian Hunter					higher_id = next_id
21261c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
21271c3ca1b3SAdrian Hunter					return str(higher_id)
21281c3ca1b3SAdrian Hunter			else:
21291c3ca1b3SAdrian Hunter				if target_time >= next_time:
21301c3ca1b3SAdrian Hunter					lower_id = next_id
21311c3ca1b3SAdrian Hunter				else:
21321c3ca1b3SAdrian Hunter					higher_id = next_id
21331c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
21341c3ca1b3SAdrian Hunter					return str(lower_id)
21351c3ca1b3SAdrian Hunter
21361c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
21371c3ca1b3SAdrian Hunter		mult = 1
21381c3ca1b3SAdrian Hunter		suffix = val[-2:]
21391c3ca1b3SAdrian Hunter		if suffix == "ms":
21401c3ca1b3SAdrian Hunter			mult = 1000000
21411c3ca1b3SAdrian Hunter		elif suffix == "us":
21421c3ca1b3SAdrian Hunter			mult = 1000
21431c3ca1b3SAdrian Hunter		elif suffix == "ns":
21441c3ca1b3SAdrian Hunter			mult = 1
21451c3ca1b3SAdrian Hunter		else:
21461c3ca1b3SAdrian Hunter			return val
21471c3ca1b3SAdrian Hunter		val = val[:-2].strip()
21481c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
21491c3ca1b3SAdrian Hunter			return val
21501c3ca1b3SAdrian Hunter		val = int(val) * mult
21511c3ca1b3SAdrian Hunter		if val >= 0:
21521c3ca1b3SAdrian Hunter			val += self.first_time
21531c3ca1b3SAdrian Hunter		else:
21541c3ca1b3SAdrian Hunter			val += self.last_time
21551c3ca1b3SAdrian Hunter		return str(val)
21561c3ca1b3SAdrian Hunter
21571c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
21581c3ca1b3SAdrian Hunter		if vrange[0] == "":
21591c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
21601c3ca1b3SAdrian Hunter		if vrange[1] == "":
21611c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
21621c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
21631c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
21641c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
21651c3ca1b3SAdrian Hunter			return False
21661c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
21671c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
21681c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
21691c3ca1b3SAdrian Hunter			return False
21701c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
21711c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
21721c3ca1b3SAdrian Hunter		return True
21731c3ca1b3SAdrian Hunter
21741c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
21751c3ca1b3SAdrian Hunter		n = value.count("-")
21761c3ca1b3SAdrian Hunter		if n == 1:
21771c3ca1b3SAdrian Hunter			pass
21781c3ca1b3SAdrian Hunter		elif n == 2:
21791c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
21801c3ca1b3SAdrian Hunter				n = 1
21811c3ca1b3SAdrian Hunter		elif n == 3:
21821c3ca1b3SAdrian Hunter			n = 2
21831c3ca1b3SAdrian Hunter		else:
21841c3ca1b3SAdrian Hunter			return False
21851c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
21861c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
21871c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
21881c3ca1b3SAdrian Hunter			ranges.append(vrange)
21891c3ca1b3SAdrian Hunter			return True
21901c3ca1b3SAdrian Hunter		return False
21911c3ca1b3SAdrian Hunter
21921c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
21931c3ca1b3SAdrian Hunter		ranges = []
21941c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
21951c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
21961c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
21971c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
21981c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
21991c3ca1b3SAdrian Hunter
22000924cd68SAdrian Hunter# Report Dialog Base
2201210cf1f9SAdrian Hunter
22020924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
2203210cf1f9SAdrian Hunter
22040924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
22050924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
2206210cf1f9SAdrian Hunter
2207210cf1f9SAdrian Hunter		self.glb = glb
2208210cf1f9SAdrian Hunter
22090bf0947aSAdrian Hunter		self.report_vars = ReportVars()
2210210cf1f9SAdrian Hunter
22110924cd68SAdrian Hunter		self.setWindowTitle(title)
2212210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
2213210cf1f9SAdrian Hunter
22141c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
2215210cf1f9SAdrian Hunter
22160924cd68SAdrian Hunter		self.partial = partial
22170924cd68SAdrian Hunter
2218210cf1f9SAdrian Hunter		self.grid = QGridLayout()
2219210cf1f9SAdrian Hunter
2220210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
2221210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
2222210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
2223210cf1f9SAdrian Hunter
2224210cf1f9SAdrian Hunter		self.status = QLabel()
2225210cf1f9SAdrian Hunter
2226210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
2227210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
2228210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
2229210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2230210cf1f9SAdrian Hunter
2231210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
2232210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
2233210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
2234210cf1f9SAdrian Hunter
2235210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
2236210cf1f9SAdrian Hunter		#self.hbox.addStretch()
2237210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
2238210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
2239210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
2240210cf1f9SAdrian Hunter
2241210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
2242210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
2243210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
2244210cf1f9SAdrian Hunter
224526688729SAdrian Hunter		self.setLayout(self.vbox)
2246210cf1f9SAdrian Hunter
2247210cf1f9SAdrian Hunter	def Ok(self):
22480bf0947aSAdrian Hunter		vars = self.report_vars
22491c3ca1b3SAdrian Hunter		for d in self.data_items:
22501c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
22511c3ca1b3SAdrian Hunter				vars.name = d.value
2252947cc38dSAdrian Hunter		if not vars.name:
2253210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2254210cf1f9SAdrian Hunter			return
2255210cf1f9SAdrian Hunter		for d in self.data_items:
2256210cf1f9SAdrian Hunter			if not d.IsValid():
2257210cf1f9SAdrian Hunter				return
2258210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2259cd358012SAdrian Hunter			if d.id == "LIMIT":
2260cd358012SAdrian Hunter				vars.limit = d.value
2261cd358012SAdrian Hunter			elif len(d.value):
22620bf0947aSAdrian Hunter				if len(vars.where_clause):
22630bf0947aSAdrian Hunter					vars.where_clause += " AND "
22640bf0947aSAdrian Hunter				vars.where_clause += d.value
22650bf0947aSAdrian Hunter		if len(vars.where_clause):
22660924cd68SAdrian Hunter			if self.partial:
22670bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2268210cf1f9SAdrian Hunter			else:
22690bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
2270210cf1f9SAdrian Hunter		self.accept()
2271210cf1f9SAdrian Hunter
2272210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2273210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2274210cf1f9SAdrian Hunter
2275210cf1f9SAdrian Hunter	def ClearMessage(self):
2276210cf1f9SAdrian Hunter		self.status.setText("")
2277210cf1f9SAdrian Hunter
22780924cd68SAdrian Hunter# Selected branch report creation dialog
22790924cd68SAdrian Hunter
22800924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
22810924cd68SAdrian Hunter
22820924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
22830924cd68SAdrian Hunter		title = "Selected Branches"
22841c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
22851c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
22861c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
22871c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
22881c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
22891c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
22901c3ca1b3SAdrian 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),
22911c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
22921c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
22930924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
22940924cd68SAdrian Hunter
229576099f98SAdrian Hunter# Event list
229676099f98SAdrian Hunter
229776099f98SAdrian Hunterdef GetEventList(db):
229876099f98SAdrian Hunter	events = []
229976099f98SAdrian Hunter	query = QSqlQuery(db)
230076099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
230176099f98SAdrian Hunter	while query.next():
230276099f98SAdrian Hunter		events.append(query.value(0))
230376099f98SAdrian Hunter	return events
230476099f98SAdrian Hunter
2305655cb952SAdrian Hunter# Is a table selectable
2306655cb952SAdrian Hunter
2307530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"):
2308655cb952SAdrian Hunter	query = QSqlQuery(db)
2309655cb952SAdrian Hunter	try:
2310530e22fdSAdrian Hunter		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
2311655cb952SAdrian Hunter	except:
2312655cb952SAdrian Hunter		return False
2313655cb952SAdrian Hunter	return True
2314655cb952SAdrian Hunter
23158392b74bSAdrian Hunter# SQL table data model item
23168392b74bSAdrian Hunter
23178392b74bSAdrian Hunterclass SQLTableItem():
23188392b74bSAdrian Hunter
23198392b74bSAdrian Hunter	def __init__(self, row, data):
23208392b74bSAdrian Hunter		self.row = row
23218392b74bSAdrian Hunter		self.data = data
23228392b74bSAdrian Hunter
23238392b74bSAdrian Hunter	def getData(self, column):
23248392b74bSAdrian Hunter		return self.data[column]
23258392b74bSAdrian Hunter
23268392b74bSAdrian Hunter# SQL table data model
23278392b74bSAdrian Hunter
23288392b74bSAdrian Hunterclass SQLTableModel(TableModel):
23298392b74bSAdrian Hunter
23308392b74bSAdrian Hunter	progress = Signal(object)
23318392b74bSAdrian Hunter
23328c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
23338392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
23348392b74bSAdrian Hunter		self.glb = glb
23358392b74bSAdrian Hunter		self.more = True
23368392b74bSAdrian Hunter		self.populated = 0
23378c90fef9SAdrian Hunter		self.column_headers = column_headers
23388453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample)
23398392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
23408392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
23418392b74bSAdrian Hunter
23428392b74bSAdrian Hunter	def DisplayData(self, item, index):
23438392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
23448392b74bSAdrian Hunter		return item.getData(index.column())
23458392b74bSAdrian Hunter
23468392b74bSAdrian Hunter	def AddSample(self, data):
23478392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
23488392b74bSAdrian Hunter		self.child_items.append(child)
23498392b74bSAdrian Hunter		self.populated += 1
23508392b74bSAdrian Hunter
23518392b74bSAdrian Hunter	def Update(self, fetched):
23528392b74bSAdrian Hunter		if not fetched:
23538392b74bSAdrian Hunter			self.more = False
23548392b74bSAdrian Hunter			self.progress.emit(0)
23558392b74bSAdrian Hunter		child_count = self.child_count
23568392b74bSAdrian Hunter		count = self.populated - child_count
23578392b74bSAdrian Hunter		if count > 0:
23588392b74bSAdrian Hunter			parent = QModelIndex()
23598392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
23608392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
23618392b74bSAdrian Hunter			self.child_count += count
23628392b74bSAdrian Hunter			self.endInsertRows()
23638392b74bSAdrian Hunter			self.progress.emit(self.child_count)
23648392b74bSAdrian Hunter
23658392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
23668392b74bSAdrian Hunter		current = self.child_count
23678392b74bSAdrian Hunter		if self.more:
23688392b74bSAdrian Hunter			self.fetcher.Fetch(count)
23698392b74bSAdrian Hunter		else:
23708392b74bSAdrian Hunter			self.progress.emit(0)
23718392b74bSAdrian Hunter		return current
23728392b74bSAdrian Hunter
23738392b74bSAdrian Hunter	def HasMoreRecords(self):
23748392b74bSAdrian Hunter		return self.more
23758392b74bSAdrian Hunter
23768c90fef9SAdrian Hunter	def columnCount(self, parent=None):
23778c90fef9SAdrian Hunter		return len(self.column_headers)
23788c90fef9SAdrian Hunter
23798c90fef9SAdrian Hunter	def columnHeader(self, column):
23808c90fef9SAdrian Hunter		return self.column_headers[column]
23818c90fef9SAdrian Hunter
23828453c936SAdrian Hunter	def SQLTableDataPrep(self, query, count):
23838453c936SAdrian Hunter		data = []
23848453c936SAdrian Hunter		for i in xrange(count):
23858453c936SAdrian Hunter			data.append(query.value(i))
23868453c936SAdrian Hunter		return data
23878453c936SAdrian Hunter
23888392b74bSAdrian Hunter# SQL automatic table data model
23898392b74bSAdrian Hunter
23908392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
23918392b74bSAdrian Hunter
23928392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
23938392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
23948392b74bSAdrian Hunter		if table_name == "comm_threads_view":
23958392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
23968392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
23978c90fef9SAdrian Hunter		column_headers = []
23988392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
23998392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
24008392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
24018392b74bSAdrian Hunter			while query.next():
24028c90fef9SAdrian Hunter				column_headers.append(query.value(1))
24038392b74bSAdrian Hunter			if table_name == "sqlite_master":
24048392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
24058392b74bSAdrian Hunter		else:
24068392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
24078392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
24088392b74bSAdrian Hunter				select_table_name = table_name[19:]
24098392b74bSAdrian Hunter				schema = "information_schema"
24108392b74bSAdrian Hunter			else:
24118392b74bSAdrian Hunter				select_table_name = table_name
24128392b74bSAdrian Hunter				schema = "public"
24138392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
24148392b74bSAdrian Hunter			while query.next():
24158c90fef9SAdrian Hunter				column_headers.append(query.value(0))
24168453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
24178453c936SAdrian Hunter			if table_name == "samples_view":
24188453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_view_DataPrep
24198453c936SAdrian Hunter			if table_name == "samples":
24208453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_DataPrep
24218c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
24228392b74bSAdrian Hunter
24238453c936SAdrian Hunter	def samples_view_DataPrep(self, query, count):
24248453c936SAdrian Hunter		data = []
24258453c936SAdrian Hunter		data.append(query.value(0))
24268453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
24278453c936SAdrian Hunter		data.append("{:>19}".format(query.value(1)))
24288453c936SAdrian Hunter		for i in xrange(2, count):
24298453c936SAdrian Hunter			data.append(query.value(i))
24308453c936SAdrian Hunter		return data
24318453c936SAdrian Hunter
24328453c936SAdrian Hunter	def samples_DataPrep(self, query, count):
24338453c936SAdrian Hunter		data = []
24348453c936SAdrian Hunter		for i in xrange(9):
24358453c936SAdrian Hunter			data.append(query.value(i))
24368453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
24378453c936SAdrian Hunter		data.append("{:>19}".format(query.value(9)))
24388453c936SAdrian Hunter		for i in xrange(10, count):
24398453c936SAdrian Hunter			data.append(query.value(i))
24408453c936SAdrian Hunter		return data
24418453c936SAdrian Hunter
24428392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
24438392b74bSAdrian Hunter
24448392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
24458392b74bSAdrian Hunter
24468392b74bSAdrian Hunter	def __init__(self, parent=None):
24478392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
24488392b74bSAdrian Hunter
24498392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
24508392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
24518392b74bSAdrian Hunter		# so implement a crude alternative
24528392b74bSAdrian Hunter		font = self.view.font()
24538392b74bSAdrian Hunter		metrics = QFontMetrics(font)
24548392b74bSAdrian Hunter		max = 0
24558392b74bSAdrian Hunter		for row in xrange(n):
24568392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
24578392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
24588392b74bSAdrian Hunter			max = len if len > max else max
24598392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
24608392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
24618392b74bSAdrian Hunter		max = len if len > max else max
24628392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
24638392b74bSAdrian Hunter
24648392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
24658392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
24668392b74bSAdrian Hunter		if n < 1:
24678392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
24688392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
24698392b74bSAdrian Hunter			return
24708392b74bSAdrian Hunter		columns = self.data_model.columnCount()
24718392b74bSAdrian Hunter		for i in xrange(columns):
24728392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
24738392b74bSAdrian Hunter
24748392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
24758392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
24768392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
24778392b74bSAdrian Hunter		self.ResizeColumnsToContents()
24788392b74bSAdrian Hunter
247996c43b9aSAdrian Hunter# Convert value to CSV
248096c43b9aSAdrian Hunter
248196c43b9aSAdrian Hunterdef ToCSValue(val):
248296c43b9aSAdrian Hunter	if '"' in val:
248396c43b9aSAdrian Hunter		val = val.replace('"', '""')
248496c43b9aSAdrian Hunter	if "," in val or '"' in val:
248596c43b9aSAdrian Hunter		val = '"' + val + '"'
248696c43b9aSAdrian Hunter	return val
248796c43b9aSAdrian Hunter
248896c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
248996c43b9aSAdrian Hunter
249096c43b9aSAdrian Hunterglb_max_cols = 1000
249196c43b9aSAdrian Hunter
249296c43b9aSAdrian Hunterdef RowColumnKey(a):
249396c43b9aSAdrian Hunter	return a.row() * glb_max_cols + a.column()
249496c43b9aSAdrian Hunter
249596c43b9aSAdrian Hunter# Copy selected table cells to clipboard
249696c43b9aSAdrian Hunter
249796c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
249896c43b9aSAdrian Hunter	indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
249996c43b9aSAdrian Hunter	idx_cnt = len(indexes)
250096c43b9aSAdrian Hunter	if not idx_cnt:
250196c43b9aSAdrian Hunter		return
250296c43b9aSAdrian Hunter	if idx_cnt == 1:
250396c43b9aSAdrian Hunter		with_hdr=False
250496c43b9aSAdrian Hunter	min_row = indexes[0].row()
250596c43b9aSAdrian Hunter	max_row = indexes[0].row()
250696c43b9aSAdrian Hunter	min_col = indexes[0].column()
250796c43b9aSAdrian Hunter	max_col = indexes[0].column()
250896c43b9aSAdrian Hunter	for i in indexes:
250996c43b9aSAdrian Hunter		min_row = min(min_row, i.row())
251096c43b9aSAdrian Hunter		max_row = max(max_row, i.row())
251196c43b9aSAdrian Hunter		min_col = min(min_col, i.column())
251296c43b9aSAdrian Hunter		max_col = max(max_col, i.column())
251396c43b9aSAdrian Hunter	if max_col > glb_max_cols:
251496c43b9aSAdrian Hunter		raise RuntimeError("glb_max_cols is too low")
251596c43b9aSAdrian Hunter	max_width = [0] * (1 + max_col - min_col)
251696c43b9aSAdrian Hunter	for i in indexes:
251796c43b9aSAdrian Hunter		c = i.column() - min_col
251896c43b9aSAdrian Hunter		max_width[c] = max(max_width[c], len(str(i.data())))
251996c43b9aSAdrian Hunter	text = ""
252096c43b9aSAdrian Hunter	pad = ""
252196c43b9aSAdrian Hunter	sep = ""
252296c43b9aSAdrian Hunter	if with_hdr:
252396c43b9aSAdrian Hunter		model = indexes[0].model()
252496c43b9aSAdrian Hunter		for col in range(min_col, max_col + 1):
252596c43b9aSAdrian Hunter			val = model.headerData(col, Qt.Horizontal)
252696c43b9aSAdrian Hunter			if as_csv:
252796c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
252896c43b9aSAdrian Hunter				sep = ","
252996c43b9aSAdrian Hunter			else:
253096c43b9aSAdrian Hunter				c = col - min_col
253196c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
253296c43b9aSAdrian Hunter				width = max_width[c]
253396c43b9aSAdrian Hunter				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
253496c43b9aSAdrian Hunter				if align & Qt.AlignRight:
253596c43b9aSAdrian Hunter					val = val.rjust(width)
253696c43b9aSAdrian Hunter				text += pad + sep + val
253796c43b9aSAdrian Hunter				pad = " " * (width - len(val))
253896c43b9aSAdrian Hunter				sep = "  "
253996c43b9aSAdrian Hunter		text += "\n"
254096c43b9aSAdrian Hunter		pad = ""
254196c43b9aSAdrian Hunter		sep = ""
254296c43b9aSAdrian Hunter	last_row = min_row
254396c43b9aSAdrian Hunter	for i in indexes:
254496c43b9aSAdrian Hunter		if i.row() > last_row:
254596c43b9aSAdrian Hunter			last_row = i.row()
254696c43b9aSAdrian Hunter			text += "\n"
254796c43b9aSAdrian Hunter			pad = ""
254896c43b9aSAdrian Hunter			sep = ""
254996c43b9aSAdrian Hunter		if as_csv:
255096c43b9aSAdrian Hunter			text += sep + ToCSValue(str(i.data()))
255196c43b9aSAdrian Hunter			sep = ","
255296c43b9aSAdrian Hunter		else:
255396c43b9aSAdrian Hunter			width = max_width[i.column() - min_col]
255496c43b9aSAdrian Hunter			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
255596c43b9aSAdrian Hunter				val = str(i.data()).rjust(width)
255696c43b9aSAdrian Hunter			else:
255796c43b9aSAdrian Hunter				val = str(i.data())
255896c43b9aSAdrian Hunter			text += pad + sep + val
255996c43b9aSAdrian Hunter			pad = " " * (width - len(val))
256096c43b9aSAdrian Hunter			sep = "  "
256196c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
256296c43b9aSAdrian Hunter
256396c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
256496c43b9aSAdrian Hunter	indexes = view.selectedIndexes()
256596c43b9aSAdrian Hunter	if not len(indexes):
256696c43b9aSAdrian Hunter		return
256796c43b9aSAdrian Hunter
256896c43b9aSAdrian Hunter	selection = view.selectionModel()
256996c43b9aSAdrian Hunter
257096c43b9aSAdrian Hunter	first = None
257196c43b9aSAdrian Hunter	for i in indexes:
257296c43b9aSAdrian Hunter		above = view.indexAbove(i)
257396c43b9aSAdrian Hunter		if not selection.isSelected(above):
257496c43b9aSAdrian Hunter			first = i
257596c43b9aSAdrian Hunter			break
257696c43b9aSAdrian Hunter
257796c43b9aSAdrian Hunter	if first is None:
257896c43b9aSAdrian Hunter		raise RuntimeError("CopyTreeCellsToClipboard internal error")
257996c43b9aSAdrian Hunter
258096c43b9aSAdrian Hunter	model = first.model()
258196c43b9aSAdrian Hunter	row_cnt = 0
258296c43b9aSAdrian Hunter	col_cnt = model.columnCount(first)
258396c43b9aSAdrian Hunter	max_width = [0] * col_cnt
258496c43b9aSAdrian Hunter
258596c43b9aSAdrian Hunter	indent_sz = 2
258696c43b9aSAdrian Hunter	indent_str = " " * indent_sz
258796c43b9aSAdrian Hunter
258896c43b9aSAdrian Hunter	expanded_mark_sz = 2
258996c43b9aSAdrian Hunter	if sys.version_info[0] == 3:
259096c43b9aSAdrian Hunter		expanded_mark = "\u25BC "
259196c43b9aSAdrian Hunter		not_expanded_mark = "\u25B6 "
259296c43b9aSAdrian Hunter	else:
259396c43b9aSAdrian Hunter		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
259496c43b9aSAdrian Hunter		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
259596c43b9aSAdrian Hunter	leaf_mark = "  "
259696c43b9aSAdrian Hunter
259796c43b9aSAdrian Hunter	if not as_csv:
259896c43b9aSAdrian Hunter		pos = first
259996c43b9aSAdrian Hunter		while True:
260096c43b9aSAdrian Hunter			row_cnt += 1
260196c43b9aSAdrian Hunter			row = pos.row()
260296c43b9aSAdrian Hunter			for c in range(col_cnt):
260396c43b9aSAdrian Hunter				i = pos.sibling(row, c)
260496c43b9aSAdrian Hunter				if c:
260596c43b9aSAdrian Hunter					n = len(str(i.data()))
260696c43b9aSAdrian Hunter				else:
260796c43b9aSAdrian Hunter					n = len(str(i.data()).strip())
260896c43b9aSAdrian Hunter					n += (i.internalPointer().level - 1) * indent_sz
260996c43b9aSAdrian Hunter					n += expanded_mark_sz
261096c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], n)
261196c43b9aSAdrian Hunter			pos = view.indexBelow(pos)
261296c43b9aSAdrian Hunter			if not selection.isSelected(pos):
261396c43b9aSAdrian Hunter				break
261496c43b9aSAdrian Hunter
261596c43b9aSAdrian Hunter	text = ""
261696c43b9aSAdrian Hunter	pad = ""
261796c43b9aSAdrian Hunter	sep = ""
261896c43b9aSAdrian Hunter	if with_hdr:
261996c43b9aSAdrian Hunter		for c in range(col_cnt):
262096c43b9aSAdrian Hunter			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
262196c43b9aSAdrian Hunter			if as_csv:
262296c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
262396c43b9aSAdrian Hunter				sep = ","
262496c43b9aSAdrian Hunter			else:
262596c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
262696c43b9aSAdrian Hunter				width = max_width[c]
262796c43b9aSAdrian Hunter				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
262896c43b9aSAdrian Hunter				if align & Qt.AlignRight:
262996c43b9aSAdrian Hunter					val = val.rjust(width)
263096c43b9aSAdrian Hunter				text += pad + sep + val
263196c43b9aSAdrian Hunter				pad = " " * (width - len(val))
263296c43b9aSAdrian Hunter				sep = "   "
263396c43b9aSAdrian Hunter		text += "\n"
263496c43b9aSAdrian Hunter		pad = ""
263596c43b9aSAdrian Hunter		sep = ""
263696c43b9aSAdrian Hunter
263796c43b9aSAdrian Hunter	pos = first
263896c43b9aSAdrian Hunter	while True:
263996c43b9aSAdrian Hunter		row = pos.row()
264096c43b9aSAdrian Hunter		for c in range(col_cnt):
264196c43b9aSAdrian Hunter			i = pos.sibling(row, c)
264296c43b9aSAdrian Hunter			val = str(i.data())
264396c43b9aSAdrian Hunter			if not c:
264496c43b9aSAdrian Hunter				if model.hasChildren(i):
264596c43b9aSAdrian Hunter					if view.isExpanded(i):
264696c43b9aSAdrian Hunter						mark = expanded_mark
264796c43b9aSAdrian Hunter					else:
264896c43b9aSAdrian Hunter						mark = not_expanded_mark
264996c43b9aSAdrian Hunter				else:
265096c43b9aSAdrian Hunter					mark = leaf_mark
265196c43b9aSAdrian Hunter				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
265296c43b9aSAdrian Hunter			if as_csv:
265396c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
265496c43b9aSAdrian Hunter				sep = ","
265596c43b9aSAdrian Hunter			else:
265696c43b9aSAdrian Hunter				width = max_width[c]
265796c43b9aSAdrian Hunter				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
265896c43b9aSAdrian Hunter					val = val.rjust(width)
265996c43b9aSAdrian Hunter				text += pad + sep + val
266096c43b9aSAdrian Hunter				pad = " " * (width - len(val))
266196c43b9aSAdrian Hunter				sep = "   "
266296c43b9aSAdrian Hunter		pos = view.indexBelow(pos)
266396c43b9aSAdrian Hunter		if not selection.isSelected(pos):
266496c43b9aSAdrian Hunter			break
266596c43b9aSAdrian Hunter		text = text.rstrip() + "\n"
266696c43b9aSAdrian Hunter		pad = ""
266796c43b9aSAdrian Hunter		sep = ""
266896c43b9aSAdrian Hunter
266996c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
267096c43b9aSAdrian Hunter
267196c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
267296c43b9aSAdrian Hunter	view.CopyCellsToClipboard(view, as_csv, with_hdr)
267396c43b9aSAdrian Hunter
267496c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view):
267596c43b9aSAdrian Hunter	CopyCellsToClipboard(view, False, True)
267696c43b9aSAdrian Hunter
267796c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view):
267896c43b9aSAdrian Hunter	CopyCellsToClipboard(view, True, True)
267996c43b9aSAdrian Hunter
26809bc4e4bfSAdrian Hunter# Context menu
26819bc4e4bfSAdrian Hunter
26829bc4e4bfSAdrian Hunterclass ContextMenu(object):
26839bc4e4bfSAdrian Hunter
26849bc4e4bfSAdrian Hunter	def __init__(self, view):
26859bc4e4bfSAdrian Hunter		self.view = view
26869bc4e4bfSAdrian Hunter		self.view.setContextMenuPolicy(Qt.CustomContextMenu)
26879bc4e4bfSAdrian Hunter		self.view.customContextMenuRequested.connect(self.ShowContextMenu)
26889bc4e4bfSAdrian Hunter
26899bc4e4bfSAdrian Hunter	def ShowContextMenu(self, pos):
26909bc4e4bfSAdrian Hunter		menu = QMenu(self.view)
26919bc4e4bfSAdrian Hunter		self.AddActions(menu)
26929bc4e4bfSAdrian Hunter		menu.exec_(self.view.mapToGlobal(pos))
26939bc4e4bfSAdrian Hunter
26949bc4e4bfSAdrian Hunter	def AddCopy(self, menu):
26959bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
26969bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
26979bc4e4bfSAdrian Hunter
26989bc4e4bfSAdrian Hunter	def AddActions(self, menu):
26999bc4e4bfSAdrian Hunter		self.AddCopy(menu)
27009bc4e4bfSAdrian Hunter
27019bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu):
27029bc4e4bfSAdrian Hunter
27039bc4e4bfSAdrian Hunter	def __init__(self, view):
27049bc4e4bfSAdrian Hunter		super(TreeContextMenu, self).__init__(view)
27059bc4e4bfSAdrian Hunter
27069bc4e4bfSAdrian Hunter	def AddActions(self, menu):
27079bc4e4bfSAdrian Hunter		i = self.view.currentIndex()
27089bc4e4bfSAdrian Hunter		text = str(i.data()).strip()
27099bc4e4bfSAdrian Hunter		if len(text):
27109bc4e4bfSAdrian Hunter			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
27119bc4e4bfSAdrian Hunter		self.AddCopy(menu)
27129bc4e4bfSAdrian Hunter
27138392b74bSAdrian Hunter# Table window
27148392b74bSAdrian Hunter
27158392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
27168392b74bSAdrian Hunter
27178392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
27188392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
27198392b74bSAdrian Hunter
27208392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
27218392b74bSAdrian Hunter
27228392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
27238392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
27248392b74bSAdrian Hunter
27258392b74bSAdrian Hunter		self.view = QTableView()
27268392b74bSAdrian Hunter		self.view.setModel(self.model)
27278392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
27288392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
27298392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
27308392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
273196c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
273296c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
27338392b74bSAdrian Hunter
27348392b74bSAdrian Hunter		self.ResizeColumnsToContents()
27358392b74bSAdrian Hunter
27369bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
27379bc4e4bfSAdrian Hunter
27388392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
27398392b74bSAdrian Hunter
27408392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
27418392b74bSAdrian Hunter
27428392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
27438392b74bSAdrian Hunter
27448392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
27458392b74bSAdrian Hunter
27468392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
27478392b74bSAdrian Hunter
27488392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
27498392b74bSAdrian Hunter
27508392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
27518392b74bSAdrian Hunter		self.view.setFocus()
27528392b74bSAdrian Hunter		self.find_bar.Busy()
27538392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
27548392b74bSAdrian Hunter
27558392b74bSAdrian Hunter	def FindDone(self, row):
27568392b74bSAdrian Hunter		self.find_bar.Idle()
27578392b74bSAdrian Hunter		if row >= 0:
275835fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
27598392b74bSAdrian Hunter		else:
27608392b74bSAdrian Hunter			self.find_bar.NotFound()
27618392b74bSAdrian Hunter
27628392b74bSAdrian Hunter# Table list
27638392b74bSAdrian Hunter
27648392b74bSAdrian Hunterdef GetTableList(glb):
27658392b74bSAdrian Hunter	tables = []
27668392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
27678392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
27688392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
27698392b74bSAdrian Hunter	else:
27708392b74bSAdrian 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")
27718392b74bSAdrian Hunter	while query.next():
27728392b74bSAdrian Hunter		tables.append(query.value(0))
27738392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
27748392b74bSAdrian Hunter		tables.append("sqlite_master")
27758392b74bSAdrian Hunter	else:
27768392b74bSAdrian Hunter		tables.append("information_schema.tables")
27778392b74bSAdrian Hunter		tables.append("information_schema.views")
27788392b74bSAdrian Hunter		tables.append("information_schema.columns")
27798392b74bSAdrian Hunter	return tables
27808392b74bSAdrian Hunter
2781cd358012SAdrian Hunter# Top Calls data model
2782cd358012SAdrian Hunter
2783cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2784cd358012SAdrian Hunter
2785cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2786cd358012SAdrian Hunter		text = ""
2787cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2788cd358012SAdrian Hunter			text = "::text"
2789cd358012SAdrian Hunter		limit = ""
2790cd358012SAdrian Hunter		if len(report_vars.limit):
2791cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2792cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2793cd358012SAdrian Hunter			" CASE"
2794cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2795cd358012SAdrian Hunter			" ELSE short_name"
2796cd358012SAdrian Hunter			" END AS dso,"
2797cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2798cd358012SAdrian Hunter			" CASE"
2799cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2800cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2801cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2802cd358012SAdrian Hunter			" ELSE ''" + text +
2803cd358012SAdrian Hunter			" END AS flags"
2804cd358012SAdrian Hunter			" FROM calls"
2805cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2806cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2807cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2808cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2809cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2810cd358012SAdrian Hunter			report_vars.where_clause +
2811cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2812cd358012SAdrian Hunter			limit
2813cd358012SAdrian Hunter			)
2814cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2815cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2816cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2817cd358012SAdrian Hunter
2818cd358012SAdrian Hunter	def columnAlignment(self, column):
2819cd358012SAdrian Hunter		return self.alignment[column]
2820cd358012SAdrian Hunter
2821cd358012SAdrian Hunter# Top Calls report creation dialog
2822cd358012SAdrian Hunter
2823cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2824cd358012SAdrian Hunter
2825cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2826cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2827cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2828cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2829cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2830cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2831cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2832cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2833cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2834cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2835cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2836cd358012SAdrian Hunter
2837cd358012SAdrian Hunter# Top Calls window
2838cd358012SAdrian Hunter
2839cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2840cd358012SAdrian Hunter
2841cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2842cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2843cd358012SAdrian Hunter
2844cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2845cd358012SAdrian Hunter		self.model = self.data_model
2846cd358012SAdrian Hunter
2847cd358012SAdrian Hunter		self.view = QTableView()
2848cd358012SAdrian Hunter		self.view.setModel(self.model)
2849cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2850cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
285196c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
285296c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
2853cd358012SAdrian Hunter
28549bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
28559bc4e4bfSAdrian Hunter
2856cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2857cd358012SAdrian Hunter
2858cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2859cd358012SAdrian Hunter
2860cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2861cd358012SAdrian Hunter
2862cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2863cd358012SAdrian Hunter
2864cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2865cd358012SAdrian Hunter
2866cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2867cd358012SAdrian Hunter
2868cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2869cd358012SAdrian Hunter
2870cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2871cd358012SAdrian Hunter		self.view.setFocus()
2872cd358012SAdrian Hunter		self.find_bar.Busy()
2873cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2874cd358012SAdrian Hunter
2875cd358012SAdrian Hunter	def FindDone(self, row):
2876cd358012SAdrian Hunter		self.find_bar.Idle()
2877cd358012SAdrian Hunter		if row >= 0:
2878cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2879cd358012SAdrian Hunter		else:
2880cd358012SAdrian Hunter			self.find_bar.NotFound()
2881cd358012SAdrian Hunter
28821beb5c7bSAdrian Hunter# Action Definition
28831beb5c7bSAdrian Hunter
28841beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
28851beb5c7bSAdrian Hunter	action = QAction(label, parent)
28861beb5c7bSAdrian Hunter	if shortcut != None:
28871beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
28881beb5c7bSAdrian Hunter	action.setStatusTip(tip)
28891beb5c7bSAdrian Hunter	action.triggered.connect(callback)
28901beb5c7bSAdrian Hunter	return action
28911beb5c7bSAdrian Hunter
28921beb5c7bSAdrian Hunter# Typical application actions
28931beb5c7bSAdrian Hunter
28941beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
28951beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
28961beb5c7bSAdrian Hunter
28971beb5c7bSAdrian Hunter# Typical MDI actions
28981beb5c7bSAdrian Hunter
28991beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
29001beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
29011beb5c7bSAdrian Hunter
29021beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
29031beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
29041beb5c7bSAdrian Hunter
29051beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
29061beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
29071beb5c7bSAdrian Hunter
29081beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
29091beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
29101beb5c7bSAdrian Hunter
29111beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
29121beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
29131beb5c7bSAdrian Hunter
29141beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
29151beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
29161beb5c7bSAdrian Hunter
29171beb5c7bSAdrian Hunter# Typical MDI window menu
29181beb5c7bSAdrian Hunter
29191beb5c7bSAdrian Hunterclass WindowMenu():
29201beb5c7bSAdrian Hunter
29211beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
29221beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
29231beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
29241beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
29251beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
29261beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
29271beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
29281beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
29291beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
29301beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
29311beb5c7bSAdrian Hunter
29321beb5c7bSAdrian Hunter	def Update(self):
29331beb5c7bSAdrian Hunter		self.window_menu.clear()
29341beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
29351beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
29361beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
29371beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
29381beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
29391beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
29401beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
29411beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
29421beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
29431beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
29441beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29451beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
29461beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
29471beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29481beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
29491beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
29501beb5c7bSAdrian Hunter		if sub_window_count == 0:
29511beb5c7bSAdrian Hunter			return
29521beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
29531beb5c7bSAdrian Hunter		nr = 1
29541beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
29551beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
29561beb5c7bSAdrian Hunter			if nr < 10:
29571beb5c7bSAdrian Hunter				label = "&" + label
29581beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
29591beb5c7bSAdrian Hunter			action.setCheckable(True)
29601beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
2961df8ea22aSAdrian Hunter			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
29621beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
29631beb5c7bSAdrian Hunter			nr += 1
29641beb5c7bSAdrian Hunter
29651beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
29661beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
29671beb5c7bSAdrian Hunter
296865b24292SAdrian Hunter# Help text
296965b24292SAdrian Hunter
297065b24292SAdrian Hunterglb_help_text = """
297165b24292SAdrian Hunter<h1>Contents</h1>
297265b24292SAdrian Hunter<style>
297365b24292SAdrian Hunterp.c1 {
297465b24292SAdrian Hunter    text-indent: 40px;
297565b24292SAdrian Hunter}
297665b24292SAdrian Hunterp.c2 {
297765b24292SAdrian Hunter    text-indent: 80px;
297865b24292SAdrian Hunter}
297965b24292SAdrian Hunter}
298065b24292SAdrian Hunter</style>
298165b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
298265b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2983ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
2984ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
2985ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
2986ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
298765b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
298865b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
298965b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
299065b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
299165b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
299265b24292SAdrian Hunterwidths to suit will display something like:
299365b24292SAdrian Hunter<pre>
299465b24292SAdrian Hunter                                         Call Graph: pt_example
299565b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
299665b24292SAdrian Hunterv- ls
299765b24292SAdrian Hunter    v- 2638:2638
299865b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
299965b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
300065b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
300165b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
300265b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
300365b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
300465b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
300565b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
300665b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
300765b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
300865b24292SAdrian Hunter</pre>
300965b24292SAdrian Hunter<h3>Points to note:</h3>
301065b24292SAdrian Hunter<ul>
301165b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
301265b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
301365b24292SAdrian Hunter<li>Subsequent levels are functions</li>
301465b24292SAdrian Hunter<li>'Count' is the number of calls</li>
301565b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
301665b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
301765b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
301865b24292SAdrian Hunter</ul>
301965b24292SAdrian Hunter<h3>Find</h3>
302065b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
302165b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
3022ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
3023ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
3024ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
3025ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
302665b24292SAdrian HunterThe All branches report displays all branches in chronological order.
302765b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
302865b24292SAdrian Hunter<h3>Disassembly</h3>
302965b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
303065b24292SAdrian Hunter<ol>
303165b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
303265b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
303365b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
303465b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
303565b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
303665b24292SAdrian Hunter</ol>
303765b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
303865b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
303965b24292SAdrian Hunter<pre>
304065b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
304165b24292SAdrian Huntergit clone https://github.com/intelxed/xed
304265b24292SAdrian Huntercd xed
304365b24292SAdrian Hunter./mfile.py --share
304465b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
304565b24292SAdrian Huntersudo ldconfig
304665b24292SAdrian Hunter</pre>
3047530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3>
3048530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
3049530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
3050530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known.
3051530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
3052530e22fdSAdrian Huntersince the previous displayed 'IPC'.
305365b24292SAdrian Hunter<h3>Find</h3>
305465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
305565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
305665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
3057ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
305865b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
305965b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
3060ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
306165b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
306265b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
306365b24292SAdrian Hunter<pre>
306465b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
306565b24292SAdrian Hunter	100us-200us		From 100us to 200us
306665b24292SAdrian Hunter	10ms-			From 10ms to the end
306765b24292SAdrian Hunter	-100ns			The first 100ns
306865b24292SAdrian Hunter	-10ms-			The last 10ms
306965b24292SAdrian Hunter</pre>
307065b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
3071ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
3072cd358012SAdrian HunterThe Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
3073cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
3074cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
307565b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
307665b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
307765b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
307865b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
307965b24292SAdrian Hunterbut that can be slow for large tables.
308065b24292SAdrian Hunter<p>There are also tables of database meta-information.
308165b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
308265b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
308365b24292SAdrian Hunter<h3>Find</h3>
308465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
308565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
308665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
308735fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
308835fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
308965b24292SAdrian Hunter"""
309065b24292SAdrian Hunter
309165b24292SAdrian Hunter# Help window
309265b24292SAdrian Hunter
309365b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
309465b24292SAdrian Hunter
309565b24292SAdrian Hunter	def __init__(self, glb, parent=None):
309665b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
309765b24292SAdrian Hunter
309865b24292SAdrian Hunter		self.text = QTextBrowser()
309965b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
310065b24292SAdrian Hunter		self.text.setReadOnly(True)
310165b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
310265b24292SAdrian Hunter
310365b24292SAdrian Hunter		self.setWidget(self.text)
310465b24292SAdrian Hunter
310565b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
310665b24292SAdrian Hunter
310765b24292SAdrian Hunter# Main window that only displays the help text
310865b24292SAdrian Hunter
310965b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
311065b24292SAdrian Hunter
311165b24292SAdrian Hunter	def __init__(self, parent=None):
311265b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
311365b24292SAdrian Hunter
311465b24292SAdrian Hunter		self.setMinimumSize(200, 100)
311565b24292SAdrian Hunter		self.resize(800, 600)
311665b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
311765b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
311865b24292SAdrian Hunter
311965b24292SAdrian Hunter		self.text = QTextBrowser()
312065b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
312165b24292SAdrian Hunter		self.text.setReadOnly(True)
312265b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
312365b24292SAdrian Hunter
312465b24292SAdrian Hunter		self.setCentralWidget(self.text)
312565b24292SAdrian Hunter
3126b62d18abSAdrian Hunter# PostqreSQL server version
3127b62d18abSAdrian Hunter
3128b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db):
3129b62d18abSAdrian Hunter	query = QSqlQuery(db)
3130b62d18abSAdrian Hunter	QueryExec(query, "SELECT VERSION()")
3131b62d18abSAdrian Hunter	if query.next():
3132b62d18abSAdrian Hunter		v_str = query.value(0)
3133b62d18abSAdrian Hunter		v_list = v_str.strip().split(" ")
3134b62d18abSAdrian Hunter		if v_list[0] == "PostgreSQL" and v_list[2] == "on":
3135b62d18abSAdrian Hunter			return v_list[1]
3136b62d18abSAdrian Hunter		return v_str
3137b62d18abSAdrian Hunter	return "Unknown"
3138b62d18abSAdrian Hunter
3139b62d18abSAdrian Hunter# SQLite version
3140b62d18abSAdrian Hunter
3141b62d18abSAdrian Hunterdef SQLiteVersion(db):
3142b62d18abSAdrian Hunter	query = QSqlQuery(db)
3143b62d18abSAdrian Hunter	QueryExec(query, "SELECT sqlite_version()")
3144b62d18abSAdrian Hunter	if query.next():
3145b62d18abSAdrian Hunter		return query.value(0)
3146b62d18abSAdrian Hunter	return "Unknown"
3147b62d18abSAdrian Hunter
3148b62d18abSAdrian Hunter# About dialog
3149b62d18abSAdrian Hunter
3150b62d18abSAdrian Hunterclass AboutDialog(QDialog):
3151b62d18abSAdrian Hunter
3152b62d18abSAdrian Hunter	def __init__(self, glb, parent=None):
3153b62d18abSAdrian Hunter		super(AboutDialog, self).__init__(parent)
3154b62d18abSAdrian Hunter
3155b62d18abSAdrian Hunter		self.setWindowTitle("About Exported SQL Viewer")
3156b62d18abSAdrian Hunter		self.setMinimumWidth(300)
3157b62d18abSAdrian Hunter
3158b62d18abSAdrian Hunter		pyside_version = "1" if pyside_version_1 else "2"
3159b62d18abSAdrian Hunter
3160b62d18abSAdrian Hunter		text = "<pre>"
3161b62d18abSAdrian Hunter		text += "Python version:     " + sys.version.split(" ")[0] + "\n"
3162b62d18abSAdrian Hunter		text += "PySide version:     " + pyside_version + "\n"
3163b62d18abSAdrian Hunter		text += "Qt version:         " + qVersion() + "\n"
3164b62d18abSAdrian Hunter		if glb.dbref.is_sqlite3:
3165b62d18abSAdrian Hunter			text += "SQLite version:     " + SQLiteVersion(glb.db) + "\n"
3166b62d18abSAdrian Hunter		else:
3167b62d18abSAdrian Hunter			text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
3168b62d18abSAdrian Hunter		text += "</pre>"
3169b62d18abSAdrian Hunter
3170b62d18abSAdrian Hunter		self.text = QTextBrowser()
3171b62d18abSAdrian Hunter		self.text.setHtml(text)
3172b62d18abSAdrian Hunter		self.text.setReadOnly(True)
3173b62d18abSAdrian Hunter		self.text.setOpenExternalLinks(True)
3174b62d18abSAdrian Hunter
3175b62d18abSAdrian Hunter		self.vbox = QVBoxLayout()
3176b62d18abSAdrian Hunter		self.vbox.addWidget(self.text)
3177b62d18abSAdrian Hunter
317826688729SAdrian Hunter		self.setLayout(self.vbox)
3179b62d18abSAdrian Hunter
318082f68e28SAdrian Hunter# Font resize
318182f68e28SAdrian Hunter
318282f68e28SAdrian Hunterdef ResizeFont(widget, diff):
318382f68e28SAdrian Hunter	font = widget.font()
318482f68e28SAdrian Hunter	sz = font.pointSize()
318582f68e28SAdrian Hunter	font.setPointSize(sz + diff)
318682f68e28SAdrian Hunter	widget.setFont(font)
318782f68e28SAdrian Hunter
318882f68e28SAdrian Hunterdef ShrinkFont(widget):
318982f68e28SAdrian Hunter	ResizeFont(widget, -1)
319082f68e28SAdrian Hunter
319182f68e28SAdrian Hunterdef EnlargeFont(widget):
319282f68e28SAdrian Hunter	ResizeFont(widget, 1)
319382f68e28SAdrian Hunter
31941beb5c7bSAdrian Hunter# Unique name for sub-windows
31951beb5c7bSAdrian Hunter
31961beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
31971beb5c7bSAdrian Hunter	if nr > 1:
31981beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
31991beb5c7bSAdrian Hunter	return name
32001beb5c7bSAdrian Hunter
32011beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
32021beb5c7bSAdrian Hunter	nr = 1
32031beb5c7bSAdrian Hunter	while True:
32041beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
32051beb5c7bSAdrian Hunter		ok = True
32061beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
32071beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
32081beb5c7bSAdrian Hunter				ok = False
32091beb5c7bSAdrian Hunter				break
32101beb5c7bSAdrian Hunter		if ok:
32111beb5c7bSAdrian Hunter			return unique_name
32121beb5c7bSAdrian Hunter		nr += 1
32131beb5c7bSAdrian Hunter
32141beb5c7bSAdrian Hunter# Add a sub-window
32151beb5c7bSAdrian Hunter
32161beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
32171beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
32181beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
32191beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
32201beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
32211beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
32221beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
32231beb5c7bSAdrian Hunter	sub_window.name = unique_name
32241beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
32251beb5c7bSAdrian Hunter	sub_window.show()
32261beb5c7bSAdrian Hunter
3227031c2a00SAdrian Hunter# Main window
3228031c2a00SAdrian Hunter
3229031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
3230031c2a00SAdrian Hunter
3231031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
3232031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
3233031c2a00SAdrian Hunter
3234031c2a00SAdrian Hunter		self.glb = glb
3235031c2a00SAdrian Hunter
32361beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
3237031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
3238031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
3239031c2a00SAdrian Hunter
32401beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
32411beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
32421beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
3243031c2a00SAdrian Hunter
32441beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
3245031c2a00SAdrian Hunter
32461beb5c7bSAdrian Hunter		menu = self.menuBar()
3247031c2a00SAdrian Hunter
32481beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
32491beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
32501beb5c7bSAdrian Hunter
3251ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
325296c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
325396c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
3254ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
32558392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
325682f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
325782f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
3258ebd70c7dSAdrian Hunter
32591beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
3260655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
32611beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
32621beb5c7bSAdrian Hunter
3263ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
3264ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
3265ae8b887cSAdrian Hunter
326676099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
326776099f98SAdrian Hunter
3268cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
3269cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
3270cd358012SAdrian Hunter
32718392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
32728392b74bSAdrian Hunter
32731beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
32741beb5c7bSAdrian Hunter
327565b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
327665b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
3277b62d18abSAdrian Hunter		help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
327865b24292SAdrian Hunter
32794b208453SAdrian Hunter	def Try(self, fn):
32804b208453SAdrian Hunter		win = self.mdi_area.activeSubWindow()
32814b208453SAdrian Hunter		if win:
32824b208453SAdrian Hunter			try:
32834b208453SAdrian Hunter				fn(win.view)
32844b208453SAdrian Hunter			except:
32854b208453SAdrian Hunter				pass
32864b208453SAdrian Hunter
328796c43b9aSAdrian Hunter	def CopyToClipboard(self):
328896c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardHdr)
328996c43b9aSAdrian Hunter
329096c43b9aSAdrian Hunter	def CopyToClipboardCSV(self):
329196c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardCSV)
329296c43b9aSAdrian Hunter
3293ebd70c7dSAdrian Hunter	def Find(self):
3294ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
3295ebd70c7dSAdrian Hunter		if win:
3296ebd70c7dSAdrian Hunter			try:
3297ebd70c7dSAdrian Hunter				win.find_bar.Activate()
3298ebd70c7dSAdrian Hunter			except:
3299ebd70c7dSAdrian Hunter				pass
3300ebd70c7dSAdrian Hunter
33018392b74bSAdrian Hunter	def FetchMoreRecords(self):
33028392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
33038392b74bSAdrian Hunter		if win:
33048392b74bSAdrian Hunter			try:
33058392b74bSAdrian Hunter				win.fetch_bar.Activate()
33068392b74bSAdrian Hunter			except:
33078392b74bSAdrian Hunter				pass
33088392b74bSAdrian Hunter
330982f68e28SAdrian Hunter	def ShrinkFont(self):
33104b208453SAdrian Hunter		self.Try(ShrinkFont)
331182f68e28SAdrian Hunter
331282f68e28SAdrian Hunter	def EnlargeFont(self):
33134b208453SAdrian Hunter		self.Try(EnlargeFont)
331482f68e28SAdrian Hunter
331576099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
331676099f98SAdrian Hunter		branches_events = 0
331776099f98SAdrian Hunter		for event in events:
331876099f98SAdrian Hunter			event = event.split(":")[0]
331976099f98SAdrian Hunter			if event == "branches":
332076099f98SAdrian Hunter				branches_events += 1
332176099f98SAdrian Hunter		dbid = 0
332276099f98SAdrian Hunter		for event in events:
332376099f98SAdrian Hunter			dbid += 1
332476099f98SAdrian Hunter			event = event.split(":")[0]
332576099f98SAdrian Hunter			if event == "branches":
332676099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
3327df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
3328210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
3329df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
333076099f98SAdrian Hunter
33318392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
33328392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
33338392b74bSAdrian Hunter		for table in tables:
3334df8ea22aSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
33358392b74bSAdrian Hunter
33361beb5c7bSAdrian Hunter	def NewCallGraph(self):
33371beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
3338031c2a00SAdrian Hunter
3339ae8b887cSAdrian Hunter	def NewCallTree(self):
3340ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
3341ae8b887cSAdrian Hunter
3342cd358012SAdrian Hunter	def NewTopCalls(self):
3343cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
3344cd358012SAdrian Hunter		ret = dialog.exec_()
3345cd358012SAdrian Hunter		if ret:
3346cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
3347cd358012SAdrian Hunter
334876099f98SAdrian Hunter	def NewBranchView(self, event_id):
3349947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
335076099f98SAdrian Hunter
3351210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
3352210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
3353210cf1f9SAdrian Hunter		ret = dialog.exec_()
3354210cf1f9SAdrian Hunter		if ret:
3355947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
3356210cf1f9SAdrian Hunter
33578392b74bSAdrian Hunter	def NewTableView(self, table_name):
33588392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
33598392b74bSAdrian Hunter
336065b24292SAdrian Hunter	def Help(self):
336165b24292SAdrian Hunter		HelpWindow(self.glb, self)
336265b24292SAdrian Hunter
3363b62d18abSAdrian Hunter	def About(self):
3364b62d18abSAdrian Hunter		dialog = AboutDialog(self.glb, self)
3365b62d18abSAdrian Hunter		dialog.exec_()
3366b62d18abSAdrian Hunter
336776099f98SAdrian Hunter# XED Disassembler
336876099f98SAdrian Hunter
336976099f98SAdrian Hunterclass xed_state_t(Structure):
337076099f98SAdrian Hunter
337176099f98SAdrian Hunter	_fields_ = [
337276099f98SAdrian Hunter		("mode", c_int),
337376099f98SAdrian Hunter		("width", c_int)
337476099f98SAdrian Hunter	]
337576099f98SAdrian Hunter
337676099f98SAdrian Hunterclass XEDInstruction():
337776099f98SAdrian Hunter
337876099f98SAdrian Hunter	def __init__(self, libxed):
337976099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
338076099f98SAdrian Hunter		xedd_t = c_byte * 512
338176099f98SAdrian Hunter		self.xedd = xedd_t()
338276099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
338376099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
338476099f98SAdrian Hunter		self.state = xed_state_t()
338576099f98SAdrian Hunter		self.statep = addressof(self.state)
338676099f98SAdrian Hunter		# Buffer for disassembled instruction text
338776099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
338876099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
338976099f98SAdrian Hunter
339076099f98SAdrian Hunterclass LibXED():
339176099f98SAdrian Hunter
339276099f98SAdrian Hunter	def __init__(self):
33935ed4419dSAdrian Hunter		try:
339476099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
33955ed4419dSAdrian Hunter		except:
33965ed4419dSAdrian Hunter			self.libxed = None
33975ed4419dSAdrian Hunter		if not self.libxed:
33985ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
339976099f98SAdrian Hunter
340076099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
340176099f98SAdrian Hunter		self.xed_tables_init.restype = None
340276099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
340376099f98SAdrian Hunter
340476099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
340576099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
340676099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
340776099f98SAdrian Hunter
340876099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
340976099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
341076099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
341176099f98SAdrian Hunter
341276099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
341376099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
341476099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
341576099f98SAdrian Hunter
341676099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
341776099f98SAdrian Hunter		self.xed_decode.restype = c_int
341876099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
341976099f98SAdrian Hunter
342076099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
342176099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
342276099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
342376099f98SAdrian Hunter
342476099f98SAdrian Hunter		self.xed_tables_init()
342576099f98SAdrian Hunter
342676099f98SAdrian Hunter	def Instruction(self):
342776099f98SAdrian Hunter		return XEDInstruction(self)
342876099f98SAdrian Hunter
342976099f98SAdrian Hunter	def SetMode(self, inst, mode):
343076099f98SAdrian Hunter		if mode:
343176099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
343276099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
343376099f98SAdrian Hunter		else:
343476099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
343576099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
343676099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
343776099f98SAdrian Hunter
343876099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
343976099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
344076099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
344176099f98SAdrian Hunter		if err:
344276099f98SAdrian Hunter			return 0, ""
344376099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
344476099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
344576099f98SAdrian Hunter		if not ok:
344676099f98SAdrian Hunter			return 0, ""
3447606bd60aSAdrian Hunter		if sys.version_info[0] == 2:
3448606bd60aSAdrian Hunter			result = inst.buffer.value
3449606bd60aSAdrian Hunter		else:
3450606bd60aSAdrian Hunter			result = inst.buffer.value.decode()
345176099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
345276099f98SAdrian Hunter		# For now, assume the length is in byte 166
3453606bd60aSAdrian Hunter		return inst.xedd[166], result
345476099f98SAdrian Hunter
345576099f98SAdrian Hunterdef TryOpen(file_name):
345676099f98SAdrian Hunter	try:
345776099f98SAdrian Hunter		return open(file_name, "rb")
345876099f98SAdrian Hunter	except:
345976099f98SAdrian Hunter		return None
346076099f98SAdrian Hunter
346176099f98SAdrian Hunterdef Is64Bit(f):
346276099f98SAdrian Hunter	result = sizeof(c_void_p)
346376099f98SAdrian Hunter	# ELF support only
346476099f98SAdrian Hunter	pos = f.tell()
346576099f98SAdrian Hunter	f.seek(0)
346676099f98SAdrian Hunter	header = f.read(7)
346776099f98SAdrian Hunter	f.seek(pos)
346876099f98SAdrian Hunter	magic = header[0:4]
3469606bd60aSAdrian Hunter	if sys.version_info[0] == 2:
347076099f98SAdrian Hunter		eclass = ord(header[4])
347176099f98SAdrian Hunter		encoding = ord(header[5])
347276099f98SAdrian Hunter		version = ord(header[6])
3473606bd60aSAdrian Hunter	else:
3474606bd60aSAdrian Hunter		eclass = header[4]
3475606bd60aSAdrian Hunter		encoding = header[5]
3476606bd60aSAdrian Hunter		version = header[6]
347776099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
347876099f98SAdrian Hunter		result = True if eclass == 2 else False
347976099f98SAdrian Hunter	return result
348076099f98SAdrian Hunter
3481031c2a00SAdrian Hunter# Global data
3482031c2a00SAdrian Hunter
3483031c2a00SAdrian Hunterclass Glb():
3484031c2a00SAdrian Hunter
3485031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
3486031c2a00SAdrian Hunter		self.dbref = dbref
3487031c2a00SAdrian Hunter		self.db = db
3488031c2a00SAdrian Hunter		self.dbname = dbname
348976099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
349076099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
349176099f98SAdrian Hunter		if self.buildid_dir:
349276099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
349376099f98SAdrian Hunter		else:
349476099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
3495031c2a00SAdrian Hunter		self.app = None
3496031c2a00SAdrian Hunter		self.mainwindow = None
34978392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
349876099f98SAdrian Hunter		try:
349976099f98SAdrian Hunter			self.disassembler = LibXED()
350076099f98SAdrian Hunter			self.have_disassembler = True
350176099f98SAdrian Hunter		except:
350276099f98SAdrian Hunter			self.have_disassembler = False
350376099f98SAdrian Hunter
350476099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
350576099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
350676099f98SAdrian Hunter		return TryOpen(file_name)
350776099f98SAdrian Hunter
350876099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
350976099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
351076099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
351176099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
351276099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
351376099f98SAdrian Hunter			if f:
351476099f98SAdrian Hunter				return f
351576099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
351676099f98SAdrian Hunter			f = TryOpen(long_name)
351776099f98SAdrian Hunter			if f:
351876099f98SAdrian Hunter				return f
351976099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
352076099f98SAdrian Hunter		if f:
352176099f98SAdrian Hunter			return f
352276099f98SAdrian Hunter		return None
35238392b74bSAdrian Hunter
35248392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
35258392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
35268392b74bSAdrian Hunter
35278392b74bSAdrian Hunter	# Shutdown any background processes or threads
35288392b74bSAdrian Hunter	def ShutdownInstances(self):
35298392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
35308392b74bSAdrian Hunter			try:
35318392b74bSAdrian Hunter				x.Shutdown()
35328392b74bSAdrian Hunter			except:
35338392b74bSAdrian Hunter				pass
3534031c2a00SAdrian Hunter
3535031c2a00SAdrian Hunter# Database reference
3536031c2a00SAdrian Hunter
3537031c2a00SAdrian Hunterclass DBRef():
3538031c2a00SAdrian Hunter
3539031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
3540031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
3541031c2a00SAdrian Hunter		self.dbname = dbname
3542031c2a00SAdrian Hunter
3543031c2a00SAdrian Hunter	def Open(self, connection_name):
3544031c2a00SAdrian Hunter		dbname = self.dbname
3545031c2a00SAdrian Hunter		if self.is_sqlite3:
3546031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
3547031c2a00SAdrian Hunter		else:
3548031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
3549031c2a00SAdrian Hunter			opts = dbname.split()
3550031c2a00SAdrian Hunter			for opt in opts:
3551031c2a00SAdrian Hunter				if "=" in opt:
3552031c2a00SAdrian Hunter					opt = opt.split("=")
3553031c2a00SAdrian Hunter					if opt[0] == "hostname":
3554031c2a00SAdrian Hunter						db.setHostName(opt[1])
3555031c2a00SAdrian Hunter					elif opt[0] == "port":
3556031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
3557031c2a00SAdrian Hunter					elif opt[0] == "username":
3558031c2a00SAdrian Hunter						db.setUserName(opt[1])
3559031c2a00SAdrian Hunter					elif opt[0] == "password":
3560031c2a00SAdrian Hunter						db.setPassword(opt[1])
3561031c2a00SAdrian Hunter					elif opt[0] == "dbname":
3562031c2a00SAdrian Hunter						dbname = opt[1]
3563031c2a00SAdrian Hunter				else:
3564031c2a00SAdrian Hunter					dbname = opt
3565031c2a00SAdrian Hunter
3566031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
3567031c2a00SAdrian Hunter		if not db.open():
3568031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
3569031c2a00SAdrian Hunter		return db, dbname
3570031c2a00SAdrian Hunter
3571031c2a00SAdrian Hunter# Main
3572031c2a00SAdrian Hunter
3573031c2a00SAdrian Hunterdef Main():
35741ed7f47fSAdrian Hunter	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
35751ed7f47fSAdrian Hunter			"   or: exported-sql-viewer.py --help-only"
35761ed7f47fSAdrian Hunter	ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
3577df8ea22aSAdrian Hunter	ap.add_argument("--pyside-version-1", action='store_true')
35781ed7f47fSAdrian Hunter	ap.add_argument("dbname", nargs="?")
35791ed7f47fSAdrian Hunter	ap.add_argument("--help-only", action='store_true')
35801ed7f47fSAdrian Hunter	args = ap.parse_args()
3581031c2a00SAdrian Hunter
35821ed7f47fSAdrian Hunter	if args.help_only:
358365b24292SAdrian Hunter		app = QApplication(sys.argv)
358465b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
358565b24292SAdrian Hunter		mainwindow.show()
358665b24292SAdrian Hunter		err = app.exec_()
358765b24292SAdrian Hunter		sys.exit(err)
3588031c2a00SAdrian Hunter
35891ed7f47fSAdrian Hunter	dbname = args.dbname
35901ed7f47fSAdrian Hunter	if dbname is None:
35911ed7f47fSAdrian Hunter		ap.print_usage()
35921ed7f47fSAdrian Hunter		print("Too few arguments")
35931ed7f47fSAdrian Hunter		sys.exit(1)
35941ed7f47fSAdrian Hunter
3595031c2a00SAdrian Hunter	is_sqlite3 = False
3596031c2a00SAdrian Hunter	try:
3597beda0e72STony Jones		f = open(dbname, "rb")
3598beda0e72STony Jones		if f.read(15) == b'SQLite format 3':
3599031c2a00SAdrian Hunter			is_sqlite3 = True
3600031c2a00SAdrian Hunter		f.close()
3601031c2a00SAdrian Hunter	except:
3602031c2a00SAdrian Hunter		pass
3603031c2a00SAdrian Hunter
3604031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
3605031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
3606031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
3607031c2a00SAdrian Hunter	app = QApplication(sys.argv)
3608031c2a00SAdrian Hunter	glb.app = app
3609031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
3610031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
3611031c2a00SAdrian Hunter	mainwindow.show()
3612031c2a00SAdrian Hunter	err = app.exec_()
36138392b74bSAdrian Hunter	glb.ShutdownInstances()
3614031c2a00SAdrian Hunter	db.close()
3615031c2a00SAdrian Hunter	sys.exit(err)
3616031c2a00SAdrian Hunter
3617031c2a00SAdrian Hunterif __name__ == "__main__":
3618031c2a00SAdrian Hunter	Main()
3619