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
108b3700f21SAdrian Hunterimport random
109b3700f21SAdrian Hunterimport copy
110b3700f21SAdrian Hunterimport math
111df8ea22aSAdrian Hunter
112df8ea22aSAdrian Hunterpyside_version_1 = True
113df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv:
114df8ea22aSAdrian Hunter	try:
115df8ea22aSAdrian Hunter		from PySide2.QtCore import *
116df8ea22aSAdrian Hunter		from PySide2.QtGui import *
117df8ea22aSAdrian Hunter		from PySide2.QtSql import *
118df8ea22aSAdrian Hunter		from PySide2.QtWidgets import *
119df8ea22aSAdrian Hunter		pyside_version_1 = False
120df8ea22aSAdrian Hunter	except:
121df8ea22aSAdrian Hunter		pass
122df8ea22aSAdrian Hunter
123df8ea22aSAdrian Hunterif pyside_version_1:
124031c2a00SAdrian Hunter	from PySide.QtCore import *
125031c2a00SAdrian Hunter	from PySide.QtGui import *
126031c2a00SAdrian Hunter	from PySide.QtSql import *
127df8ea22aSAdrian Hunter
128*fd931b2eSAdrian Hunterfrom decimal import Decimal, ROUND_HALF_UP
129*fd931b2eSAdrian Hunterfrom ctypes import CDLL, Structure, create_string_buffer, addressof, sizeof, \
130*fd931b2eSAdrian Hunter		   c_void_p, c_bool, c_byte, c_char, c_int, c_uint, c_longlong, c_ulonglong
1318392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
132031c2a00SAdrian Hunter
133beda0e72STony Jones# xrange is range in Python3
134beda0e72STony Jonestry:
135beda0e72STony Jones	xrange
136beda0e72STony Jonesexcept NameError:
137beda0e72STony Jones	xrange = range
138beda0e72STony Jones
139beda0e72STony Jonesdef printerr(*args, **keyword_args):
140beda0e72STony Jones	print(*args, file=sys.stderr, **keyword_args)
141beda0e72STony Jones
142031c2a00SAdrian Hunter# Data formatting helpers
143031c2a00SAdrian Hunter
14476099f98SAdrian Hunterdef tohex(ip):
14576099f98SAdrian Hunter	if ip < 0:
14676099f98SAdrian Hunter		ip += 1 << 64
14776099f98SAdrian Hunter	return "%x" % ip
14876099f98SAdrian Hunter
14976099f98SAdrian Hunterdef offstr(offset):
15076099f98SAdrian Hunter	if offset:
15176099f98SAdrian Hunter		return "+0x%x" % offset
15276099f98SAdrian Hunter	return ""
15376099f98SAdrian Hunter
154031c2a00SAdrian Hunterdef dsoname(name):
155031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
156031c2a00SAdrian Hunter		return "[kernel]"
157031c2a00SAdrian Hunter	return name
158031c2a00SAdrian Hunter
159210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
160210cf1f9SAdrian Hunter	pos = s.find(sub)
161210cf1f9SAdrian Hunter	if pos < 0:
162210cf1f9SAdrian Hunter		return pos
163210cf1f9SAdrian Hunter	if n <= 1:
164210cf1f9SAdrian Hunter		return offs + pos
165210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
166210cf1f9SAdrian Hunter
167031c2a00SAdrian Hunter# Percent to one decimal place
168031c2a00SAdrian Hunter
169031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
170031c2a00SAdrian Hunter	if not d:
171031c2a00SAdrian Hunter		return "0.0"
172031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
173031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
174031c2a00SAdrian Hunter
175031c2a00SAdrian Hunter# Helper for queries that must not fail
176031c2a00SAdrian Hunter
177031c2a00SAdrian Hunterdef QueryExec(query, stmt):
178031c2a00SAdrian Hunter	ret = query.exec_(stmt)
179031c2a00SAdrian Hunter	if not ret:
180031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
181031c2a00SAdrian Hunter
182ebd70c7dSAdrian Hunter# Background thread
183ebd70c7dSAdrian Hunter
184ebd70c7dSAdrian Hunterclass Thread(QThread):
185ebd70c7dSAdrian Hunter
186ebd70c7dSAdrian Hunter	done = Signal(object)
187ebd70c7dSAdrian Hunter
188ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
189ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
190ebd70c7dSAdrian Hunter		self.task = task
191ebd70c7dSAdrian Hunter		self.param = param
192ebd70c7dSAdrian Hunter
193ebd70c7dSAdrian Hunter	def run(self):
194ebd70c7dSAdrian Hunter		while True:
195ebd70c7dSAdrian Hunter			if self.param is None:
196ebd70c7dSAdrian Hunter				done, result = self.task()
197ebd70c7dSAdrian Hunter			else:
198ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
199ebd70c7dSAdrian Hunter			self.done.emit(result)
200ebd70c7dSAdrian Hunter			if done:
201ebd70c7dSAdrian Hunter				break
202ebd70c7dSAdrian Hunter
203031c2a00SAdrian Hunter# Tree data model
204031c2a00SAdrian Hunter
205031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
206031c2a00SAdrian Hunter
2074a0979d4SAdrian Hunter	def __init__(self, glb, params, parent=None):
208031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
209a448ba23SAdrian Hunter		self.glb = glb
2104a0979d4SAdrian Hunter		self.params = params
211a448ba23SAdrian Hunter		self.root = self.GetRoot()
212031c2a00SAdrian Hunter		self.last_row_read = 0
213031c2a00SAdrian Hunter
214031c2a00SAdrian Hunter	def Item(self, parent):
215031c2a00SAdrian Hunter		if parent.isValid():
216031c2a00SAdrian Hunter			return parent.internalPointer()
217031c2a00SAdrian Hunter		else:
218031c2a00SAdrian Hunter			return self.root
219031c2a00SAdrian Hunter
220031c2a00SAdrian Hunter	def rowCount(self, parent):
221031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
222031c2a00SAdrian Hunter		if result < 0:
223031c2a00SAdrian Hunter			result = 0
224031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
225031c2a00SAdrian Hunter		return result
226031c2a00SAdrian Hunter
227031c2a00SAdrian Hunter	def hasChildren(self, parent):
228031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
229031c2a00SAdrian Hunter
230031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
231031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
232031c2a00SAdrian Hunter			return self.columnAlignment(section)
233031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
234031c2a00SAdrian Hunter			return None
235031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
236031c2a00SAdrian Hunter			return None
237031c2a00SAdrian Hunter		return self.columnHeader(section)
238031c2a00SAdrian Hunter
239031c2a00SAdrian Hunter	def parent(self, child):
240031c2a00SAdrian Hunter		child_item = child.internalPointer()
241031c2a00SAdrian Hunter		if child_item is self.root:
242031c2a00SAdrian Hunter			return QModelIndex()
243031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
244031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
245031c2a00SAdrian Hunter
246031c2a00SAdrian Hunter	def index(self, row, column, parent):
247031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
248031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
249031c2a00SAdrian Hunter
250031c2a00SAdrian Hunter	def DisplayData(self, item, index):
251031c2a00SAdrian Hunter		return item.getData(index.column())
252031c2a00SAdrian Hunter
2538392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2548392b74bSAdrian Hunter		if row > self.last_row_read:
2558392b74bSAdrian Hunter			self.last_row_read = row
2568392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2578392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2588392b74bSAdrian Hunter
2598392b74bSAdrian Hunter	def columnAlignment(self, column):
2608392b74bSAdrian Hunter		return Qt.AlignLeft
2618392b74bSAdrian Hunter
2628392b74bSAdrian Hunter	def columnFont(self, column):
2638392b74bSAdrian Hunter		return None
2648392b74bSAdrian Hunter
2658392b74bSAdrian Hunter	def data(self, index, role):
2668392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2678392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2688392b74bSAdrian Hunter		if role == Qt.FontRole:
2698392b74bSAdrian Hunter			return self.columnFont(index.column())
2708392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2718392b74bSAdrian Hunter			return None
2728392b74bSAdrian Hunter		item = index.internalPointer()
2738392b74bSAdrian Hunter		return self.DisplayData(item, index)
2748392b74bSAdrian Hunter
2758392b74bSAdrian Hunter# Table data model
2768392b74bSAdrian Hunter
2778392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2788392b74bSAdrian Hunter
2798392b74bSAdrian Hunter	def __init__(self, parent=None):
2808392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2818392b74bSAdrian Hunter		self.child_count = 0
2828392b74bSAdrian Hunter		self.child_items = []
2838392b74bSAdrian Hunter		self.last_row_read = 0
2848392b74bSAdrian Hunter
2858392b74bSAdrian Hunter	def Item(self, parent):
2868392b74bSAdrian Hunter		if parent.isValid():
2878392b74bSAdrian Hunter			return parent.internalPointer()
2888392b74bSAdrian Hunter		else:
2898392b74bSAdrian Hunter			return self
2908392b74bSAdrian Hunter
2918392b74bSAdrian Hunter	def rowCount(self, parent):
2928392b74bSAdrian Hunter		return self.child_count
2938392b74bSAdrian Hunter
2948392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2958392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2968392b74bSAdrian Hunter			return self.columnAlignment(section)
2978392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2988392b74bSAdrian Hunter			return None
2998392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
3008392b74bSAdrian Hunter			return None
3018392b74bSAdrian Hunter		return self.columnHeader(section)
3028392b74bSAdrian Hunter
3038392b74bSAdrian Hunter	def index(self, row, column, parent):
3048392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
3058392b74bSAdrian Hunter
3068392b74bSAdrian Hunter	def DisplayData(self, item, index):
3078392b74bSAdrian Hunter		return item.getData(index.column())
3088392b74bSAdrian Hunter
3098392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
3108392b74bSAdrian Hunter		if row > self.last_row_read:
3118392b74bSAdrian Hunter			self.last_row_read = row
3128392b74bSAdrian Hunter			if row + 10 >= self.child_count:
3138392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
3148392b74bSAdrian Hunter
315031c2a00SAdrian Hunter	def columnAlignment(self, column):
316031c2a00SAdrian Hunter		return Qt.AlignLeft
317031c2a00SAdrian Hunter
318031c2a00SAdrian Hunter	def columnFont(self, column):
319031c2a00SAdrian Hunter		return None
320031c2a00SAdrian Hunter
321031c2a00SAdrian Hunter	def data(self, index, role):
322031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
323031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
324031c2a00SAdrian Hunter		if role == Qt.FontRole:
325031c2a00SAdrian Hunter			return self.columnFont(index.column())
326031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
327031c2a00SAdrian Hunter			return None
328031c2a00SAdrian Hunter		item = index.internalPointer()
329031c2a00SAdrian Hunter		return self.DisplayData(item, index)
330031c2a00SAdrian Hunter
3311beb5c7bSAdrian Hunter# Model cache
3321beb5c7bSAdrian Hunter
3331beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
3341beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
3351beb5c7bSAdrian Hunter
3361beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
3371beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3381beb5c7bSAdrian Hunter	try:
3391beb5c7bSAdrian Hunter		model = model_cache[model_name]
3401beb5c7bSAdrian Hunter	except:
3411beb5c7bSAdrian Hunter		model = None
3421beb5c7bSAdrian Hunter	if model is None:
3431beb5c7bSAdrian Hunter		model = create_fn()
3441beb5c7bSAdrian Hunter		model_cache[model_name] = model
3451beb5c7bSAdrian Hunter	model_cache_lock.release()
3461beb5c7bSAdrian Hunter	return model
3471beb5c7bSAdrian Hunter
348181ea40aSAdrian Hunterdef LookupModel(model_name):
349181ea40aSAdrian Hunter	model_cache_lock.acquire()
350181ea40aSAdrian Hunter	try:
351181ea40aSAdrian Hunter		model = model_cache[model_name]
352181ea40aSAdrian Hunter	except:
353181ea40aSAdrian Hunter		model = None
354181ea40aSAdrian Hunter	model_cache_lock.release()
355181ea40aSAdrian Hunter	return model
356181ea40aSAdrian Hunter
357ebd70c7dSAdrian Hunter# Find bar
358ebd70c7dSAdrian Hunter
359ebd70c7dSAdrian Hunterclass FindBar():
360ebd70c7dSAdrian Hunter
361ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
362ebd70c7dSAdrian Hunter		self.finder = finder
363ebd70c7dSAdrian Hunter		self.context = []
364ebd70c7dSAdrian Hunter		self.last_value = None
365ebd70c7dSAdrian Hunter		self.last_pattern = None
366ebd70c7dSAdrian Hunter
367ebd70c7dSAdrian Hunter		label = QLabel("Find:")
368ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
369ebd70c7dSAdrian Hunter
370ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
371ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
372ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
373ebd70c7dSAdrian Hunter
374ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
375ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
376ebd70c7dSAdrian Hunter		self.progress.hide()
377ebd70c7dSAdrian Hunter
378ebd70c7dSAdrian Hunter		if is_reg_expr:
379ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
380ebd70c7dSAdrian Hunter		else:
381ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
382ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
383ebd70c7dSAdrian Hunter
384ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
385ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
386ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
387ebd70c7dSAdrian Hunter
388ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
389ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
390ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
391ebd70c7dSAdrian Hunter
392ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
393ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
394ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
395ebd70c7dSAdrian Hunter
396ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
397ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
398ebd70c7dSAdrian Hunter
399ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
400ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
401ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
402ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
403ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
404ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
405ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
406ebd70c7dSAdrian Hunter
407ebd70c7dSAdrian Hunter		self.bar = QWidget()
40826688729SAdrian Hunter		self.bar.setLayout(self.hbox)
409ebd70c7dSAdrian Hunter		self.bar.hide()
410ebd70c7dSAdrian Hunter
411ebd70c7dSAdrian Hunter	def Widget(self):
412ebd70c7dSAdrian Hunter		return self.bar
413ebd70c7dSAdrian Hunter
414ebd70c7dSAdrian Hunter	def Activate(self):
415ebd70c7dSAdrian Hunter		self.bar.show()
41680b3fb64SAdrian Hunter		self.textbox.lineEdit().selectAll()
417ebd70c7dSAdrian Hunter		self.textbox.setFocus()
418ebd70c7dSAdrian Hunter
419ebd70c7dSAdrian Hunter	def Deactivate(self):
420ebd70c7dSAdrian Hunter		self.bar.hide()
421ebd70c7dSAdrian Hunter
422ebd70c7dSAdrian Hunter	def Busy(self):
423ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
424ebd70c7dSAdrian Hunter		self.pattern.hide()
425ebd70c7dSAdrian Hunter		self.next_button.hide()
426ebd70c7dSAdrian Hunter		self.prev_button.hide()
427ebd70c7dSAdrian Hunter		self.progress.show()
428ebd70c7dSAdrian Hunter
429ebd70c7dSAdrian Hunter	def Idle(self):
430ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
431ebd70c7dSAdrian Hunter		self.progress.hide()
432ebd70c7dSAdrian Hunter		self.pattern.show()
433ebd70c7dSAdrian Hunter		self.next_button.show()
434ebd70c7dSAdrian Hunter		self.prev_button.show()
435ebd70c7dSAdrian Hunter
436ebd70c7dSAdrian Hunter	def Find(self, direction):
437ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
438ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
439ebd70c7dSAdrian Hunter		self.last_value = value
440ebd70c7dSAdrian Hunter		self.last_pattern = pattern
441ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
442ebd70c7dSAdrian Hunter
443ebd70c7dSAdrian Hunter	def ValueChanged(self):
444ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
445ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
446ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
447ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
448ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
449ebd70c7dSAdrian Hunter		if data == None:
450ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
451ebd70c7dSAdrian Hunter		else:
452ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
453ebd70c7dSAdrian Hunter		self.Find(0)
454ebd70c7dSAdrian Hunter
455ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
456ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
457ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
458ebd70c7dSAdrian Hunter		if value != self.last_value:
459ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
460ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
461ebd70c7dSAdrian Hunter			if index < 0:
462ebd70c7dSAdrian Hunter				index = self.textbox.count()
463ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
464ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
465ebd70c7dSAdrian Hunter				return
466ebd70c7dSAdrian Hunter			else:
467ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
468ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
469ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
470ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
471ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
472ebd70c7dSAdrian Hunter		self.Find(direction)
473ebd70c7dSAdrian Hunter
474ebd70c7dSAdrian Hunter	def NotFound(self):
475ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
476ebd70c7dSAdrian Hunter
477031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
478031c2a00SAdrian Hunter
479031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
480031c2a00SAdrian Hunter
4814a0979d4SAdrian Hunter	def __init__(self, glb, params, row, parent_item):
482031c2a00SAdrian Hunter		self.glb = glb
4834a0979d4SAdrian Hunter		self.params = params
484031c2a00SAdrian Hunter		self.row = row
485031c2a00SAdrian Hunter		self.parent_item = parent_item
48626688729SAdrian Hunter		self.query_done = False
487031c2a00SAdrian Hunter		self.child_count = 0
488031c2a00SAdrian Hunter		self.child_items = []
4893ac641f4SAdrian Hunter		if parent_item:
4903ac641f4SAdrian Hunter			self.level = parent_item.level + 1
4913ac641f4SAdrian Hunter		else:
4923ac641f4SAdrian Hunter			self.level = 0
493031c2a00SAdrian Hunter
494031c2a00SAdrian Hunter	def getChildItem(self, row):
495031c2a00SAdrian Hunter		return self.child_items[row]
496031c2a00SAdrian Hunter
497031c2a00SAdrian Hunter	def getParentItem(self):
498031c2a00SAdrian Hunter		return self.parent_item
499031c2a00SAdrian Hunter
500031c2a00SAdrian Hunter	def getRow(self):
501031c2a00SAdrian Hunter		return self.row
502031c2a00SAdrian Hunter
503031c2a00SAdrian Hunter	def childCount(self):
504031c2a00SAdrian Hunter		if not self.query_done:
505031c2a00SAdrian Hunter			self.Select()
506031c2a00SAdrian Hunter			if not self.child_count:
507031c2a00SAdrian Hunter				return -1
508031c2a00SAdrian Hunter		return self.child_count
509031c2a00SAdrian Hunter
510031c2a00SAdrian Hunter	def hasChildren(self):
511031c2a00SAdrian Hunter		if not self.query_done:
512031c2a00SAdrian Hunter			return True
513031c2a00SAdrian Hunter		return self.child_count > 0
514031c2a00SAdrian Hunter
515031c2a00SAdrian Hunter	def getData(self, column):
516031c2a00SAdrian Hunter		return self.data[column]
517031c2a00SAdrian Hunter
518031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
519031c2a00SAdrian Hunter
520031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
521031c2a00SAdrian Hunter
52238a846d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
5234a0979d4SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
524031c2a00SAdrian Hunter		self.comm_id = comm_id
525031c2a00SAdrian Hunter		self.thread_id = thread_id
526031c2a00SAdrian Hunter		self.call_path_id = call_path_id
52738a846d4SAdrian Hunter		self.insn_cnt = insn_cnt
52838a846d4SAdrian Hunter		self.cyc_cnt = cyc_cnt
529031c2a00SAdrian Hunter		self.branch_count = branch_count
530031c2a00SAdrian Hunter		self.time = time
531031c2a00SAdrian Hunter
532031c2a00SAdrian Hunter	def Select(self):
53326688729SAdrian Hunter		self.query_done = True
534031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
53538a846d4SAdrian Hunter		if self.params.have_ipc:
53638a846d4SAdrian Hunter			ipc_str = ", SUM(insn_count), SUM(cyc_count)"
53738a846d4SAdrian Hunter		else:
53838a846d4SAdrian Hunter			ipc_str = ""
53938a846d4SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
540031c2a00SAdrian Hunter					" FROM calls"
541031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
542031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
543031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
544031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
545031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
546031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
547031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
548031c2a00SAdrian Hunter					" ORDER BY call_path_id")
549031c2a00SAdrian Hunter		while query.next():
55038a846d4SAdrian Hunter			if self.params.have_ipc:
55138a846d4SAdrian Hunter				insn_cnt = int(query.value(5))
55238a846d4SAdrian Hunter				cyc_cnt = int(query.value(6))
55338a846d4SAdrian Hunter				branch_count = int(query.value(7))
55438a846d4SAdrian Hunter			else:
55538a846d4SAdrian Hunter				insn_cnt = 0
55638a846d4SAdrian Hunter				cyc_cnt = 0
55738a846d4SAdrian Hunter				branch_count = int(query.value(5))
55838a846d4SAdrian 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)
559031c2a00SAdrian Hunter			self.child_items.append(child_item)
560031c2a00SAdrian Hunter			self.child_count += 1
561031c2a00SAdrian Hunter
562031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
563031c2a00SAdrian Hunter
564031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
565031c2a00SAdrian Hunter
56638a846d4SAdrian 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):
56738a846d4SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
568031c2a00SAdrian Hunter		dso = dsoname(dso)
56938a846d4SAdrian Hunter		if self.params.have_ipc:
57038a846d4SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
57138a846d4SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
57238a846d4SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
57338a846d4SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
57438a846d4SAdrian 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 ]
57538a846d4SAdrian Hunter		else:
576031c2a00SAdrian Hunter			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
577031c2a00SAdrian Hunter		self.dbid = call_path_id
578031c2a00SAdrian Hunter
579031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
580031c2a00SAdrian Hunter
581031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
582031c2a00SAdrian Hunter
5834a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
58438a846d4SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
58538a846d4SAdrian Hunter		if self.params.have_ipc:
58638a846d4SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
58738a846d4SAdrian Hunter		else:
588031c2a00SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
589031c2a00SAdrian Hunter		self.dbid = thread_id
590031c2a00SAdrian Hunter
591031c2a00SAdrian Hunter	def Select(self):
592031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
593031c2a00SAdrian Hunter		for child_item in self.child_items:
594031c2a00SAdrian Hunter			self.time += child_item.time
59538a846d4SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
59638a846d4SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
597031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
598031c2a00SAdrian Hunter		for child_item in self.child_items:
599031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
60038a846d4SAdrian Hunter			if self.params.have_ipc:
60138a846d4SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
60238a846d4SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
60338a846d4SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
60438a846d4SAdrian Hunter			else:
605031c2a00SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
606031c2a00SAdrian Hunter
607031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
608031c2a00SAdrian Hunter
609031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
610031c2a00SAdrian Hunter
6114a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
6124a0979d4SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
61338a846d4SAdrian Hunter		if self.params.have_ipc:
61438a846d4SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
61538a846d4SAdrian Hunter		else:
616031c2a00SAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
617031c2a00SAdrian Hunter		self.dbid = comm_id
618031c2a00SAdrian Hunter
619031c2a00SAdrian Hunter	def Select(self):
62026688729SAdrian Hunter		self.query_done = True
621031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
622031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
623031c2a00SAdrian Hunter					" FROM comm_threads"
624031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
625031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
626031c2a00SAdrian Hunter		while query.next():
6274a0979d4SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
628031c2a00SAdrian Hunter			self.child_items.append(child_item)
629031c2a00SAdrian Hunter			self.child_count += 1
630031c2a00SAdrian Hunter
631031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
632031c2a00SAdrian Hunter
633031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
634031c2a00SAdrian Hunter
6354a0979d4SAdrian Hunter	def __init__(self, glb, params):
6364a0979d4SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, params, 0, None)
637031c2a00SAdrian Hunter		self.dbid = 0
63826688729SAdrian Hunter		self.query_done = True
63926c11206SAdrian Hunter		if_has_calls = ""
64026c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
641af833988SAdrian Hunter			if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE
642031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
64326c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
644031c2a00SAdrian Hunter		while query.next():
645031c2a00SAdrian Hunter			if not query.value(0):
646031c2a00SAdrian Hunter				continue
6474a0979d4SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
648031c2a00SAdrian Hunter			self.child_items.append(child_item)
649031c2a00SAdrian Hunter			self.child_count += 1
650031c2a00SAdrian Hunter
6514a0979d4SAdrian Hunter# Call graph model parameters
6524a0979d4SAdrian Hunter
6534a0979d4SAdrian Hunterclass CallGraphModelParams():
6544a0979d4SAdrian Hunter
6554a0979d4SAdrian Hunter	def __init__(self, glb, parent=None):
6564a0979d4SAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
6574a0979d4SAdrian Hunter
658254c0d82SAdrian Hunter# Context-sensitive call graph data model base
659031c2a00SAdrian Hunter
660254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
661031c2a00SAdrian Hunter
662031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
6634a0979d4SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
664031c2a00SAdrian Hunter
665ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
666ebd70c7dSAdrian Hunter		if pattern:
667ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
668ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
669ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
670ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
671ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
672ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
673ebd70c7dSAdrian Hunter				# Escape % and _
674ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
675ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
676ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
677ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
678ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
679ebd70c7dSAdrian Hunter			else:
680ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
681ebd70c7dSAdrian Hunter		else:
682ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
683254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
684ebd70c7dSAdrian Hunter
685ebd70c7dSAdrian Hunter	def Found(self, query, found):
686ebd70c7dSAdrian Hunter		if found:
687ebd70c7dSAdrian Hunter			return self.FindPath(query)
688ebd70c7dSAdrian Hunter		return []
689ebd70c7dSAdrian Hunter
690ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
691ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
692ebd70c7dSAdrian Hunter			found = query.first()
693ebd70c7dSAdrian Hunter		else:
694ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
695ebd70c7dSAdrian Hunter			found = query.next()
696ebd70c7dSAdrian Hunter		return self.Found(query, found)
697ebd70c7dSAdrian Hunter
698ebd70c7dSAdrian Hunter	def FindNext(self, query):
699ebd70c7dSAdrian Hunter		found = query.next()
700ebd70c7dSAdrian Hunter		if not found:
701ebd70c7dSAdrian Hunter			found = query.first()
702ebd70c7dSAdrian Hunter		return self.Found(query, found)
703ebd70c7dSAdrian Hunter
704ebd70c7dSAdrian Hunter	def FindPrev(self, query):
705ebd70c7dSAdrian Hunter		found = query.previous()
706ebd70c7dSAdrian Hunter		if not found:
707ebd70c7dSAdrian Hunter			found = query.last()
708ebd70c7dSAdrian Hunter		return self.Found(query, found)
709ebd70c7dSAdrian Hunter
710ebd70c7dSAdrian Hunter	def FindThread(self, c):
711ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
712ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
713ebd70c7dSAdrian Hunter		elif c.direction > 0:
714ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
715ebd70c7dSAdrian Hunter		else:
716ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
717ebd70c7dSAdrian Hunter		return (True, ids)
718ebd70c7dSAdrian Hunter
719ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
720ebd70c7dSAdrian Hunter		class Context():
721ebd70c7dSAdrian Hunter			def __init__(self, *x):
722ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
723ebd70c7dSAdrian Hunter			def Update(self, *x):
724ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
725ebd70c7dSAdrian Hunter		if len(context):
726ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
727ebd70c7dSAdrian Hunter		else:
728ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
729ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
730ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
731ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
732ebd70c7dSAdrian Hunter		thread.start()
733ebd70c7dSAdrian Hunter
734ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
735ebd70c7dSAdrian Hunter		callback(ids)
736ebd70c7dSAdrian Hunter
737254c0d82SAdrian Hunter# Context-sensitive call graph data model
738254c0d82SAdrian Hunter
739254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
740254c0d82SAdrian Hunter
741254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
742254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
743254c0d82SAdrian Hunter
744254c0d82SAdrian Hunter	def GetRoot(self):
7454a0979d4SAdrian Hunter		return CallGraphRootItem(self.glb, self.params)
746254c0d82SAdrian Hunter
747254c0d82SAdrian Hunter	def columnCount(self, parent=None):
74838a846d4SAdrian Hunter		if self.params.have_ipc:
74938a846d4SAdrian Hunter			return 12
75038a846d4SAdrian Hunter		else:
751254c0d82SAdrian Hunter			return 7
752254c0d82SAdrian Hunter
753254c0d82SAdrian Hunter	def columnHeader(self, column):
75438a846d4SAdrian Hunter		if self.params.have_ipc:
75538a846d4SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
75638a846d4SAdrian Hunter		else:
757254c0d82SAdrian Hunter			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
758254c0d82SAdrian Hunter		return headers[column]
759254c0d82SAdrian Hunter
760254c0d82SAdrian Hunter	def columnAlignment(self, column):
76138a846d4SAdrian Hunter		if self.params.have_ipc:
76238a846d4SAdrian 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 ]
76338a846d4SAdrian Hunter		else:
764254c0d82SAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
765254c0d82SAdrian Hunter		return alignment[column]
766254c0d82SAdrian Hunter
767254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
768254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
769254c0d82SAdrian Hunter						" FROM calls"
770254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
771254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
7727ff520b0SAdrian Hunter						" WHERE calls.id <> 0"
7737ff520b0SAdrian Hunter						" AND symbols.name" + match +
774254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
775254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
776254c0d82SAdrian Hunter
777254c0d82SAdrian Hunter	def FindPath(self, query):
778254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
779254c0d82SAdrian Hunter		# to open the tree at the right place.
780254c0d82SAdrian Hunter		ids = []
781254c0d82SAdrian Hunter		parent_id = query.value(0)
782254c0d82SAdrian Hunter		while parent_id:
783254c0d82SAdrian Hunter			ids.insert(0, parent_id)
784254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
785254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
786254c0d82SAdrian Hunter					" FROM call_paths"
787254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
788254c0d82SAdrian Hunter			if not q2.next():
789254c0d82SAdrian Hunter				break
790254c0d82SAdrian Hunter			parent_id = q2.value(0)
791254c0d82SAdrian Hunter		# The call path root is not used
792254c0d82SAdrian Hunter		if ids[0] == 1:
793254c0d82SAdrian Hunter			del ids[0]
794254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
795254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
796254c0d82SAdrian Hunter		return ids
797254c0d82SAdrian Hunter
798ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
799ae8b887cSAdrian Hunter
800ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
801ae8b887cSAdrian Hunter
802da4264f5SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
8034a0979d4SAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
804ae8b887cSAdrian Hunter		self.comm_id = comm_id
805ae8b887cSAdrian Hunter		self.thread_id = thread_id
806ae8b887cSAdrian Hunter		self.calls_id = calls_id
807da4264f5SAdrian Hunter		self.call_time = call_time
808da4264f5SAdrian Hunter		self.time = time
809b3b66079SAdrian Hunter		self.insn_cnt = insn_cnt
810b3b66079SAdrian Hunter		self.cyc_cnt = cyc_cnt
811ae8b887cSAdrian Hunter		self.branch_count = branch_count
812ae8b887cSAdrian Hunter
813ae8b887cSAdrian Hunter	def Select(self):
81426688729SAdrian Hunter		self.query_done = True
815ae8b887cSAdrian Hunter		if self.calls_id == 0:
816ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
817ae8b887cSAdrian Hunter		else:
818ae8b887cSAdrian Hunter			comm_thread = ""
819b3b66079SAdrian Hunter		if self.params.have_ipc:
820b3b66079SAdrian Hunter			ipc_str = ", insn_count, cyc_count"
821b3b66079SAdrian Hunter		else:
822b3b66079SAdrian Hunter			ipc_str = ""
823ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
824b3b66079SAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
825ae8b887cSAdrian Hunter					" FROM calls"
826ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
827ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
828ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
829ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
830ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
831ae8b887cSAdrian Hunter		while query.next():
832b3b66079SAdrian Hunter			if self.params.have_ipc:
833b3b66079SAdrian Hunter				insn_cnt = int(query.value(5))
834b3b66079SAdrian Hunter				cyc_cnt = int(query.value(6))
835b3b66079SAdrian Hunter				branch_count = int(query.value(7))
836b3b66079SAdrian Hunter			else:
837b3b66079SAdrian Hunter				insn_cnt = 0
838b3b66079SAdrian Hunter				cyc_cnt = 0
839b3b66079SAdrian Hunter				branch_count = int(query.value(5))
840b3b66079SAdrian 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)
841ae8b887cSAdrian Hunter			self.child_items.append(child_item)
842ae8b887cSAdrian Hunter			self.child_count += 1
843ae8b887cSAdrian Hunter
844ae8b887cSAdrian Hunter# Call tree data model level three item
845ae8b887cSAdrian Hunter
846ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
847ae8b887cSAdrian Hunter
848da4264f5SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
849da4264f5SAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item)
850ae8b887cSAdrian Hunter		dso = dsoname(dso)
851b3b66079SAdrian Hunter		if self.params.have_ipc:
852b3b66079SAdrian Hunter			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
853b3b66079SAdrian Hunter			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
854b3b66079SAdrian Hunter			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
855b3b66079SAdrian Hunter			ipc = CalcIPC(cyc_cnt, insn_cnt)
856da4264f5SAdrian Hunter			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
857b3b66079SAdrian Hunter		else:
858da4264f5SAdrian Hunter			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
859ae8b887cSAdrian Hunter		self.dbid = calls_id
860ae8b887cSAdrian Hunter
861ae8b887cSAdrian Hunter# Call tree data model level two item
862ae8b887cSAdrian Hunter
863ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
864ae8b887cSAdrian Hunter
8654a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
866da4264f5SAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, 0, parent_item)
867b3b66079SAdrian Hunter		if self.params.have_ipc:
868b3b66079SAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
869b3b66079SAdrian Hunter		else:
870ae8b887cSAdrian Hunter			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
871ae8b887cSAdrian Hunter		self.dbid = thread_id
872ae8b887cSAdrian Hunter
873ae8b887cSAdrian Hunter	def Select(self):
874ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
875ae8b887cSAdrian Hunter		for child_item in self.child_items:
876ae8b887cSAdrian Hunter			self.time += child_item.time
877b3b66079SAdrian Hunter			self.insn_cnt += child_item.insn_cnt
878b3b66079SAdrian Hunter			self.cyc_cnt += child_item.cyc_cnt
879ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
880ae8b887cSAdrian Hunter		for child_item in self.child_items:
881ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
882b3b66079SAdrian Hunter			if self.params.have_ipc:
883b3b66079SAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
884b3b66079SAdrian Hunter				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
885b3b66079SAdrian Hunter				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
886b3b66079SAdrian Hunter			else:
887ae8b887cSAdrian Hunter				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
888ae8b887cSAdrian Hunter
889ae8b887cSAdrian Hunter# Call tree data model level one item
890ae8b887cSAdrian Hunter
891ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
892ae8b887cSAdrian Hunter
8934a0979d4SAdrian Hunter	def __init__(self, glb, params, row, comm_id, comm, parent_item):
8944a0979d4SAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
895b3b66079SAdrian Hunter		if self.params.have_ipc:
896b3b66079SAdrian Hunter			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
897b3b66079SAdrian Hunter		else:
898ae8b887cSAdrian Hunter			self.data = [comm, "", "", "", "", "", ""]
899ae8b887cSAdrian Hunter		self.dbid = comm_id
900ae8b887cSAdrian Hunter
901ae8b887cSAdrian Hunter	def Select(self):
90226688729SAdrian Hunter		self.query_done = True
903ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
904ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
905ae8b887cSAdrian Hunter					" FROM comm_threads"
906ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
907ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
908ae8b887cSAdrian Hunter		while query.next():
9094a0979d4SAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
910ae8b887cSAdrian Hunter			self.child_items.append(child_item)
911ae8b887cSAdrian Hunter			self.child_count += 1
912ae8b887cSAdrian Hunter
913ae8b887cSAdrian Hunter# Call tree data model root item
914ae8b887cSAdrian Hunter
915ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
916ae8b887cSAdrian Hunter
9174a0979d4SAdrian Hunter	def __init__(self, glb, params):
9184a0979d4SAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, params, 0, None)
919ae8b887cSAdrian Hunter		self.dbid = 0
92026688729SAdrian Hunter		self.query_done = True
92126c11206SAdrian Hunter		if_has_calls = ""
92226c11206SAdrian Hunter		if IsSelectable(glb.db, "comms", columns = "has_calls"):
923af833988SAdrian Hunter			if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE
924ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
92526c11206SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
926ae8b887cSAdrian Hunter		while query.next():
927ae8b887cSAdrian Hunter			if not query.value(0):
928ae8b887cSAdrian Hunter				continue
9294a0979d4SAdrian Hunter			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
930ae8b887cSAdrian Hunter			self.child_items.append(child_item)
931ae8b887cSAdrian Hunter			self.child_count += 1
932ae8b887cSAdrian Hunter
933ae8b887cSAdrian Hunter# Call Tree data model
934ae8b887cSAdrian Hunter
935ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
936ae8b887cSAdrian Hunter
937ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
938ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
939ae8b887cSAdrian Hunter
940ae8b887cSAdrian Hunter	def GetRoot(self):
9414a0979d4SAdrian Hunter		return CallTreeRootItem(self.glb, self.params)
942ae8b887cSAdrian Hunter
943ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
944b3b66079SAdrian Hunter		if self.params.have_ipc:
945b3b66079SAdrian Hunter			return 12
946b3b66079SAdrian Hunter		else:
947ae8b887cSAdrian Hunter			return 7
948ae8b887cSAdrian Hunter
949ae8b887cSAdrian Hunter	def columnHeader(self, column):
950b3b66079SAdrian Hunter		if self.params.have_ipc:
951b3b66079SAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
952b3b66079SAdrian Hunter		else:
953ae8b887cSAdrian Hunter			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
954ae8b887cSAdrian Hunter		return headers[column]
955ae8b887cSAdrian Hunter
956ae8b887cSAdrian Hunter	def columnAlignment(self, column):
957b3b66079SAdrian Hunter		if self.params.have_ipc:
958b3b66079SAdrian 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 ]
959b3b66079SAdrian Hunter		else:
960ae8b887cSAdrian Hunter			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
961ae8b887cSAdrian Hunter		return alignment[column]
962ae8b887cSAdrian Hunter
963ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
964ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
965ae8b887cSAdrian Hunter						" FROM calls"
966ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
967ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
968031c8d5eSAdrian Hunter						" WHERE calls.id <> 0"
969031c8d5eSAdrian Hunter						" AND symbols.name" + match +
970ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
971ae8b887cSAdrian Hunter
972ae8b887cSAdrian Hunter	def FindPath(self, query):
973ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
974ae8b887cSAdrian Hunter		# to open the tree at the right place.
975ae8b887cSAdrian Hunter		ids = []
976ae8b887cSAdrian Hunter		parent_id = query.value(0)
977ae8b887cSAdrian Hunter		while parent_id:
978ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
979ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
980ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
981ae8b887cSAdrian Hunter					" FROM calls"
982ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
983ae8b887cSAdrian Hunter			if not q2.next():
984ae8b887cSAdrian Hunter				break
985ae8b887cSAdrian Hunter			parent_id = q2.value(0)
986ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
987ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
988ae8b887cSAdrian Hunter		return ids
989ae8b887cSAdrian Hunter
99042c303ffSAdrian Hunter# Vertical layout
99142c303ffSAdrian Hunter
99242c303ffSAdrian Hunterclass HBoxLayout(QHBoxLayout):
99342c303ffSAdrian Hunter
99442c303ffSAdrian Hunter	def __init__(self, *children):
99542c303ffSAdrian Hunter		super(HBoxLayout, self).__init__()
99642c303ffSAdrian Hunter
99742c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
99842c303ffSAdrian Hunter		for child in children:
99942c303ffSAdrian Hunter			if child.isWidgetType():
100042c303ffSAdrian Hunter				self.layout().addWidget(child)
100142c303ffSAdrian Hunter			else:
100242c303ffSAdrian Hunter				self.layout().addLayout(child)
100342c303ffSAdrian Hunter
100442c303ffSAdrian Hunter# Horizontal layout
100542c303ffSAdrian Hunter
100642c303ffSAdrian Hunterclass VBoxLayout(QVBoxLayout):
100742c303ffSAdrian Hunter
100842c303ffSAdrian Hunter	def __init__(self, *children):
100942c303ffSAdrian Hunter		super(VBoxLayout, self).__init__()
101042c303ffSAdrian Hunter
101142c303ffSAdrian Hunter		self.layout().setContentsMargins(0, 0, 0, 0)
101242c303ffSAdrian Hunter		for child in children:
101342c303ffSAdrian Hunter			if child.isWidgetType():
101442c303ffSAdrian Hunter				self.layout().addWidget(child)
101542c303ffSAdrian Hunter			else:
101642c303ffSAdrian Hunter				self.layout().addLayout(child)
101742c303ffSAdrian Hunter
101842c303ffSAdrian Hunter# Vertical layout widget
1019ebd70c7dSAdrian Hunter
1020ebd70c7dSAdrian Hunterclass VBox():
1021ebd70c7dSAdrian Hunter
102242c303ffSAdrian Hunter	def __init__(self, *children):
1023ebd70c7dSAdrian Hunter		self.vbox = QWidget()
102442c303ffSAdrian Hunter		self.vbox.setLayout(VBoxLayout(*children))
1025ebd70c7dSAdrian Hunter
1026ebd70c7dSAdrian Hunter	def Widget(self):
1027ebd70c7dSAdrian Hunter		return self.vbox
1028ebd70c7dSAdrian Hunter
1029a731cc4cSAdrian Hunter# Tree window base
10301beb5c7bSAdrian Hunter
1031a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
10321beb5c7bSAdrian Hunter
1033a731cc4cSAdrian Hunter	def __init__(self, parent=None):
1034a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
10351beb5c7bSAdrian Hunter
1036a731cc4cSAdrian Hunter		self.model = None
1037a731cc4cSAdrian Hunter		self.find_bar = None
10381beb5c7bSAdrian Hunter
1039be6e7471SAdrian Hunter		self.view = QTreeView()
104096c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
104196c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
1042be6e7471SAdrian Hunter
10439bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
10449bc4e4bfSAdrian Hunter
1045ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
1046ebd70c7dSAdrian Hunter		if not len(ids):
1047ebd70c7dSAdrian Hunter			return False
1048ebd70c7dSAdrian Hunter		parent = QModelIndex()
1049ebd70c7dSAdrian Hunter		for dbid in ids:
1050ebd70c7dSAdrian Hunter			found = False
1051ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
1052ebd70c7dSAdrian Hunter			for row in xrange(n):
1053ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
1054ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
1055ebd70c7dSAdrian Hunter					found = True
10563a3cf7c5SAdrian Hunter					self.view.setExpanded(parent, True)
1057ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
1058ebd70c7dSAdrian Hunter					parent = child
1059ebd70c7dSAdrian Hunter					break
1060ebd70c7dSAdrian Hunter			if not found:
1061ebd70c7dSAdrian Hunter				break
1062ebd70c7dSAdrian Hunter		return found
1063ebd70c7dSAdrian Hunter
1064ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
1065ebd70c7dSAdrian Hunter		self.view.setFocus()
1066ebd70c7dSAdrian Hunter		self.find_bar.Busy()
1067ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
1068ebd70c7dSAdrian Hunter
1069ebd70c7dSAdrian Hunter	def FindDone(self, ids):
1070ebd70c7dSAdrian Hunter		found = True
1071ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
1072ebd70c7dSAdrian Hunter			found = False
1073ebd70c7dSAdrian Hunter		self.find_bar.Idle()
1074ebd70c7dSAdrian Hunter		if not found:
1075ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
1076ebd70c7dSAdrian Hunter
1077a731cc4cSAdrian Hunter
1078a731cc4cSAdrian Hunter# Context-sensitive call graph window
1079a731cc4cSAdrian Hunter
1080a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
1081a731cc4cSAdrian Hunter
1082a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
1083a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
1084a731cc4cSAdrian Hunter
1085a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
1086a731cc4cSAdrian Hunter
1087a731cc4cSAdrian Hunter		self.view.setModel(self.model)
1088a731cc4cSAdrian Hunter
1089a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
1090a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
1091a731cc4cSAdrian Hunter
1092a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
1093a731cc4cSAdrian Hunter
1094a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1095a731cc4cSAdrian Hunter
1096a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1097a731cc4cSAdrian Hunter
1098a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
1099a731cc4cSAdrian Hunter
1100ae8b887cSAdrian Hunter# Call tree window
1101ae8b887cSAdrian Hunter
1102ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
1103ae8b887cSAdrian Hunter
1104e69d5df7SAdrian Hunter	def __init__(self, glb, parent=None, thread_at_time=None):
1105ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
1106ae8b887cSAdrian Hunter
1107ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
1108ae8b887cSAdrian Hunter
1109ae8b887cSAdrian Hunter		self.view.setModel(self.model)
1110ae8b887cSAdrian Hunter
1111ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
1112ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
1113ae8b887cSAdrian Hunter
1114ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
1115ae8b887cSAdrian Hunter
1116ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
1117ae8b887cSAdrian Hunter
1118ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
1119ae8b887cSAdrian Hunter
1120ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
1121ae8b887cSAdrian Hunter
1122e69d5df7SAdrian Hunter		if thread_at_time:
1123e69d5df7SAdrian Hunter			self.DisplayThreadAtTime(*thread_at_time)
1124e69d5df7SAdrian Hunter
1125e69d5df7SAdrian Hunter	def DisplayThreadAtTime(self, comm_id, thread_id, time):
1126e69d5df7SAdrian Hunter		parent = QModelIndex()
1127e69d5df7SAdrian Hunter		for dbid in (comm_id, thread_id):
1128e69d5df7SAdrian Hunter			found = False
1129e69d5df7SAdrian Hunter			n = self.model.rowCount(parent)
1130e69d5df7SAdrian Hunter			for row in xrange(n):
1131e69d5df7SAdrian Hunter				child = self.model.index(row, 0, parent)
1132e69d5df7SAdrian Hunter				if child.internalPointer().dbid == dbid:
1133e69d5df7SAdrian Hunter					found = True
1134f18d5cf8SAdrian Hunter					self.view.setExpanded(parent, True)
1135e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1136e69d5df7SAdrian Hunter					parent = child
1137e69d5df7SAdrian Hunter					break
1138e69d5df7SAdrian Hunter			if not found:
1139e69d5df7SAdrian Hunter				return
1140e69d5df7SAdrian Hunter		found = False
1141e69d5df7SAdrian Hunter		while True:
1142e69d5df7SAdrian Hunter			n = self.model.rowCount(parent)
1143e69d5df7SAdrian Hunter			if not n:
1144e69d5df7SAdrian Hunter				return
1145e69d5df7SAdrian Hunter			last_child = None
1146e69d5df7SAdrian Hunter			for row in xrange(n):
1147f18d5cf8SAdrian Hunter				self.view.setExpanded(parent, True)
1148e69d5df7SAdrian Hunter				child = self.model.index(row, 0, parent)
1149e69d5df7SAdrian Hunter				child_call_time = child.internalPointer().call_time
1150e69d5df7SAdrian Hunter				if child_call_time < time:
1151e69d5df7SAdrian Hunter					last_child = child
1152e69d5df7SAdrian Hunter				elif child_call_time == time:
1153e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1154e69d5df7SAdrian Hunter					return
1155e69d5df7SAdrian Hunter				elif child_call_time > time:
1156e69d5df7SAdrian Hunter					break
1157e69d5df7SAdrian Hunter			if not last_child:
1158e69d5df7SAdrian Hunter				if not found:
1159e69d5df7SAdrian Hunter					child = self.model.index(0, 0, parent)
1160f18d5cf8SAdrian Hunter					self.view.setExpanded(parent, True)
1161e69d5df7SAdrian Hunter					self.view.setCurrentIndex(child)
1162e69d5df7SAdrian Hunter				return
1163e69d5df7SAdrian Hunter			found = True
1164f18d5cf8SAdrian Hunter			self.view.setExpanded(parent, True)
1165e69d5df7SAdrian Hunter			self.view.setCurrentIndex(last_child)
1166e69d5df7SAdrian Hunter			parent = last_child
1167e69d5df7SAdrian Hunter
1168b3700f21SAdrian Hunter# ExecComm() gets the comm_id of the command string that was set when the process exec'd i.e. the program name
1169b3700f21SAdrian Hunter
1170b3700f21SAdrian Hunterdef ExecComm(db, thread_id, time):
1171b3700f21SAdrian Hunter	query = QSqlQuery(db)
1172b3700f21SAdrian Hunter	QueryExec(query, "SELECT comm_threads.comm_id, comms.c_time, comms.exec_flag"
1173b3700f21SAdrian Hunter				" FROM comm_threads"
1174b3700f21SAdrian Hunter				" INNER JOIN comms ON comms.id = comm_threads.comm_id"
1175b3700f21SAdrian Hunter				" WHERE comm_threads.thread_id = " + str(thread_id) +
1176b3700f21SAdrian Hunter				" ORDER BY comms.c_time, comms.id")
1177b3700f21SAdrian Hunter	first = None
1178b3700f21SAdrian Hunter	last = None
1179b3700f21SAdrian Hunter	while query.next():
1180b3700f21SAdrian Hunter		if first is None:
1181b3700f21SAdrian Hunter			first = query.value(0)
1182b3700f21SAdrian Hunter		if query.value(2) and Decimal(query.value(1)) <= Decimal(time):
1183b3700f21SAdrian Hunter			last = query.value(0)
1184b3700f21SAdrian Hunter	if not(last is None):
1185b3700f21SAdrian Hunter		return last
1186b3700f21SAdrian Hunter	return first
1187b3700f21SAdrian Hunter
1188b3700f21SAdrian Hunter# Container for (x, y) data
1189b3700f21SAdrian Hunter
1190b3700f21SAdrian Hunterclass XY():
1191b3700f21SAdrian Hunter	def __init__(self, x=0, y=0):
1192b3700f21SAdrian Hunter		self.x = x
1193b3700f21SAdrian Hunter		self.y = y
1194b3700f21SAdrian Hunter
1195b3700f21SAdrian Hunter	def __str__(self):
1196b3700f21SAdrian Hunter		return "XY({}, {})".format(str(self.x), str(self.y))
1197b3700f21SAdrian Hunter
1198b3700f21SAdrian Hunter# Container for sub-range data
1199b3700f21SAdrian Hunter
1200b3700f21SAdrian Hunterclass Subrange():
1201b3700f21SAdrian Hunter	def __init__(self, lo=0, hi=0):
1202b3700f21SAdrian Hunter		self.lo = lo
1203b3700f21SAdrian Hunter		self.hi = hi
1204b3700f21SAdrian Hunter
1205b3700f21SAdrian Hunter	def __str__(self):
1206b3700f21SAdrian Hunter		return "Subrange({}, {})".format(str(self.lo), str(self.hi))
1207b3700f21SAdrian Hunter
1208b3700f21SAdrian Hunter# Graph data region base class
1209b3700f21SAdrian Hunter
1210b3700f21SAdrian Hunterclass GraphDataRegion(object):
1211b3700f21SAdrian Hunter
1212b3700f21SAdrian Hunter	def __init__(self, key, title = "", ordinal = ""):
1213b3700f21SAdrian Hunter		self.key = key
1214b3700f21SAdrian Hunter		self.title = title
1215b3700f21SAdrian Hunter		self.ordinal = ordinal
1216b3700f21SAdrian Hunter
1217b3700f21SAdrian Hunter# Function to sort GraphDataRegion
1218b3700f21SAdrian Hunter
1219b3700f21SAdrian Hunterdef GraphDataRegionOrdinal(data_region):
1220b3700f21SAdrian Hunter	return data_region.ordinal
1221b3700f21SAdrian Hunter
1222b3700f21SAdrian Hunter# Attributes for a graph region
1223b3700f21SAdrian Hunter
1224b3700f21SAdrian Hunterclass GraphRegionAttribute():
1225b3700f21SAdrian Hunter
1226b3700f21SAdrian Hunter	def __init__(self, colour):
1227b3700f21SAdrian Hunter		self.colour = colour
1228b3700f21SAdrian Hunter
1229b3700f21SAdrian Hunter# Switch graph data region represents a task
1230b3700f21SAdrian Hunter
1231b3700f21SAdrian Hunterclass SwitchGraphDataRegion(GraphDataRegion):
1232b3700f21SAdrian Hunter
1233b3700f21SAdrian Hunter	def __init__(self, key, exec_comm_id, pid, tid, comm, thread_id, comm_id):
1234b3700f21SAdrian Hunter		super(SwitchGraphDataRegion, self).__init__(key)
1235b3700f21SAdrian Hunter
1236b3700f21SAdrian Hunter		self.title = str(pid) + " / " + str(tid) + " " + comm
1237b3700f21SAdrian Hunter		# Order graph legend within exec comm by pid / tid / time
1238b3700f21SAdrian Hunter		self.ordinal = str(pid).rjust(16) + str(exec_comm_id).rjust(8) + str(tid).rjust(16)
1239b3700f21SAdrian Hunter		self.exec_comm_id = exec_comm_id
1240b3700f21SAdrian Hunter		self.pid = pid
1241b3700f21SAdrian Hunter		self.tid = tid
1242b3700f21SAdrian Hunter		self.comm = comm
1243b3700f21SAdrian Hunter		self.thread_id = thread_id
1244b3700f21SAdrian Hunter		self.comm_id = comm_id
1245b3700f21SAdrian Hunter
1246b3700f21SAdrian Hunter# Graph data point
1247b3700f21SAdrian Hunter
1248b3700f21SAdrian Hunterclass GraphDataPoint():
1249b3700f21SAdrian Hunter
1250b3700f21SAdrian Hunter	def __init__(self, data, index, x, y, altx=None, alty=None, hregion=None, vregion=None):
1251b3700f21SAdrian Hunter		self.data = data
1252b3700f21SAdrian Hunter		self.index = index
1253b3700f21SAdrian Hunter		self.x = x
1254b3700f21SAdrian Hunter		self.y = y
1255b3700f21SAdrian Hunter		self.altx = altx
1256b3700f21SAdrian Hunter		self.alty = alty
1257b3700f21SAdrian Hunter		self.hregion = hregion
1258b3700f21SAdrian Hunter		self.vregion = vregion
1259b3700f21SAdrian Hunter
1260b3700f21SAdrian Hunter# Graph data (single graph) base class
1261b3700f21SAdrian Hunter
1262b3700f21SAdrian Hunterclass GraphData(object):
1263b3700f21SAdrian Hunter
1264b3700f21SAdrian Hunter	def __init__(self, collection, xbase=Decimal(0), ybase=Decimal(0)):
1265b3700f21SAdrian Hunter		self.collection = collection
1266b3700f21SAdrian Hunter		self.points = []
1267b3700f21SAdrian Hunter		self.xbase = xbase
1268b3700f21SAdrian Hunter		self.ybase = ybase
1269b3700f21SAdrian Hunter		self.title = ""
1270b3700f21SAdrian Hunter
1271b3700f21SAdrian Hunter	def AddPoint(self, x, y, altx=None, alty=None, hregion=None, vregion=None):
1272b3700f21SAdrian Hunter		index = len(self.points)
1273b3700f21SAdrian Hunter
1274b3700f21SAdrian Hunter		x = float(Decimal(x) - self.xbase)
1275b3700f21SAdrian Hunter		y = float(Decimal(y) - self.ybase)
1276b3700f21SAdrian Hunter
1277b3700f21SAdrian Hunter		self.points.append(GraphDataPoint(self, index, x, y, altx, alty, hregion, vregion))
1278b3700f21SAdrian Hunter
1279b3700f21SAdrian Hunter	def XToData(self, x):
1280b3700f21SAdrian Hunter		return Decimal(x) + self.xbase
1281b3700f21SAdrian Hunter
1282b3700f21SAdrian Hunter	def YToData(self, y):
1283b3700f21SAdrian Hunter		return Decimal(y) + self.ybase
1284b3700f21SAdrian Hunter
1285b3700f21SAdrian Hunter# Switch graph data (for one CPU)
1286b3700f21SAdrian Hunter
1287b3700f21SAdrian Hunterclass SwitchGraphData(GraphData):
1288b3700f21SAdrian Hunter
1289b3700f21SAdrian Hunter	def __init__(self, db, collection, cpu, xbase):
1290b3700f21SAdrian Hunter		super(SwitchGraphData, self).__init__(collection, xbase)
1291b3700f21SAdrian Hunter
1292b3700f21SAdrian Hunter		self.cpu = cpu
1293b3700f21SAdrian Hunter		self.title = "CPU " + str(cpu)
1294b3700f21SAdrian Hunter		self.SelectSwitches(db)
1295b3700f21SAdrian Hunter
1296b3700f21SAdrian Hunter	def SelectComms(self, db, thread_id, last_comm_id, start_time, end_time):
1297b3700f21SAdrian Hunter		query = QSqlQuery(db)
1298b3700f21SAdrian Hunter		QueryExec(query, "SELECT id, c_time"
1299b3700f21SAdrian Hunter					" FROM comms"
1300b3700f21SAdrian Hunter					" WHERE c_thread_id = " + str(thread_id) +
1301af833988SAdrian Hunter					"   AND exec_flag = " + self.collection.glb.dbref.TRUE +
1302b3700f21SAdrian Hunter					"   AND c_time >= " + str(start_time) +
1303b3700f21SAdrian Hunter					"   AND c_time <= " + str(end_time) +
1304b3700f21SAdrian Hunter					" ORDER BY c_time, id")
1305b3700f21SAdrian Hunter		while query.next():
1306b3700f21SAdrian Hunter			comm_id = query.value(0)
1307b3700f21SAdrian Hunter			if comm_id == last_comm_id:
1308b3700f21SAdrian Hunter				continue
1309b3700f21SAdrian Hunter			time = query.value(1)
1310b3700f21SAdrian Hunter			hregion = self.HRegion(db, thread_id, comm_id, time)
1311b3700f21SAdrian Hunter			self.AddPoint(time, 1000, None, None, hregion)
1312b3700f21SAdrian Hunter
1313b3700f21SAdrian Hunter	def SelectSwitches(self, db):
1314b3700f21SAdrian Hunter		last_time = None
1315b3700f21SAdrian Hunter		last_comm_id = None
1316b3700f21SAdrian Hunter		last_thread_id = None
1317b3700f21SAdrian Hunter		query = QSqlQuery(db)
1318b3700f21SAdrian Hunter		QueryExec(query, "SELECT time, thread_out_id, thread_in_id, comm_out_id, comm_in_id, flags"
1319b3700f21SAdrian Hunter					" FROM context_switches"
1320b3700f21SAdrian Hunter					" WHERE machine_id = " + str(self.collection.machine_id) +
1321b3700f21SAdrian Hunter					"   AND cpu = " + str(self.cpu) +
1322b3700f21SAdrian Hunter					" ORDER BY time, id")
1323b3700f21SAdrian Hunter		while query.next():
1324b3700f21SAdrian Hunter			flags = int(query.value(5))
1325b3700f21SAdrian Hunter			if flags & 1:
1326b3700f21SAdrian Hunter				# Schedule-out: detect and add exec's
1327b3700f21SAdrian Hunter				if last_thread_id == query.value(1) and last_comm_id is not None and last_comm_id != query.value(3):
1328b3700f21SAdrian Hunter					self.SelectComms(db, last_thread_id, last_comm_id, last_time, query.value(0))
1329b3700f21SAdrian Hunter				continue
1330b3700f21SAdrian Hunter			# Schedule-in: add data point
1331b3700f21SAdrian Hunter			if len(self.points) == 0:
1332b3700f21SAdrian Hunter				start_time = self.collection.glb.StartTime(self.collection.machine_id)
1333b3700f21SAdrian Hunter				hregion = self.HRegion(db, query.value(1), query.value(3), start_time)
1334b3700f21SAdrian Hunter				self.AddPoint(start_time, 1000, None, None, hregion)
1335b3700f21SAdrian Hunter			time = query.value(0)
1336b3700f21SAdrian Hunter			comm_id = query.value(4)
1337b3700f21SAdrian Hunter			thread_id = query.value(2)
1338b3700f21SAdrian Hunter			hregion = self.HRegion(db, thread_id, comm_id, time)
1339b3700f21SAdrian Hunter			self.AddPoint(time, 1000, None, None, hregion)
1340b3700f21SAdrian Hunter			last_time = time
1341b3700f21SAdrian Hunter			last_comm_id = comm_id
1342b3700f21SAdrian Hunter			last_thread_id = thread_id
1343b3700f21SAdrian Hunter
1344b3700f21SAdrian Hunter	def NewHRegion(self, db, key, thread_id, comm_id, time):
1345b3700f21SAdrian Hunter		exec_comm_id = ExecComm(db, thread_id, time)
1346b3700f21SAdrian Hunter		query = QSqlQuery(db)
1347b3700f21SAdrian Hunter		QueryExec(query, "SELECT pid, tid FROM threads WHERE id = " + str(thread_id))
1348b3700f21SAdrian Hunter		if query.next():
1349b3700f21SAdrian Hunter			pid = query.value(0)
1350b3700f21SAdrian Hunter			tid = query.value(1)
1351b3700f21SAdrian Hunter		else:
1352b3700f21SAdrian Hunter			pid = -1
1353b3700f21SAdrian Hunter			tid = -1
1354b3700f21SAdrian Hunter		query = QSqlQuery(db)
1355b3700f21SAdrian Hunter		QueryExec(query, "SELECT comm FROM comms WHERE id = " + str(comm_id))
1356b3700f21SAdrian Hunter		if query.next():
1357b3700f21SAdrian Hunter			comm = query.value(0)
1358b3700f21SAdrian Hunter		else:
1359b3700f21SAdrian Hunter			comm = ""
1360b3700f21SAdrian Hunter		return SwitchGraphDataRegion(key, exec_comm_id, pid, tid, comm, thread_id, comm_id)
1361b3700f21SAdrian Hunter
1362b3700f21SAdrian Hunter	def HRegion(self, db, thread_id, comm_id, time):
1363b3700f21SAdrian Hunter		key = str(thread_id) + ":" + str(comm_id)
1364b3700f21SAdrian Hunter		hregion = self.collection.LookupHRegion(key)
1365b3700f21SAdrian Hunter		if hregion is None:
1366b3700f21SAdrian Hunter			hregion = self.NewHRegion(db, key, thread_id, comm_id, time)
1367b3700f21SAdrian Hunter			self.collection.AddHRegion(key, hregion)
1368b3700f21SAdrian Hunter		return hregion
1369b3700f21SAdrian Hunter
1370b3700f21SAdrian Hunter# Graph data collection (multiple related graphs) base class
1371b3700f21SAdrian Hunter
1372b3700f21SAdrian Hunterclass GraphDataCollection(object):
1373b3700f21SAdrian Hunter
1374b3700f21SAdrian Hunter	def __init__(self, glb):
1375b3700f21SAdrian Hunter		self.glb = glb
1376b3700f21SAdrian Hunter		self.data = []
1377b3700f21SAdrian Hunter		self.hregions = {}
1378b3700f21SAdrian Hunter		self.xrangelo = None
1379b3700f21SAdrian Hunter		self.xrangehi = None
1380b3700f21SAdrian Hunter		self.yrangelo = None
1381b3700f21SAdrian Hunter		self.yrangehi = None
1382b3700f21SAdrian Hunter		self.dp = XY(0, 0)
1383b3700f21SAdrian Hunter
1384b3700f21SAdrian Hunter	def AddGraphData(self, data):
1385b3700f21SAdrian Hunter		self.data.append(data)
1386b3700f21SAdrian Hunter
1387b3700f21SAdrian Hunter	def LookupHRegion(self, key):
1388b3700f21SAdrian Hunter		if key in self.hregions:
1389b3700f21SAdrian Hunter			return self.hregions[key]
1390b3700f21SAdrian Hunter		return None
1391b3700f21SAdrian Hunter
1392b3700f21SAdrian Hunter	def AddHRegion(self, key, hregion):
1393b3700f21SAdrian Hunter		self.hregions[key] = hregion
1394b3700f21SAdrian Hunter
1395b3700f21SAdrian Hunter# Switch graph data collection (SwitchGraphData for each CPU)
1396b3700f21SAdrian Hunter
1397b3700f21SAdrian Hunterclass SwitchGraphDataCollection(GraphDataCollection):
1398b3700f21SAdrian Hunter
1399b3700f21SAdrian Hunter	def __init__(self, glb, db, machine_id):
1400b3700f21SAdrian Hunter		super(SwitchGraphDataCollection, self).__init__(glb)
1401b3700f21SAdrian Hunter
1402b3700f21SAdrian Hunter		self.machine_id = machine_id
1403b3700f21SAdrian Hunter		self.cpus = self.SelectCPUs(db)
1404b3700f21SAdrian Hunter
1405b3700f21SAdrian Hunter		self.xrangelo = glb.StartTime(machine_id)
1406b3700f21SAdrian Hunter		self.xrangehi = glb.FinishTime(machine_id)
1407b3700f21SAdrian Hunter
1408b3700f21SAdrian Hunter		self.yrangelo = Decimal(0)
1409b3700f21SAdrian Hunter		self.yrangehi = Decimal(1000)
1410b3700f21SAdrian Hunter
1411b3700f21SAdrian Hunter		for cpu in self.cpus:
1412b3700f21SAdrian Hunter			self.AddGraphData(SwitchGraphData(db, self, cpu, self.xrangelo))
1413b3700f21SAdrian Hunter
1414b3700f21SAdrian Hunter	def SelectCPUs(self, db):
1415b3700f21SAdrian Hunter		cpus = []
1416b3700f21SAdrian Hunter		query = QSqlQuery(db)
1417b3700f21SAdrian Hunter		QueryExec(query, "SELECT DISTINCT cpu"
1418b3700f21SAdrian Hunter					" FROM context_switches"
1419b3700f21SAdrian Hunter					" WHERE machine_id = " + str(self.machine_id))
1420b3700f21SAdrian Hunter		while query.next():
1421b3700f21SAdrian Hunter			cpus.append(int(query.value(0)))
1422b3700f21SAdrian Hunter		return sorted(cpus)
1423b3700f21SAdrian Hunter
1424b3700f21SAdrian Hunter# Switch graph data graphics item displays the graphed data
1425b3700f21SAdrian Hunter
1426b3700f21SAdrian Hunterclass SwitchGraphDataGraphicsItem(QGraphicsItem):
1427b3700f21SAdrian Hunter
1428b3700f21SAdrian Hunter	def __init__(self, data, graph_width, graph_height, attrs, event_handler, parent=None):
1429b3700f21SAdrian Hunter		super(SwitchGraphDataGraphicsItem, self).__init__(parent)
1430b3700f21SAdrian Hunter
1431b3700f21SAdrian Hunter		self.data = data
1432b3700f21SAdrian Hunter		self.graph_width = graph_width
1433b3700f21SAdrian Hunter		self.graph_height = graph_height
1434b3700f21SAdrian Hunter		self.attrs = attrs
1435b3700f21SAdrian Hunter		self.event_handler = event_handler
1436b3700f21SAdrian Hunter		self.setAcceptHoverEvents(True)
1437b3700f21SAdrian Hunter
1438b3700f21SAdrian Hunter	def boundingRect(self):
1439b3700f21SAdrian Hunter		return QRectF(0, 0, self.graph_width, self.graph_height)
1440b3700f21SAdrian Hunter
1441b3700f21SAdrian Hunter	def PaintPoint(self, painter, last, x):
1442b3700f21SAdrian Hunter		if not(last is None or last.hregion.pid == 0 or x < self.attrs.subrange.x.lo):
1443b3700f21SAdrian Hunter			if last.x < self.attrs.subrange.x.lo:
1444b3700f21SAdrian Hunter				x0 = self.attrs.subrange.x.lo
1445b3700f21SAdrian Hunter			else:
1446b3700f21SAdrian Hunter				x0 = last.x
1447b3700f21SAdrian Hunter			if x > self.attrs.subrange.x.hi:
1448b3700f21SAdrian Hunter				x1 = self.attrs.subrange.x.hi
1449b3700f21SAdrian Hunter			else:
1450b3700f21SAdrian Hunter				x1 = x - 1
1451b3700f21SAdrian Hunter			x0 = self.attrs.XToPixel(x0)
1452b3700f21SAdrian Hunter			x1 = self.attrs.XToPixel(x1)
1453b3700f21SAdrian Hunter
1454b3700f21SAdrian Hunter			y0 = self.attrs.YToPixel(last.y)
1455b3700f21SAdrian Hunter
1456b3700f21SAdrian Hunter			colour = self.attrs.region_attributes[last.hregion.key].colour
1457b3700f21SAdrian Hunter
1458b3700f21SAdrian Hunter			width = x1 - x0 + 1
1459b3700f21SAdrian Hunter			if width < 2:
1460b3700f21SAdrian Hunter				painter.setPen(colour)
1461b3700f21SAdrian Hunter				painter.drawLine(x0, self.graph_height - y0, x0, self.graph_height)
1462b3700f21SAdrian Hunter			else:
1463b3700f21SAdrian Hunter				painter.fillRect(x0, self.graph_height - y0, width, self.graph_height - 1, colour)
1464b3700f21SAdrian Hunter
1465b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1466b3700f21SAdrian Hunter		last = None
1467b3700f21SAdrian Hunter		for point in self.data.points:
1468b3700f21SAdrian Hunter			self.PaintPoint(painter, last, point.x)
1469b3700f21SAdrian Hunter			if point.x > self.attrs.subrange.x.hi:
1470b3700f21SAdrian Hunter				break;
1471b3700f21SAdrian Hunter			last = point
1472b3700f21SAdrian Hunter		self.PaintPoint(painter, last, self.attrs.subrange.x.hi + 1)
1473b3700f21SAdrian Hunter
1474b3700f21SAdrian Hunter	def BinarySearchPoint(self, target):
1475b3700f21SAdrian Hunter		lower_pos = 0
1476b3700f21SAdrian Hunter		higher_pos = len(self.data.points)
1477b3700f21SAdrian Hunter		while True:
1478b3700f21SAdrian Hunter			pos = int((lower_pos + higher_pos) / 2)
1479b3700f21SAdrian Hunter			val = self.data.points[pos].x
1480b3700f21SAdrian Hunter			if target >= val:
1481b3700f21SAdrian Hunter				lower_pos = pos
1482b3700f21SAdrian Hunter			else:
1483b3700f21SAdrian Hunter				higher_pos = pos
1484b3700f21SAdrian Hunter			if higher_pos <= lower_pos + 1:
1485b3700f21SAdrian Hunter				return lower_pos
1486b3700f21SAdrian Hunter
1487b3700f21SAdrian Hunter	def XPixelToData(self, x):
1488b3700f21SAdrian Hunter		x = self.attrs.PixelToX(x)
1489b3700f21SAdrian Hunter		if x < self.data.points[0].x:
1490b3700f21SAdrian Hunter			x = 0
1491b3700f21SAdrian Hunter			pos = 0
1492b3700f21SAdrian Hunter			low = True
1493b3700f21SAdrian Hunter		else:
1494b3700f21SAdrian Hunter			pos = self.BinarySearchPoint(x)
1495b3700f21SAdrian Hunter			low = False
1496b3700f21SAdrian Hunter		return (low, pos, self.data.XToData(x))
1497b3700f21SAdrian Hunter
1498b3700f21SAdrian Hunter	def EventToData(self, event):
1499b3700f21SAdrian Hunter		no_data = (None,) * 4
1500b3700f21SAdrian Hunter		if len(self.data.points) < 1:
1501b3700f21SAdrian Hunter			return no_data
1502b3700f21SAdrian Hunter		x = event.pos().x()
1503b3700f21SAdrian Hunter		if x < 0:
1504b3700f21SAdrian Hunter			return no_data
1505b3700f21SAdrian Hunter		low0, pos0, time_from = self.XPixelToData(x)
1506b3700f21SAdrian Hunter		low1, pos1, time_to = self.XPixelToData(x + 1)
1507b3700f21SAdrian Hunter		hregions = set()
1508b3700f21SAdrian Hunter		hregion_times = []
1509b3700f21SAdrian Hunter		if not low1:
1510b3700f21SAdrian Hunter			for i in xrange(pos0, pos1 + 1):
1511b3700f21SAdrian Hunter				hregion = self.data.points[i].hregion
1512b3700f21SAdrian Hunter				hregions.add(hregion)
1513b3700f21SAdrian Hunter				if i == pos0:
1514b3700f21SAdrian Hunter					time = time_from
1515b3700f21SAdrian Hunter				else:
1516b3700f21SAdrian Hunter					time = self.data.XToData(self.data.points[i].x)
1517b3700f21SAdrian Hunter				hregion_times.append((hregion, time))
1518b3700f21SAdrian Hunter		return (time_from, time_to, hregions, hregion_times)
1519b3700f21SAdrian Hunter
1520b3700f21SAdrian Hunter	def hoverMoveEvent(self, event):
1521b3700f21SAdrian Hunter		time_from, time_to, hregions, hregion_times = self.EventToData(event)
1522b3700f21SAdrian Hunter		if time_from is not None:
1523b3700f21SAdrian Hunter			self.event_handler.PointEvent(self.data.cpu, time_from, time_to, hregions)
1524b3700f21SAdrian Hunter
1525b3700f21SAdrian Hunter	def hoverLeaveEvent(self, event):
1526b3700f21SAdrian Hunter		self.event_handler.NoPointEvent()
1527b3700f21SAdrian Hunter
1528b3700f21SAdrian Hunter	def mousePressEvent(self, event):
1529b3700f21SAdrian Hunter		if event.button() != Qt.RightButton:
1530b3700f21SAdrian Hunter			super(SwitchGraphDataGraphicsItem, self).mousePressEvent(event)
1531b3700f21SAdrian Hunter			return
1532b3700f21SAdrian Hunter		time_from, time_to, hregions, hregion_times = self.EventToData(event)
1533b3700f21SAdrian Hunter		if hregion_times:
1534b3700f21SAdrian Hunter			self.event_handler.RightClickEvent(self.data.cpu, hregion_times, event.screenPos())
1535b3700f21SAdrian Hunter
1536b3700f21SAdrian Hunter# X-axis graphics item
1537b3700f21SAdrian Hunter
1538b3700f21SAdrian Hunterclass XAxisGraphicsItem(QGraphicsItem):
1539b3700f21SAdrian Hunter
1540b3700f21SAdrian Hunter	def __init__(self, width, parent=None):
1541b3700f21SAdrian Hunter		super(XAxisGraphicsItem, self).__init__(parent)
1542b3700f21SAdrian Hunter
1543b3700f21SAdrian Hunter		self.width = width
1544b3700f21SAdrian Hunter		self.max_mark_sz = 4
1545b3700f21SAdrian Hunter		self.height = self.max_mark_sz + 1
1546b3700f21SAdrian Hunter
1547b3700f21SAdrian Hunter	def boundingRect(self):
1548b3700f21SAdrian Hunter		return QRectF(0, 0, self.width, self.height)
1549b3700f21SAdrian Hunter
1550b3700f21SAdrian Hunter	def Step(self):
1551b3700f21SAdrian Hunter		attrs = self.parentItem().attrs
1552b3700f21SAdrian Hunter		subrange = attrs.subrange.x
1553b3700f21SAdrian Hunter		t = subrange.hi - subrange.lo
1554b3700f21SAdrian Hunter		s = (3.0 * t) / self.width
1555b3700f21SAdrian Hunter		n = 1.0
1556b3700f21SAdrian Hunter		while s > n:
1557b3700f21SAdrian Hunter			n = n * 10.0
1558b3700f21SAdrian Hunter		return n
1559b3700f21SAdrian Hunter
1560b3700f21SAdrian Hunter	def PaintMarks(self, painter, at_y, lo, hi, step, i):
1561b3700f21SAdrian Hunter		attrs = self.parentItem().attrs
1562b3700f21SAdrian Hunter		x = lo
1563b3700f21SAdrian Hunter		while x <= hi:
1564b3700f21SAdrian Hunter			xp = attrs.XToPixel(x)
1565b3700f21SAdrian Hunter			if i % 10:
1566b3700f21SAdrian Hunter				if i % 5:
1567b3700f21SAdrian Hunter					sz = 1
1568b3700f21SAdrian Hunter				else:
1569b3700f21SAdrian Hunter					sz = 2
1570b3700f21SAdrian Hunter			else:
1571b3700f21SAdrian Hunter				sz = self.max_mark_sz
1572b3700f21SAdrian Hunter				i = 0
1573b3700f21SAdrian Hunter			painter.drawLine(xp, at_y, xp, at_y + sz)
1574b3700f21SAdrian Hunter			x += step
1575b3700f21SAdrian Hunter			i += 1
1576b3700f21SAdrian Hunter
1577b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1578b3700f21SAdrian Hunter		# Using QPainter::drawLine(int x1, int y1, int x2, int y2) so x2 = width -1
1579b3700f21SAdrian Hunter		painter.drawLine(0, 0, self.width - 1, 0)
1580b3700f21SAdrian Hunter		n = self.Step()
1581b3700f21SAdrian Hunter		attrs = self.parentItem().attrs
1582b3700f21SAdrian Hunter		subrange = attrs.subrange.x
1583b3700f21SAdrian Hunter		if subrange.lo:
1584b3700f21SAdrian Hunter			x_offset = n - (subrange.lo % n)
1585b3700f21SAdrian Hunter		else:
1586b3700f21SAdrian Hunter			x_offset = 0.0
1587b3700f21SAdrian Hunter		x = subrange.lo + x_offset
1588b3700f21SAdrian Hunter		i = (x / n) % 10
1589b3700f21SAdrian Hunter		self.PaintMarks(painter, 0, x, subrange.hi, n, i)
1590b3700f21SAdrian Hunter
1591b3700f21SAdrian Hunter	def ScaleDimensions(self):
1592b3700f21SAdrian Hunter		n = self.Step()
1593b3700f21SAdrian Hunter		attrs = self.parentItem().attrs
1594b3700f21SAdrian Hunter		lo = attrs.subrange.x.lo
1595b3700f21SAdrian Hunter		hi = (n * 10.0) + lo
1596b3700f21SAdrian Hunter		width = attrs.XToPixel(hi)
1597b3700f21SAdrian Hunter		if width > 500:
1598b3700f21SAdrian Hunter			width = 0
1599b3700f21SAdrian Hunter		return (n, lo, hi, width)
1600b3700f21SAdrian Hunter
1601b3700f21SAdrian Hunter	def PaintScale(self, painter, at_x, at_y):
1602b3700f21SAdrian Hunter		n, lo, hi, width = self.ScaleDimensions()
1603b3700f21SAdrian Hunter		if not width:
1604b3700f21SAdrian Hunter			return
1605b3700f21SAdrian Hunter		painter.drawLine(at_x, at_y, at_x + width, at_y)
1606b3700f21SAdrian Hunter		self.PaintMarks(painter, at_y, lo, hi, n, 0)
1607b3700f21SAdrian Hunter
1608b3700f21SAdrian Hunter	def ScaleWidth(self):
1609b3700f21SAdrian Hunter		n, lo, hi, width = self.ScaleDimensions()
1610b3700f21SAdrian Hunter		return width
1611b3700f21SAdrian Hunter
1612b3700f21SAdrian Hunter	def ScaleHeight(self):
1613b3700f21SAdrian Hunter		return self.height
1614b3700f21SAdrian Hunter
1615b3700f21SAdrian Hunter	def ScaleUnit(self):
1616b3700f21SAdrian Hunter		return self.Step() * 10
1617b3700f21SAdrian Hunter
1618b3700f21SAdrian Hunter# Scale graphics item base class
1619b3700f21SAdrian Hunter
1620b3700f21SAdrian Hunterclass ScaleGraphicsItem(QGraphicsItem):
1621b3700f21SAdrian Hunter
1622b3700f21SAdrian Hunter	def __init__(self, axis, parent=None):
1623b3700f21SAdrian Hunter		super(ScaleGraphicsItem, self).__init__(parent)
1624b3700f21SAdrian Hunter		self.axis = axis
1625b3700f21SAdrian Hunter
1626b3700f21SAdrian Hunter	def boundingRect(self):
1627b3700f21SAdrian Hunter		scale_width = self.axis.ScaleWidth()
1628b3700f21SAdrian Hunter		if not scale_width:
1629b3700f21SAdrian Hunter			return QRectF()
1630b3700f21SAdrian Hunter		return QRectF(0, 0, self.axis.ScaleWidth() + 100, self.axis.ScaleHeight())
1631b3700f21SAdrian Hunter
1632b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1633b3700f21SAdrian Hunter		scale_width = self.axis.ScaleWidth()
1634b3700f21SAdrian Hunter		if not scale_width:
1635b3700f21SAdrian Hunter			return
1636b3700f21SAdrian Hunter		self.axis.PaintScale(painter, 0, 5)
1637b3700f21SAdrian Hunter		x = scale_width + 4
1638b3700f21SAdrian Hunter		painter.drawText(QPointF(x, 10), self.Text())
1639b3700f21SAdrian Hunter
1640b3700f21SAdrian Hunter	def Unit(self):
1641b3700f21SAdrian Hunter		return self.axis.ScaleUnit()
1642b3700f21SAdrian Hunter
1643b3700f21SAdrian Hunter	def Text(self):
1644b3700f21SAdrian Hunter		return ""
1645b3700f21SAdrian Hunter
1646b3700f21SAdrian Hunter# Switch graph scale graphics item
1647b3700f21SAdrian Hunter
1648b3700f21SAdrian Hunterclass SwitchScaleGraphicsItem(ScaleGraphicsItem):
1649b3700f21SAdrian Hunter
1650b3700f21SAdrian Hunter	def __init__(self, axis, parent=None):
1651b3700f21SAdrian Hunter		super(SwitchScaleGraphicsItem, self).__init__(axis, parent)
1652b3700f21SAdrian Hunter
1653b3700f21SAdrian Hunter	def Text(self):
1654b3700f21SAdrian Hunter		unit = self.Unit()
1655b3700f21SAdrian Hunter		if unit >= 1000000000:
1656b3700f21SAdrian Hunter			unit = int(unit / 1000000000)
1657b3700f21SAdrian Hunter			us = "s"
1658b3700f21SAdrian Hunter		elif unit >= 1000000:
1659b3700f21SAdrian Hunter			unit = int(unit / 1000000)
1660b3700f21SAdrian Hunter			us = "ms"
1661b3700f21SAdrian Hunter		elif unit >= 1000:
1662b3700f21SAdrian Hunter			unit = int(unit / 1000)
1663b3700f21SAdrian Hunter			us = "us"
1664b3700f21SAdrian Hunter		else:
1665b3700f21SAdrian Hunter			unit = int(unit)
1666b3700f21SAdrian Hunter			us = "ns"
1667b3700f21SAdrian Hunter		return " = " + str(unit) + " " + us
1668b3700f21SAdrian Hunter
1669b3700f21SAdrian Hunter# Switch graph graphics item contains graph title, scale, x/y-axis, and the graphed data
1670b3700f21SAdrian Hunter
1671b3700f21SAdrian Hunterclass SwitchGraphGraphicsItem(QGraphicsItem):
1672b3700f21SAdrian Hunter
1673b3700f21SAdrian Hunter	def __init__(self, collection, data, attrs, event_handler, first, parent=None):
1674b3700f21SAdrian Hunter		super(SwitchGraphGraphicsItem, self).__init__(parent)
1675b3700f21SAdrian Hunter		self.collection = collection
1676b3700f21SAdrian Hunter		self.data = data
1677b3700f21SAdrian Hunter		self.attrs = attrs
1678b3700f21SAdrian Hunter		self.event_handler = event_handler
1679b3700f21SAdrian Hunter
1680b3700f21SAdrian Hunter		margin = 20
1681b3700f21SAdrian Hunter		title_width = 50
1682b3700f21SAdrian Hunter
1683b3700f21SAdrian Hunter		self.title_graphics = QGraphicsSimpleTextItem(data.title, self)
1684b3700f21SAdrian Hunter
1685b3700f21SAdrian Hunter		self.title_graphics.setPos(margin, margin)
1686b3700f21SAdrian Hunter		graph_width = attrs.XToPixel(attrs.subrange.x.hi) + 1
1687b3700f21SAdrian Hunter		graph_height = attrs.YToPixel(attrs.subrange.y.hi) + 1
1688b3700f21SAdrian Hunter
1689b3700f21SAdrian Hunter		self.graph_origin_x = margin + title_width + margin
1690b3700f21SAdrian Hunter		self.graph_origin_y = graph_height + margin
1691b3700f21SAdrian Hunter
1692b3700f21SAdrian Hunter		x_axis_size = 1
1693b3700f21SAdrian Hunter		y_axis_size = 1
1694b3700f21SAdrian Hunter		self.yline = QGraphicsLineItem(0, 0, 0, graph_height, self)
1695b3700f21SAdrian Hunter
1696b3700f21SAdrian Hunter		self.x_axis = XAxisGraphicsItem(graph_width, self)
1697b3700f21SAdrian Hunter		self.x_axis.setPos(self.graph_origin_x, self.graph_origin_y + 1)
1698b3700f21SAdrian Hunter
1699b3700f21SAdrian Hunter		if first:
1700b3700f21SAdrian Hunter			self.scale_item = SwitchScaleGraphicsItem(self.x_axis, self)
1701b3700f21SAdrian Hunter			self.scale_item.setPos(self.graph_origin_x, self.graph_origin_y + 10)
1702b3700f21SAdrian Hunter
1703b3700f21SAdrian Hunter		self.yline.setPos(self.graph_origin_x - y_axis_size, self.graph_origin_y - graph_height)
1704b3700f21SAdrian Hunter
1705b3700f21SAdrian Hunter		self.axis_point = QGraphicsLineItem(0, 0, 0, 0, self)
1706b3700f21SAdrian Hunter		self.axis_point.setPos(self.graph_origin_x - 1, self.graph_origin_y +1)
1707b3700f21SAdrian Hunter
1708b3700f21SAdrian Hunter		self.width = self.graph_origin_x + graph_width + margin
1709b3700f21SAdrian Hunter		self.height = self.graph_origin_y + margin
1710b3700f21SAdrian Hunter
1711b3700f21SAdrian Hunter		self.graph = SwitchGraphDataGraphicsItem(data, graph_width, graph_height, attrs, event_handler, self)
1712b3700f21SAdrian Hunter		self.graph.setPos(self.graph_origin_x, self.graph_origin_y - graph_height)
1713b3700f21SAdrian Hunter
1714b3700f21SAdrian Hunter		if parent and 'EnableRubberBand' in dir(parent):
1715b3700f21SAdrian Hunter			parent.EnableRubberBand(self.graph_origin_x, self.graph_origin_x + graph_width - 1, self)
1716b3700f21SAdrian Hunter
1717b3700f21SAdrian Hunter	def boundingRect(self):
1718b3700f21SAdrian Hunter		return QRectF(0, 0, self.width, self.height)
1719b3700f21SAdrian Hunter
1720b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1721b3700f21SAdrian Hunter		pass
1722b3700f21SAdrian Hunter
1723b3700f21SAdrian Hunter	def RBXToPixel(self, x):
1724b3700f21SAdrian Hunter		return self.attrs.PixelToX(x - self.graph_origin_x)
1725b3700f21SAdrian Hunter
1726b3700f21SAdrian Hunter	def RBXRangeToPixel(self, x0, x1):
1727b3700f21SAdrian Hunter		return (self.RBXToPixel(x0), self.RBXToPixel(x1 + 1))
1728b3700f21SAdrian Hunter
1729b3700f21SAdrian Hunter	def RBPixelToTime(self, x):
1730b3700f21SAdrian Hunter		if x < self.data.points[0].x:
1731b3700f21SAdrian Hunter			return self.data.XToData(0)
1732b3700f21SAdrian Hunter		return self.data.XToData(x)
1733b3700f21SAdrian Hunter
1734b3700f21SAdrian Hunter	def RBEventTimes(self, x0, x1):
1735b3700f21SAdrian Hunter		x0, x1 = self.RBXRangeToPixel(x0, x1)
1736b3700f21SAdrian Hunter		time_from = self.RBPixelToTime(x0)
1737b3700f21SAdrian Hunter		time_to = self.RBPixelToTime(x1)
1738b3700f21SAdrian Hunter		return (time_from, time_to)
1739b3700f21SAdrian Hunter
1740b3700f21SAdrian Hunter	def RBEvent(self, x0, x1):
1741b3700f21SAdrian Hunter		time_from, time_to = self.RBEventTimes(x0, x1)
1742b3700f21SAdrian Hunter		self.event_handler.RangeEvent(time_from, time_to)
1743b3700f21SAdrian Hunter
1744b3700f21SAdrian Hunter	def RBMoveEvent(self, x0, x1):
1745b3700f21SAdrian Hunter		if x1 < x0:
1746b3700f21SAdrian Hunter			x0, x1 = x1, x0
1747b3700f21SAdrian Hunter		self.RBEvent(x0, x1)
1748b3700f21SAdrian Hunter
1749b3700f21SAdrian Hunter	def RBReleaseEvent(self, x0, x1, selection_state):
1750b3700f21SAdrian Hunter		if x1 < x0:
1751b3700f21SAdrian Hunter			x0, x1 = x1, x0
1752b3700f21SAdrian Hunter		x0, x1 = self.RBXRangeToPixel(x0, x1)
1753b3700f21SAdrian Hunter		self.event_handler.SelectEvent(x0, x1, selection_state)
1754b3700f21SAdrian Hunter
1755b3700f21SAdrian Hunter# Graphics item to draw a vertical bracket (used to highlight "forward" sub-range)
1756b3700f21SAdrian Hunter
1757b3700f21SAdrian Hunterclass VerticalBracketGraphicsItem(QGraphicsItem):
1758b3700f21SAdrian Hunter
1759b3700f21SAdrian Hunter	def __init__(self, parent=None):
1760b3700f21SAdrian Hunter		super(VerticalBracketGraphicsItem, self).__init__(parent)
1761b3700f21SAdrian Hunter
1762b3700f21SAdrian Hunter		self.width = 0
1763b3700f21SAdrian Hunter		self.height = 0
1764b3700f21SAdrian Hunter		self.hide()
1765b3700f21SAdrian Hunter
1766b3700f21SAdrian Hunter	def SetSize(self, width, height):
1767b3700f21SAdrian Hunter		self.width = width + 1
1768b3700f21SAdrian Hunter		self.height = height + 1
1769b3700f21SAdrian Hunter
1770b3700f21SAdrian Hunter	def boundingRect(self):
1771b3700f21SAdrian Hunter		return QRectF(0, 0, self.width, self.height)
1772b3700f21SAdrian Hunter
1773b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1774b3700f21SAdrian Hunter		colour = QColor(255, 255, 0, 32)
1775b3700f21SAdrian Hunter		painter.fillRect(0, 0, self.width, self.height, colour)
1776b3700f21SAdrian Hunter		x1 = self.width - 1
1777b3700f21SAdrian Hunter		y1 = self.height - 1
1778b3700f21SAdrian Hunter		painter.drawLine(0, 0, x1, 0)
1779b3700f21SAdrian Hunter		painter.drawLine(0, 0, 0, 3)
1780b3700f21SAdrian Hunter		painter.drawLine(x1, 0, x1, 3)
1781b3700f21SAdrian Hunter		painter.drawLine(0, y1, x1, y1)
1782b3700f21SAdrian Hunter		painter.drawLine(0, y1, 0, y1 - 3)
1783b3700f21SAdrian Hunter		painter.drawLine(x1, y1, x1, y1 - 3)
1784b3700f21SAdrian Hunter
1785b3700f21SAdrian Hunter# Graphics item to contain graphs arranged vertically
1786b3700f21SAdrian Hunter
1787b3700f21SAdrian Hunterclass VertcalGraphSetGraphicsItem(QGraphicsItem):
1788b3700f21SAdrian Hunter
1789b3700f21SAdrian Hunter	def __init__(self, collection, attrs, event_handler, child_class, parent=None):
1790b3700f21SAdrian Hunter		super(VertcalGraphSetGraphicsItem, self).__init__(parent)
1791b3700f21SAdrian Hunter
1792b3700f21SAdrian Hunter		self.collection = collection
1793b3700f21SAdrian Hunter
1794b3700f21SAdrian Hunter		self.top = 10
1795b3700f21SAdrian Hunter
1796b3700f21SAdrian Hunter		self.width = 0
1797b3700f21SAdrian Hunter		self.height = self.top
1798b3700f21SAdrian Hunter
1799b3700f21SAdrian Hunter		self.rubber_band = None
1800b3700f21SAdrian Hunter		self.rb_enabled = False
1801b3700f21SAdrian Hunter
1802b3700f21SAdrian Hunter		first = True
1803b3700f21SAdrian Hunter		for data in collection.data:
1804b3700f21SAdrian Hunter			child = child_class(collection, data, attrs, event_handler, first, self)
1805b3700f21SAdrian Hunter			child.setPos(0, self.height + 1)
1806b3700f21SAdrian Hunter			rect = child.boundingRect()
1807b3700f21SAdrian Hunter			if rect.right() > self.width:
1808b3700f21SAdrian Hunter				self.width = rect.right()
1809b3700f21SAdrian Hunter			self.height = self.height + rect.bottom() + 1
1810b3700f21SAdrian Hunter			first = False
1811b3700f21SAdrian Hunter
1812b3700f21SAdrian Hunter		self.bracket = VerticalBracketGraphicsItem(self)
1813b3700f21SAdrian Hunter
1814b3700f21SAdrian Hunter	def EnableRubberBand(self, xlo, xhi, rb_event_handler):
1815b3700f21SAdrian Hunter		if self.rb_enabled:
1816b3700f21SAdrian Hunter			return
1817b3700f21SAdrian Hunter		self.rb_enabled = True
1818b3700f21SAdrian Hunter		self.rb_in_view = False
1819b3700f21SAdrian Hunter		self.setAcceptedMouseButtons(Qt.LeftButton)
1820b3700f21SAdrian Hunter		self.rb_xlo = xlo
1821b3700f21SAdrian Hunter		self.rb_xhi = xhi
1822b3700f21SAdrian Hunter		self.rb_event_handler = rb_event_handler
1823b3700f21SAdrian Hunter		self.mousePressEvent = self.MousePressEvent
1824b3700f21SAdrian Hunter		self.mouseMoveEvent = self.MouseMoveEvent
1825b3700f21SAdrian Hunter		self.mouseReleaseEvent = self.MouseReleaseEvent
1826b3700f21SAdrian Hunter
1827b3700f21SAdrian Hunter	def boundingRect(self):
1828b3700f21SAdrian Hunter		return QRectF(0, 0, self.width, self.height)
1829b3700f21SAdrian Hunter
1830b3700f21SAdrian Hunter	def paint(self, painter, option, widget):
1831b3700f21SAdrian Hunter		pass
1832b3700f21SAdrian Hunter
1833b3700f21SAdrian Hunter	def RubberBandParent(self):
1834b3700f21SAdrian Hunter		scene = self.scene()
1835b3700f21SAdrian Hunter		view = scene.views()[0]
1836b3700f21SAdrian Hunter		viewport = view.viewport()
1837b3700f21SAdrian Hunter		return viewport
1838b3700f21SAdrian Hunter
1839b3700f21SAdrian Hunter	def RubberBandSetGeometry(self, rect):
1840b3700f21SAdrian Hunter		scene_rectf = self.mapRectToScene(QRectF(rect))
1841b3700f21SAdrian Hunter		scene = self.scene()
1842b3700f21SAdrian Hunter		view = scene.views()[0]
1843b3700f21SAdrian Hunter		poly = view.mapFromScene(scene_rectf)
1844b3700f21SAdrian Hunter		self.rubber_band.setGeometry(poly.boundingRect())
1845b3700f21SAdrian Hunter
1846b3700f21SAdrian Hunter	def SetSelection(self, selection_state):
1847b3700f21SAdrian Hunter		if self.rubber_band:
1848b3700f21SAdrian Hunter			if selection_state:
1849b3700f21SAdrian Hunter				self.RubberBandSetGeometry(selection_state)
1850b3700f21SAdrian Hunter				self.rubber_band.show()
1851b3700f21SAdrian Hunter			else:
1852b3700f21SAdrian Hunter				self.rubber_band.hide()
1853b3700f21SAdrian Hunter
1854b3700f21SAdrian Hunter	def SetBracket(self, rect):
1855b3700f21SAdrian Hunter		if rect:
1856b3700f21SAdrian Hunter			x, y, width, height = rect.x(), rect.y(), rect.width(), rect.height()
1857b3700f21SAdrian Hunter			self.bracket.setPos(x, y)
1858b3700f21SAdrian Hunter			self.bracket.SetSize(width, height)
1859b3700f21SAdrian Hunter			self.bracket.show()
1860b3700f21SAdrian Hunter		else:
1861b3700f21SAdrian Hunter			self.bracket.hide()
1862b3700f21SAdrian Hunter
1863b3700f21SAdrian Hunter	def RubberBandX(self, event):
1864b3700f21SAdrian Hunter		x = event.pos().toPoint().x()
1865b3700f21SAdrian Hunter		if x < self.rb_xlo:
1866b3700f21SAdrian Hunter			x = self.rb_xlo
1867b3700f21SAdrian Hunter		elif x > self.rb_xhi:
1868b3700f21SAdrian Hunter			x = self.rb_xhi
1869b3700f21SAdrian Hunter		else:
1870b3700f21SAdrian Hunter			self.rb_in_view = True
1871b3700f21SAdrian Hunter		return x
1872b3700f21SAdrian Hunter
1873b3700f21SAdrian Hunter	def RubberBandRect(self, x):
1874b3700f21SAdrian Hunter		if self.rb_origin.x() <= x:
1875b3700f21SAdrian Hunter			width = x - self.rb_origin.x()
1876b3700f21SAdrian Hunter			rect = QRect(self.rb_origin, QSize(width, self.height))
1877b3700f21SAdrian Hunter		else:
1878b3700f21SAdrian Hunter			width = self.rb_origin.x() - x
1879b3700f21SAdrian Hunter			top_left = QPoint(self.rb_origin.x() - width, self.rb_origin.y())
1880b3700f21SAdrian Hunter			rect = QRect(top_left, QSize(width, self.height))
1881b3700f21SAdrian Hunter		return rect
1882b3700f21SAdrian Hunter
1883b3700f21SAdrian Hunter	def MousePressEvent(self, event):
1884b3700f21SAdrian Hunter		self.rb_in_view = False
1885b3700f21SAdrian Hunter		x = self.RubberBandX(event)
1886b3700f21SAdrian Hunter		self.rb_origin = QPoint(x, self.top)
1887b3700f21SAdrian Hunter		if self.rubber_band is None:
1888b3700f21SAdrian Hunter			self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.RubberBandParent())
1889b3700f21SAdrian Hunter		self.RubberBandSetGeometry(QRect(self.rb_origin, QSize(0, self.height)))
1890b3700f21SAdrian Hunter		if self.rb_in_view:
1891b3700f21SAdrian Hunter			self.rubber_band.show()
1892b3700f21SAdrian Hunter			self.rb_event_handler.RBMoveEvent(x, x)
1893b3700f21SAdrian Hunter		else:
1894b3700f21SAdrian Hunter			self.rubber_band.hide()
1895b3700f21SAdrian Hunter
1896b3700f21SAdrian Hunter	def MouseMoveEvent(self, event):
1897b3700f21SAdrian Hunter		x = self.RubberBandX(event)
1898b3700f21SAdrian Hunter		rect = self.RubberBandRect(x)
1899b3700f21SAdrian Hunter		self.RubberBandSetGeometry(rect)
1900b3700f21SAdrian Hunter		if self.rb_in_view:
1901b3700f21SAdrian Hunter			self.rubber_band.show()
1902b3700f21SAdrian Hunter			self.rb_event_handler.RBMoveEvent(self.rb_origin.x(), x)
1903b3700f21SAdrian Hunter
1904b3700f21SAdrian Hunter	def MouseReleaseEvent(self, event):
1905b3700f21SAdrian Hunter		x = self.RubberBandX(event)
1906b3700f21SAdrian Hunter		if self.rb_in_view:
1907b3700f21SAdrian Hunter			selection_state = self.RubberBandRect(x)
1908b3700f21SAdrian Hunter		else:
1909b3700f21SAdrian Hunter			selection_state = None
1910b3700f21SAdrian Hunter		self.rb_event_handler.RBReleaseEvent(self.rb_origin.x(), x, selection_state)
1911b3700f21SAdrian Hunter
1912b3700f21SAdrian Hunter# Switch graph legend data model
1913b3700f21SAdrian Hunter
1914b3700f21SAdrian Hunterclass SwitchGraphLegendModel(QAbstractTableModel):
1915b3700f21SAdrian Hunter
1916b3700f21SAdrian Hunter	def __init__(self, collection, region_attributes, parent=None):
1917b3700f21SAdrian Hunter		super(SwitchGraphLegendModel, self).__init__(parent)
1918b3700f21SAdrian Hunter
1919b3700f21SAdrian Hunter		self.region_attributes = region_attributes
1920b3700f21SAdrian Hunter
1921b3700f21SAdrian Hunter		self.child_items = sorted(collection.hregions.values(), key=GraphDataRegionOrdinal)
1922b3700f21SAdrian Hunter		self.child_count = len(self.child_items)
1923b3700f21SAdrian Hunter
1924b3700f21SAdrian Hunter		self.highlight_set = set()
1925b3700f21SAdrian Hunter
1926b3700f21SAdrian Hunter		self.column_headers = ("pid", "tid", "comm")
1927b3700f21SAdrian Hunter
1928b3700f21SAdrian Hunter	def rowCount(self, parent):
1929b3700f21SAdrian Hunter		return self.child_count
1930b3700f21SAdrian Hunter
1931b3700f21SAdrian Hunter	def headerData(self, section, orientation, role):
1932b3700f21SAdrian Hunter		if role != Qt.DisplayRole:
1933b3700f21SAdrian Hunter			return None
1934b3700f21SAdrian Hunter		if orientation != Qt.Horizontal:
1935b3700f21SAdrian Hunter			return None
1936b3700f21SAdrian Hunter		return self.columnHeader(section)
1937b3700f21SAdrian Hunter
1938b3700f21SAdrian Hunter	def index(self, row, column, parent):
1939b3700f21SAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
1940b3700f21SAdrian Hunter
1941b3700f21SAdrian Hunter	def columnCount(self, parent=None):
1942b3700f21SAdrian Hunter		return len(self.column_headers)
1943b3700f21SAdrian Hunter
1944b3700f21SAdrian Hunter	def columnHeader(self, column):
1945b3700f21SAdrian Hunter		return self.column_headers[column]
1946b3700f21SAdrian Hunter
1947b3700f21SAdrian Hunter	def data(self, index, role):
1948b3700f21SAdrian Hunter		if role == Qt.BackgroundRole:
1949b3700f21SAdrian Hunter			child = self.child_items[index.row()]
1950b3700f21SAdrian Hunter			if child in self.highlight_set:
1951b3700f21SAdrian Hunter				return self.region_attributes[child.key].colour
1952b3700f21SAdrian Hunter			return None
1953b3700f21SAdrian Hunter		if role == Qt.ForegroundRole:
1954b3700f21SAdrian Hunter			child = self.child_items[index.row()]
1955b3700f21SAdrian Hunter			if child in self.highlight_set:
1956b3700f21SAdrian Hunter				return QColor(255, 255, 255)
1957b3700f21SAdrian Hunter			return self.region_attributes[child.key].colour
1958b3700f21SAdrian Hunter		if role != Qt.DisplayRole:
1959b3700f21SAdrian Hunter			return None
1960b3700f21SAdrian Hunter		hregion = self.child_items[index.row()]
1961b3700f21SAdrian Hunter		col = index.column()
1962b3700f21SAdrian Hunter		if col == 0:
1963b3700f21SAdrian Hunter			return hregion.pid
1964b3700f21SAdrian Hunter		if col == 1:
1965b3700f21SAdrian Hunter			return hregion.tid
1966b3700f21SAdrian Hunter		if col == 2:
1967b3700f21SAdrian Hunter			return hregion.comm
1968b3700f21SAdrian Hunter		return None
1969b3700f21SAdrian Hunter
1970b3700f21SAdrian Hunter	def SetHighlight(self, row, set_highlight):
1971b3700f21SAdrian Hunter		child = self.child_items[row]
1972b3700f21SAdrian Hunter		top_left = self.createIndex(row, 0, child)
1973b3700f21SAdrian Hunter		bottom_right = self.createIndex(row, len(self.column_headers) - 1, child)
1974b3700f21SAdrian Hunter		self.dataChanged.emit(top_left, bottom_right)
1975b3700f21SAdrian Hunter
1976b3700f21SAdrian Hunter	def Highlight(self, highlight_set):
1977b3700f21SAdrian Hunter		for row in xrange(self.child_count):
1978b3700f21SAdrian Hunter			child = self.child_items[row]
1979b3700f21SAdrian Hunter			if child in self.highlight_set:
1980b3700f21SAdrian Hunter				if child not in highlight_set:
1981b3700f21SAdrian Hunter					self.SetHighlight(row, False)
1982b3700f21SAdrian Hunter			elif child in highlight_set:
1983b3700f21SAdrian Hunter				self.SetHighlight(row, True)
1984b3700f21SAdrian Hunter		self.highlight_set = highlight_set
1985b3700f21SAdrian Hunter
1986b3700f21SAdrian Hunter# Switch graph legend is a table
1987b3700f21SAdrian Hunter
1988b3700f21SAdrian Hunterclass SwitchGraphLegend(QWidget):
1989b3700f21SAdrian Hunter
1990b3700f21SAdrian Hunter	def __init__(self, collection, region_attributes, parent=None):
1991b3700f21SAdrian Hunter		super(SwitchGraphLegend, self).__init__(parent)
1992b3700f21SAdrian Hunter
1993b3700f21SAdrian Hunter		self.data_model = SwitchGraphLegendModel(collection, region_attributes)
1994b3700f21SAdrian Hunter
1995b3700f21SAdrian Hunter		self.model = QSortFilterProxyModel()
1996b3700f21SAdrian Hunter		self.model.setSourceModel(self.data_model)
1997b3700f21SAdrian Hunter
1998b3700f21SAdrian Hunter		self.view = QTableView()
1999b3700f21SAdrian Hunter		self.view.setModel(self.model)
2000b3700f21SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2001b3700f21SAdrian Hunter		self.view.verticalHeader().setVisible(False)
2002b3700f21SAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
2003b3700f21SAdrian Hunter		self.view.setSortingEnabled(True)
2004b3700f21SAdrian Hunter		self.view.resizeColumnsToContents()
2005b3700f21SAdrian Hunter		self.view.resizeRowsToContents()
2006b3700f21SAdrian Hunter
2007b3700f21SAdrian Hunter		self.vbox = VBoxLayout(self.view)
2008b3700f21SAdrian Hunter		self.setLayout(self.vbox)
2009b3700f21SAdrian Hunter
2010b3700f21SAdrian Hunter		sz1 = self.view.columnWidth(0) + self.view.columnWidth(1) + self.view.columnWidth(2) + 2
2011b3700f21SAdrian Hunter		sz1 = sz1 + self.view.verticalScrollBar().sizeHint().width()
2012b3700f21SAdrian Hunter		self.saved_size = sz1
2013b3700f21SAdrian Hunter
2014b3700f21SAdrian Hunter	def resizeEvent(self, event):
2015b3700f21SAdrian Hunter		self.saved_size = self.size().width()
2016b3700f21SAdrian Hunter		super(SwitchGraphLegend, self).resizeEvent(event)
2017b3700f21SAdrian Hunter
2018b3700f21SAdrian Hunter	def Highlight(self, highlight_set):
2019b3700f21SAdrian Hunter		self.data_model.Highlight(highlight_set)
2020b3700f21SAdrian Hunter		self.update()
2021b3700f21SAdrian Hunter
2022b3700f21SAdrian Hunter	def changeEvent(self, event):
2023b3700f21SAdrian Hunter		if event.type() == QEvent.FontChange:
2024b3700f21SAdrian Hunter			self.view.resizeRowsToContents()
2025b3700f21SAdrian Hunter			self.view.resizeColumnsToContents()
2026b3700f21SAdrian Hunter			# Need to resize rows again after column resize
2027b3700f21SAdrian Hunter			self.view.resizeRowsToContents()
2028b3700f21SAdrian Hunter		super(SwitchGraphLegend, self).changeEvent(event)
2029b3700f21SAdrian Hunter
2030b3700f21SAdrian Hunter# Random colour generation
2031b3700f21SAdrian Hunter
2032b3700f21SAdrian Hunterdef RGBColourTooLight(r, g, b):
2033b3700f21SAdrian Hunter	if g > 230:
2034b3700f21SAdrian Hunter		return True
2035b3700f21SAdrian Hunter	if g <= 160:
2036b3700f21SAdrian Hunter		return False
2037b3700f21SAdrian Hunter	if r <= 180 and g <= 180:
2038b3700f21SAdrian Hunter		return False
2039b3700f21SAdrian Hunter	if r < 60:
2040b3700f21SAdrian Hunter		return False
2041b3700f21SAdrian Hunter	return True
2042b3700f21SAdrian Hunter
2043b3700f21SAdrian Hunterdef GenerateColours(x):
2044b3700f21SAdrian Hunter	cs = [0]
2045b3700f21SAdrian Hunter	for i in xrange(1, x):
2046b3700f21SAdrian Hunter		cs.append(int((255.0 / i) + 0.5))
2047b3700f21SAdrian Hunter	colours = []
2048b3700f21SAdrian Hunter	for r in cs:
2049b3700f21SAdrian Hunter		for g in cs:
2050b3700f21SAdrian Hunter			for b in cs:
2051b3700f21SAdrian Hunter				# Exclude black and colours that look too light against a white background
2052b3700f21SAdrian Hunter				if (r, g, b) == (0, 0, 0) or RGBColourTooLight(r, g, b):
2053b3700f21SAdrian Hunter					continue
2054b3700f21SAdrian Hunter				colours.append(QColor(r, g, b))
2055b3700f21SAdrian Hunter	return colours
2056b3700f21SAdrian Hunter
2057b3700f21SAdrian Hunterdef GenerateNColours(n):
2058b3700f21SAdrian Hunter	for x in xrange(2, n + 2):
2059b3700f21SAdrian Hunter		colours = GenerateColours(x)
2060b3700f21SAdrian Hunter		if len(colours) >= n:
2061b3700f21SAdrian Hunter			return colours
2062b3700f21SAdrian Hunter	return []
2063b3700f21SAdrian Hunter
2064b3700f21SAdrian Hunterdef GenerateNRandomColours(n, seed):
2065b3700f21SAdrian Hunter	colours = GenerateNColours(n)
2066b3700f21SAdrian Hunter	random.seed(seed)
2067b3700f21SAdrian Hunter	random.shuffle(colours)
2068b3700f21SAdrian Hunter	return colours
2069b3700f21SAdrian Hunter
2070b3700f21SAdrian Hunter# Graph attributes, in particular the scale and subrange that change when zooming
2071b3700f21SAdrian Hunter
2072b3700f21SAdrian Hunterclass GraphAttributes():
2073b3700f21SAdrian Hunter
2074b3700f21SAdrian Hunter	def __init__(self, scale, subrange, region_attributes, dp):
2075b3700f21SAdrian Hunter		self.scale = scale
2076b3700f21SAdrian Hunter		self.subrange = subrange
2077b3700f21SAdrian Hunter		self.region_attributes = region_attributes
2078b3700f21SAdrian Hunter		# Rounding avoids errors due to finite floating point precision
2079b3700f21SAdrian Hunter		self.dp = dp	# data decimal places
2080b3700f21SAdrian Hunter		self.Update()
2081b3700f21SAdrian Hunter
2082b3700f21SAdrian Hunter	def XToPixel(self, x):
2083b3700f21SAdrian Hunter		return int(round((x - self.subrange.x.lo) * self.scale.x, self.pdp.x))
2084b3700f21SAdrian Hunter
2085b3700f21SAdrian Hunter	def YToPixel(self, y):
2086b3700f21SAdrian Hunter		return int(round((y - self.subrange.y.lo) * self.scale.y, self.pdp.y))
2087b3700f21SAdrian Hunter
2088b3700f21SAdrian Hunter	def PixelToXRounded(self, px):
2089b3700f21SAdrian Hunter		return round((round(px, 0) / self.scale.x), self.dp.x) + self.subrange.x.lo
2090b3700f21SAdrian Hunter
2091b3700f21SAdrian Hunter	def PixelToYRounded(self, py):
2092b3700f21SAdrian Hunter		return round((round(py, 0) / self.scale.y), self.dp.y) + self.subrange.y.lo
2093b3700f21SAdrian Hunter
2094b3700f21SAdrian Hunter	def PixelToX(self, px):
2095b3700f21SAdrian Hunter		x = self.PixelToXRounded(px)
2096b3700f21SAdrian Hunter		if self.pdp.x == 0:
2097b3700f21SAdrian Hunter			rt = self.XToPixel(x)
2098b3700f21SAdrian Hunter			if rt > px:
2099b3700f21SAdrian Hunter				return x - 1
2100b3700f21SAdrian Hunter		return x
2101b3700f21SAdrian Hunter
2102b3700f21SAdrian Hunter	def PixelToY(self, py):
2103b3700f21SAdrian Hunter		y = self.PixelToYRounded(py)
2104b3700f21SAdrian Hunter		if self.pdp.y == 0:
2105b3700f21SAdrian Hunter			rt = self.YToPixel(y)
2106b3700f21SAdrian Hunter			if rt > py:
2107b3700f21SAdrian Hunter				return y - 1
2108b3700f21SAdrian Hunter		return y
2109b3700f21SAdrian Hunter
2110b3700f21SAdrian Hunter	def ToPDP(self, dp, scale):
2111b3700f21SAdrian Hunter		# Calculate pixel decimal places:
2112b3700f21SAdrian Hunter		#    (10 ** dp) is the minimum delta in the data
2113b3700f21SAdrian Hunter		#    scale it to get the minimum delta in pixels
2114b3700f21SAdrian Hunter		#    log10 gives the number of decimals places negatively
2115b3700f21SAdrian Hunter		#    subtrace 1 to divide by 10
2116b3700f21SAdrian Hunter		#    round to the lower negative number
2117b3700f21SAdrian Hunter		#    change the sign to get the number of decimals positively
2118b3700f21SAdrian Hunter		x = math.log10((10 ** dp) * scale)
2119b3700f21SAdrian Hunter		if x < 0:
2120b3700f21SAdrian Hunter			x -= 1
2121b3700f21SAdrian Hunter			x = -int(math.floor(x) - 0.1)
2122b3700f21SAdrian Hunter		else:
2123b3700f21SAdrian Hunter			x = 0
2124b3700f21SAdrian Hunter		return x
2125b3700f21SAdrian Hunter
2126b3700f21SAdrian Hunter	def Update(self):
2127b3700f21SAdrian Hunter		x = self.ToPDP(self.dp.x, self.scale.x)
2128b3700f21SAdrian Hunter		y = self.ToPDP(self.dp.y, self.scale.y)
2129b3700f21SAdrian Hunter		self.pdp = XY(x, y) # pixel decimal places
2130b3700f21SAdrian Hunter
2131b3700f21SAdrian Hunter# Switch graph splitter which divides the CPU graphs from the legend
2132b3700f21SAdrian Hunter
2133b3700f21SAdrian Hunterclass SwitchGraphSplitter(QSplitter):
2134b3700f21SAdrian Hunter
2135b3700f21SAdrian Hunter	def __init__(self, parent=None):
2136b3700f21SAdrian Hunter		super(SwitchGraphSplitter, self).__init__(parent)
2137b3700f21SAdrian Hunter
2138b3700f21SAdrian Hunter		self.first_time = False
2139b3700f21SAdrian Hunter
2140b3700f21SAdrian Hunter	def resizeEvent(self, ev):
2141b3700f21SAdrian Hunter		if self.first_time:
2142b3700f21SAdrian Hunter			self.first_time = False
2143b3700f21SAdrian Hunter			sz1 = self.widget(1).view.columnWidth(0) + self.widget(1).view.columnWidth(1) + self.widget(1).view.columnWidth(2) + 2
2144b3700f21SAdrian Hunter			sz1 = sz1 + self.widget(1).view.verticalScrollBar().sizeHint().width()
2145b3700f21SAdrian Hunter			sz0 = self.size().width() - self.handleWidth() - sz1
2146b3700f21SAdrian Hunter			self.setSizes([sz0, sz1])
2147b3700f21SAdrian Hunter		elif not(self.widget(1).saved_size is None):
2148b3700f21SAdrian Hunter			sz1 = self.widget(1).saved_size
2149b3700f21SAdrian Hunter			sz0 = self.size().width() - self.handleWidth() - sz1
2150b3700f21SAdrian Hunter			self.setSizes([sz0, sz1])
2151b3700f21SAdrian Hunter		super(SwitchGraphSplitter, self).resizeEvent(ev)
2152b3700f21SAdrian Hunter
2153b3700f21SAdrian Hunter# Graph widget base class
2154b3700f21SAdrian Hunter
2155b3700f21SAdrian Hunterclass GraphWidget(QWidget):
2156b3700f21SAdrian Hunter
2157b3700f21SAdrian Hunter	graph_title_changed = Signal(object)
2158b3700f21SAdrian Hunter
2159b3700f21SAdrian Hunter	def __init__(self, parent=None):
2160b3700f21SAdrian Hunter		super(GraphWidget, self).__init__(parent)
2161b3700f21SAdrian Hunter
2162b3700f21SAdrian Hunter	def GraphTitleChanged(self, title):
2163b3700f21SAdrian Hunter		self.graph_title_changed.emit(title)
2164b3700f21SAdrian Hunter
2165b3700f21SAdrian Hunter	def Title(self):
2166b3700f21SAdrian Hunter		return ""
2167b3700f21SAdrian Hunter
2168b3700f21SAdrian Hunter# Display time in s, ms, us or ns
2169b3700f21SAdrian Hunter
2170b3700f21SAdrian Hunterdef ToTimeStr(val):
2171b3700f21SAdrian Hunter	val = Decimal(val)
2172b3700f21SAdrian Hunter	if val >= 1000000000:
2173b3700f21SAdrian Hunter		return "{} s".format((val / 1000000000).quantize(Decimal("0.000000001")))
2174b3700f21SAdrian Hunter	if val >= 1000000:
2175b3700f21SAdrian Hunter		return "{} ms".format((val / 1000000).quantize(Decimal("0.000001")))
2176b3700f21SAdrian Hunter	if val >= 1000:
2177b3700f21SAdrian Hunter		return "{} us".format((val / 1000).quantize(Decimal("0.001")))
2178b3700f21SAdrian Hunter	return "{} ns".format(val.quantize(Decimal("1")))
2179b3700f21SAdrian Hunter
2180b3700f21SAdrian Hunter# Switch (i.e. context switch i.e. Time Chart by CPU) graph widget which contains the CPU graphs and the legend and control buttons
2181b3700f21SAdrian Hunter
2182b3700f21SAdrian Hunterclass SwitchGraphWidget(GraphWidget):
2183b3700f21SAdrian Hunter
2184b3700f21SAdrian Hunter	def __init__(self, glb, collection, parent=None):
2185b3700f21SAdrian Hunter		super(SwitchGraphWidget, self).__init__(parent)
2186b3700f21SAdrian Hunter
2187b3700f21SAdrian Hunter		self.glb = glb
2188b3700f21SAdrian Hunter		self.collection = collection
2189b3700f21SAdrian Hunter
2190b3700f21SAdrian Hunter		self.back_state = []
2191b3700f21SAdrian Hunter		self.forward_state = []
2192b3700f21SAdrian Hunter		self.selection_state = (None, None)
2193b3700f21SAdrian Hunter		self.fwd_rect = None
2194b3700f21SAdrian Hunter		self.start_time = self.glb.StartTime(collection.machine_id)
2195b3700f21SAdrian Hunter
2196b3700f21SAdrian Hunter		i = 0
2197b3700f21SAdrian Hunter		hregions = collection.hregions.values()
2198b3700f21SAdrian Hunter		colours = GenerateNRandomColours(len(hregions), 1013)
2199b3700f21SAdrian Hunter		region_attributes = {}
2200b3700f21SAdrian Hunter		for hregion in hregions:
2201b3700f21SAdrian Hunter			if hregion.pid == 0 and hregion.tid == 0:
2202b3700f21SAdrian Hunter				region_attributes[hregion.key] = GraphRegionAttribute(QColor(0, 0, 0))
2203b3700f21SAdrian Hunter			else:
2204b3700f21SAdrian Hunter				region_attributes[hregion.key] = GraphRegionAttribute(colours[i])
2205b3700f21SAdrian Hunter				i = i + 1
2206b3700f21SAdrian Hunter
2207b3700f21SAdrian Hunter		# Default to entire range
2208b3700f21SAdrian Hunter		xsubrange = Subrange(0.0, float(collection.xrangehi - collection.xrangelo) + 1.0)
2209b3700f21SAdrian Hunter		ysubrange = Subrange(0.0, float(collection.yrangehi - collection.yrangelo) + 1.0)
2210b3700f21SAdrian Hunter		subrange = XY(xsubrange, ysubrange)
2211b3700f21SAdrian Hunter
2212b3700f21SAdrian Hunter		scale = self.GetScaleForRange(subrange)
2213b3700f21SAdrian Hunter
2214b3700f21SAdrian Hunter		self.attrs = GraphAttributes(scale, subrange, region_attributes, collection.dp)
2215b3700f21SAdrian Hunter
2216b3700f21SAdrian Hunter		self.item = VertcalGraphSetGraphicsItem(collection, self.attrs, self, SwitchGraphGraphicsItem)
2217b3700f21SAdrian Hunter
2218b3700f21SAdrian Hunter		self.scene = QGraphicsScene()
2219b3700f21SAdrian Hunter		self.scene.addItem(self.item)
2220b3700f21SAdrian Hunter
2221b3700f21SAdrian Hunter		self.view = QGraphicsView(self.scene)
2222b3700f21SAdrian Hunter		self.view.centerOn(0, 0)
2223b3700f21SAdrian Hunter		self.view.setAlignment(Qt.AlignLeft | Qt.AlignTop)
2224b3700f21SAdrian Hunter
2225b3700f21SAdrian Hunter		self.legend = SwitchGraphLegend(collection, region_attributes)
2226b3700f21SAdrian Hunter
2227b3700f21SAdrian Hunter		self.splitter = SwitchGraphSplitter()
2228b3700f21SAdrian Hunter		self.splitter.addWidget(self.view)
2229b3700f21SAdrian Hunter		self.splitter.addWidget(self.legend)
2230b3700f21SAdrian Hunter
2231b3700f21SAdrian Hunter		self.point_label = QLabel("")
2232b3700f21SAdrian Hunter		self.point_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
2233b3700f21SAdrian Hunter
2234b3700f21SAdrian Hunter		self.back_button = QToolButton()
2235b3700f21SAdrian Hunter		self.back_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowLeft))
2236b3700f21SAdrian Hunter		self.back_button.setDisabled(True)
2237b3700f21SAdrian Hunter		self.back_button.released.connect(lambda: self.Back())
2238b3700f21SAdrian Hunter
2239b3700f21SAdrian Hunter		self.forward_button = QToolButton()
2240b3700f21SAdrian Hunter		self.forward_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowRight))
2241b3700f21SAdrian Hunter		self.forward_button.setDisabled(True)
2242b3700f21SAdrian Hunter		self.forward_button.released.connect(lambda: self.Forward())
2243b3700f21SAdrian Hunter
2244b3700f21SAdrian Hunter		self.zoom_button = QToolButton()
2245b3700f21SAdrian Hunter		self.zoom_button.setText("Zoom")
2246b3700f21SAdrian Hunter		self.zoom_button.setDisabled(True)
2247b3700f21SAdrian Hunter		self.zoom_button.released.connect(lambda: self.Zoom())
2248b3700f21SAdrian Hunter
2249b3700f21SAdrian Hunter		self.hbox = HBoxLayout(self.back_button, self.forward_button, self.zoom_button, self.point_label)
2250b3700f21SAdrian Hunter
2251b3700f21SAdrian Hunter		self.vbox = VBoxLayout(self.splitter, self.hbox)
2252b3700f21SAdrian Hunter
2253b3700f21SAdrian Hunter		self.setLayout(self.vbox)
2254b3700f21SAdrian Hunter
2255b3700f21SAdrian Hunter	def GetScaleForRangeX(self, xsubrange):
2256b3700f21SAdrian Hunter		# Default graph 1000 pixels wide
2257b3700f21SAdrian Hunter		dflt = 1000.0
2258b3700f21SAdrian Hunter		r = xsubrange.hi - xsubrange.lo
2259b3700f21SAdrian Hunter		return dflt / r
2260b3700f21SAdrian Hunter
2261b3700f21SAdrian Hunter	def GetScaleForRangeY(self, ysubrange):
2262b3700f21SAdrian Hunter		# Default graph 50 pixels high
2263b3700f21SAdrian Hunter		dflt = 50.0
2264b3700f21SAdrian Hunter		r = ysubrange.hi - ysubrange.lo
2265b3700f21SAdrian Hunter		return dflt / r
2266b3700f21SAdrian Hunter
2267b3700f21SAdrian Hunter	def GetScaleForRange(self, subrange):
2268b3700f21SAdrian Hunter		# Default graph 1000 pixels wide, 50 pixels high
2269b3700f21SAdrian Hunter		xscale = self.GetScaleForRangeX(subrange.x)
2270b3700f21SAdrian Hunter		yscale = self.GetScaleForRangeY(subrange.y)
2271b3700f21SAdrian Hunter		return XY(xscale, yscale)
2272b3700f21SAdrian Hunter
2273b3700f21SAdrian Hunter	def PointEvent(self, cpu, time_from, time_to, hregions):
2274b3700f21SAdrian Hunter		text = "CPU: " + str(cpu)
2275b3700f21SAdrian Hunter		time_from = time_from.quantize(Decimal(1))
2276b3700f21SAdrian Hunter		rel_time_from = time_from - self.glb.StartTime(self.collection.machine_id)
2277b3700f21SAdrian Hunter		text = text + " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ")"
2278b3700f21SAdrian Hunter		self.point_label.setText(text)
2279b3700f21SAdrian Hunter		self.legend.Highlight(hregions)
2280b3700f21SAdrian Hunter
2281b3700f21SAdrian Hunter	def RightClickEvent(self, cpu, hregion_times, pos):
2282b3700f21SAdrian Hunter		if not IsSelectable(self.glb.db, "calls", "WHERE parent_id >= 0"):
2283b3700f21SAdrian Hunter			return
2284b3700f21SAdrian Hunter		menu = QMenu(self.view)
2285b3700f21SAdrian Hunter		for hregion, time in hregion_times:
2286b3700f21SAdrian Hunter			thread_at_time = (hregion.exec_comm_id, hregion.thread_id, time)
2287b3700f21SAdrian Hunter			menu_text = "Show Call Tree for {} {}:{} at {}".format(hregion.comm, hregion.pid, hregion.tid, time)
2288b3700f21SAdrian Hunter			menu.addAction(CreateAction(menu_text, "Show Call Tree", lambda a=None, args=thread_at_time: self.RightClickSelect(args), self.view))
2289b3700f21SAdrian Hunter		menu.exec_(pos)
2290b3700f21SAdrian Hunter
2291b3700f21SAdrian Hunter	def RightClickSelect(self, args):
2292b3700f21SAdrian Hunter		CallTreeWindow(self.glb, self.glb.mainwindow, thread_at_time=args)
2293b3700f21SAdrian Hunter
2294b3700f21SAdrian Hunter	def NoPointEvent(self):
2295b3700f21SAdrian Hunter		self.point_label.setText("")
2296b3700f21SAdrian Hunter		self.legend.Highlight({})
2297b3700f21SAdrian Hunter
2298b3700f21SAdrian Hunter	def RangeEvent(self, time_from, time_to):
2299b3700f21SAdrian Hunter		time_from = time_from.quantize(Decimal(1))
2300b3700f21SAdrian Hunter		time_to = time_to.quantize(Decimal(1))
2301b3700f21SAdrian Hunter		if time_to <= time_from:
2302b3700f21SAdrian Hunter			self.point_label.setText("")
2303b3700f21SAdrian Hunter			return
2304b3700f21SAdrian Hunter		rel_time_from = time_from - self.start_time
2305b3700f21SAdrian Hunter		rel_time_to = time_to - self.start_time
2306b3700f21SAdrian Hunter		text = " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ") to: " + str(time_to) + " (+" + ToTimeStr(rel_time_to) + ")"
2307b3700f21SAdrian Hunter		text = text + " duration: " + ToTimeStr(time_to - time_from)
2308b3700f21SAdrian Hunter		self.point_label.setText(text)
2309b3700f21SAdrian Hunter
2310b3700f21SAdrian Hunter	def BackState(self):
2311b3700f21SAdrian Hunter		return (self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect)
2312b3700f21SAdrian Hunter
2313b3700f21SAdrian Hunter	def PushBackState(self):
2314b3700f21SAdrian Hunter		state = copy.deepcopy(self.BackState())
2315b3700f21SAdrian Hunter		self.back_state.append(state)
2316b3700f21SAdrian Hunter		self.back_button.setEnabled(True)
2317b3700f21SAdrian Hunter
2318b3700f21SAdrian Hunter	def PopBackState(self):
2319b3700f21SAdrian Hunter		self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.back_state.pop()
2320b3700f21SAdrian Hunter		self.attrs.Update()
2321b3700f21SAdrian Hunter		if not self.back_state:
2322b3700f21SAdrian Hunter			self.back_button.setDisabled(True)
2323b3700f21SAdrian Hunter
2324b3700f21SAdrian Hunter	def PushForwardState(self):
2325b3700f21SAdrian Hunter		state = copy.deepcopy(self.BackState())
2326b3700f21SAdrian Hunter		self.forward_state.append(state)
2327b3700f21SAdrian Hunter		self.forward_button.setEnabled(True)
2328b3700f21SAdrian Hunter
2329b3700f21SAdrian Hunter	def PopForwardState(self):
2330b3700f21SAdrian Hunter		self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.forward_state.pop()
2331b3700f21SAdrian Hunter		self.attrs.Update()
2332b3700f21SAdrian Hunter		if not self.forward_state:
2333b3700f21SAdrian Hunter			self.forward_button.setDisabled(True)
2334b3700f21SAdrian Hunter
2335b3700f21SAdrian Hunter	def Title(self):
2336b3700f21SAdrian Hunter		time_from = self.collection.xrangelo + Decimal(self.attrs.subrange.x.lo)
2337b3700f21SAdrian Hunter		time_to = self.collection.xrangelo + Decimal(self.attrs.subrange.x.hi)
2338b3700f21SAdrian Hunter		rel_time_from = time_from - self.start_time
2339b3700f21SAdrian Hunter		rel_time_to = time_to - self.start_time
2340b3700f21SAdrian Hunter		title = "+" + ToTimeStr(rel_time_from) + " to +" + ToTimeStr(rel_time_to)
2341b3700f21SAdrian Hunter		title = title + " (" + ToTimeStr(time_to - time_from) + ")"
2342b3700f21SAdrian Hunter		return title
2343b3700f21SAdrian Hunter
2344b3700f21SAdrian Hunter	def Update(self):
2345b3700f21SAdrian Hunter		selected_subrange, selection_state = self.selection_state
2346b3700f21SAdrian Hunter		self.item.SetSelection(selection_state)
2347b3700f21SAdrian Hunter		self.item.SetBracket(self.fwd_rect)
2348b3700f21SAdrian Hunter		self.zoom_button.setDisabled(selected_subrange is None)
2349b3700f21SAdrian Hunter		self.GraphTitleChanged(self.Title())
2350b3700f21SAdrian Hunter		self.item.update(self.item.boundingRect())
2351b3700f21SAdrian Hunter
2352b3700f21SAdrian Hunter	def Back(self):
2353b3700f21SAdrian Hunter		if not self.back_state:
2354b3700f21SAdrian Hunter			return
2355b3700f21SAdrian Hunter		self.PushForwardState()
2356b3700f21SAdrian Hunter		self.PopBackState()
2357b3700f21SAdrian Hunter		self.Update()
2358b3700f21SAdrian Hunter
2359b3700f21SAdrian Hunter	def Forward(self):
2360b3700f21SAdrian Hunter		if not self.forward_state:
2361b3700f21SAdrian Hunter			return
2362b3700f21SAdrian Hunter		self.PushBackState()
2363b3700f21SAdrian Hunter		self.PopForwardState()
2364b3700f21SAdrian Hunter		self.Update()
2365b3700f21SAdrian Hunter
2366b3700f21SAdrian Hunter	def SelectEvent(self, x0, x1, selection_state):
2367b3700f21SAdrian Hunter		if selection_state is None:
2368b3700f21SAdrian Hunter			selected_subrange = None
2369b3700f21SAdrian Hunter		else:
2370b3700f21SAdrian Hunter			if x1 - x0 < 1.0:
2371b3700f21SAdrian Hunter				x1 += 1.0
2372b3700f21SAdrian Hunter			selected_subrange = Subrange(x0, x1)
2373b3700f21SAdrian Hunter		self.selection_state = (selected_subrange, selection_state)
2374b3700f21SAdrian Hunter		self.zoom_button.setDisabled(selected_subrange is None)
2375b3700f21SAdrian Hunter
2376b3700f21SAdrian Hunter	def Zoom(self):
2377b3700f21SAdrian Hunter		selected_subrange, selection_state = self.selection_state
2378b3700f21SAdrian Hunter		if selected_subrange is None:
2379b3700f21SAdrian Hunter			return
2380b3700f21SAdrian Hunter		self.fwd_rect = selection_state
2381b3700f21SAdrian Hunter		self.item.SetSelection(None)
2382b3700f21SAdrian Hunter		self.PushBackState()
2383b3700f21SAdrian Hunter		self.attrs.subrange.x = selected_subrange
2384b3700f21SAdrian Hunter		self.forward_state = []
2385b3700f21SAdrian Hunter		self.forward_button.setDisabled(True)
2386b3700f21SAdrian Hunter		self.selection_state = (None, None)
2387b3700f21SAdrian Hunter		self.fwd_rect = None
2388b3700f21SAdrian Hunter		self.attrs.scale.x = self.GetScaleForRangeX(self.attrs.subrange.x)
2389b3700f21SAdrian Hunter		self.attrs.Update()
2390b3700f21SAdrian Hunter		self.Update()
2391b3700f21SAdrian Hunter
2392b3700f21SAdrian Hunter# Slow initialization - perform non-GUI initialization in a separate thread and put up a modal message box while waiting
2393b3700f21SAdrian Hunter
2394b3700f21SAdrian Hunterclass SlowInitClass():
2395b3700f21SAdrian Hunter
2396b3700f21SAdrian Hunter	def __init__(self, glb, title, init_fn):
2397b3700f21SAdrian Hunter		self.init_fn = init_fn
2398b3700f21SAdrian Hunter		self.done = False
2399b3700f21SAdrian Hunter		self.result = None
2400b3700f21SAdrian Hunter
2401b3700f21SAdrian Hunter		self.msg_box = QMessageBox(glb.mainwindow)
2402b3700f21SAdrian Hunter		self.msg_box.setText("Initializing " + title + ". Please wait.")
2403b3700f21SAdrian Hunter		self.msg_box.setWindowTitle("Initializing " + title)
2404b3700f21SAdrian Hunter		self.msg_box.setWindowIcon(glb.mainwindow.style().standardIcon(QStyle.SP_MessageBoxInformation))
2405b3700f21SAdrian Hunter
2406b3700f21SAdrian Hunter		self.init_thread = Thread(self.ThreadFn, glb)
2407b3700f21SAdrian Hunter		self.init_thread.done.connect(lambda: self.Done(), Qt.QueuedConnection)
2408b3700f21SAdrian Hunter
2409b3700f21SAdrian Hunter		self.init_thread.start()
2410b3700f21SAdrian Hunter
2411b3700f21SAdrian Hunter	def Done(self):
2412b3700f21SAdrian Hunter		self.msg_box.done(0)
2413b3700f21SAdrian Hunter
2414b3700f21SAdrian Hunter	def ThreadFn(self, glb):
2415b3700f21SAdrian Hunter		conn_name = "SlowInitClass" + str(os.getpid())
2416b3700f21SAdrian Hunter		db, dbname = glb.dbref.Open(conn_name)
2417b3700f21SAdrian Hunter		self.result = self.init_fn(db)
2418b3700f21SAdrian Hunter		self.done = True
2419b3700f21SAdrian Hunter		return (True, 0)
2420b3700f21SAdrian Hunter
2421b3700f21SAdrian Hunter	def Result(self):
2422b3700f21SAdrian Hunter		while not self.done:
2423b3700f21SAdrian Hunter			self.msg_box.exec_()
2424b3700f21SAdrian Hunter		self.init_thread.wait()
2425b3700f21SAdrian Hunter		return self.result
2426b3700f21SAdrian Hunter
2427b3700f21SAdrian Hunterdef SlowInit(glb, title, init_fn):
2428b3700f21SAdrian Hunter	init = SlowInitClass(glb, title, init_fn)
2429b3700f21SAdrian Hunter	return init.Result()
2430b3700f21SAdrian Hunter
2431b3700f21SAdrian Hunter# Time chart by CPU window
2432b3700f21SAdrian Hunter
2433b3700f21SAdrian Hunterclass TimeChartByCPUWindow(QMdiSubWindow):
2434b3700f21SAdrian Hunter
2435b3700f21SAdrian Hunter	def __init__(self, glb, parent=None):
2436b3700f21SAdrian Hunter		super(TimeChartByCPUWindow, self).__init__(parent)
2437b3700f21SAdrian Hunter
2438b3700f21SAdrian Hunter		self.glb = glb
2439b3700f21SAdrian Hunter		self.machine_id = glb.HostMachineId()
2440b3700f21SAdrian Hunter		self.collection_name = "SwitchGraphDataCollection " + str(self.machine_id)
2441b3700f21SAdrian Hunter
2442b3700f21SAdrian Hunter		collection = LookupModel(self.collection_name)
2443b3700f21SAdrian Hunter		if collection is None:
2444b3700f21SAdrian Hunter			collection = SlowInit(glb, "Time Chart", self.Init)
2445b3700f21SAdrian Hunter
2446b3700f21SAdrian Hunter		self.widget = SwitchGraphWidget(glb, collection, self)
2447b3700f21SAdrian Hunter		self.view = self.widget
2448b3700f21SAdrian Hunter
2449b3700f21SAdrian Hunter		self.base_title = "Time Chart by CPU"
2450b3700f21SAdrian Hunter		self.setWindowTitle(self.base_title + self.widget.Title())
2451b3700f21SAdrian Hunter		self.widget.graph_title_changed.connect(self.GraphTitleChanged)
2452b3700f21SAdrian Hunter
2453b3700f21SAdrian Hunter		self.setWidget(self.widget)
2454b3700f21SAdrian Hunter
2455b3700f21SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, self.windowTitle())
2456b3700f21SAdrian Hunter
2457b3700f21SAdrian Hunter	def Init(self, db):
2458b3700f21SAdrian Hunter		return LookupCreateModel(self.collection_name, lambda : SwitchGraphDataCollection(self.glb, db, self.machine_id))
2459b3700f21SAdrian Hunter
2460b3700f21SAdrian Hunter	def GraphTitleChanged(self, title):
2461b3700f21SAdrian Hunter		self.setWindowTitle(self.base_title + " : " + title)
2462b3700f21SAdrian Hunter
24638392b74bSAdrian Hunter# Child data item  finder
24648392b74bSAdrian Hunter
24658392b74bSAdrian Hunterclass ChildDataItemFinder():
24668392b74bSAdrian Hunter
24678392b74bSAdrian Hunter	def __init__(self, root):
24688392b74bSAdrian Hunter		self.root = root
24698392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
24708392b74bSAdrian Hunter		self.rows = []
24718392b74bSAdrian Hunter		self.pos = 0
24728392b74bSAdrian Hunter
24738392b74bSAdrian Hunter	def FindSelect(self):
24748392b74bSAdrian Hunter		self.rows = []
24758392b74bSAdrian Hunter		if self.pattern:
24768392b74bSAdrian Hunter			pattern = re.compile(self.value)
24778392b74bSAdrian Hunter			for child in self.root.child_items:
24788392b74bSAdrian Hunter				for column_data in child.data:
24798392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
24808392b74bSAdrian Hunter						self.rows.append(child.row)
24818392b74bSAdrian Hunter						break
24828392b74bSAdrian Hunter		else:
24838392b74bSAdrian Hunter			for child in self.root.child_items:
24848392b74bSAdrian Hunter				for column_data in child.data:
24858392b74bSAdrian Hunter					if self.value in str(column_data):
24868392b74bSAdrian Hunter						self.rows.append(child.row)
24878392b74bSAdrian Hunter						break
24888392b74bSAdrian Hunter
24898392b74bSAdrian Hunter	def FindValue(self):
24908392b74bSAdrian Hunter		self.pos = 0
24918392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
24928392b74bSAdrian Hunter			self.FindSelect()
24938392b74bSAdrian Hunter		if not len(self.rows):
24948392b74bSAdrian Hunter			return -1
24958392b74bSAdrian Hunter		return self.rows[self.pos]
24968392b74bSAdrian Hunter
24978392b74bSAdrian Hunter	def FindThread(self):
24988392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
24998392b74bSAdrian Hunter			row = self.FindValue()
25008392b74bSAdrian Hunter		elif len(self.rows):
25018392b74bSAdrian Hunter			if self.direction > 0:
25028392b74bSAdrian Hunter				self.pos += 1
25038392b74bSAdrian Hunter				if self.pos >= len(self.rows):
25048392b74bSAdrian Hunter					self.pos = 0
25058392b74bSAdrian Hunter			else:
25068392b74bSAdrian Hunter				self.pos -= 1
25078392b74bSAdrian Hunter				if self.pos < 0:
25088392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
25098392b74bSAdrian Hunter			row = self.rows[self.pos]
25108392b74bSAdrian Hunter		else:
25118392b74bSAdrian Hunter			row = -1
25128392b74bSAdrian Hunter		return (True, row)
25138392b74bSAdrian Hunter
25148392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
25158392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
25168392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
25178392b74bSAdrian Hunter		thread = Thread(self.FindThread)
25188392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
25198392b74bSAdrian Hunter		thread.start()
25208392b74bSAdrian Hunter
25218392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
25228392b74bSAdrian Hunter		callback(row)
25238392b74bSAdrian Hunter
25248392b74bSAdrian Hunter# Number of database records to fetch in one go
25258392b74bSAdrian Hunter
25268392b74bSAdrian Hunterglb_chunk_sz = 10000
25278392b74bSAdrian Hunter
25288392b74bSAdrian Hunter# Background process for SQL data fetcher
25298392b74bSAdrian Hunter
25308392b74bSAdrian Hunterclass SQLFetcherProcess():
25318392b74bSAdrian Hunter
25328392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
25338392b74bSAdrian Hunter		# Need a unique connection name
25348392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
25358392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
25368392b74bSAdrian Hunter		self.sql = sql
25378392b74bSAdrian Hunter		self.buffer = buffer
25388392b74bSAdrian Hunter		self.head = head
25398392b74bSAdrian Hunter		self.tail = tail
25408392b74bSAdrian Hunter		self.fetch_count = fetch_count
25418392b74bSAdrian Hunter		self.fetching_done = fetching_done
25428392b74bSAdrian Hunter		self.process_target = process_target
25438392b74bSAdrian Hunter		self.wait_event = wait_event
25448392b74bSAdrian Hunter		self.fetched_event = fetched_event
25458392b74bSAdrian Hunter		self.prep = prep
25468392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
25478392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
25488392b74bSAdrian Hunter		self.last_id = -1
25498392b74bSAdrian Hunter		self.fetched = 0
25508392b74bSAdrian Hunter		self.more = True
25518392b74bSAdrian Hunter		self.local_head = self.head.value
25528392b74bSAdrian Hunter		self.local_tail = self.tail.value
25538392b74bSAdrian Hunter
25548392b74bSAdrian Hunter	def Select(self):
25558392b74bSAdrian Hunter		if self.query_limit:
25568392b74bSAdrian Hunter			if self.query_limit == 1:
25578392b74bSAdrian Hunter				return
25588392b74bSAdrian Hunter			self.query_limit -= 1
25598392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
25608392b74bSAdrian Hunter		QueryExec(self.query, stmt)
25618392b74bSAdrian Hunter
25628392b74bSAdrian Hunter	def Next(self):
25638392b74bSAdrian Hunter		if not self.query.next():
25648392b74bSAdrian Hunter			self.Select()
25658392b74bSAdrian Hunter			if not self.query.next():
25668392b74bSAdrian Hunter				return None
25678392b74bSAdrian Hunter		self.last_id = self.query.value(0)
25688392b74bSAdrian Hunter		return self.prep(self.query)
25698392b74bSAdrian Hunter
25708392b74bSAdrian Hunter	def WaitForTarget(self):
25718392b74bSAdrian Hunter		while True:
25728392b74bSAdrian Hunter			self.wait_event.clear()
25738392b74bSAdrian Hunter			target = self.process_target.value
25748392b74bSAdrian Hunter			if target > self.fetched or target < 0:
25758392b74bSAdrian Hunter				break
25768392b74bSAdrian Hunter			self.wait_event.wait()
25778392b74bSAdrian Hunter		return target
25788392b74bSAdrian Hunter
25798392b74bSAdrian Hunter	def HasSpace(self, sz):
25808392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
25818392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
25828392b74bSAdrian Hunter			if space > sz:
25838392b74bSAdrian Hunter				return True
25848392b74bSAdrian Hunter			if space >= glb_nsz:
25858392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
2586beda0e72STony Jones				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
25878392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
25888392b74bSAdrian Hunter			self.local_head = 0
25898392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
25908392b74bSAdrian Hunter			return True
25918392b74bSAdrian Hunter		return False
25928392b74bSAdrian Hunter
25938392b74bSAdrian Hunter	def WaitForSpace(self, sz):
25948392b74bSAdrian Hunter		if self.HasSpace(sz):
25958392b74bSAdrian Hunter			return
25968392b74bSAdrian Hunter		while True:
25978392b74bSAdrian Hunter			self.wait_event.clear()
25988392b74bSAdrian Hunter			self.local_tail = self.tail.value
25998392b74bSAdrian Hunter			if self.HasSpace(sz):
26008392b74bSAdrian Hunter				return
26018392b74bSAdrian Hunter			self.wait_event.wait()
26028392b74bSAdrian Hunter
26038392b74bSAdrian Hunter	def AddToBuffer(self, obj):
2604beda0e72STony Jones		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
26058392b74bSAdrian Hunter		n = len(d)
2606beda0e72STony Jones		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
26078392b74bSAdrian Hunter		sz = n + glb_nsz
26088392b74bSAdrian Hunter		self.WaitForSpace(sz)
26098392b74bSAdrian Hunter		pos = self.local_head
26108392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
26118392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
26128392b74bSAdrian Hunter		self.local_head += sz
26138392b74bSAdrian Hunter
26148392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
26158392b74bSAdrian Hunter		fetched = 0
26168392b74bSAdrian Hunter		while batch_size > fetched:
26178392b74bSAdrian Hunter			obj = self.Next()
26188392b74bSAdrian Hunter			if obj is None:
26198392b74bSAdrian Hunter				self.more = False
26208392b74bSAdrian Hunter				break
26218392b74bSAdrian Hunter			self.AddToBuffer(obj)
26228392b74bSAdrian Hunter			fetched += 1
26238392b74bSAdrian Hunter		if fetched:
26248392b74bSAdrian Hunter			self.fetched += fetched
26258392b74bSAdrian Hunter			with self.fetch_count.get_lock():
26268392b74bSAdrian Hunter				self.fetch_count.value += fetched
26278392b74bSAdrian Hunter			self.head.value = self.local_head
26288392b74bSAdrian Hunter			self.fetched_event.set()
26298392b74bSAdrian Hunter
26308392b74bSAdrian Hunter	def Run(self):
26318392b74bSAdrian Hunter		while self.more:
26328392b74bSAdrian Hunter			target = self.WaitForTarget()
26338392b74bSAdrian Hunter			if target < 0:
26348392b74bSAdrian Hunter				break
26358392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
26368392b74bSAdrian Hunter			self.FetchBatch(batch_size)
26378392b74bSAdrian Hunter		self.fetching_done.value = True
26388392b74bSAdrian Hunter		self.fetched_event.set()
26398392b74bSAdrian Hunter
26408392b74bSAdrian Hunterdef SQLFetcherFn(*x):
26418392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
26428392b74bSAdrian Hunter	process.Run()
26438392b74bSAdrian Hunter
26448392b74bSAdrian Hunter# SQL data fetcher
26458392b74bSAdrian Hunter
26468392b74bSAdrian Hunterclass SQLFetcher(QObject):
26478392b74bSAdrian Hunter
26488392b74bSAdrian Hunter	done = Signal(object)
26498392b74bSAdrian Hunter
26508392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
26518392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
26528392b74bSAdrian Hunter		self.process_data = process_data
26538392b74bSAdrian Hunter		self.more = True
26548392b74bSAdrian Hunter		self.target = 0
26558392b74bSAdrian Hunter		self.last_target = 0
26568392b74bSAdrian Hunter		self.fetched = 0
26578392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
26588392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
26598392b74bSAdrian Hunter		self.head = Value(c_longlong)
26608392b74bSAdrian Hunter		self.tail = Value(c_longlong)
26618392b74bSAdrian Hunter		self.local_tail = 0
26628392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
26638392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
26648392b74bSAdrian Hunter		self.last_count = 0
26658392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
26668392b74bSAdrian Hunter		self.wait_event = Event()
26678392b74bSAdrian Hunter		self.fetched_event = Event()
26688392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
26698392b74bSAdrian 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))
26708392b74bSAdrian Hunter		self.process.start()
26718392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
26728392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
26738392b74bSAdrian Hunter		self.thread.start()
26748392b74bSAdrian Hunter
26758392b74bSAdrian Hunter	def Shutdown(self):
26768392b74bSAdrian Hunter		# Tell the thread and process to exit
26778392b74bSAdrian Hunter		self.process_target.value = -1
26788392b74bSAdrian Hunter		self.wait_event.set()
26798392b74bSAdrian Hunter		self.more = False
26808392b74bSAdrian Hunter		self.fetching_done.value = True
26818392b74bSAdrian Hunter		self.fetched_event.set()
26828392b74bSAdrian Hunter
26838392b74bSAdrian Hunter	def Thread(self):
26848392b74bSAdrian Hunter		if not self.more:
26858392b74bSAdrian Hunter			return True, 0
26868392b74bSAdrian Hunter		while True:
26878392b74bSAdrian Hunter			self.fetched_event.clear()
26888392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
26898392b74bSAdrian Hunter			if fetch_count != self.last_count:
26908392b74bSAdrian Hunter				break
26918392b74bSAdrian Hunter			if self.fetching_done.value:
26928392b74bSAdrian Hunter				self.more = False
26938392b74bSAdrian Hunter				return True, 0
26948392b74bSAdrian Hunter			self.fetched_event.wait()
26958392b74bSAdrian Hunter		count = fetch_count - self.last_count
26968392b74bSAdrian Hunter		self.last_count = fetch_count
26978392b74bSAdrian Hunter		self.fetched += count
26988392b74bSAdrian Hunter		return False, count
26998392b74bSAdrian Hunter
27008392b74bSAdrian Hunter	def Fetch(self, nr):
27018392b74bSAdrian Hunter		if not self.more:
27028392b74bSAdrian Hunter			# -1 inidcates there are no more
27038392b74bSAdrian Hunter			return -1
27048392b74bSAdrian Hunter		result = self.fetched
27058392b74bSAdrian Hunter		extra = result + nr - self.target
27068392b74bSAdrian Hunter		if extra > 0:
27078392b74bSAdrian Hunter			self.target += extra
27088392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
27098392b74bSAdrian Hunter			if self.process_target.value >= 0:
27108392b74bSAdrian Hunter				self.process_target.value = self.target
27118392b74bSAdrian Hunter			self.wait_event.set()
27128392b74bSAdrian Hunter		return result
27138392b74bSAdrian Hunter
27148392b74bSAdrian Hunter	def RemoveFromBuffer(self):
27158392b74bSAdrian Hunter		pos = self.local_tail
27168392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
27178392b74bSAdrian Hunter			pos = 0
2718beda0e72STony Jones		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
27198392b74bSAdrian Hunter		if n == 0:
27208392b74bSAdrian Hunter			pos = 0
2721beda0e72STony Jones			n = pickle.loads(self.buffer[0 : glb_nsz])
27228392b74bSAdrian Hunter		pos += glb_nsz
2723beda0e72STony Jones		obj = pickle.loads(self.buffer[pos : pos + n])
27248392b74bSAdrian Hunter		self.local_tail = pos + n
27258392b74bSAdrian Hunter		return obj
27268392b74bSAdrian Hunter
27278392b74bSAdrian Hunter	def ProcessData(self, count):
27288392b74bSAdrian Hunter		for i in xrange(count):
27298392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
27308392b74bSAdrian Hunter			self.process_data(obj)
27318392b74bSAdrian Hunter		self.tail.value = self.local_tail
27328392b74bSAdrian Hunter		self.wait_event.set()
27338392b74bSAdrian Hunter		self.done.emit(count)
27348392b74bSAdrian Hunter
27358392b74bSAdrian Hunter# Fetch more records bar
27368392b74bSAdrian Hunter
27378392b74bSAdrian Hunterclass FetchMoreRecordsBar():
27388392b74bSAdrian Hunter
27398392b74bSAdrian Hunter	def __init__(self, model, parent):
27408392b74bSAdrian Hunter		self.model = model
27418392b74bSAdrian Hunter
27428392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
27438392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
27448392b74bSAdrian Hunter
27458392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
27468392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
27478392b74bSAdrian Hunter		self.fetch_count.setValue(10)
27488392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
27498392b74bSAdrian Hunter
27508392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
27518392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
27528392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
27538392b74bSAdrian Hunter
27548392b74bSAdrian Hunter		self.progress = QProgressBar()
27558392b74bSAdrian Hunter		self.progress.setRange(0, 100)
27568392b74bSAdrian Hunter		self.progress.hide()
27578392b74bSAdrian Hunter
27588392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
27598392b74bSAdrian Hunter		self.done_label.hide()
27608392b74bSAdrian Hunter
27618392b74bSAdrian Hunter		self.spacer = QLabel("")
27628392b74bSAdrian Hunter
27638392b74bSAdrian Hunter		self.close_button = QToolButton()
27648392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
27658392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
27668392b74bSAdrian Hunter
27678392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
27688392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
27698392b74bSAdrian Hunter
27708392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
27718392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
27728392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
27738392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
27748392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
27758392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
27768392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
27778392b74bSAdrian Hunter
27788392b74bSAdrian Hunter		self.bar = QWidget()
277926688729SAdrian Hunter		self.bar.setLayout(self.hbox)
27808392b74bSAdrian Hunter		self.bar.show()
27818392b74bSAdrian Hunter
27828392b74bSAdrian Hunter		self.in_progress = False
27838392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
27848392b74bSAdrian Hunter
27858392b74bSAdrian Hunter		self.done = False
27868392b74bSAdrian Hunter
27878392b74bSAdrian Hunter		if not model.HasMoreRecords():
27888392b74bSAdrian Hunter			self.Done()
27898392b74bSAdrian Hunter
27908392b74bSAdrian Hunter	def Widget(self):
27918392b74bSAdrian Hunter		return self.bar
27928392b74bSAdrian Hunter
27938392b74bSAdrian Hunter	def Activate(self):
27948392b74bSAdrian Hunter		self.bar.show()
27958392b74bSAdrian Hunter		self.fetch.setFocus()
27968392b74bSAdrian Hunter
27978392b74bSAdrian Hunter	def Deactivate(self):
27988392b74bSAdrian Hunter		self.bar.hide()
27998392b74bSAdrian Hunter
28008392b74bSAdrian Hunter	def Enable(self, enable):
28018392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
28028392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
28038392b74bSAdrian Hunter
28048392b74bSAdrian Hunter	def Busy(self):
28058392b74bSAdrian Hunter		self.Enable(False)
28068392b74bSAdrian Hunter		self.fetch.hide()
28078392b74bSAdrian Hunter		self.spacer.hide()
28088392b74bSAdrian Hunter		self.progress.show()
28098392b74bSAdrian Hunter
28108392b74bSAdrian Hunter	def Idle(self):
28118392b74bSAdrian Hunter		self.in_progress = False
28128392b74bSAdrian Hunter		self.Enable(True)
28138392b74bSAdrian Hunter		self.progress.hide()
28148392b74bSAdrian Hunter		self.fetch.show()
28158392b74bSAdrian Hunter		self.spacer.show()
28168392b74bSAdrian Hunter
28178392b74bSAdrian Hunter	def Target(self):
28188392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
28198392b74bSAdrian Hunter
28208392b74bSAdrian Hunter	def Done(self):
28218392b74bSAdrian Hunter		self.done = True
28228392b74bSAdrian Hunter		self.Idle()
28238392b74bSAdrian Hunter		self.label.hide()
28248392b74bSAdrian Hunter		self.fetch_count.hide()
28258392b74bSAdrian Hunter		self.fetch.hide()
28268392b74bSAdrian Hunter		self.spacer.hide()
28278392b74bSAdrian Hunter		self.done_label.show()
28288392b74bSAdrian Hunter
28298392b74bSAdrian Hunter	def Progress(self, count):
28308392b74bSAdrian Hunter		if self.in_progress:
28318392b74bSAdrian Hunter			if count:
28328392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
28338392b74bSAdrian Hunter				if percent >= 100:
28348392b74bSAdrian Hunter					self.Idle()
28358392b74bSAdrian Hunter				else:
28368392b74bSAdrian Hunter					self.progress.setValue(percent)
28378392b74bSAdrian Hunter		if not count:
28388392b74bSAdrian Hunter			# Count value of zero means no more records
28398392b74bSAdrian Hunter			self.Done()
28408392b74bSAdrian Hunter
28418392b74bSAdrian Hunter	def FetchMoreRecords(self):
28428392b74bSAdrian Hunter		if self.done:
28438392b74bSAdrian Hunter			return
28448392b74bSAdrian Hunter		self.progress.setValue(0)
28458392b74bSAdrian Hunter		self.Busy()
28468392b74bSAdrian Hunter		self.in_progress = True
28478392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
28488392b74bSAdrian Hunter
284976099f98SAdrian Hunter# Brance data model level two item
285076099f98SAdrian Hunter
285176099f98SAdrian Hunterclass BranchLevelTwoItem():
285276099f98SAdrian Hunter
2853530e22fdSAdrian Hunter	def __init__(self, row, col, text, parent_item):
285476099f98SAdrian Hunter		self.row = row
285576099f98SAdrian Hunter		self.parent_item = parent_item
2856530e22fdSAdrian Hunter		self.data = [""] * (col + 1)
2857530e22fdSAdrian Hunter		self.data[col] = text
285876099f98SAdrian Hunter		self.level = 2
285976099f98SAdrian Hunter
286076099f98SAdrian Hunter	def getParentItem(self):
286176099f98SAdrian Hunter		return self.parent_item
286276099f98SAdrian Hunter
286376099f98SAdrian Hunter	def getRow(self):
286476099f98SAdrian Hunter		return self.row
286576099f98SAdrian Hunter
286676099f98SAdrian Hunter	def childCount(self):
286776099f98SAdrian Hunter		return 0
286876099f98SAdrian Hunter
286976099f98SAdrian Hunter	def hasChildren(self):
287076099f98SAdrian Hunter		return False
287176099f98SAdrian Hunter
287276099f98SAdrian Hunter	def getData(self, column):
287376099f98SAdrian Hunter		return self.data[column]
287476099f98SAdrian Hunter
287576099f98SAdrian Hunter# Brance data model level one item
287676099f98SAdrian Hunter
287776099f98SAdrian Hunterclass BranchLevelOneItem():
287876099f98SAdrian Hunter
287976099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
288076099f98SAdrian Hunter		self.glb = glb
288176099f98SAdrian Hunter		self.row = row
288276099f98SAdrian Hunter		self.parent_item = parent_item
288376099f98SAdrian Hunter		self.child_count = 0
288476099f98SAdrian Hunter		self.child_items = []
288576099f98SAdrian Hunter		self.data = data[1:]
288676099f98SAdrian Hunter		self.dbid = data[0]
288776099f98SAdrian Hunter		self.level = 1
288876099f98SAdrian Hunter		self.query_done = False
2889530e22fdSAdrian Hunter		self.br_col = len(self.data) - 1
289076099f98SAdrian Hunter
289176099f98SAdrian Hunter	def getChildItem(self, row):
289276099f98SAdrian Hunter		return self.child_items[row]
289376099f98SAdrian Hunter
289476099f98SAdrian Hunter	def getParentItem(self):
289576099f98SAdrian Hunter		return self.parent_item
289676099f98SAdrian Hunter
289776099f98SAdrian Hunter	def getRow(self):
289876099f98SAdrian Hunter		return self.row
289976099f98SAdrian Hunter
290076099f98SAdrian Hunter	def Select(self):
290176099f98SAdrian Hunter		self.query_done = True
290276099f98SAdrian Hunter
290376099f98SAdrian Hunter		if not self.glb.have_disassembler:
290476099f98SAdrian Hunter			return
290576099f98SAdrian Hunter
290676099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
290776099f98SAdrian Hunter
290876099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
290976099f98SAdrian Hunter				  " FROM samples"
291076099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
291176099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
291276099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
291376099f98SAdrian Hunter		if not query.next():
291476099f98SAdrian Hunter			return
291576099f98SAdrian Hunter		cpu = query.value(0)
291676099f98SAdrian Hunter		dso = query.value(1)
291776099f98SAdrian Hunter		sym = query.value(2)
291876099f98SAdrian Hunter		if dso == 0 or sym == 0:
291976099f98SAdrian Hunter			return
292076099f98SAdrian Hunter		off = query.value(3)
292176099f98SAdrian Hunter		short_name = query.value(4)
292276099f98SAdrian Hunter		long_name = query.value(5)
292376099f98SAdrian Hunter		build_id = query.value(6)
292476099f98SAdrian Hunter		sym_start = query.value(7)
292576099f98SAdrian Hunter		ip = query.value(8)
292676099f98SAdrian Hunter
292776099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
292876099f98SAdrian Hunter				  " FROM samples"
292976099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
293076099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
293176099f98SAdrian Hunter				  " ORDER BY samples.id"
293276099f98SAdrian Hunter				  " LIMIT 1")
293376099f98SAdrian Hunter		if not query.next():
293476099f98SAdrian Hunter			return
293576099f98SAdrian Hunter		if query.value(0) != dso:
293676099f98SAdrian Hunter			# Cannot disassemble from one dso to another
293776099f98SAdrian Hunter			return
293876099f98SAdrian Hunter		bsym = query.value(1)
293976099f98SAdrian Hunter		boff = query.value(2)
294076099f98SAdrian Hunter		bsym_start = query.value(3)
294176099f98SAdrian Hunter		if bsym == 0:
294276099f98SAdrian Hunter			return
294376099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
294476099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
294576099f98SAdrian Hunter			return
294676099f98SAdrian Hunter
294776099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
294876099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
294976099f98SAdrian Hunter		if not f:
295076099f98SAdrian Hunter			return
295176099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
295276099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
295376099f98SAdrian Hunter
295476099f98SAdrian Hunter		buf_sz = tot + 16
295576099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
295676099f98SAdrian Hunter		f.seek(sym_start + off)
295776099f98SAdrian Hunter		buf.value = f.read(buf_sz)
295876099f98SAdrian Hunter		buf_ptr = addressof(buf)
295976099f98SAdrian Hunter		i = 0
296076099f98SAdrian Hunter		while tot > 0:
296176099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
296276099f98SAdrian Hunter			if cnt:
296376099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
296476099f98SAdrian Hunter				for k in xrange(cnt):
296576099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
296676099f98SAdrian Hunter					i += 1
296776099f98SAdrian Hunter				while k < 15:
296876099f98SAdrian Hunter					byte_str += "   "
296976099f98SAdrian Hunter					k += 1
2970530e22fdSAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
297176099f98SAdrian Hunter				self.child_count += 1
297276099f98SAdrian Hunter			else:
297376099f98SAdrian Hunter				return
297476099f98SAdrian Hunter			buf_ptr += cnt
297576099f98SAdrian Hunter			tot -= cnt
297676099f98SAdrian Hunter			buf_sz -= cnt
297776099f98SAdrian Hunter			ip += cnt
297876099f98SAdrian Hunter
297976099f98SAdrian Hunter	def childCount(self):
298076099f98SAdrian Hunter		if not self.query_done:
298176099f98SAdrian Hunter			self.Select()
298276099f98SAdrian Hunter			if not self.child_count:
298376099f98SAdrian Hunter				return -1
298476099f98SAdrian Hunter		return self.child_count
298576099f98SAdrian Hunter
298676099f98SAdrian Hunter	def hasChildren(self):
298776099f98SAdrian Hunter		if not self.query_done:
298876099f98SAdrian Hunter			return True
298976099f98SAdrian Hunter		return self.child_count > 0
299076099f98SAdrian Hunter
299176099f98SAdrian Hunter	def getData(self, column):
299276099f98SAdrian Hunter		return self.data[column]
299376099f98SAdrian Hunter
299476099f98SAdrian Hunter# Brance data model root item
299576099f98SAdrian Hunter
299676099f98SAdrian Hunterclass BranchRootItem():
299776099f98SAdrian Hunter
299876099f98SAdrian Hunter	def __init__(self):
299976099f98SAdrian Hunter		self.child_count = 0
300076099f98SAdrian Hunter		self.child_items = []
300176099f98SAdrian Hunter		self.level = 0
300276099f98SAdrian Hunter
300376099f98SAdrian Hunter	def getChildItem(self, row):
300476099f98SAdrian Hunter		return self.child_items[row]
300576099f98SAdrian Hunter
300676099f98SAdrian Hunter	def getParentItem(self):
300776099f98SAdrian Hunter		return None
300876099f98SAdrian Hunter
300976099f98SAdrian Hunter	def getRow(self):
301076099f98SAdrian Hunter		return 0
301176099f98SAdrian Hunter
301276099f98SAdrian Hunter	def childCount(self):
301376099f98SAdrian Hunter		return self.child_count
301476099f98SAdrian Hunter
301576099f98SAdrian Hunter	def hasChildren(self):
301676099f98SAdrian Hunter		return self.child_count > 0
301776099f98SAdrian Hunter
301876099f98SAdrian Hunter	def getData(self, column):
301976099f98SAdrian Hunter		return ""
302076099f98SAdrian Hunter
3021530e22fdSAdrian Hunter# Calculate instructions per cycle
3022530e22fdSAdrian Hunter
3023530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt):
3024530e22fdSAdrian Hunter	if cyc_cnt and insn_cnt:
3025530e22fdSAdrian Hunter		ipc = Decimal(float(insn_cnt) / cyc_cnt)
3026530e22fdSAdrian Hunter		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
3027530e22fdSAdrian Hunter	else:
3028530e22fdSAdrian Hunter		ipc = "0"
3029530e22fdSAdrian Hunter	return ipc
3030530e22fdSAdrian Hunter
303176099f98SAdrian Hunter# Branch data preparation
303276099f98SAdrian Hunter
3033530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data):
3034530e22fdSAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
3035530e22fdSAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
3036530e22fdSAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
3037530e22fdSAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
3038530e22fdSAdrian Hunter
3039530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data):
3040530e22fdSAdrian Hunter	insn_cnt = query.value(16)
3041530e22fdSAdrian Hunter	cyc_cnt = query.value(17)
3042530e22fdSAdrian Hunter	ipc = CalcIPC(cyc_cnt, insn_cnt)
3043530e22fdSAdrian Hunter	data.append(insn_cnt)
3044530e22fdSAdrian Hunter	data.append(cyc_cnt)
3045530e22fdSAdrian Hunter	data.append(ipc)
3046530e22fdSAdrian Hunter
304776099f98SAdrian Hunterdef BranchDataPrep(query):
304876099f98SAdrian Hunter	data = []
304976099f98SAdrian Hunter	for i in xrange(0, 8):
305076099f98SAdrian Hunter		data.append(query.value(i))
3051530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
305276099f98SAdrian Hunter	return data
305376099f98SAdrian Hunter
30548453c936SAdrian Hunterdef BranchDataPrepWA(query):
30558453c936SAdrian Hunter	data = []
30568453c936SAdrian Hunter	data.append(query.value(0))
30578453c936SAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
30588453c936SAdrian Hunter	data.append("{:>19}".format(query.value(1)))
30598453c936SAdrian Hunter	for i in xrange(2, 8):
30608453c936SAdrian Hunter		data.append(query.value(i))
3061530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
3062530e22fdSAdrian Hunter	return data
3063530e22fdSAdrian Hunter
3064530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query):
3065530e22fdSAdrian Hunter	data = []
3066530e22fdSAdrian Hunter	for i in xrange(0, 8):
3067530e22fdSAdrian Hunter		data.append(query.value(i))
3068530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
3069530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
3070530e22fdSAdrian Hunter	return data
3071530e22fdSAdrian Hunter
3072530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query):
3073530e22fdSAdrian Hunter	data = []
3074530e22fdSAdrian Hunter	data.append(query.value(0))
3075530e22fdSAdrian Hunter	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
3076530e22fdSAdrian Hunter	data.append("{:>19}".format(query.value(1)))
3077530e22fdSAdrian Hunter	for i in xrange(2, 8):
3078530e22fdSAdrian Hunter		data.append(query.value(i))
3079530e22fdSAdrian Hunter	BranchDataPrepIPC(query, data)
3080530e22fdSAdrian Hunter	BranchDataPrepBr(query, data)
30818453c936SAdrian Hunter	return data
30828453c936SAdrian Hunter
308376099f98SAdrian Hunter# Branch data model
308476099f98SAdrian Hunter
308576099f98SAdrian Hunterclass BranchModel(TreeModel):
308676099f98SAdrian Hunter
308776099f98SAdrian Hunter	progress = Signal(object)
308876099f98SAdrian Hunter
308976099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
30904a0979d4SAdrian Hunter		super(BranchModel, self).__init__(glb, None, parent)
309176099f98SAdrian Hunter		self.event_id = event_id
309276099f98SAdrian Hunter		self.more = True
309376099f98SAdrian Hunter		self.populated = 0
3094530e22fdSAdrian Hunter		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
3095530e22fdSAdrian Hunter		if self.have_ipc:
3096530e22fdSAdrian Hunter			select_ipc = ", insn_count, cyc_count"
3097530e22fdSAdrian Hunter			prep_fn = BranchDataWithIPCPrep
3098530e22fdSAdrian Hunter			prep_wa_fn = BranchDataWithIPCPrepWA
3099530e22fdSAdrian Hunter		else:
3100530e22fdSAdrian Hunter			select_ipc = ""
3101530e22fdSAdrian Hunter			prep_fn = BranchDataPrep
3102530e22fdSAdrian Hunter			prep_wa_fn = BranchDataPrepWA
310376099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
310476099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
310576099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
310676099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
3107530e22fdSAdrian Hunter			+ select_ipc +
310876099f98SAdrian Hunter			" FROM samples"
310976099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
311076099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
311176099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
311276099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
311376099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
311476099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
311576099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
311676099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
311776099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
311876099f98SAdrian Hunter			" ORDER BY samples.id"
311976099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
31208453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
3121530e22fdSAdrian Hunter			prep = prep_fn
31228453c936SAdrian Hunter		else:
3123530e22fdSAdrian Hunter			prep = prep_wa_fn
31248453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
312576099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
312676099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
312776099f98SAdrian Hunter
3128a448ba23SAdrian Hunter	def GetRoot(self):
3129a448ba23SAdrian Hunter		return BranchRootItem()
3130a448ba23SAdrian Hunter
313176099f98SAdrian Hunter	def columnCount(self, parent=None):
3132530e22fdSAdrian Hunter		if self.have_ipc:
3133530e22fdSAdrian Hunter			return 11
3134530e22fdSAdrian Hunter		else:
313576099f98SAdrian Hunter			return 8
313676099f98SAdrian Hunter
313776099f98SAdrian Hunter	def columnHeader(self, column):
3138530e22fdSAdrian Hunter		if self.have_ipc:
3139530e22fdSAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
3140530e22fdSAdrian Hunter		else:
314176099f98SAdrian Hunter			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
314276099f98SAdrian Hunter
314376099f98SAdrian Hunter	def columnFont(self, column):
3144530e22fdSAdrian Hunter		if self.have_ipc:
3145530e22fdSAdrian Hunter			br_col = 10
3146530e22fdSAdrian Hunter		else:
3147530e22fdSAdrian Hunter			br_col = 7
3148530e22fdSAdrian Hunter		if column != br_col:
314976099f98SAdrian Hunter			return None
315076099f98SAdrian Hunter		return QFont("Monospace")
315176099f98SAdrian Hunter
315276099f98SAdrian Hunter	def DisplayData(self, item, index):
315376099f98SAdrian Hunter		if item.level == 1:
315476099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
315576099f98SAdrian Hunter		return item.getData(index.column())
315676099f98SAdrian Hunter
315776099f98SAdrian Hunter	def AddSample(self, data):
315876099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
315976099f98SAdrian Hunter		self.root.child_items.append(child)
316076099f98SAdrian Hunter		self.populated += 1
316176099f98SAdrian Hunter
316276099f98SAdrian Hunter	def Update(self, fetched):
316376099f98SAdrian Hunter		if not fetched:
316476099f98SAdrian Hunter			self.more = False
316576099f98SAdrian Hunter			self.progress.emit(0)
316676099f98SAdrian Hunter		child_count = self.root.child_count
316776099f98SAdrian Hunter		count = self.populated - child_count
316876099f98SAdrian Hunter		if count > 0:
316976099f98SAdrian Hunter			parent = QModelIndex()
317076099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
317176099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
317276099f98SAdrian Hunter			self.root.child_count += count
317376099f98SAdrian Hunter			self.endInsertRows()
317476099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
317576099f98SAdrian Hunter
317676099f98SAdrian Hunter	def FetchMoreRecords(self, count):
317776099f98SAdrian Hunter		current = self.root.child_count
317876099f98SAdrian Hunter		if self.more:
317976099f98SAdrian Hunter			self.fetcher.Fetch(count)
318076099f98SAdrian Hunter		else:
318176099f98SAdrian Hunter			self.progress.emit(0)
318276099f98SAdrian Hunter		return current
318376099f98SAdrian Hunter
318476099f98SAdrian Hunter	def HasMoreRecords(self):
318576099f98SAdrian Hunter		return self.more
318676099f98SAdrian Hunter
31870bf0947aSAdrian Hunter# Report Variables
31880bf0947aSAdrian Hunter
31890bf0947aSAdrian Hunterclass ReportVars():
31900bf0947aSAdrian Hunter
3191cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
3192947cc38dSAdrian Hunter		self.name = name
31930bf0947aSAdrian Hunter		self.where_clause = where_clause
3194cd358012SAdrian Hunter		self.limit = limit
31950bf0947aSAdrian Hunter
31960bf0947aSAdrian Hunter	def UniqueId(self):
3197cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
31980bf0947aSAdrian Hunter
319976099f98SAdrian Hunter# Branch window
320076099f98SAdrian Hunter
320176099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
320276099f98SAdrian Hunter
3203947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
320476099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
320576099f98SAdrian Hunter
32060bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
320776099f98SAdrian Hunter
32080bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
320976099f98SAdrian Hunter
321076099f98SAdrian Hunter		self.view = QTreeView()
321176099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
321296c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
321396c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
321476099f98SAdrian Hunter		self.view.setModel(self.model)
321576099f98SAdrian Hunter
321676099f98SAdrian Hunter		self.ResizeColumnsToContents()
321776099f98SAdrian Hunter
32189bc4e4bfSAdrian Hunter		self.context_menu = TreeContextMenu(self.view)
32199bc4e4bfSAdrian Hunter
322076099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
322176099f98SAdrian Hunter
322276099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
322376099f98SAdrian Hunter
322476099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
322576099f98SAdrian Hunter
322676099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
322776099f98SAdrian Hunter
322876099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
322976099f98SAdrian Hunter
3230947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
323176099f98SAdrian Hunter
323276099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
323376099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
323476099f98SAdrian Hunter		# so implement a crude alternative
323576099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
323676099f98SAdrian Hunter		font = self.view.font()
323776099f98SAdrian Hunter		metrics = QFontMetrics(font)
323876099f98SAdrian Hunter		max = 0
323976099f98SAdrian Hunter		for row in xrange(n):
324076099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
324176099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
324276099f98SAdrian Hunter			max = len if len > max else max
324376099f98SAdrian Hunter		val = self.model.columnHeader(column)
324476099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
324576099f98SAdrian Hunter		max = len if len > max else max
324676099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
324776099f98SAdrian Hunter
324876099f98SAdrian Hunter	def ResizeColumnsToContents(self):
324976099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
325076099f98SAdrian Hunter		if n < 1:
325176099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
325276099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
325376099f98SAdrian Hunter			return
325476099f98SAdrian Hunter		columns = self.model.columnCount()
325576099f98SAdrian Hunter		for i in xrange(columns):
325676099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
325776099f98SAdrian Hunter
325876099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
325976099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
326076099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
326176099f98SAdrian Hunter		self.ResizeColumnsToContents()
326276099f98SAdrian Hunter
326376099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
326476099f98SAdrian Hunter		self.view.setFocus()
326576099f98SAdrian Hunter		self.find_bar.Busy()
326676099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
326776099f98SAdrian Hunter
326876099f98SAdrian Hunter	def FindDone(self, row):
326976099f98SAdrian Hunter		self.find_bar.Idle()
327076099f98SAdrian Hunter		if row >= 0:
327176099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
327276099f98SAdrian Hunter		else:
327376099f98SAdrian Hunter			self.find_bar.NotFound()
327476099f98SAdrian Hunter
32751c3ca1b3SAdrian Hunter# Line edit data item
32761c3ca1b3SAdrian Hunter
32771c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
32781c3ca1b3SAdrian Hunter
3279cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
32801c3ca1b3SAdrian Hunter		self.glb = glb
32811c3ca1b3SAdrian Hunter		self.label = label
32821c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
32831c3ca1b3SAdrian Hunter		self.parent = parent
32841c3ca1b3SAdrian Hunter		self.id = id
32851c3ca1b3SAdrian Hunter
3286cd358012SAdrian Hunter		self.value = default
32871c3ca1b3SAdrian Hunter
3288cd358012SAdrian Hunter		self.widget = QLineEdit(default)
32891c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
32901c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
32911c3ca1b3SAdrian Hunter		self.red = False
32921c3ca1b3SAdrian Hunter		self.error = ""
32931c3ca1b3SAdrian Hunter		self.validated = True
32941c3ca1b3SAdrian Hunter
32951c3ca1b3SAdrian Hunter		if placeholder_text:
32961c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
32971c3ca1b3SAdrian Hunter
32981c3ca1b3SAdrian Hunter	def TurnTextRed(self):
32991c3ca1b3SAdrian Hunter		if not self.red:
33001c3ca1b3SAdrian Hunter			palette = QPalette()
33011c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
33021c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
33031c3ca1b3SAdrian Hunter			self.red = True
33041c3ca1b3SAdrian Hunter
33051c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
33061c3ca1b3SAdrian Hunter		if self.red:
33071c3ca1b3SAdrian Hunter			palette = QPalette()
33081c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
33091c3ca1b3SAdrian Hunter			self.red = False
33101c3ca1b3SAdrian Hunter
33111c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
33121c3ca1b3SAdrian Hunter		self.value = ""
33131c3ca1b3SAdrian Hunter		self.TurnTextRed()
33141c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
33151c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
33161c3ca1b3SAdrian Hunter
33171c3ca1b3SAdrian Hunter	def Invalidate(self):
33181c3ca1b3SAdrian Hunter		self.validated = False
33191c3ca1b3SAdrian Hunter
33201c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
33211c3ca1b3SAdrian Hunter		self.value = input_string.strip()
33221c3ca1b3SAdrian Hunter
33231c3ca1b3SAdrian Hunter	def Validate(self):
33241c3ca1b3SAdrian Hunter		self.validated = True
33251c3ca1b3SAdrian Hunter		self.error = ""
33261c3ca1b3SAdrian Hunter		self.TurnTextNormal()
33271c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
33281c3ca1b3SAdrian Hunter		input_string = self.widget.text()
33291c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
33301c3ca1b3SAdrian Hunter			self.value = ""
33311c3ca1b3SAdrian Hunter			return
33321c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
33331c3ca1b3SAdrian Hunter
33341c3ca1b3SAdrian Hunter	def IsValid(self):
33351c3ca1b3SAdrian Hunter		if not self.validated:
33361c3ca1b3SAdrian Hunter			self.Validate()
33371c3ca1b3SAdrian Hunter		if len(self.error):
33381c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
33391c3ca1b3SAdrian Hunter			return False
33401c3ca1b3SAdrian Hunter		return True
33411c3ca1b3SAdrian Hunter
33421c3ca1b3SAdrian Hunter	def IsNumber(self, value):
33431c3ca1b3SAdrian Hunter		try:
33441c3ca1b3SAdrian Hunter			x = int(value)
33451c3ca1b3SAdrian Hunter		except:
33461c3ca1b3SAdrian Hunter			x = 0
33471c3ca1b3SAdrian Hunter		return str(x) == value
33481c3ca1b3SAdrian Hunter
33491c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
33501c3ca1b3SAdrian Hunter
33511c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
33521c3ca1b3SAdrian Hunter
33531c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
33541c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
33551c3ca1b3SAdrian Hunter
33561c3ca1b3SAdrian Hunter		self.column_name = column_name
33571c3ca1b3SAdrian Hunter
33581c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
33591c3ca1b3SAdrian Hunter		singles = []
33601c3ca1b3SAdrian Hunter		ranges = []
33611c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
33621c3ca1b3SAdrian Hunter			if "-" in value:
33631c3ca1b3SAdrian Hunter				vrange = value.split("-")
33641c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
33651c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
33661c3ca1b3SAdrian Hunter				ranges.append(vrange)
33671c3ca1b3SAdrian Hunter			else:
33681c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
33691c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
33701c3ca1b3SAdrian Hunter				singles.append(value)
33711c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
33721c3ca1b3SAdrian Hunter		if len(singles):
33731c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
33741c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
33751c3ca1b3SAdrian Hunter
3376cd358012SAdrian Hunter# Positive integer dialog data item
3377cd358012SAdrian Hunter
3378cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
3379cd358012SAdrian Hunter
3380cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
3381cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
3382cd358012SAdrian Hunter
3383cd358012SAdrian Hunter	def DoValidate(self, input_string):
3384cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
3385cd358012SAdrian Hunter			return self.InvalidValue(input_string)
3386cd358012SAdrian Hunter		value = int(input_string.strip())
3387cd358012SAdrian Hunter		if value <= 0:
3388cd358012SAdrian Hunter			return self.InvalidValue(input_string)
3389cd358012SAdrian Hunter		self.value = str(value)
3390cd358012SAdrian Hunter
33911c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
33921c3ca1b3SAdrian Hunter
33931c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
33941c3ca1b3SAdrian Hunter
33951c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
33961c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
33971c3ca1b3SAdrian Hunter
33981c3ca1b3SAdrian Hunter		self.table_name = table_name
33991c3ca1b3SAdrian Hunter		self.match_column = match_column
34001c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
34011c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
34021c3ca1b3SAdrian Hunter
34031c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
34041c3ca1b3SAdrian Hunter		ids = []
34051c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
34061c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
34071c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
34081c3ca1b3SAdrian Hunter		if ret:
34091c3ca1b3SAdrian Hunter			while query.next():
34101c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
34111c3ca1b3SAdrian Hunter		return ids
34121c3ca1b3SAdrian Hunter
34131c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
34141c3ca1b3SAdrian Hunter		all_ids = []
34151c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
34161c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
34171c3ca1b3SAdrian Hunter			if len(ids):
34181c3ca1b3SAdrian Hunter				all_ids.extend(ids)
34191c3ca1b3SAdrian Hunter			else:
34201c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
34211c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
34221c3ca1b3SAdrian Hunter		if self.column_name2:
34231c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
34241c3ca1b3SAdrian Hunter
34251c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
34261c3ca1b3SAdrian Hunter
34271c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
34281c3ca1b3SAdrian Hunter
34291c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
34301c3ca1b3SAdrian Hunter		self.column_name = column_name
34311c3ca1b3SAdrian Hunter
34321c3ca1b3SAdrian Hunter		self.last_id = 0
34331c3ca1b3SAdrian Hunter		self.first_time = 0
34341c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
34351c3ca1b3SAdrian Hunter
34361c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
34371c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
34381c3ca1b3SAdrian Hunter		if query.next():
34391c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
34409a9dae36SAdrian Hunter		self.first_time = int(glb.HostStartTime())
34419a9dae36SAdrian Hunter		self.last_time = int(glb.HostFinishTime())
34421c3ca1b3SAdrian Hunter		if placeholder_text:
34431c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
34441c3ca1b3SAdrian Hunter
34451c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
34461c3ca1b3SAdrian Hunter
34471c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
34481c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
34491c3ca1b3SAdrian Hunter		if query.next():
34501c3ca1b3SAdrian Hunter			return True, int(query.value(0))
34511c3ca1b3SAdrian Hunter		else:
34521c3ca1b3SAdrian Hunter			return False, 0
34531c3ca1b3SAdrian Hunter
34541c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
34551c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
34561c3ca1b3SAdrian Hunter		while True:
34571c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
34581c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
34591c3ca1b3SAdrian Hunter			if not query.next():
34601c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
34611c3ca1b3SAdrian Hunter				if not ok:
34621c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
34631c3ca1b3SAdrian Hunter					if not ok:
34641c3ca1b3SAdrian Hunter						return str(higher_id)
34651c3ca1b3SAdrian Hunter				next_id = dbid
34661c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
34671c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
34681c3ca1b3SAdrian Hunter			if get_floor:
34691c3ca1b3SAdrian Hunter				if target_time > next_time:
34701c3ca1b3SAdrian Hunter					lower_id = next_id
34711c3ca1b3SAdrian Hunter				else:
34721c3ca1b3SAdrian Hunter					higher_id = next_id
34731c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
34741c3ca1b3SAdrian Hunter					return str(higher_id)
34751c3ca1b3SAdrian Hunter			else:
34761c3ca1b3SAdrian Hunter				if target_time >= next_time:
34771c3ca1b3SAdrian Hunter					lower_id = next_id
34781c3ca1b3SAdrian Hunter				else:
34791c3ca1b3SAdrian Hunter					higher_id = next_id
34801c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
34811c3ca1b3SAdrian Hunter					return str(lower_id)
34821c3ca1b3SAdrian Hunter
34831c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
34841c3ca1b3SAdrian Hunter		mult = 1
34851c3ca1b3SAdrian Hunter		suffix = val[-2:]
34861c3ca1b3SAdrian Hunter		if suffix == "ms":
34871c3ca1b3SAdrian Hunter			mult = 1000000
34881c3ca1b3SAdrian Hunter		elif suffix == "us":
34891c3ca1b3SAdrian Hunter			mult = 1000
34901c3ca1b3SAdrian Hunter		elif suffix == "ns":
34911c3ca1b3SAdrian Hunter			mult = 1
34921c3ca1b3SAdrian Hunter		else:
34931c3ca1b3SAdrian Hunter			return val
34941c3ca1b3SAdrian Hunter		val = val[:-2].strip()
34951c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
34961c3ca1b3SAdrian Hunter			return val
34971c3ca1b3SAdrian Hunter		val = int(val) * mult
34981c3ca1b3SAdrian Hunter		if val >= 0:
34991c3ca1b3SAdrian Hunter			val += self.first_time
35001c3ca1b3SAdrian Hunter		else:
35011c3ca1b3SAdrian Hunter			val += self.last_time
35021c3ca1b3SAdrian Hunter		return str(val)
35031c3ca1b3SAdrian Hunter
35041c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
35051c3ca1b3SAdrian Hunter		if vrange[0] == "":
35061c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
35071c3ca1b3SAdrian Hunter		if vrange[1] == "":
35081c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
35091c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
35101c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
35111c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
35121c3ca1b3SAdrian Hunter			return False
35131c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
35141c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
35151c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
35161c3ca1b3SAdrian Hunter			return False
35171c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
35181c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
35191c3ca1b3SAdrian Hunter		return True
35201c3ca1b3SAdrian Hunter
35211c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
35221c3ca1b3SAdrian Hunter		n = value.count("-")
35231c3ca1b3SAdrian Hunter		if n == 1:
35241c3ca1b3SAdrian Hunter			pass
35251c3ca1b3SAdrian Hunter		elif n == 2:
35261c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
35271c3ca1b3SAdrian Hunter				n = 1
35281c3ca1b3SAdrian Hunter		elif n == 3:
35291c3ca1b3SAdrian Hunter			n = 2
35301c3ca1b3SAdrian Hunter		else:
35311c3ca1b3SAdrian Hunter			return False
35321c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
35331c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
35341c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
35351c3ca1b3SAdrian Hunter			ranges.append(vrange)
35361c3ca1b3SAdrian Hunter			return True
35371c3ca1b3SAdrian Hunter		return False
35381c3ca1b3SAdrian Hunter
35391c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
35401c3ca1b3SAdrian Hunter		ranges = []
35411c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
35421c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
35431c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
35441c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
35451c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
35461c3ca1b3SAdrian Hunter
35470924cd68SAdrian Hunter# Report Dialog Base
3548210cf1f9SAdrian Hunter
35490924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
3550210cf1f9SAdrian Hunter
35510924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
35520924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
3553210cf1f9SAdrian Hunter
3554210cf1f9SAdrian Hunter		self.glb = glb
3555210cf1f9SAdrian Hunter
35560bf0947aSAdrian Hunter		self.report_vars = ReportVars()
3557210cf1f9SAdrian Hunter
35580924cd68SAdrian Hunter		self.setWindowTitle(title)
3559210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
3560210cf1f9SAdrian Hunter
35611c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
3562210cf1f9SAdrian Hunter
35630924cd68SAdrian Hunter		self.partial = partial
35640924cd68SAdrian Hunter
3565210cf1f9SAdrian Hunter		self.grid = QGridLayout()
3566210cf1f9SAdrian Hunter
3567210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
3568210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
3569210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
3570210cf1f9SAdrian Hunter
3571210cf1f9SAdrian Hunter		self.status = QLabel()
3572210cf1f9SAdrian Hunter
3573210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
3574210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
3575210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
3576210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
3577210cf1f9SAdrian Hunter
3578210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
3579210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
3580210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
3581210cf1f9SAdrian Hunter
3582210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
3583210cf1f9SAdrian Hunter		#self.hbox.addStretch()
3584210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
3585210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
3586210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
3587210cf1f9SAdrian Hunter
3588210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
3589210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
3590210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
3591210cf1f9SAdrian Hunter
359226688729SAdrian Hunter		self.setLayout(self.vbox)
3593210cf1f9SAdrian Hunter
3594210cf1f9SAdrian Hunter	def Ok(self):
35950bf0947aSAdrian Hunter		vars = self.report_vars
35961c3ca1b3SAdrian Hunter		for d in self.data_items:
35971c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
35981c3ca1b3SAdrian Hunter				vars.name = d.value
3599947cc38dSAdrian Hunter		if not vars.name:
3600210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
3601210cf1f9SAdrian Hunter			return
3602210cf1f9SAdrian Hunter		for d in self.data_items:
3603210cf1f9SAdrian Hunter			if not d.IsValid():
3604210cf1f9SAdrian Hunter				return
3605210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
3606cd358012SAdrian Hunter			if d.id == "LIMIT":
3607cd358012SAdrian Hunter				vars.limit = d.value
3608cd358012SAdrian Hunter			elif len(d.value):
36090bf0947aSAdrian Hunter				if len(vars.where_clause):
36100bf0947aSAdrian Hunter					vars.where_clause += " AND "
36110bf0947aSAdrian Hunter				vars.where_clause += d.value
36120bf0947aSAdrian Hunter		if len(vars.where_clause):
36130924cd68SAdrian Hunter			if self.partial:
36140bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
3615210cf1f9SAdrian Hunter			else:
36160bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
3617210cf1f9SAdrian Hunter		self.accept()
3618210cf1f9SAdrian Hunter
3619210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
3620210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
3621210cf1f9SAdrian Hunter
3622210cf1f9SAdrian Hunter	def ClearMessage(self):
3623210cf1f9SAdrian Hunter		self.status.setText("")
3624210cf1f9SAdrian Hunter
36250924cd68SAdrian Hunter# Selected branch report creation dialog
36260924cd68SAdrian Hunter
36270924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
36280924cd68SAdrian Hunter
36290924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
36300924cd68SAdrian Hunter		title = "Selected Branches"
36311c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
36321c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
36331c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
36341c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
36351c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
36361c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
36371c3ca1b3SAdrian 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),
36381c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
36391c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
36400924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
36410924cd68SAdrian Hunter
364276099f98SAdrian Hunter# Event list
364376099f98SAdrian Hunter
364476099f98SAdrian Hunterdef GetEventList(db):
364576099f98SAdrian Hunter	events = []
364676099f98SAdrian Hunter	query = QSqlQuery(db)
364776099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
364876099f98SAdrian Hunter	while query.next():
364976099f98SAdrian Hunter		events.append(query.value(0))
365076099f98SAdrian Hunter	return events
365176099f98SAdrian Hunter
3652655cb952SAdrian Hunter# Is a table selectable
3653655cb952SAdrian Hunter
3654530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"):
3655655cb952SAdrian Hunter	query = QSqlQuery(db)
3656655cb952SAdrian Hunter	try:
3657530e22fdSAdrian Hunter		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
3658655cb952SAdrian Hunter	except:
3659655cb952SAdrian Hunter		return False
3660655cb952SAdrian Hunter	return True
3661655cb952SAdrian Hunter
36628392b74bSAdrian Hunter# SQL table data model item
36638392b74bSAdrian Hunter
36648392b74bSAdrian Hunterclass SQLTableItem():
36658392b74bSAdrian Hunter
36668392b74bSAdrian Hunter	def __init__(self, row, data):
36678392b74bSAdrian Hunter		self.row = row
36688392b74bSAdrian Hunter		self.data = data
36698392b74bSAdrian Hunter
36708392b74bSAdrian Hunter	def getData(self, column):
36718392b74bSAdrian Hunter		return self.data[column]
36728392b74bSAdrian Hunter
36738392b74bSAdrian Hunter# SQL table data model
36748392b74bSAdrian Hunter
36758392b74bSAdrian Hunterclass SQLTableModel(TableModel):
36768392b74bSAdrian Hunter
36778392b74bSAdrian Hunter	progress = Signal(object)
36788392b74bSAdrian Hunter
36798c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
36808392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
36818392b74bSAdrian Hunter		self.glb = glb
36828392b74bSAdrian Hunter		self.more = True
36838392b74bSAdrian Hunter		self.populated = 0
36848c90fef9SAdrian Hunter		self.column_headers = column_headers
36858453c936SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample)
36868392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
36878392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
36888392b74bSAdrian Hunter
36898392b74bSAdrian Hunter	def DisplayData(self, item, index):
36908392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
36918392b74bSAdrian Hunter		return item.getData(index.column())
36928392b74bSAdrian Hunter
36938392b74bSAdrian Hunter	def AddSample(self, data):
36948392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
36958392b74bSAdrian Hunter		self.child_items.append(child)
36968392b74bSAdrian Hunter		self.populated += 1
36978392b74bSAdrian Hunter
36988392b74bSAdrian Hunter	def Update(self, fetched):
36998392b74bSAdrian Hunter		if not fetched:
37008392b74bSAdrian Hunter			self.more = False
37018392b74bSAdrian Hunter			self.progress.emit(0)
37028392b74bSAdrian Hunter		child_count = self.child_count
37038392b74bSAdrian Hunter		count = self.populated - child_count
37048392b74bSAdrian Hunter		if count > 0:
37058392b74bSAdrian Hunter			parent = QModelIndex()
37068392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
37078392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
37088392b74bSAdrian Hunter			self.child_count += count
37098392b74bSAdrian Hunter			self.endInsertRows()
37108392b74bSAdrian Hunter			self.progress.emit(self.child_count)
37118392b74bSAdrian Hunter
37128392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
37138392b74bSAdrian Hunter		current = self.child_count
37148392b74bSAdrian Hunter		if self.more:
37158392b74bSAdrian Hunter			self.fetcher.Fetch(count)
37168392b74bSAdrian Hunter		else:
37178392b74bSAdrian Hunter			self.progress.emit(0)
37188392b74bSAdrian Hunter		return current
37198392b74bSAdrian Hunter
37208392b74bSAdrian Hunter	def HasMoreRecords(self):
37218392b74bSAdrian Hunter		return self.more
37228392b74bSAdrian Hunter
37238c90fef9SAdrian Hunter	def columnCount(self, parent=None):
37248c90fef9SAdrian Hunter		return len(self.column_headers)
37258c90fef9SAdrian Hunter
37268c90fef9SAdrian Hunter	def columnHeader(self, column):
37278c90fef9SAdrian Hunter		return self.column_headers[column]
37288c90fef9SAdrian Hunter
37298453c936SAdrian Hunter	def SQLTableDataPrep(self, query, count):
37308453c936SAdrian Hunter		data = []
37318453c936SAdrian Hunter		for i in xrange(count):
37328453c936SAdrian Hunter			data.append(query.value(i))
37338453c936SAdrian Hunter		return data
37348453c936SAdrian Hunter
37358392b74bSAdrian Hunter# SQL automatic table data model
37368392b74bSAdrian Hunter
37378392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
37388392b74bSAdrian Hunter
37398392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
37408392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
37418392b74bSAdrian Hunter		if table_name == "comm_threads_view":
37428392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
37438392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
37448c90fef9SAdrian Hunter		column_headers = []
37458392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
37468392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
37478392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
37488392b74bSAdrian Hunter			while query.next():
37498c90fef9SAdrian Hunter				column_headers.append(query.value(1))
37508392b74bSAdrian Hunter			if table_name == "sqlite_master":
37518392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
37528392b74bSAdrian Hunter		else:
37538392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
37548392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
37558392b74bSAdrian Hunter				select_table_name = table_name[19:]
37568392b74bSAdrian Hunter				schema = "information_schema"
37578392b74bSAdrian Hunter			else:
37588392b74bSAdrian Hunter				select_table_name = table_name
37598392b74bSAdrian Hunter				schema = "public"
37608392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
37618392b74bSAdrian Hunter			while query.next():
37628c90fef9SAdrian Hunter				column_headers.append(query.value(0))
37638453c936SAdrian Hunter		if pyside_version_1 and sys.version_info[0] == 3:
37648453c936SAdrian Hunter			if table_name == "samples_view":
37658453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_view_DataPrep
37668453c936SAdrian Hunter			if table_name == "samples":
37678453c936SAdrian Hunter				self.SQLTableDataPrep = self.samples_DataPrep
37688c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
37698392b74bSAdrian Hunter
37708453c936SAdrian Hunter	def samples_view_DataPrep(self, query, count):
37718453c936SAdrian Hunter		data = []
37728453c936SAdrian Hunter		data.append(query.value(0))
37738453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
37748453c936SAdrian Hunter		data.append("{:>19}".format(query.value(1)))
37758453c936SAdrian Hunter		for i in xrange(2, count):
37768453c936SAdrian Hunter			data.append(query.value(i))
37778453c936SAdrian Hunter		return data
37788453c936SAdrian Hunter
37798453c936SAdrian Hunter	def samples_DataPrep(self, query, count):
37808453c936SAdrian Hunter		data = []
37818453c936SAdrian Hunter		for i in xrange(9):
37828453c936SAdrian Hunter			data.append(query.value(i))
37838453c936SAdrian Hunter		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
37848453c936SAdrian Hunter		data.append("{:>19}".format(query.value(9)))
37858453c936SAdrian Hunter		for i in xrange(10, count):
37868453c936SAdrian Hunter			data.append(query.value(i))
37878453c936SAdrian Hunter		return data
37888453c936SAdrian Hunter
37898392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
37908392b74bSAdrian Hunter
37918392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
37928392b74bSAdrian Hunter
37938392b74bSAdrian Hunter	def __init__(self, parent=None):
37948392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
37958392b74bSAdrian Hunter
37968392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
37978392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
37988392b74bSAdrian Hunter		# so implement a crude alternative
37998392b74bSAdrian Hunter		font = self.view.font()
38008392b74bSAdrian Hunter		metrics = QFontMetrics(font)
38018392b74bSAdrian Hunter		max = 0
38028392b74bSAdrian Hunter		for row in xrange(n):
38038392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
38048392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
38058392b74bSAdrian Hunter			max = len if len > max else max
38068392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
38078392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
38088392b74bSAdrian Hunter		max = len if len > max else max
38098392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
38108392b74bSAdrian Hunter
38118392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
38128392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
38138392b74bSAdrian Hunter		if n < 1:
38148392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
38158392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
38168392b74bSAdrian Hunter			return
38178392b74bSAdrian Hunter		columns = self.data_model.columnCount()
38188392b74bSAdrian Hunter		for i in xrange(columns):
38198392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
38208392b74bSAdrian Hunter
38218392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
38228392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
38238392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
38248392b74bSAdrian Hunter		self.ResizeColumnsToContents()
38258392b74bSAdrian Hunter
382696c43b9aSAdrian Hunter# Convert value to CSV
382796c43b9aSAdrian Hunter
382896c43b9aSAdrian Hunterdef ToCSValue(val):
382996c43b9aSAdrian Hunter	if '"' in val:
383096c43b9aSAdrian Hunter		val = val.replace('"', '""')
383196c43b9aSAdrian Hunter	if "," in val or '"' in val:
383296c43b9aSAdrian Hunter		val = '"' + val + '"'
383396c43b9aSAdrian Hunter	return val
383496c43b9aSAdrian Hunter
383596c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
383696c43b9aSAdrian Hunter
383796c43b9aSAdrian Hunterglb_max_cols = 1000
383896c43b9aSAdrian Hunter
383996c43b9aSAdrian Hunterdef RowColumnKey(a):
384096c43b9aSAdrian Hunter	return a.row() * glb_max_cols + a.column()
384196c43b9aSAdrian Hunter
384296c43b9aSAdrian Hunter# Copy selected table cells to clipboard
384396c43b9aSAdrian Hunter
384496c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
384596c43b9aSAdrian Hunter	indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
384696c43b9aSAdrian Hunter	idx_cnt = len(indexes)
384796c43b9aSAdrian Hunter	if not idx_cnt:
384896c43b9aSAdrian Hunter		return
384996c43b9aSAdrian Hunter	if idx_cnt == 1:
385096c43b9aSAdrian Hunter		with_hdr=False
385196c43b9aSAdrian Hunter	min_row = indexes[0].row()
385296c43b9aSAdrian Hunter	max_row = indexes[0].row()
385396c43b9aSAdrian Hunter	min_col = indexes[0].column()
385496c43b9aSAdrian Hunter	max_col = indexes[0].column()
385596c43b9aSAdrian Hunter	for i in indexes:
385696c43b9aSAdrian Hunter		min_row = min(min_row, i.row())
385796c43b9aSAdrian Hunter		max_row = max(max_row, i.row())
385896c43b9aSAdrian Hunter		min_col = min(min_col, i.column())
385996c43b9aSAdrian Hunter		max_col = max(max_col, i.column())
386096c43b9aSAdrian Hunter	if max_col > glb_max_cols:
386196c43b9aSAdrian Hunter		raise RuntimeError("glb_max_cols is too low")
386296c43b9aSAdrian Hunter	max_width = [0] * (1 + max_col - min_col)
386396c43b9aSAdrian Hunter	for i in indexes:
386496c43b9aSAdrian Hunter		c = i.column() - min_col
386596c43b9aSAdrian Hunter		max_width[c] = max(max_width[c], len(str(i.data())))
386696c43b9aSAdrian Hunter	text = ""
386796c43b9aSAdrian Hunter	pad = ""
386896c43b9aSAdrian Hunter	sep = ""
386996c43b9aSAdrian Hunter	if with_hdr:
387096c43b9aSAdrian Hunter		model = indexes[0].model()
387196c43b9aSAdrian Hunter		for col in range(min_col, max_col + 1):
3872a6172059SAdrian Hunter			val = model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
387396c43b9aSAdrian Hunter			if as_csv:
387496c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
387596c43b9aSAdrian Hunter				sep = ","
387696c43b9aSAdrian Hunter			else:
387796c43b9aSAdrian Hunter				c = col - min_col
387896c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
387996c43b9aSAdrian Hunter				width = max_width[c]
388096c43b9aSAdrian Hunter				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
388196c43b9aSAdrian Hunter				if align & Qt.AlignRight:
388296c43b9aSAdrian Hunter					val = val.rjust(width)
388396c43b9aSAdrian Hunter				text += pad + sep + val
388496c43b9aSAdrian Hunter				pad = " " * (width - len(val))
388596c43b9aSAdrian Hunter				sep = "  "
388696c43b9aSAdrian Hunter		text += "\n"
388796c43b9aSAdrian Hunter		pad = ""
388896c43b9aSAdrian Hunter		sep = ""
388996c43b9aSAdrian Hunter	last_row = min_row
389096c43b9aSAdrian Hunter	for i in indexes:
389196c43b9aSAdrian Hunter		if i.row() > last_row:
389296c43b9aSAdrian Hunter			last_row = i.row()
389396c43b9aSAdrian Hunter			text += "\n"
389496c43b9aSAdrian Hunter			pad = ""
389596c43b9aSAdrian Hunter			sep = ""
389696c43b9aSAdrian Hunter		if as_csv:
389796c43b9aSAdrian Hunter			text += sep + ToCSValue(str(i.data()))
389896c43b9aSAdrian Hunter			sep = ","
389996c43b9aSAdrian Hunter		else:
390096c43b9aSAdrian Hunter			width = max_width[i.column() - min_col]
390196c43b9aSAdrian Hunter			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
390296c43b9aSAdrian Hunter				val = str(i.data()).rjust(width)
390396c43b9aSAdrian Hunter			else:
390496c43b9aSAdrian Hunter				val = str(i.data())
390596c43b9aSAdrian Hunter			text += pad + sep + val
390696c43b9aSAdrian Hunter			pad = " " * (width - len(val))
390796c43b9aSAdrian Hunter			sep = "  "
390896c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
390996c43b9aSAdrian Hunter
391096c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
391196c43b9aSAdrian Hunter	indexes = view.selectedIndexes()
391296c43b9aSAdrian Hunter	if not len(indexes):
391396c43b9aSAdrian Hunter		return
391496c43b9aSAdrian Hunter
391596c43b9aSAdrian Hunter	selection = view.selectionModel()
391696c43b9aSAdrian Hunter
391796c43b9aSAdrian Hunter	first = None
391896c43b9aSAdrian Hunter	for i in indexes:
391996c43b9aSAdrian Hunter		above = view.indexAbove(i)
392096c43b9aSAdrian Hunter		if not selection.isSelected(above):
392196c43b9aSAdrian Hunter			first = i
392296c43b9aSAdrian Hunter			break
392396c43b9aSAdrian Hunter
392496c43b9aSAdrian Hunter	if first is None:
392596c43b9aSAdrian Hunter		raise RuntimeError("CopyTreeCellsToClipboard internal error")
392696c43b9aSAdrian Hunter
392796c43b9aSAdrian Hunter	model = first.model()
392896c43b9aSAdrian Hunter	row_cnt = 0
392996c43b9aSAdrian Hunter	col_cnt = model.columnCount(first)
393096c43b9aSAdrian Hunter	max_width = [0] * col_cnt
393196c43b9aSAdrian Hunter
393296c43b9aSAdrian Hunter	indent_sz = 2
393396c43b9aSAdrian Hunter	indent_str = " " * indent_sz
393496c43b9aSAdrian Hunter
393596c43b9aSAdrian Hunter	expanded_mark_sz = 2
393696c43b9aSAdrian Hunter	if sys.version_info[0] == 3:
393796c43b9aSAdrian Hunter		expanded_mark = "\u25BC "
393896c43b9aSAdrian Hunter		not_expanded_mark = "\u25B6 "
393996c43b9aSAdrian Hunter	else:
394096c43b9aSAdrian Hunter		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
394196c43b9aSAdrian Hunter		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
394296c43b9aSAdrian Hunter	leaf_mark = "  "
394396c43b9aSAdrian Hunter
394496c43b9aSAdrian Hunter	if not as_csv:
394596c43b9aSAdrian Hunter		pos = first
394696c43b9aSAdrian Hunter		while True:
394796c43b9aSAdrian Hunter			row_cnt += 1
394896c43b9aSAdrian Hunter			row = pos.row()
394996c43b9aSAdrian Hunter			for c in range(col_cnt):
395096c43b9aSAdrian Hunter				i = pos.sibling(row, c)
395196c43b9aSAdrian Hunter				if c:
395296c43b9aSAdrian Hunter					n = len(str(i.data()))
395396c43b9aSAdrian Hunter				else:
395496c43b9aSAdrian Hunter					n = len(str(i.data()).strip())
395596c43b9aSAdrian Hunter					n += (i.internalPointer().level - 1) * indent_sz
395696c43b9aSAdrian Hunter					n += expanded_mark_sz
395796c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], n)
395896c43b9aSAdrian Hunter			pos = view.indexBelow(pos)
395996c43b9aSAdrian Hunter			if not selection.isSelected(pos):
396096c43b9aSAdrian Hunter				break
396196c43b9aSAdrian Hunter
396296c43b9aSAdrian Hunter	text = ""
396396c43b9aSAdrian Hunter	pad = ""
396496c43b9aSAdrian Hunter	sep = ""
396596c43b9aSAdrian Hunter	if with_hdr:
396696c43b9aSAdrian Hunter		for c in range(col_cnt):
396796c43b9aSAdrian Hunter			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
396896c43b9aSAdrian Hunter			if as_csv:
396996c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
397096c43b9aSAdrian Hunter				sep = ","
397196c43b9aSAdrian Hunter			else:
397296c43b9aSAdrian Hunter				max_width[c] = max(max_width[c], len(val))
397396c43b9aSAdrian Hunter				width = max_width[c]
397496c43b9aSAdrian Hunter				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
397596c43b9aSAdrian Hunter				if align & Qt.AlignRight:
397696c43b9aSAdrian Hunter					val = val.rjust(width)
397796c43b9aSAdrian Hunter				text += pad + sep + val
397896c43b9aSAdrian Hunter				pad = " " * (width - len(val))
397996c43b9aSAdrian Hunter				sep = "   "
398096c43b9aSAdrian Hunter		text += "\n"
398196c43b9aSAdrian Hunter		pad = ""
398296c43b9aSAdrian Hunter		sep = ""
398396c43b9aSAdrian Hunter
398496c43b9aSAdrian Hunter	pos = first
398596c43b9aSAdrian Hunter	while True:
398696c43b9aSAdrian Hunter		row = pos.row()
398796c43b9aSAdrian Hunter		for c in range(col_cnt):
398896c43b9aSAdrian Hunter			i = pos.sibling(row, c)
398996c43b9aSAdrian Hunter			val = str(i.data())
399096c43b9aSAdrian Hunter			if not c:
399196c43b9aSAdrian Hunter				if model.hasChildren(i):
399296c43b9aSAdrian Hunter					if view.isExpanded(i):
399396c43b9aSAdrian Hunter						mark = expanded_mark
399496c43b9aSAdrian Hunter					else:
399596c43b9aSAdrian Hunter						mark = not_expanded_mark
399696c43b9aSAdrian Hunter				else:
399796c43b9aSAdrian Hunter					mark = leaf_mark
399896c43b9aSAdrian Hunter				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
399996c43b9aSAdrian Hunter			if as_csv:
400096c43b9aSAdrian Hunter				text += sep + ToCSValue(val)
400196c43b9aSAdrian Hunter				sep = ","
400296c43b9aSAdrian Hunter			else:
400396c43b9aSAdrian Hunter				width = max_width[c]
400496c43b9aSAdrian Hunter				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
400596c43b9aSAdrian Hunter					val = val.rjust(width)
400696c43b9aSAdrian Hunter				text += pad + sep + val
400796c43b9aSAdrian Hunter				pad = " " * (width - len(val))
400896c43b9aSAdrian Hunter				sep = "   "
400996c43b9aSAdrian Hunter		pos = view.indexBelow(pos)
401096c43b9aSAdrian Hunter		if not selection.isSelected(pos):
401196c43b9aSAdrian Hunter			break
401296c43b9aSAdrian Hunter		text = text.rstrip() + "\n"
401396c43b9aSAdrian Hunter		pad = ""
401496c43b9aSAdrian Hunter		sep = ""
401596c43b9aSAdrian Hunter
401696c43b9aSAdrian Hunter	QApplication.clipboard().setText(text)
401796c43b9aSAdrian Hunter
401896c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
401996c43b9aSAdrian Hunter	view.CopyCellsToClipboard(view, as_csv, with_hdr)
402096c43b9aSAdrian Hunter
402196c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view):
402296c43b9aSAdrian Hunter	CopyCellsToClipboard(view, False, True)
402396c43b9aSAdrian Hunter
402496c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view):
402596c43b9aSAdrian Hunter	CopyCellsToClipboard(view, True, True)
402696c43b9aSAdrian Hunter
40279bc4e4bfSAdrian Hunter# Context menu
40289bc4e4bfSAdrian Hunter
40299bc4e4bfSAdrian Hunterclass ContextMenu(object):
40309bc4e4bfSAdrian Hunter
40319bc4e4bfSAdrian Hunter	def __init__(self, view):
40329bc4e4bfSAdrian Hunter		self.view = view
40339bc4e4bfSAdrian Hunter		self.view.setContextMenuPolicy(Qt.CustomContextMenu)
40349bc4e4bfSAdrian Hunter		self.view.customContextMenuRequested.connect(self.ShowContextMenu)
40359bc4e4bfSAdrian Hunter
40369bc4e4bfSAdrian Hunter	def ShowContextMenu(self, pos):
40379bc4e4bfSAdrian Hunter		menu = QMenu(self.view)
40389bc4e4bfSAdrian Hunter		self.AddActions(menu)
40399bc4e4bfSAdrian Hunter		menu.exec_(self.view.mapToGlobal(pos))
40409bc4e4bfSAdrian Hunter
40419bc4e4bfSAdrian Hunter	def AddCopy(self, menu):
40429bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
40439bc4e4bfSAdrian Hunter		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
40449bc4e4bfSAdrian Hunter
40459bc4e4bfSAdrian Hunter	def AddActions(self, menu):
40469bc4e4bfSAdrian Hunter		self.AddCopy(menu)
40479bc4e4bfSAdrian Hunter
40489bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu):
40499bc4e4bfSAdrian Hunter
40509bc4e4bfSAdrian Hunter	def __init__(self, view):
40519bc4e4bfSAdrian Hunter		super(TreeContextMenu, self).__init__(view)
40529bc4e4bfSAdrian Hunter
40539bc4e4bfSAdrian Hunter	def AddActions(self, menu):
40549bc4e4bfSAdrian Hunter		i = self.view.currentIndex()
40559bc4e4bfSAdrian Hunter		text = str(i.data()).strip()
40569bc4e4bfSAdrian Hunter		if len(text):
40579bc4e4bfSAdrian Hunter			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
40589bc4e4bfSAdrian Hunter		self.AddCopy(menu)
40599bc4e4bfSAdrian Hunter
40608392b74bSAdrian Hunter# Table window
40618392b74bSAdrian Hunter
40628392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
40638392b74bSAdrian Hunter
40648392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
40658392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
40668392b74bSAdrian Hunter
40678392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
40688392b74bSAdrian Hunter
40698392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
40708392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
40718392b74bSAdrian Hunter
40728392b74bSAdrian Hunter		self.view = QTableView()
40738392b74bSAdrian Hunter		self.view.setModel(self.model)
40748392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
40758392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
40768392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
40778392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
407896c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
407996c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
40808392b74bSAdrian Hunter
40818392b74bSAdrian Hunter		self.ResizeColumnsToContents()
40828392b74bSAdrian Hunter
40839bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
40849bc4e4bfSAdrian Hunter
40858392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
40868392b74bSAdrian Hunter
40878392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
40888392b74bSAdrian Hunter
40898392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
40908392b74bSAdrian Hunter
40918392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
40928392b74bSAdrian Hunter
40938392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
40948392b74bSAdrian Hunter
40958392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
40968392b74bSAdrian Hunter
40978392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
40988392b74bSAdrian Hunter		self.view.setFocus()
40998392b74bSAdrian Hunter		self.find_bar.Busy()
41008392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
41018392b74bSAdrian Hunter
41028392b74bSAdrian Hunter	def FindDone(self, row):
41038392b74bSAdrian Hunter		self.find_bar.Idle()
41048392b74bSAdrian Hunter		if row >= 0:
410535fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
41068392b74bSAdrian Hunter		else:
41078392b74bSAdrian Hunter			self.find_bar.NotFound()
41088392b74bSAdrian Hunter
41098392b74bSAdrian Hunter# Table list
41108392b74bSAdrian Hunter
41118392b74bSAdrian Hunterdef GetTableList(glb):
41128392b74bSAdrian Hunter	tables = []
41138392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
41148392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
41158392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
41168392b74bSAdrian Hunter	else:
41178392b74bSAdrian 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")
41188392b74bSAdrian Hunter	while query.next():
41198392b74bSAdrian Hunter		tables.append(query.value(0))
41208392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
41218392b74bSAdrian Hunter		tables.append("sqlite_master")
41228392b74bSAdrian Hunter	else:
41238392b74bSAdrian Hunter		tables.append("information_schema.tables")
41248392b74bSAdrian Hunter		tables.append("information_schema.views")
41258392b74bSAdrian Hunter		tables.append("information_schema.columns")
41268392b74bSAdrian Hunter	return tables
41278392b74bSAdrian Hunter
4128cd358012SAdrian Hunter# Top Calls data model
4129cd358012SAdrian Hunter
4130cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
4131cd358012SAdrian Hunter
4132cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
4133cd358012SAdrian Hunter		text = ""
4134cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
4135cd358012SAdrian Hunter			text = "::text"
4136cd358012SAdrian Hunter		limit = ""
4137cd358012SAdrian Hunter		if len(report_vars.limit):
4138cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
4139cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
4140cd358012SAdrian Hunter			" CASE"
4141cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
4142cd358012SAdrian Hunter			" ELSE short_name"
4143cd358012SAdrian Hunter			" END AS dso,"
4144cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
4145cd358012SAdrian Hunter			" CASE"
4146cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
4147cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
4148cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
4149cd358012SAdrian Hunter			" ELSE ''" + text +
4150cd358012SAdrian Hunter			" END AS flags"
4151cd358012SAdrian Hunter			" FROM calls"
4152cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
4153cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
4154cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
4155cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
4156cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
4157cd358012SAdrian Hunter			report_vars.where_clause +
4158cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
4159cd358012SAdrian Hunter			limit
4160cd358012SAdrian Hunter			)
4161cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
4162cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
4163cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
4164cd358012SAdrian Hunter
4165cd358012SAdrian Hunter	def columnAlignment(self, column):
4166cd358012SAdrian Hunter		return self.alignment[column]
4167cd358012SAdrian Hunter
4168cd358012SAdrian Hunter# Top Calls report creation dialog
4169cd358012SAdrian Hunter
4170cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
4171cd358012SAdrian Hunter
4172cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
4173cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
4174cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
4175cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
4176cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
4177cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
4178cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
4179cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
4180cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
4181cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
4182cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
4183cd358012SAdrian Hunter
4184cd358012SAdrian Hunter# Top Calls window
4185cd358012SAdrian Hunter
4186cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
4187cd358012SAdrian Hunter
4188cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
4189cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
4190cd358012SAdrian Hunter
4191cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
4192cd358012SAdrian Hunter		self.model = self.data_model
4193cd358012SAdrian Hunter
4194cd358012SAdrian Hunter		self.view = QTableView()
4195cd358012SAdrian Hunter		self.view.setModel(self.model)
4196cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
4197cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
419896c43b9aSAdrian Hunter		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
419996c43b9aSAdrian Hunter		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
4200cd358012SAdrian Hunter
42019bc4e4bfSAdrian Hunter		self.context_menu = ContextMenu(self.view)
42029bc4e4bfSAdrian Hunter
4203cd358012SAdrian Hunter		self.ResizeColumnsToContents()
4204cd358012SAdrian Hunter
4205cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
4206cd358012SAdrian Hunter
4207cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
4208cd358012SAdrian Hunter
4209cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
4210cd358012SAdrian Hunter
4211cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
4212cd358012SAdrian Hunter
4213cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
4214cd358012SAdrian Hunter
4215cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
4216cd358012SAdrian Hunter
4217cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
4218cd358012SAdrian Hunter		self.view.setFocus()
4219cd358012SAdrian Hunter		self.find_bar.Busy()
4220cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
4221cd358012SAdrian Hunter
4222cd358012SAdrian Hunter	def FindDone(self, row):
4223cd358012SAdrian Hunter		self.find_bar.Idle()
4224cd358012SAdrian Hunter		if row >= 0:
4225cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
4226cd358012SAdrian Hunter		else:
4227cd358012SAdrian Hunter			self.find_bar.NotFound()
4228cd358012SAdrian Hunter
42291beb5c7bSAdrian Hunter# Action Definition
42301beb5c7bSAdrian Hunter
42311beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
42321beb5c7bSAdrian Hunter	action = QAction(label, parent)
42331beb5c7bSAdrian Hunter	if shortcut != None:
42341beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
42351beb5c7bSAdrian Hunter	action.setStatusTip(tip)
42361beb5c7bSAdrian Hunter	action.triggered.connect(callback)
42371beb5c7bSAdrian Hunter	return action
42381beb5c7bSAdrian Hunter
42391beb5c7bSAdrian Hunter# Typical application actions
42401beb5c7bSAdrian Hunter
42411beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
42421beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
42431beb5c7bSAdrian Hunter
42441beb5c7bSAdrian Hunter# Typical MDI actions
42451beb5c7bSAdrian Hunter
42461beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
42471beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
42481beb5c7bSAdrian Hunter
42491beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
42501beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
42511beb5c7bSAdrian Hunter
42521beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
42531beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
42541beb5c7bSAdrian Hunter
42551beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
42561beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
42571beb5c7bSAdrian Hunter
42581beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
42591beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
42601beb5c7bSAdrian Hunter
42611beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
42621beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
42631beb5c7bSAdrian Hunter
42641beb5c7bSAdrian Hunter# Typical MDI window menu
42651beb5c7bSAdrian Hunter
42661beb5c7bSAdrian Hunterclass WindowMenu():
42671beb5c7bSAdrian Hunter
42681beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
42691beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
42701beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
42711beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
42721beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
42731beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
42741beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
42751beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
42761beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
42771beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
42781beb5c7bSAdrian Hunter
42791beb5c7bSAdrian Hunter	def Update(self):
42801beb5c7bSAdrian Hunter		self.window_menu.clear()
42811beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
42821beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
42831beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
42841beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
42851beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
42861beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
42871beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
42881beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
42891beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
42901beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
42911beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
42921beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
42931beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
42941beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
42951beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
42961beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
42971beb5c7bSAdrian Hunter		if sub_window_count == 0:
42981beb5c7bSAdrian Hunter			return
42991beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
43001beb5c7bSAdrian Hunter		nr = 1
43011beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
43021beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
43031beb5c7bSAdrian Hunter			if nr < 10:
43041beb5c7bSAdrian Hunter				label = "&" + label
43051beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
43061beb5c7bSAdrian Hunter			action.setCheckable(True)
43071beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
4308df8ea22aSAdrian Hunter			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
43091beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
43101beb5c7bSAdrian Hunter			nr += 1
43111beb5c7bSAdrian Hunter
43121beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
43131beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
43141beb5c7bSAdrian Hunter
431565b24292SAdrian Hunter# Help text
431665b24292SAdrian Hunter
431765b24292SAdrian Hunterglb_help_text = """
431865b24292SAdrian Hunter<h1>Contents</h1>
431965b24292SAdrian Hunter<style>
432065b24292SAdrian Hunterp.c1 {
432165b24292SAdrian Hunter    text-indent: 40px;
432265b24292SAdrian Hunter}
432365b24292SAdrian Hunterp.c2 {
432465b24292SAdrian Hunter    text-indent: 80px;
432565b24292SAdrian Hunter}
432665b24292SAdrian Hunter}
432765b24292SAdrian Hunter</style>
432865b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
432965b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
4330ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
4331ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
4332ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
4333ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
4334b3700f21SAdrian Hunter<p class=c1><a href=#charts>2. Charts</a></p>
4335b3700f21SAdrian Hunter<p class=c2><a href=#timechartbycpu>2.1 Time chart by CPU</a></p>
4336b3700f21SAdrian Hunter<p class=c1><a href=#tables>3. Tables</a></p>
433765b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
433865b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
433965b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
434065b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
434165b24292SAdrian Hunterwidths to suit will display something like:
434265b24292SAdrian Hunter<pre>
434365b24292SAdrian Hunter                                         Call Graph: pt_example
434465b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
434565b24292SAdrian Hunterv- ls
434665b24292SAdrian Hunter    v- 2638:2638
434765b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
434865b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
434965b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
435065b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
435165b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
435265b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
435365b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
435465b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
435565b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
435665b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
435765b24292SAdrian Hunter</pre>
435865b24292SAdrian Hunter<h3>Points to note:</h3>
435965b24292SAdrian Hunter<ul>
436065b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
436165b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
436265b24292SAdrian Hunter<li>Subsequent levels are functions</li>
436365b24292SAdrian Hunter<li>'Count' is the number of calls</li>
436465b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
436565b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
436665b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
436765b24292SAdrian Hunter</ul>
436865b24292SAdrian Hunter<h3>Find</h3>
436965b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
437065b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
4371ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
4372ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
4373ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
4374ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
437565b24292SAdrian HunterThe All branches report displays all branches in chronological order.
437665b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
437765b24292SAdrian Hunter<h3>Disassembly</h3>
437865b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
437965b24292SAdrian Hunter<ol>
438065b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
438165b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
438265b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
438365b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
438465b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
438565b24292SAdrian Hunter</ol>
438665b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
438765b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
438865b24292SAdrian Hunter<pre>
438965b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
439065b24292SAdrian Huntergit clone https://github.com/intelxed/xed
439165b24292SAdrian Huntercd xed
439265b24292SAdrian Hunter./mfile.py --share
439365b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
439465b24292SAdrian Huntersudo ldconfig
439565b24292SAdrian Hunter</pre>
4396530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3>
4397530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
4398530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
4399530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known.
4400530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
4401530e22fdSAdrian Huntersince the previous displayed 'IPC'.
440265b24292SAdrian Hunter<h3>Find</h3>
440365b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
440465b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
440565b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
4406ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
440765b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
440865b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
4409ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
441065b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
441165b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
441265b24292SAdrian Hunter<pre>
441365b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
441465b24292SAdrian Hunter	100us-200us		From 100us to 200us
441565b24292SAdrian Hunter	10ms-			From 10ms to the end
441665b24292SAdrian Hunter	-100ns			The first 100ns
441765b24292SAdrian Hunter	-10ms-			The last 10ms
441865b24292SAdrian Hunter</pre>
441965b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
4420ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
4421cd358012SAdrian 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.
4422cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
4423cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
4424b3700f21SAdrian Hunter<h1 id=charts>2. Charts</h1>
4425b3700f21SAdrian Hunter<h2 id=timechartbycpu>2.1 Time chart by CPU</h2>
4426b3700f21SAdrian HunterThis chart displays context switch information when that data is available. Refer to context_switches_view on the Tables menu.
4427b3700f21SAdrian Hunter<h3>Features</h3>
4428b3700f21SAdrian Hunter<ol>
4429b3700f21SAdrian Hunter<li>Mouse over to highight the task and show the time</li>
4430b3700f21SAdrian Hunter<li>Drag the mouse to select a region and zoom by pushing the Zoom button</li>
4431b3700f21SAdrian Hunter<li>Go back and forward by pressing the arrow buttons</li>
4432b3700f21SAdrian Hunter<li>If call information is available, right-click to show a call tree opened to that task and time.
4433b3700f21SAdrian HunterNote, the call tree may take some time to appear, and there may not be call information for the task or time selected.
4434b3700f21SAdrian Hunter</li>
4435b3700f21SAdrian Hunter</ol>
4436b3700f21SAdrian Hunter<h3>Important</h3>
4437b3700f21SAdrian HunterThe graph can be misleading in the following respects:
4438b3700f21SAdrian Hunter<ol>
4439b3700f21SAdrian Hunter<li>The graph shows the first task on each CPU as running from the beginning of the time range.
4440b3700f21SAdrian HunterBecause tracing might start on different CPUs at different times, that is not necessarily the case.
4441b3700f21SAdrian HunterRefer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li>
4442b3700f21SAdrian Hunter<li>Similarly, the last task on each CPU can be showing running longer than it really was.
4443b3700f21SAdrian HunterAgain, refer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li>
4444b3700f21SAdrian Hunter<li>When the mouse is over a task, the highlighted task might not be visible on the legend without scrolling if the legend does not fit fully in the window</li>
4445b3700f21SAdrian Hunter</ol>
4446b3700f21SAdrian Hunter<h1 id=tables>3. Tables</h1>
444765b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
444865b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
444965b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
445065b24292SAdrian Hunterbut that can be slow for large tables.
445165b24292SAdrian Hunter<p>There are also tables of database meta-information.
445265b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
445365b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
445465b24292SAdrian Hunter<h3>Find</h3>
445565b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
445665b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
445765b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
445835fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
445935fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
446065b24292SAdrian Hunter"""
446165b24292SAdrian Hunter
446265b24292SAdrian Hunter# Help window
446365b24292SAdrian Hunter
446465b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
446565b24292SAdrian Hunter
446665b24292SAdrian Hunter	def __init__(self, glb, parent=None):
446765b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
446865b24292SAdrian Hunter
446965b24292SAdrian Hunter		self.text = QTextBrowser()
447065b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
447165b24292SAdrian Hunter		self.text.setReadOnly(True)
447265b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
447365b24292SAdrian Hunter
447465b24292SAdrian Hunter		self.setWidget(self.text)
447565b24292SAdrian Hunter
447665b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
447765b24292SAdrian Hunter
447865b24292SAdrian Hunter# Main window that only displays the help text
447965b24292SAdrian Hunter
448065b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
448165b24292SAdrian Hunter
448265b24292SAdrian Hunter	def __init__(self, parent=None):
448365b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
448465b24292SAdrian Hunter
448565b24292SAdrian Hunter		self.setMinimumSize(200, 100)
448665b24292SAdrian Hunter		self.resize(800, 600)
448765b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
448865b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
448965b24292SAdrian Hunter
449065b24292SAdrian Hunter		self.text = QTextBrowser()
449165b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
449265b24292SAdrian Hunter		self.text.setReadOnly(True)
449365b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
449465b24292SAdrian Hunter
449565b24292SAdrian Hunter		self.setCentralWidget(self.text)
449665b24292SAdrian Hunter
4497b62d18abSAdrian Hunter# PostqreSQL server version
4498b62d18abSAdrian Hunter
4499b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db):
4500b62d18abSAdrian Hunter	query = QSqlQuery(db)
4501b62d18abSAdrian Hunter	QueryExec(query, "SELECT VERSION()")
4502b62d18abSAdrian Hunter	if query.next():
4503b62d18abSAdrian Hunter		v_str = query.value(0)
4504b62d18abSAdrian Hunter		v_list = v_str.strip().split(" ")
4505b62d18abSAdrian Hunter		if v_list[0] == "PostgreSQL" and v_list[2] == "on":
4506b62d18abSAdrian Hunter			return v_list[1]
4507b62d18abSAdrian Hunter		return v_str
4508b62d18abSAdrian Hunter	return "Unknown"
4509b62d18abSAdrian Hunter
4510b62d18abSAdrian Hunter# SQLite version
4511b62d18abSAdrian Hunter
4512b62d18abSAdrian Hunterdef SQLiteVersion(db):
4513b62d18abSAdrian Hunter	query = QSqlQuery(db)
4514b62d18abSAdrian Hunter	QueryExec(query, "SELECT sqlite_version()")
4515b62d18abSAdrian Hunter	if query.next():
4516b62d18abSAdrian Hunter		return query.value(0)
4517b62d18abSAdrian Hunter	return "Unknown"
4518b62d18abSAdrian Hunter
4519b62d18abSAdrian Hunter# About dialog
4520b62d18abSAdrian Hunter
4521b62d18abSAdrian Hunterclass AboutDialog(QDialog):
4522b62d18abSAdrian Hunter
4523b62d18abSAdrian Hunter	def __init__(self, glb, parent=None):
4524b62d18abSAdrian Hunter		super(AboutDialog, self).__init__(parent)
4525b62d18abSAdrian Hunter
4526b62d18abSAdrian Hunter		self.setWindowTitle("About Exported SQL Viewer")
4527b62d18abSAdrian Hunter		self.setMinimumWidth(300)
4528b62d18abSAdrian Hunter
4529b62d18abSAdrian Hunter		pyside_version = "1" if pyside_version_1 else "2"
4530b62d18abSAdrian Hunter
4531b62d18abSAdrian Hunter		text = "<pre>"
4532b62d18abSAdrian Hunter		text += "Python version:     " + sys.version.split(" ")[0] + "\n"
4533b62d18abSAdrian Hunter		text += "PySide version:     " + pyside_version + "\n"
4534b62d18abSAdrian Hunter		text += "Qt version:         " + qVersion() + "\n"
4535b62d18abSAdrian Hunter		if glb.dbref.is_sqlite3:
4536b62d18abSAdrian Hunter			text += "SQLite version:     " + SQLiteVersion(glb.db) + "\n"
4537b62d18abSAdrian Hunter		else:
4538b62d18abSAdrian Hunter			text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
4539b62d18abSAdrian Hunter		text += "</pre>"
4540b62d18abSAdrian Hunter
4541b62d18abSAdrian Hunter		self.text = QTextBrowser()
4542b62d18abSAdrian Hunter		self.text.setHtml(text)
4543b62d18abSAdrian Hunter		self.text.setReadOnly(True)
4544b62d18abSAdrian Hunter		self.text.setOpenExternalLinks(True)
4545b62d18abSAdrian Hunter
4546b62d18abSAdrian Hunter		self.vbox = QVBoxLayout()
4547b62d18abSAdrian Hunter		self.vbox.addWidget(self.text)
4548b62d18abSAdrian Hunter
454926688729SAdrian Hunter		self.setLayout(self.vbox)
4550b62d18abSAdrian Hunter
455182f68e28SAdrian Hunter# Font resize
455282f68e28SAdrian Hunter
455382f68e28SAdrian Hunterdef ResizeFont(widget, diff):
455482f68e28SAdrian Hunter	font = widget.font()
455582f68e28SAdrian Hunter	sz = font.pointSize()
455682f68e28SAdrian Hunter	font.setPointSize(sz + diff)
455782f68e28SAdrian Hunter	widget.setFont(font)
455882f68e28SAdrian Hunter
455982f68e28SAdrian Hunterdef ShrinkFont(widget):
456082f68e28SAdrian Hunter	ResizeFont(widget, -1)
456182f68e28SAdrian Hunter
456282f68e28SAdrian Hunterdef EnlargeFont(widget):
456382f68e28SAdrian Hunter	ResizeFont(widget, 1)
456482f68e28SAdrian Hunter
45651beb5c7bSAdrian Hunter# Unique name for sub-windows
45661beb5c7bSAdrian Hunter
45671beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
45681beb5c7bSAdrian Hunter	if nr > 1:
45691beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
45701beb5c7bSAdrian Hunter	return name
45711beb5c7bSAdrian Hunter
45721beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
45731beb5c7bSAdrian Hunter	nr = 1
45741beb5c7bSAdrian Hunter	while True:
45751beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
45761beb5c7bSAdrian Hunter		ok = True
45771beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
45781beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
45791beb5c7bSAdrian Hunter				ok = False
45801beb5c7bSAdrian Hunter				break
45811beb5c7bSAdrian Hunter		if ok:
45821beb5c7bSAdrian Hunter			return unique_name
45831beb5c7bSAdrian Hunter		nr += 1
45841beb5c7bSAdrian Hunter
45851beb5c7bSAdrian Hunter# Add a sub-window
45861beb5c7bSAdrian Hunter
45871beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
45881beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
45891beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
45901beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
45911beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
45921beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
45931beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
45941beb5c7bSAdrian Hunter	sub_window.name = unique_name
45951beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
45961beb5c7bSAdrian Hunter	sub_window.show()
45971beb5c7bSAdrian Hunter
4598031c2a00SAdrian Hunter# Main window
4599031c2a00SAdrian Hunter
4600031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
4601031c2a00SAdrian Hunter
4602031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
4603031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
4604031c2a00SAdrian Hunter
4605031c2a00SAdrian Hunter		self.glb = glb
4606031c2a00SAdrian Hunter
46071beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
4608031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
4609031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
4610031c2a00SAdrian Hunter
46111beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
46121beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
46131beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
4614031c2a00SAdrian Hunter
46151beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
4616031c2a00SAdrian Hunter
46171beb5c7bSAdrian Hunter		menu = self.menuBar()
4618031c2a00SAdrian Hunter
46191beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
46201beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
46211beb5c7bSAdrian Hunter
4622ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
462396c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
462496c43b9aSAdrian Hunter		edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
4625ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
46268392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
462782f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
462882f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
4629ebd70c7dSAdrian Hunter
46301beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
4631655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
46321beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
46331beb5c7bSAdrian Hunter
4634ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
4635ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
4636ae8b887cSAdrian Hunter
463776099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
463876099f98SAdrian Hunter
4639cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
4640cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
4641cd358012SAdrian Hunter
4642b3700f21SAdrian Hunter		if IsSelectable(glb.db, "context_switches"):
4643b3700f21SAdrian Hunter			charts_menu = menu.addMenu("&Charts")
4644b3700f21SAdrian Hunter			charts_menu.addAction(CreateAction("&Time chart by CPU", "Create a new window displaying time charts by CPU", self.TimeChartByCPU, self))
4645b3700f21SAdrian Hunter
46468392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
46478392b74bSAdrian Hunter
46481beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
46491beb5c7bSAdrian Hunter
465065b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
465165b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
4652b62d18abSAdrian Hunter		help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
465365b24292SAdrian Hunter
46544b208453SAdrian Hunter	def Try(self, fn):
46554b208453SAdrian Hunter		win = self.mdi_area.activeSubWindow()
46564b208453SAdrian Hunter		if win:
46574b208453SAdrian Hunter			try:
46584b208453SAdrian Hunter				fn(win.view)
46594b208453SAdrian Hunter			except:
46604b208453SAdrian Hunter				pass
46614b208453SAdrian Hunter
466296c43b9aSAdrian Hunter	def CopyToClipboard(self):
466396c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardHdr)
466496c43b9aSAdrian Hunter
466596c43b9aSAdrian Hunter	def CopyToClipboardCSV(self):
466696c43b9aSAdrian Hunter		self.Try(CopyCellsToClipboardCSV)
466796c43b9aSAdrian Hunter
4668ebd70c7dSAdrian Hunter	def Find(self):
4669ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
4670ebd70c7dSAdrian Hunter		if win:
4671ebd70c7dSAdrian Hunter			try:
4672ebd70c7dSAdrian Hunter				win.find_bar.Activate()
4673ebd70c7dSAdrian Hunter			except:
4674ebd70c7dSAdrian Hunter				pass
4675ebd70c7dSAdrian Hunter
46768392b74bSAdrian Hunter	def FetchMoreRecords(self):
46778392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
46788392b74bSAdrian Hunter		if win:
46798392b74bSAdrian Hunter			try:
46808392b74bSAdrian Hunter				win.fetch_bar.Activate()
46818392b74bSAdrian Hunter			except:
46828392b74bSAdrian Hunter				pass
46838392b74bSAdrian Hunter
468482f68e28SAdrian Hunter	def ShrinkFont(self):
46854b208453SAdrian Hunter		self.Try(ShrinkFont)
468682f68e28SAdrian Hunter
468782f68e28SAdrian Hunter	def EnlargeFont(self):
46884b208453SAdrian Hunter		self.Try(EnlargeFont)
468982f68e28SAdrian Hunter
469076099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
469176099f98SAdrian Hunter		branches_events = 0
469276099f98SAdrian Hunter		for event in events:
469376099f98SAdrian Hunter			event = event.split(":")[0]
469476099f98SAdrian Hunter			if event == "branches":
469576099f98SAdrian Hunter				branches_events += 1
469676099f98SAdrian Hunter		dbid = 0
469776099f98SAdrian Hunter		for event in events:
469876099f98SAdrian Hunter			dbid += 1
469976099f98SAdrian Hunter			event = event.split(":")[0]
470076099f98SAdrian Hunter			if event == "branches":
470176099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
4702df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
4703210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
4704df8ea22aSAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
470576099f98SAdrian Hunter
4706b3700f21SAdrian Hunter	def TimeChartByCPU(self):
4707b3700f21SAdrian Hunter		TimeChartByCPUWindow(self.glb, self)
4708b3700f21SAdrian Hunter
47098392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
47108392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
47118392b74bSAdrian Hunter		for table in tables:
4712df8ea22aSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
47138392b74bSAdrian Hunter
47141beb5c7bSAdrian Hunter	def NewCallGraph(self):
47151beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
4716031c2a00SAdrian Hunter
4717ae8b887cSAdrian Hunter	def NewCallTree(self):
4718ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
4719ae8b887cSAdrian Hunter
4720cd358012SAdrian Hunter	def NewTopCalls(self):
4721cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
4722cd358012SAdrian Hunter		ret = dialog.exec_()
4723cd358012SAdrian Hunter		if ret:
4724cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
4725cd358012SAdrian Hunter
472676099f98SAdrian Hunter	def NewBranchView(self, event_id):
4727947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
472876099f98SAdrian Hunter
4729210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
4730210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
4731210cf1f9SAdrian Hunter		ret = dialog.exec_()
4732210cf1f9SAdrian Hunter		if ret:
4733947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
4734210cf1f9SAdrian Hunter
47358392b74bSAdrian Hunter	def NewTableView(self, table_name):
47368392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
47378392b74bSAdrian Hunter
473865b24292SAdrian Hunter	def Help(self):
473965b24292SAdrian Hunter		HelpWindow(self.glb, self)
474065b24292SAdrian Hunter
4741b62d18abSAdrian Hunter	def About(self):
4742b62d18abSAdrian Hunter		dialog = AboutDialog(self.glb, self)
4743b62d18abSAdrian Hunter		dialog.exec_()
4744b62d18abSAdrian Hunter
474576099f98SAdrian Hunter# XED Disassembler
474676099f98SAdrian Hunter
474776099f98SAdrian Hunterclass xed_state_t(Structure):
474876099f98SAdrian Hunter
474976099f98SAdrian Hunter	_fields_ = [
475076099f98SAdrian Hunter		("mode", c_int),
475176099f98SAdrian Hunter		("width", c_int)
475276099f98SAdrian Hunter	]
475376099f98SAdrian Hunter
475476099f98SAdrian Hunterclass XEDInstruction():
475576099f98SAdrian Hunter
475676099f98SAdrian Hunter	def __init__(self, libxed):
475776099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
475876099f98SAdrian Hunter		xedd_t = c_byte * 512
475976099f98SAdrian Hunter		self.xedd = xedd_t()
476076099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
476176099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
476276099f98SAdrian Hunter		self.state = xed_state_t()
476376099f98SAdrian Hunter		self.statep = addressof(self.state)
476476099f98SAdrian Hunter		# Buffer for disassembled instruction text
476576099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
476676099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
476776099f98SAdrian Hunter
476876099f98SAdrian Hunterclass LibXED():
476976099f98SAdrian Hunter
477076099f98SAdrian Hunter	def __init__(self):
47715ed4419dSAdrian Hunter		try:
477276099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
47735ed4419dSAdrian Hunter		except:
47745ed4419dSAdrian Hunter			self.libxed = None
47755ed4419dSAdrian Hunter		if not self.libxed:
47765ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
477776099f98SAdrian Hunter
477876099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
477976099f98SAdrian Hunter		self.xed_tables_init.restype = None
478076099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
478176099f98SAdrian Hunter
478276099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
478376099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
478476099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
478576099f98SAdrian Hunter
478676099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
478776099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
478876099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
478976099f98SAdrian Hunter
479076099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
479176099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
479276099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
479376099f98SAdrian Hunter
479476099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
479576099f98SAdrian Hunter		self.xed_decode.restype = c_int
479676099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
479776099f98SAdrian Hunter
479876099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
479976099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
480076099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
480176099f98SAdrian Hunter
480276099f98SAdrian Hunter		self.xed_tables_init()
480376099f98SAdrian Hunter
480476099f98SAdrian Hunter	def Instruction(self):
480576099f98SAdrian Hunter		return XEDInstruction(self)
480676099f98SAdrian Hunter
480776099f98SAdrian Hunter	def SetMode(self, inst, mode):
480876099f98SAdrian Hunter		if mode:
480976099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
481076099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
481176099f98SAdrian Hunter		else:
481276099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
481376099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
481476099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
481576099f98SAdrian Hunter
481676099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
481776099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
481876099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
481976099f98SAdrian Hunter		if err:
482076099f98SAdrian Hunter			return 0, ""
482176099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
482276099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
482376099f98SAdrian Hunter		if not ok:
482476099f98SAdrian Hunter			return 0, ""
4825606bd60aSAdrian Hunter		if sys.version_info[0] == 2:
4826606bd60aSAdrian Hunter			result = inst.buffer.value
4827606bd60aSAdrian Hunter		else:
4828606bd60aSAdrian Hunter			result = inst.buffer.value.decode()
482976099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
483076099f98SAdrian Hunter		# For now, assume the length is in byte 166
4831606bd60aSAdrian Hunter		return inst.xedd[166], result
483276099f98SAdrian Hunter
483376099f98SAdrian Hunterdef TryOpen(file_name):
483476099f98SAdrian Hunter	try:
483576099f98SAdrian Hunter		return open(file_name, "rb")
483676099f98SAdrian Hunter	except:
483776099f98SAdrian Hunter		return None
483876099f98SAdrian Hunter
483976099f98SAdrian Hunterdef Is64Bit(f):
484076099f98SAdrian Hunter	result = sizeof(c_void_p)
484176099f98SAdrian Hunter	# ELF support only
484276099f98SAdrian Hunter	pos = f.tell()
484376099f98SAdrian Hunter	f.seek(0)
484476099f98SAdrian Hunter	header = f.read(7)
484576099f98SAdrian Hunter	f.seek(pos)
484676099f98SAdrian Hunter	magic = header[0:4]
4847606bd60aSAdrian Hunter	if sys.version_info[0] == 2:
484876099f98SAdrian Hunter		eclass = ord(header[4])
484976099f98SAdrian Hunter		encoding = ord(header[5])
485076099f98SAdrian Hunter		version = ord(header[6])
4851606bd60aSAdrian Hunter	else:
4852606bd60aSAdrian Hunter		eclass = header[4]
4853606bd60aSAdrian Hunter		encoding = header[5]
4854606bd60aSAdrian Hunter		version = header[6]
485576099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
485676099f98SAdrian Hunter		result = True if eclass == 2 else False
485776099f98SAdrian Hunter	return result
485876099f98SAdrian Hunter
4859031c2a00SAdrian Hunter# Global data
4860031c2a00SAdrian Hunter
4861031c2a00SAdrian Hunterclass Glb():
4862031c2a00SAdrian Hunter
4863031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
4864031c2a00SAdrian Hunter		self.dbref = dbref
4865031c2a00SAdrian Hunter		self.db = db
4866031c2a00SAdrian Hunter		self.dbname = dbname
486776099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
486876099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
486976099f98SAdrian Hunter		if self.buildid_dir:
487076099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
487176099f98SAdrian Hunter		else:
487276099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
4873031c2a00SAdrian Hunter		self.app = None
4874031c2a00SAdrian Hunter		self.mainwindow = None
48758392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
487676099f98SAdrian Hunter		try:
487776099f98SAdrian Hunter			self.disassembler = LibXED()
487876099f98SAdrian Hunter			self.have_disassembler = True
487976099f98SAdrian Hunter		except:
488076099f98SAdrian Hunter			self.have_disassembler = False
48819a9dae36SAdrian Hunter		self.host_machine_id = 0
48829a9dae36SAdrian Hunter		self.host_start_time = 0
48839a9dae36SAdrian Hunter		self.host_finish_time = 0
488476099f98SAdrian Hunter
488576099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
488676099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
488776099f98SAdrian Hunter		return TryOpen(file_name)
488876099f98SAdrian Hunter
488976099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
489076099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
489176099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
489276099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
489376099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
489476099f98SAdrian Hunter			if f:
489576099f98SAdrian Hunter				return f
489676099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
489776099f98SAdrian Hunter			f = TryOpen(long_name)
489876099f98SAdrian Hunter			if f:
489976099f98SAdrian Hunter				return f
490076099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
490176099f98SAdrian Hunter		if f:
490276099f98SAdrian Hunter			return f
490376099f98SAdrian Hunter		return None
49048392b74bSAdrian Hunter
49058392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
49068392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
49078392b74bSAdrian Hunter
49088392b74bSAdrian Hunter	# Shutdown any background processes or threads
49098392b74bSAdrian Hunter	def ShutdownInstances(self):
49108392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
49118392b74bSAdrian Hunter			try:
49128392b74bSAdrian Hunter				x.Shutdown()
49138392b74bSAdrian Hunter			except:
49148392b74bSAdrian Hunter				pass
4915031c2a00SAdrian Hunter
49169a9dae36SAdrian Hunter	def GetHostMachineId(self):
49179a9dae36SAdrian Hunter		query = QSqlQuery(self.db)
49189a9dae36SAdrian Hunter		QueryExec(query, "SELECT id FROM machines WHERE pid = -1")
49199a9dae36SAdrian Hunter		if query.next():
49209a9dae36SAdrian Hunter			self.host_machine_id = query.value(0)
49219a9dae36SAdrian Hunter		else:
49229a9dae36SAdrian Hunter			self.host_machine_id = 0
49239a9dae36SAdrian Hunter		return self.host_machine_id
49249a9dae36SAdrian Hunter
49259a9dae36SAdrian Hunter	def HostMachineId(self):
49269a9dae36SAdrian Hunter		if self.host_machine_id:
49279a9dae36SAdrian Hunter			return self.host_machine_id
49289a9dae36SAdrian Hunter		return self.GetHostMachineId()
49299a9dae36SAdrian Hunter
49309a9dae36SAdrian Hunter	def SelectValue(self, sql):
49319a9dae36SAdrian Hunter		query = QSqlQuery(self.db)
49329a9dae36SAdrian Hunter		try:
49339a9dae36SAdrian Hunter			QueryExec(query, sql)
49349a9dae36SAdrian Hunter		except:
49359a9dae36SAdrian Hunter			return None
49369a9dae36SAdrian Hunter		if query.next():
49379a9dae36SAdrian Hunter			return Decimal(query.value(0))
49389a9dae36SAdrian Hunter		return None
49399a9dae36SAdrian Hunter
49409a9dae36SAdrian Hunter	def SwitchesMinTime(self, machine_id):
49419a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
49429a9dae36SAdrian Hunter					" FROM context_switches"
49439a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
49449a9dae36SAdrian Hunter					" ORDER BY id LIMIT 1")
49459a9dae36SAdrian Hunter
49469a9dae36SAdrian Hunter	def SwitchesMaxTime(self, machine_id):
49479a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
49489a9dae36SAdrian Hunter					" FROM context_switches"
49499a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
49509a9dae36SAdrian Hunter					" ORDER BY id DESC LIMIT 1")
49519a9dae36SAdrian Hunter
49529a9dae36SAdrian Hunter	def SamplesMinTime(self, machine_id):
49539a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
49549a9dae36SAdrian Hunter					" FROM samples"
49559a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
49569a9dae36SAdrian Hunter					" ORDER BY id LIMIT 1")
49579a9dae36SAdrian Hunter
49589a9dae36SAdrian Hunter	def SamplesMaxTime(self, machine_id):
49599a9dae36SAdrian Hunter		return self.SelectValue("SELECT time"
49609a9dae36SAdrian Hunter					" FROM samples"
49619a9dae36SAdrian Hunter					" WHERE time != 0 AND machine_id = " + str(machine_id) +
49629a9dae36SAdrian Hunter					" ORDER BY id DESC LIMIT 1")
49639a9dae36SAdrian Hunter
49649a9dae36SAdrian Hunter	def CallsMinTime(self, machine_id):
49659a9dae36SAdrian Hunter		return self.SelectValue("SELECT calls.call_time"
49669a9dae36SAdrian Hunter					" FROM calls"
49679a9dae36SAdrian Hunter					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
49689a9dae36SAdrian Hunter					" WHERE calls.call_time != 0 AND threads.machine_id = " + str(machine_id) +
49699a9dae36SAdrian Hunter					" ORDER BY calls.id LIMIT 1")
49709a9dae36SAdrian Hunter
49719a9dae36SAdrian Hunter	def CallsMaxTime(self, machine_id):
49729a9dae36SAdrian Hunter		return self.SelectValue("SELECT calls.return_time"
49739a9dae36SAdrian Hunter					" FROM calls"
49749a9dae36SAdrian Hunter					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
49759a9dae36SAdrian Hunter					" WHERE calls.return_time != 0 AND threads.machine_id = " + str(machine_id) +
49769a9dae36SAdrian Hunter					" ORDER BY calls.return_time DESC LIMIT 1")
49779a9dae36SAdrian Hunter
49789a9dae36SAdrian Hunter	def GetStartTime(self, machine_id):
49799a9dae36SAdrian Hunter		t0 = self.SwitchesMinTime(machine_id)
49809a9dae36SAdrian Hunter		t1 = self.SamplesMinTime(machine_id)
49819a9dae36SAdrian Hunter		t2 = self.CallsMinTime(machine_id)
49829a9dae36SAdrian Hunter		if t0 is None or (not(t1 is None) and t1 < t0):
49839a9dae36SAdrian Hunter			t0 = t1
49849a9dae36SAdrian Hunter		if t0 is None or (not(t2 is None) and t2 < t0):
49859a9dae36SAdrian Hunter			t0 = t2
49869a9dae36SAdrian Hunter		return t0
49879a9dae36SAdrian Hunter
49889a9dae36SAdrian Hunter	def GetFinishTime(self, machine_id):
49899a9dae36SAdrian Hunter		t0 = self.SwitchesMaxTime(machine_id)
49909a9dae36SAdrian Hunter		t1 = self.SamplesMaxTime(machine_id)
49919a9dae36SAdrian Hunter		t2 = self.CallsMaxTime(machine_id)
49929a9dae36SAdrian Hunter		if t0 is None or (not(t1 is None) and t1 > t0):
49939a9dae36SAdrian Hunter			t0 = t1
49949a9dae36SAdrian Hunter		if t0 is None or (not(t2 is None) and t2 > t0):
49959a9dae36SAdrian Hunter			t0 = t2
49969a9dae36SAdrian Hunter		return t0
49979a9dae36SAdrian Hunter
49989a9dae36SAdrian Hunter	def HostStartTime(self):
49999a9dae36SAdrian Hunter		if self.host_start_time:
50009a9dae36SAdrian Hunter			return self.host_start_time
50019a9dae36SAdrian Hunter		self.host_start_time = self.GetStartTime(self.HostMachineId())
50029a9dae36SAdrian Hunter		return self.host_start_time
50039a9dae36SAdrian Hunter
50049a9dae36SAdrian Hunter	def HostFinishTime(self):
50059a9dae36SAdrian Hunter		if self.host_finish_time:
50069a9dae36SAdrian Hunter			return self.host_finish_time
50079a9dae36SAdrian Hunter		self.host_finish_time = self.GetFinishTime(self.HostMachineId())
50089a9dae36SAdrian Hunter		return self.host_finish_time
50099a9dae36SAdrian Hunter
50109a9dae36SAdrian Hunter	def StartTime(self, machine_id):
50119a9dae36SAdrian Hunter		if machine_id == self.HostMachineId():
50129a9dae36SAdrian Hunter			return self.HostStartTime()
50139a9dae36SAdrian Hunter		return self.GetStartTime(machine_id)
50149a9dae36SAdrian Hunter
50159a9dae36SAdrian Hunter	def FinishTime(self, machine_id):
50169a9dae36SAdrian Hunter		if machine_id == self.HostMachineId():
50179a9dae36SAdrian Hunter			return self.HostFinishTime()
50189a9dae36SAdrian Hunter		return self.GetFinishTime(machine_id)
50199a9dae36SAdrian Hunter
5020031c2a00SAdrian Hunter# Database reference
5021031c2a00SAdrian Hunter
5022031c2a00SAdrian Hunterclass DBRef():
5023031c2a00SAdrian Hunter
5024031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
5025031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
5026031c2a00SAdrian Hunter		self.dbname = dbname
5027af833988SAdrian Hunter		self.TRUE = "TRUE"
5028af833988SAdrian Hunter		self.FALSE = "FALSE"
5029af833988SAdrian Hunter		# SQLite prior to version 3.23 does not support TRUE and FALSE
5030af833988SAdrian Hunter		if self.is_sqlite3:
5031af833988SAdrian Hunter			self.TRUE = "1"
5032af833988SAdrian Hunter			self.FALSE = "0"
5033031c2a00SAdrian Hunter
5034031c2a00SAdrian Hunter	def Open(self, connection_name):
5035031c2a00SAdrian Hunter		dbname = self.dbname
5036031c2a00SAdrian Hunter		if self.is_sqlite3:
5037031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
5038031c2a00SAdrian Hunter		else:
5039031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
5040031c2a00SAdrian Hunter			opts = dbname.split()
5041031c2a00SAdrian Hunter			for opt in opts:
5042031c2a00SAdrian Hunter				if "=" in opt:
5043031c2a00SAdrian Hunter					opt = opt.split("=")
5044031c2a00SAdrian Hunter					if opt[0] == "hostname":
5045031c2a00SAdrian Hunter						db.setHostName(opt[1])
5046031c2a00SAdrian Hunter					elif opt[0] == "port":
5047031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
5048031c2a00SAdrian Hunter					elif opt[0] == "username":
5049031c2a00SAdrian Hunter						db.setUserName(opt[1])
5050031c2a00SAdrian Hunter					elif opt[0] == "password":
5051031c2a00SAdrian Hunter						db.setPassword(opt[1])
5052031c2a00SAdrian Hunter					elif opt[0] == "dbname":
5053031c2a00SAdrian Hunter						dbname = opt[1]
5054031c2a00SAdrian Hunter				else:
5055031c2a00SAdrian Hunter					dbname = opt
5056031c2a00SAdrian Hunter
5057031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
5058031c2a00SAdrian Hunter		if not db.open():
5059031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
5060031c2a00SAdrian Hunter		return db, dbname
5061031c2a00SAdrian Hunter
5062031c2a00SAdrian Hunter# Main
5063031c2a00SAdrian Hunter
5064031c2a00SAdrian Hunterdef Main():
50651ed7f47fSAdrian Hunter	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
50661ed7f47fSAdrian Hunter			"   or: exported-sql-viewer.py --help-only"
50671ed7f47fSAdrian Hunter	ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
5068df8ea22aSAdrian Hunter	ap.add_argument("--pyside-version-1", action='store_true')
50691ed7f47fSAdrian Hunter	ap.add_argument("dbname", nargs="?")
50701ed7f47fSAdrian Hunter	ap.add_argument("--help-only", action='store_true')
50711ed7f47fSAdrian Hunter	args = ap.parse_args()
5072031c2a00SAdrian Hunter
50731ed7f47fSAdrian Hunter	if args.help_only:
507465b24292SAdrian Hunter		app = QApplication(sys.argv)
507565b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
507665b24292SAdrian Hunter		mainwindow.show()
507765b24292SAdrian Hunter		err = app.exec_()
507865b24292SAdrian Hunter		sys.exit(err)
5079031c2a00SAdrian Hunter
50801ed7f47fSAdrian Hunter	dbname = args.dbname
50811ed7f47fSAdrian Hunter	if dbname is None:
50821ed7f47fSAdrian Hunter		ap.print_usage()
50831ed7f47fSAdrian Hunter		print("Too few arguments")
50841ed7f47fSAdrian Hunter		sys.exit(1)
50851ed7f47fSAdrian Hunter
5086031c2a00SAdrian Hunter	is_sqlite3 = False
5087031c2a00SAdrian Hunter	try:
5088beda0e72STony Jones		f = open(dbname, "rb")
5089beda0e72STony Jones		if f.read(15) == b'SQLite format 3':
5090031c2a00SAdrian Hunter			is_sqlite3 = True
5091031c2a00SAdrian Hunter		f.close()
5092031c2a00SAdrian Hunter	except:
5093031c2a00SAdrian Hunter		pass
5094031c2a00SAdrian Hunter
5095031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
5096031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
5097031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
5098031c2a00SAdrian Hunter	app = QApplication(sys.argv)
5099031c2a00SAdrian Hunter	glb.app = app
5100031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
5101031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
5102031c2a00SAdrian Hunter	mainwindow.show()
5103031c2a00SAdrian Hunter	err = app.exec_()
51048392b74bSAdrian Hunter	glb.ShutdownInstances()
5105031c2a00SAdrian Hunter	db.close()
5106031c2a00SAdrian Hunter	sys.exit(err)
5107031c2a00SAdrian Hunter
5108031c2a00SAdrian Hunterif __name__ == "__main__":
5109031c2a00SAdrian Hunter	Main()
5110