1b3a67546SAdrian Hunter#!/usr/bin/env python2
2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5031c2a00SAdrian Hunter
6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8031c2a00SAdrian Hunter# scripts for details.
9031c2a00SAdrian Hunter#
10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12031c2a00SAdrian Hunter#
13031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14031c2a00SAdrian Hunter#
15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17031c2a00SAdrian Hunter#
18031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19031c2a00SAdrian Hunter#
20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22031c2a00SAdrian Hunter# widths to suit will display something like:
23031c2a00SAdrian Hunter#
24031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26031c2a00SAdrian Hunter# v- ls
27031c2a00SAdrian Hunter#     v- 2638:2638
28031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38031c2a00SAdrian Hunter#
39031c2a00SAdrian Hunter# Points to note:
40031c2a00SAdrian Hunter#	The top level is a command name (comm)
41031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42031c2a00SAdrian Hunter#	Subsequent levels are functions
43031c2a00SAdrian Hunter#	'Count' is the number of calls
44031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45031c2a00SAdrian Hunter#	Percentages are relative to the level above
46031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47031c2a00SAdrian Hunter#       functions that it calls
48031c2a00SAdrian Hunter
4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and
5076099f98SAdrian Hunter# possibly disassembly.  However, presently, the only supported disassembler is
5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID
5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install
5376099f98SAdrian Hunter# libxed.so:
5476099f98SAdrian Hunter#            git clone https://github.com/intelxed/mbuild.git mbuild
5576099f98SAdrian Hunter#            git clone https://github.com/intelxed/xed
5676099f98SAdrian Hunter#            cd xed
5776099f98SAdrian Hunter#            ./mfile.py --share
5876099f98SAdrian Hunter#            sudo ./mfile.py --prefix=/usr/local install
5976099f98SAdrian Hunter#            sudo ldconfig
6076099f98SAdrian Hunter#
6176099f98SAdrian Hunter# Example report:
6276099f98SAdrian Hunter#
6376099f98SAdrian Hunter# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6476099f98SAdrian Hunter# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6576099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6676099f98SAdrian Hunter# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6776099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6876099f98SAdrian Hunter#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6976099f98SAdrian Hunter#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7076099f98SAdrian Hunter# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7176099f98SAdrian Hunter#                                                                              7fab593ea930 55                                              pushq  %rbp
7276099f98SAdrian Hunter#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7376099f98SAdrian Hunter#                                                                              7fab593ea934 41 57                                           pushq  %r15
7476099f98SAdrian Hunter#                                                                              7fab593ea936 41 56                                           pushq  %r14
7576099f98SAdrian Hunter#                                                                              7fab593ea938 41 55                                           pushq  %r13
7676099f98SAdrian Hunter#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7776099f98SAdrian Hunter#                                                                              7fab593ea93c 53                                              pushq  %rbx
7876099f98SAdrian Hunter#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7976099f98SAdrian Hunter#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8076099f98SAdrian Hunter#                                                                              7fab593ea944 0f 31                                           rdtsc
8176099f98SAdrian Hunter#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8276099f98SAdrian Hunter#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8376099f98SAdrian Hunter#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8476099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8576099f98SAdrian Hunter# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8676099f98SAdrian Hunter# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8776099f98SAdrian Hunter#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8876099f98SAdrian Hunter#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8976099f98SAdrian Hunter# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9076099f98SAdrian Hunter
91031c2a00SAdrian Hunterimport sys
921beb5c7bSAdrian Hunterimport weakref
931beb5c7bSAdrian Hunterimport threading
94ebd70c7dSAdrian Hunterimport string
958392b74bSAdrian Hunterimport cPickle
968392b74bSAdrian Hunterimport re
978392b74bSAdrian Hunterimport os
98031c2a00SAdrian Hunterfrom PySide.QtCore import *
99031c2a00SAdrian Hunterfrom PySide.QtGui import *
100031c2a00SAdrian Hunterfrom PySide.QtSql import *
101031c2a00SAdrian Hunterfrom decimal import *
1028392b74bSAdrian Hunterfrom ctypes import *
1038392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event
104031c2a00SAdrian Hunter
105031c2a00SAdrian Hunter# Data formatting helpers
106031c2a00SAdrian Hunter
10776099f98SAdrian Hunterdef tohex(ip):
10876099f98SAdrian Hunter	if ip < 0:
10976099f98SAdrian Hunter		ip += 1 << 64
11076099f98SAdrian Hunter	return "%x" % ip
11176099f98SAdrian Hunter
11276099f98SAdrian Hunterdef offstr(offset):
11376099f98SAdrian Hunter	if offset:
11476099f98SAdrian Hunter		return "+0x%x" % offset
11576099f98SAdrian Hunter	return ""
11676099f98SAdrian Hunter
117031c2a00SAdrian Hunterdef dsoname(name):
118031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
119031c2a00SAdrian Hunter		return "[kernel]"
120031c2a00SAdrian Hunter	return name
121031c2a00SAdrian Hunter
122210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0):
123210cf1f9SAdrian Hunter	pos = s.find(sub)
124210cf1f9SAdrian Hunter	if pos < 0:
125210cf1f9SAdrian Hunter		return pos
126210cf1f9SAdrian Hunter	if n <= 1:
127210cf1f9SAdrian Hunter		return offs + pos
128210cf1f9SAdrian Hunter	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
129210cf1f9SAdrian Hunter
130031c2a00SAdrian Hunter# Percent to one decimal place
131031c2a00SAdrian Hunter
132031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
133031c2a00SAdrian Hunter	if not d:
134031c2a00SAdrian Hunter		return "0.0"
135031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
136031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
137031c2a00SAdrian Hunter
138031c2a00SAdrian Hunter# Helper for queries that must not fail
139031c2a00SAdrian Hunter
140031c2a00SAdrian Hunterdef QueryExec(query, stmt):
141031c2a00SAdrian Hunter	ret = query.exec_(stmt)
142031c2a00SAdrian Hunter	if not ret:
143031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
144031c2a00SAdrian Hunter
145ebd70c7dSAdrian Hunter# Background thread
146ebd70c7dSAdrian Hunter
147ebd70c7dSAdrian Hunterclass Thread(QThread):
148ebd70c7dSAdrian Hunter
149ebd70c7dSAdrian Hunter	done = Signal(object)
150ebd70c7dSAdrian Hunter
151ebd70c7dSAdrian Hunter	def __init__(self, task, param=None, parent=None):
152ebd70c7dSAdrian Hunter		super(Thread, self).__init__(parent)
153ebd70c7dSAdrian Hunter		self.task = task
154ebd70c7dSAdrian Hunter		self.param = param
155ebd70c7dSAdrian Hunter
156ebd70c7dSAdrian Hunter	def run(self):
157ebd70c7dSAdrian Hunter		while True:
158ebd70c7dSAdrian Hunter			if self.param is None:
159ebd70c7dSAdrian Hunter				done, result = self.task()
160ebd70c7dSAdrian Hunter			else:
161ebd70c7dSAdrian Hunter				done, result = self.task(self.param)
162ebd70c7dSAdrian Hunter			self.done.emit(result)
163ebd70c7dSAdrian Hunter			if done:
164ebd70c7dSAdrian Hunter				break
165ebd70c7dSAdrian Hunter
166031c2a00SAdrian Hunter# Tree data model
167031c2a00SAdrian Hunter
168031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
169031c2a00SAdrian Hunter
170a448ba23SAdrian Hunter	def __init__(self, glb, parent=None):
171031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
172a448ba23SAdrian Hunter		self.glb = glb
173a448ba23SAdrian Hunter		self.root = self.GetRoot()
174031c2a00SAdrian Hunter		self.last_row_read = 0
175031c2a00SAdrian Hunter
176031c2a00SAdrian Hunter	def Item(self, parent):
177031c2a00SAdrian Hunter		if parent.isValid():
178031c2a00SAdrian Hunter			return parent.internalPointer()
179031c2a00SAdrian Hunter		else:
180031c2a00SAdrian Hunter			return self.root
181031c2a00SAdrian Hunter
182031c2a00SAdrian Hunter	def rowCount(self, parent):
183031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
184031c2a00SAdrian Hunter		if result < 0:
185031c2a00SAdrian Hunter			result = 0
186031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
187031c2a00SAdrian Hunter		return result
188031c2a00SAdrian Hunter
189031c2a00SAdrian Hunter	def hasChildren(self, parent):
190031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
191031c2a00SAdrian Hunter
192031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
193031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
194031c2a00SAdrian Hunter			return self.columnAlignment(section)
195031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
196031c2a00SAdrian Hunter			return None
197031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
198031c2a00SAdrian Hunter			return None
199031c2a00SAdrian Hunter		return self.columnHeader(section)
200031c2a00SAdrian Hunter
201031c2a00SAdrian Hunter	def parent(self, child):
202031c2a00SAdrian Hunter		child_item = child.internalPointer()
203031c2a00SAdrian Hunter		if child_item is self.root:
204031c2a00SAdrian Hunter			return QModelIndex()
205031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
206031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
207031c2a00SAdrian Hunter
208031c2a00SAdrian Hunter	def index(self, row, column, parent):
209031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
210031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
211031c2a00SAdrian Hunter
212031c2a00SAdrian Hunter	def DisplayData(self, item, index):
213031c2a00SAdrian Hunter		return item.getData(index.column())
214031c2a00SAdrian Hunter
2158392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2168392b74bSAdrian Hunter		if row > self.last_row_read:
2178392b74bSAdrian Hunter			self.last_row_read = row
2188392b74bSAdrian Hunter			if row + 10 >= self.root.child_count:
2198392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2208392b74bSAdrian Hunter
2218392b74bSAdrian Hunter	def columnAlignment(self, column):
2228392b74bSAdrian Hunter		return Qt.AlignLeft
2238392b74bSAdrian Hunter
2248392b74bSAdrian Hunter	def columnFont(self, column):
2258392b74bSAdrian Hunter		return None
2268392b74bSAdrian Hunter
2278392b74bSAdrian Hunter	def data(self, index, role):
2288392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2298392b74bSAdrian Hunter			return self.columnAlignment(index.column())
2308392b74bSAdrian Hunter		if role == Qt.FontRole:
2318392b74bSAdrian Hunter			return self.columnFont(index.column())
2328392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2338392b74bSAdrian Hunter			return None
2348392b74bSAdrian Hunter		item = index.internalPointer()
2358392b74bSAdrian Hunter		return self.DisplayData(item, index)
2368392b74bSAdrian Hunter
2378392b74bSAdrian Hunter# Table data model
2388392b74bSAdrian Hunter
2398392b74bSAdrian Hunterclass TableModel(QAbstractTableModel):
2408392b74bSAdrian Hunter
2418392b74bSAdrian Hunter	def __init__(self, parent=None):
2428392b74bSAdrian Hunter		super(TableModel, self).__init__(parent)
2438392b74bSAdrian Hunter		self.child_count = 0
2448392b74bSAdrian Hunter		self.child_items = []
2458392b74bSAdrian Hunter		self.last_row_read = 0
2468392b74bSAdrian Hunter
2478392b74bSAdrian Hunter	def Item(self, parent):
2488392b74bSAdrian Hunter		if parent.isValid():
2498392b74bSAdrian Hunter			return parent.internalPointer()
2508392b74bSAdrian Hunter		else:
2518392b74bSAdrian Hunter			return self
2528392b74bSAdrian Hunter
2538392b74bSAdrian Hunter	def rowCount(self, parent):
2548392b74bSAdrian Hunter		return self.child_count
2558392b74bSAdrian Hunter
2568392b74bSAdrian Hunter	def headerData(self, section, orientation, role):
2578392b74bSAdrian Hunter		if role == Qt.TextAlignmentRole:
2588392b74bSAdrian Hunter			return self.columnAlignment(section)
2598392b74bSAdrian Hunter		if role != Qt.DisplayRole:
2608392b74bSAdrian Hunter			return None
2618392b74bSAdrian Hunter		if orientation != Qt.Horizontal:
2628392b74bSAdrian Hunter			return None
2638392b74bSAdrian Hunter		return self.columnHeader(section)
2648392b74bSAdrian Hunter
2658392b74bSAdrian Hunter	def index(self, row, column, parent):
2668392b74bSAdrian Hunter		return self.createIndex(row, column, self.child_items[row])
2678392b74bSAdrian Hunter
2688392b74bSAdrian Hunter	def DisplayData(self, item, index):
2698392b74bSAdrian Hunter		return item.getData(index.column())
2708392b74bSAdrian Hunter
2718392b74bSAdrian Hunter	def FetchIfNeeded(self, row):
2728392b74bSAdrian Hunter		if row > self.last_row_read:
2738392b74bSAdrian Hunter			self.last_row_read = row
2748392b74bSAdrian Hunter			if row + 10 >= self.child_count:
2758392b74bSAdrian Hunter				self.fetcher.Fetch(glb_chunk_sz)
2768392b74bSAdrian Hunter
277031c2a00SAdrian Hunter	def columnAlignment(self, column):
278031c2a00SAdrian Hunter		return Qt.AlignLeft
279031c2a00SAdrian Hunter
280031c2a00SAdrian Hunter	def columnFont(self, column):
281031c2a00SAdrian Hunter		return None
282031c2a00SAdrian Hunter
283031c2a00SAdrian Hunter	def data(self, index, role):
284031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
285031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
286031c2a00SAdrian Hunter		if role == Qt.FontRole:
287031c2a00SAdrian Hunter			return self.columnFont(index.column())
288031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
289031c2a00SAdrian Hunter			return None
290031c2a00SAdrian Hunter		item = index.internalPointer()
291031c2a00SAdrian Hunter		return self.DisplayData(item, index)
292031c2a00SAdrian Hunter
2931beb5c7bSAdrian Hunter# Model cache
2941beb5c7bSAdrian Hunter
2951beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary()
2961beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock()
2971beb5c7bSAdrian Hunter
2981beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn):
2991beb5c7bSAdrian Hunter	model_cache_lock.acquire()
3001beb5c7bSAdrian Hunter	try:
3011beb5c7bSAdrian Hunter		model = model_cache[model_name]
3021beb5c7bSAdrian Hunter	except:
3031beb5c7bSAdrian Hunter		model = None
3041beb5c7bSAdrian Hunter	if model is None:
3051beb5c7bSAdrian Hunter		model = create_fn()
3061beb5c7bSAdrian Hunter		model_cache[model_name] = model
3071beb5c7bSAdrian Hunter	model_cache_lock.release()
3081beb5c7bSAdrian Hunter	return model
3091beb5c7bSAdrian Hunter
310ebd70c7dSAdrian Hunter# Find bar
311ebd70c7dSAdrian Hunter
312ebd70c7dSAdrian Hunterclass FindBar():
313ebd70c7dSAdrian Hunter
314ebd70c7dSAdrian Hunter	def __init__(self, parent, finder, is_reg_expr=False):
315ebd70c7dSAdrian Hunter		self.finder = finder
316ebd70c7dSAdrian Hunter		self.context = []
317ebd70c7dSAdrian Hunter		self.last_value = None
318ebd70c7dSAdrian Hunter		self.last_pattern = None
319ebd70c7dSAdrian Hunter
320ebd70c7dSAdrian Hunter		label = QLabel("Find:")
321ebd70c7dSAdrian Hunter		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
322ebd70c7dSAdrian Hunter
323ebd70c7dSAdrian Hunter		self.textbox = QComboBox()
324ebd70c7dSAdrian Hunter		self.textbox.setEditable(True)
325ebd70c7dSAdrian Hunter		self.textbox.currentIndexChanged.connect(self.ValueChanged)
326ebd70c7dSAdrian Hunter
327ebd70c7dSAdrian Hunter		self.progress = QProgressBar()
328ebd70c7dSAdrian Hunter		self.progress.setRange(0, 0)
329ebd70c7dSAdrian Hunter		self.progress.hide()
330ebd70c7dSAdrian Hunter
331ebd70c7dSAdrian Hunter		if is_reg_expr:
332ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Regular Expression")
333ebd70c7dSAdrian Hunter		else:
334ebd70c7dSAdrian Hunter			self.pattern = QCheckBox("Pattern")
335ebd70c7dSAdrian Hunter		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
336ebd70c7dSAdrian Hunter
337ebd70c7dSAdrian Hunter		self.next_button = QToolButton()
338ebd70c7dSAdrian Hunter		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
339ebd70c7dSAdrian Hunter		self.next_button.released.connect(lambda: self.NextPrev(1))
340ebd70c7dSAdrian Hunter
341ebd70c7dSAdrian Hunter		self.prev_button = QToolButton()
342ebd70c7dSAdrian Hunter		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
343ebd70c7dSAdrian Hunter		self.prev_button.released.connect(lambda: self.NextPrev(-1))
344ebd70c7dSAdrian Hunter
345ebd70c7dSAdrian Hunter		self.close_button = QToolButton()
346ebd70c7dSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
347ebd70c7dSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
348ebd70c7dSAdrian Hunter
349ebd70c7dSAdrian Hunter		self.hbox = QHBoxLayout()
350ebd70c7dSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
351ebd70c7dSAdrian Hunter
352ebd70c7dSAdrian Hunter		self.hbox.addWidget(label)
353ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.textbox)
354ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.progress)
355ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.pattern)
356ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.next_button)
357ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.prev_button)
358ebd70c7dSAdrian Hunter		self.hbox.addWidget(self.close_button)
359ebd70c7dSAdrian Hunter
360ebd70c7dSAdrian Hunter		self.bar = QWidget()
361ebd70c7dSAdrian Hunter		self.bar.setLayout(self.hbox);
362ebd70c7dSAdrian Hunter		self.bar.hide()
363ebd70c7dSAdrian Hunter
364ebd70c7dSAdrian Hunter	def Widget(self):
365ebd70c7dSAdrian Hunter		return self.bar
366ebd70c7dSAdrian Hunter
367ebd70c7dSAdrian Hunter	def Activate(self):
368ebd70c7dSAdrian Hunter		self.bar.show()
369ebd70c7dSAdrian Hunter		self.textbox.setFocus()
370ebd70c7dSAdrian Hunter
371ebd70c7dSAdrian Hunter	def Deactivate(self):
372ebd70c7dSAdrian Hunter		self.bar.hide()
373ebd70c7dSAdrian Hunter
374ebd70c7dSAdrian Hunter	def Busy(self):
375ebd70c7dSAdrian Hunter		self.textbox.setEnabled(False)
376ebd70c7dSAdrian Hunter		self.pattern.hide()
377ebd70c7dSAdrian Hunter		self.next_button.hide()
378ebd70c7dSAdrian Hunter		self.prev_button.hide()
379ebd70c7dSAdrian Hunter		self.progress.show()
380ebd70c7dSAdrian Hunter
381ebd70c7dSAdrian Hunter	def Idle(self):
382ebd70c7dSAdrian Hunter		self.textbox.setEnabled(True)
383ebd70c7dSAdrian Hunter		self.progress.hide()
384ebd70c7dSAdrian Hunter		self.pattern.show()
385ebd70c7dSAdrian Hunter		self.next_button.show()
386ebd70c7dSAdrian Hunter		self.prev_button.show()
387ebd70c7dSAdrian Hunter
388ebd70c7dSAdrian Hunter	def Find(self, direction):
389ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
390ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
391ebd70c7dSAdrian Hunter		self.last_value = value
392ebd70c7dSAdrian Hunter		self.last_pattern = pattern
393ebd70c7dSAdrian Hunter		self.finder.Find(value, direction, pattern, self.context)
394ebd70c7dSAdrian Hunter
395ebd70c7dSAdrian Hunter	def ValueChanged(self):
396ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
397ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
398ebd70c7dSAdrian Hunter		index = self.textbox.currentIndex()
399ebd70c7dSAdrian Hunter		data = self.textbox.itemData(index)
400ebd70c7dSAdrian Hunter		# Store the pattern in the combo box to keep it with the text value
401ebd70c7dSAdrian Hunter		if data == None:
402ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
403ebd70c7dSAdrian Hunter		else:
404ebd70c7dSAdrian Hunter			self.pattern.setChecked(data)
405ebd70c7dSAdrian Hunter		self.Find(0)
406ebd70c7dSAdrian Hunter
407ebd70c7dSAdrian Hunter	def NextPrev(self, direction):
408ebd70c7dSAdrian Hunter		value = self.textbox.currentText()
409ebd70c7dSAdrian Hunter		pattern = self.pattern.isChecked()
410ebd70c7dSAdrian Hunter		if value != self.last_value:
411ebd70c7dSAdrian Hunter			index = self.textbox.findText(value)
412ebd70c7dSAdrian Hunter			# Allow for a button press before the value has been added to the combo box
413ebd70c7dSAdrian Hunter			if index < 0:
414ebd70c7dSAdrian Hunter				index = self.textbox.count()
415ebd70c7dSAdrian Hunter				self.textbox.addItem(value, pattern)
416ebd70c7dSAdrian Hunter				self.textbox.setCurrentIndex(index)
417ebd70c7dSAdrian Hunter				return
418ebd70c7dSAdrian Hunter			else:
419ebd70c7dSAdrian Hunter				self.textbox.setItemData(index, pattern)
420ebd70c7dSAdrian Hunter		elif pattern != self.last_pattern:
421ebd70c7dSAdrian Hunter			# Keep the pattern recorded in the combo box up to date
422ebd70c7dSAdrian Hunter			index = self.textbox.currentIndex()
423ebd70c7dSAdrian Hunter			self.textbox.setItemData(index, pattern)
424ebd70c7dSAdrian Hunter		self.Find(direction)
425ebd70c7dSAdrian Hunter
426ebd70c7dSAdrian Hunter	def NotFound(self):
427ebd70c7dSAdrian Hunter		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
428ebd70c7dSAdrian Hunter
429031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
430031c2a00SAdrian Hunter
431031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
432031c2a00SAdrian Hunter
433031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
434031c2a00SAdrian Hunter		self.glb = glb
435031c2a00SAdrian Hunter		self.row = row
436031c2a00SAdrian Hunter		self.parent_item = parent_item
437031c2a00SAdrian Hunter		self.query_done = False;
438031c2a00SAdrian Hunter		self.child_count = 0
439031c2a00SAdrian Hunter		self.child_items = []
440031c2a00SAdrian Hunter
441031c2a00SAdrian Hunter	def getChildItem(self, row):
442031c2a00SAdrian Hunter		return self.child_items[row]
443031c2a00SAdrian Hunter
444031c2a00SAdrian Hunter	def getParentItem(self):
445031c2a00SAdrian Hunter		return self.parent_item
446031c2a00SAdrian Hunter
447031c2a00SAdrian Hunter	def getRow(self):
448031c2a00SAdrian Hunter		return self.row
449031c2a00SAdrian Hunter
450031c2a00SAdrian Hunter	def childCount(self):
451031c2a00SAdrian Hunter		if not self.query_done:
452031c2a00SAdrian Hunter			self.Select()
453031c2a00SAdrian Hunter			if not self.child_count:
454031c2a00SAdrian Hunter				return -1
455031c2a00SAdrian Hunter		return self.child_count
456031c2a00SAdrian Hunter
457031c2a00SAdrian Hunter	def hasChildren(self):
458031c2a00SAdrian Hunter		if not self.query_done:
459031c2a00SAdrian Hunter			return True
460031c2a00SAdrian Hunter		return self.child_count > 0
461031c2a00SAdrian Hunter
462031c2a00SAdrian Hunter	def getData(self, column):
463031c2a00SAdrian Hunter		return self.data[column]
464031c2a00SAdrian Hunter
465031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
466031c2a00SAdrian Hunter
467031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
468031c2a00SAdrian Hunter
469031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
470031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
471031c2a00SAdrian Hunter		self.comm_id = comm_id
472031c2a00SAdrian Hunter		self.thread_id = thread_id
473031c2a00SAdrian Hunter		self.call_path_id = call_path_id
474031c2a00SAdrian Hunter		self.branch_count = branch_count
475031c2a00SAdrian Hunter		self.time = time
476031c2a00SAdrian Hunter
477031c2a00SAdrian Hunter	def Select(self):
478031c2a00SAdrian Hunter		self.query_done = True;
479031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
480031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
481031c2a00SAdrian Hunter					" FROM calls"
482031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
483031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
484031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
485031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
486031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
487031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
488031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
489031c2a00SAdrian Hunter					" ORDER BY call_path_id")
490031c2a00SAdrian Hunter		while query.next():
491031c2a00SAdrian Hunter			child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
492031c2a00SAdrian Hunter			self.child_items.append(child_item)
493031c2a00SAdrian Hunter			self.child_count += 1
494031c2a00SAdrian Hunter
495031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
496031c2a00SAdrian Hunter
497031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
498031c2a00SAdrian Hunter
499031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
500031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
501031c2a00SAdrian Hunter		dso = dsoname(dso)
502031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
503031c2a00SAdrian Hunter		self.dbid = call_path_id
504031c2a00SAdrian Hunter
505031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
506031c2a00SAdrian Hunter
507031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
508031c2a00SAdrian Hunter
509031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
510031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
511031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
512031c2a00SAdrian Hunter		self.dbid = thread_id
513031c2a00SAdrian Hunter
514031c2a00SAdrian Hunter	def Select(self):
515031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
516031c2a00SAdrian Hunter		for child_item in self.child_items:
517031c2a00SAdrian Hunter			self.time += child_item.time
518031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
519031c2a00SAdrian Hunter		for child_item in self.child_items:
520031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
521031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
522031c2a00SAdrian Hunter
523031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
524031c2a00SAdrian Hunter
525031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
526031c2a00SAdrian Hunter
527031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
528031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
529031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
530031c2a00SAdrian Hunter		self.dbid = comm_id
531031c2a00SAdrian Hunter
532031c2a00SAdrian Hunter	def Select(self):
533031c2a00SAdrian Hunter		self.query_done = True;
534031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
535031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
536031c2a00SAdrian Hunter					" FROM comm_threads"
537031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
538031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
539031c2a00SAdrian Hunter		while query.next():
540031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
541031c2a00SAdrian Hunter			self.child_items.append(child_item)
542031c2a00SAdrian Hunter			self.child_count += 1
543031c2a00SAdrian Hunter
544031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
545031c2a00SAdrian Hunter
546031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
547031c2a00SAdrian Hunter
548031c2a00SAdrian Hunter	def __init__(self, glb):
549031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
550031c2a00SAdrian Hunter		self.dbid = 0
551031c2a00SAdrian Hunter		self.query_done = True;
552031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
553031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
554031c2a00SAdrian Hunter		while query.next():
555031c2a00SAdrian Hunter			if not query.value(0):
556031c2a00SAdrian Hunter				continue
557031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
558031c2a00SAdrian Hunter			self.child_items.append(child_item)
559031c2a00SAdrian Hunter			self.child_count += 1
560031c2a00SAdrian Hunter
561254c0d82SAdrian Hunter# Context-sensitive call graph data model base
562031c2a00SAdrian Hunter
563254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel):
564031c2a00SAdrian Hunter
565031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
566254c0d82SAdrian Hunter		super(CallGraphModelBase, self).__init__(glb, parent)
567031c2a00SAdrian Hunter
568ebd70c7dSAdrian Hunter	def FindSelect(self, value, pattern, query):
569ebd70c7dSAdrian Hunter		if pattern:
570ebd70c7dSAdrian Hunter			# postgresql and sqlite pattern patching differences:
571ebd70c7dSAdrian Hunter			#   postgresql LIKE is case sensitive but sqlite LIKE is not
572ebd70c7dSAdrian Hunter			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
573ebd70c7dSAdrian Hunter			#   postgresql supports ILIKE which is case insensitive
574ebd70c7dSAdrian Hunter			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
575ebd70c7dSAdrian Hunter			if not self.glb.dbref.is_sqlite3:
576ebd70c7dSAdrian Hunter				# Escape % and _
577ebd70c7dSAdrian Hunter				s = value.replace("%", "\%")
578ebd70c7dSAdrian Hunter				s = s.replace("_", "\_")
579ebd70c7dSAdrian Hunter				# Translate * and ? into SQL LIKE pattern characters % and _
580ebd70c7dSAdrian Hunter				trans = string.maketrans("*?", "%_")
581ebd70c7dSAdrian Hunter				match = " LIKE '" + str(s).translate(trans) + "'"
582ebd70c7dSAdrian Hunter			else:
583ebd70c7dSAdrian Hunter				match = " GLOB '" + str(value) + "'"
584ebd70c7dSAdrian Hunter		else:
585ebd70c7dSAdrian Hunter			match = " = '" + str(value) + "'"
586254c0d82SAdrian Hunter		self.DoFindSelect(query, match)
587ebd70c7dSAdrian Hunter
588ebd70c7dSAdrian Hunter	def Found(self, query, found):
589ebd70c7dSAdrian Hunter		if found:
590ebd70c7dSAdrian Hunter			return self.FindPath(query)
591ebd70c7dSAdrian Hunter		return []
592ebd70c7dSAdrian Hunter
593ebd70c7dSAdrian Hunter	def FindValue(self, value, pattern, query, last_value, last_pattern):
594ebd70c7dSAdrian Hunter		if last_value == value and pattern == last_pattern:
595ebd70c7dSAdrian Hunter			found = query.first()
596ebd70c7dSAdrian Hunter		else:
597ebd70c7dSAdrian Hunter			self.FindSelect(value, pattern, query)
598ebd70c7dSAdrian Hunter			found = query.next()
599ebd70c7dSAdrian Hunter		return self.Found(query, found)
600ebd70c7dSAdrian Hunter
601ebd70c7dSAdrian Hunter	def FindNext(self, query):
602ebd70c7dSAdrian Hunter		found = query.next()
603ebd70c7dSAdrian Hunter		if not found:
604ebd70c7dSAdrian Hunter			found = query.first()
605ebd70c7dSAdrian Hunter		return self.Found(query, found)
606ebd70c7dSAdrian Hunter
607ebd70c7dSAdrian Hunter	def FindPrev(self, query):
608ebd70c7dSAdrian Hunter		found = query.previous()
609ebd70c7dSAdrian Hunter		if not found:
610ebd70c7dSAdrian Hunter			found = query.last()
611ebd70c7dSAdrian Hunter		return self.Found(query, found)
612ebd70c7dSAdrian Hunter
613ebd70c7dSAdrian Hunter	def FindThread(self, c):
614ebd70c7dSAdrian Hunter		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
615ebd70c7dSAdrian Hunter			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
616ebd70c7dSAdrian Hunter		elif c.direction > 0:
617ebd70c7dSAdrian Hunter			ids = self.FindNext(c.query)
618ebd70c7dSAdrian Hunter		else:
619ebd70c7dSAdrian Hunter			ids = self.FindPrev(c.query)
620ebd70c7dSAdrian Hunter		return (True, ids)
621ebd70c7dSAdrian Hunter
622ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
623ebd70c7dSAdrian Hunter		class Context():
624ebd70c7dSAdrian Hunter			def __init__(self, *x):
625ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
626ebd70c7dSAdrian Hunter			def Update(self, *x):
627ebd70c7dSAdrian Hunter				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
628ebd70c7dSAdrian Hunter		if len(context):
629ebd70c7dSAdrian Hunter			context[0].Update(value, direction, pattern)
630ebd70c7dSAdrian Hunter		else:
631ebd70c7dSAdrian Hunter			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
632ebd70c7dSAdrian Hunter		# Use a thread so the UI is not blocked during the SELECT
633ebd70c7dSAdrian Hunter		thread = Thread(self.FindThread, context[0])
634ebd70c7dSAdrian Hunter		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
635ebd70c7dSAdrian Hunter		thread.start()
636ebd70c7dSAdrian Hunter
637ebd70c7dSAdrian Hunter	def FindDone(self, thread, callback, ids):
638ebd70c7dSAdrian Hunter		callback(ids)
639ebd70c7dSAdrian Hunter
640254c0d82SAdrian Hunter# Context-sensitive call graph data model
641254c0d82SAdrian Hunter
642254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase):
643254c0d82SAdrian Hunter
644254c0d82SAdrian Hunter	def __init__(self, glb, parent=None):
645254c0d82SAdrian Hunter		super(CallGraphModel, self).__init__(glb, parent)
646254c0d82SAdrian Hunter
647254c0d82SAdrian Hunter	def GetRoot(self):
648254c0d82SAdrian Hunter		return CallGraphRootItem(self.glb)
649254c0d82SAdrian Hunter
650254c0d82SAdrian Hunter	def columnCount(self, parent=None):
651254c0d82SAdrian Hunter		return 7
652254c0d82SAdrian Hunter
653254c0d82SAdrian Hunter	def columnHeader(self, column):
654254c0d82SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
655254c0d82SAdrian Hunter		return headers[column]
656254c0d82SAdrian Hunter
657254c0d82SAdrian Hunter	def columnAlignment(self, column):
658254c0d82SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
659254c0d82SAdrian Hunter		return alignment[column]
660254c0d82SAdrian Hunter
661254c0d82SAdrian Hunter	def DoFindSelect(self, query, match):
662254c0d82SAdrian Hunter		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
663254c0d82SAdrian Hunter						" FROM calls"
664254c0d82SAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
665254c0d82SAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
666254c0d82SAdrian Hunter						" WHERE symbols.name" + match +
667254c0d82SAdrian Hunter						" GROUP BY comm_id, thread_id, call_path_id"
668254c0d82SAdrian Hunter						" ORDER BY comm_id, thread_id, call_path_id")
669254c0d82SAdrian Hunter
670254c0d82SAdrian Hunter	def FindPath(self, query):
671254c0d82SAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
672254c0d82SAdrian Hunter		# to open the tree at the right place.
673254c0d82SAdrian Hunter		ids = []
674254c0d82SAdrian Hunter		parent_id = query.value(0)
675254c0d82SAdrian Hunter		while parent_id:
676254c0d82SAdrian Hunter			ids.insert(0, parent_id)
677254c0d82SAdrian Hunter			q2 = QSqlQuery(self.glb.db)
678254c0d82SAdrian Hunter			QueryExec(q2, "SELECT parent_id"
679254c0d82SAdrian Hunter					" FROM call_paths"
680254c0d82SAdrian Hunter					" WHERE id = " + str(parent_id))
681254c0d82SAdrian Hunter			if not q2.next():
682254c0d82SAdrian Hunter				break
683254c0d82SAdrian Hunter			parent_id = q2.value(0)
684254c0d82SAdrian Hunter		# The call path root is not used
685254c0d82SAdrian Hunter		if ids[0] == 1:
686254c0d82SAdrian Hunter			del ids[0]
687254c0d82SAdrian Hunter		ids.insert(0, query.value(2))
688254c0d82SAdrian Hunter		ids.insert(0, query.value(1))
689254c0d82SAdrian Hunter		return ids
690254c0d82SAdrian Hunter
691*ae8b887cSAdrian Hunter# Call tree data model level 2+ item base
692*ae8b887cSAdrian Hunter
693*ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
694*ae8b887cSAdrian Hunter
695*ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item):
696*ae8b887cSAdrian Hunter		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
697*ae8b887cSAdrian Hunter		self.comm_id = comm_id
698*ae8b887cSAdrian Hunter		self.thread_id = thread_id
699*ae8b887cSAdrian Hunter		self.calls_id = calls_id
700*ae8b887cSAdrian Hunter		self.branch_count = branch_count
701*ae8b887cSAdrian Hunter		self.time = time
702*ae8b887cSAdrian Hunter
703*ae8b887cSAdrian Hunter	def Select(self):
704*ae8b887cSAdrian Hunter		self.query_done = True;
705*ae8b887cSAdrian Hunter		if self.calls_id == 0:
706*ae8b887cSAdrian Hunter			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
707*ae8b887cSAdrian Hunter		else:
708*ae8b887cSAdrian Hunter			comm_thread = ""
709*ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
710*ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count"
711*ae8b887cSAdrian Hunter					" FROM calls"
712*ae8b887cSAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
713*ae8b887cSAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
714*ae8b887cSAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
715*ae8b887cSAdrian Hunter					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
716*ae8b887cSAdrian Hunter					" ORDER BY call_time, calls.id")
717*ae8b887cSAdrian Hunter		while query.next():
718*ae8b887cSAdrian Hunter			child_item = CallTreeLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
719*ae8b887cSAdrian Hunter			self.child_items.append(child_item)
720*ae8b887cSAdrian Hunter			self.child_count += 1
721*ae8b887cSAdrian Hunter
722*ae8b887cSAdrian Hunter# Call tree data model level three item
723*ae8b887cSAdrian Hunter
724*ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
725*ae8b887cSAdrian Hunter
726*ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item):
727*ae8b887cSAdrian Hunter		super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item)
728*ae8b887cSAdrian Hunter		dso = dsoname(dso)
729*ae8b887cSAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
730*ae8b887cSAdrian Hunter		self.dbid = calls_id
731*ae8b887cSAdrian Hunter
732*ae8b887cSAdrian Hunter# Call tree data model level two item
733*ae8b887cSAdrian Hunter
734*ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
735*ae8b887cSAdrian Hunter
736*ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
737*ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item)
738*ae8b887cSAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
739*ae8b887cSAdrian Hunter		self.dbid = thread_id
740*ae8b887cSAdrian Hunter
741*ae8b887cSAdrian Hunter	def Select(self):
742*ae8b887cSAdrian Hunter		super(CallTreeLevelTwoItem, self).Select()
743*ae8b887cSAdrian Hunter		for child_item in self.child_items:
744*ae8b887cSAdrian Hunter			self.time += child_item.time
745*ae8b887cSAdrian Hunter			self.branch_count += child_item.branch_count
746*ae8b887cSAdrian Hunter		for child_item in self.child_items:
747*ae8b887cSAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
748*ae8b887cSAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
749*ae8b887cSAdrian Hunter
750*ae8b887cSAdrian Hunter# Call tree data model level one item
751*ae8b887cSAdrian Hunter
752*ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase):
753*ae8b887cSAdrian Hunter
754*ae8b887cSAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
755*ae8b887cSAdrian Hunter		super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item)
756*ae8b887cSAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
757*ae8b887cSAdrian Hunter		self.dbid = comm_id
758*ae8b887cSAdrian Hunter
759*ae8b887cSAdrian Hunter	def Select(self):
760*ae8b887cSAdrian Hunter		self.query_done = True;
761*ae8b887cSAdrian Hunter		query = QSqlQuery(self.glb.db)
762*ae8b887cSAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
763*ae8b887cSAdrian Hunter					" FROM comm_threads"
764*ae8b887cSAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
765*ae8b887cSAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
766*ae8b887cSAdrian Hunter		while query.next():
767*ae8b887cSAdrian Hunter			child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
768*ae8b887cSAdrian Hunter			self.child_items.append(child_item)
769*ae8b887cSAdrian Hunter			self.child_count += 1
770*ae8b887cSAdrian Hunter
771*ae8b887cSAdrian Hunter# Call tree data model root item
772*ae8b887cSAdrian Hunter
773*ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase):
774*ae8b887cSAdrian Hunter
775*ae8b887cSAdrian Hunter	def __init__(self, glb):
776*ae8b887cSAdrian Hunter		super(CallTreeRootItem, self).__init__(glb, 0, None)
777*ae8b887cSAdrian Hunter		self.dbid = 0
778*ae8b887cSAdrian Hunter		self.query_done = True;
779*ae8b887cSAdrian Hunter		query = QSqlQuery(glb.db)
780*ae8b887cSAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
781*ae8b887cSAdrian Hunter		while query.next():
782*ae8b887cSAdrian Hunter			if not query.value(0):
783*ae8b887cSAdrian Hunter				continue
784*ae8b887cSAdrian Hunter			child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
785*ae8b887cSAdrian Hunter			self.child_items.append(child_item)
786*ae8b887cSAdrian Hunter			self.child_count += 1
787*ae8b887cSAdrian Hunter
788*ae8b887cSAdrian Hunter# Call Tree data model
789*ae8b887cSAdrian Hunter
790*ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase):
791*ae8b887cSAdrian Hunter
792*ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
793*ae8b887cSAdrian Hunter		super(CallTreeModel, self).__init__(glb, parent)
794*ae8b887cSAdrian Hunter
795*ae8b887cSAdrian Hunter	def GetRoot(self):
796*ae8b887cSAdrian Hunter		return CallTreeRootItem(self.glb)
797*ae8b887cSAdrian Hunter
798*ae8b887cSAdrian Hunter	def columnCount(self, parent=None):
799*ae8b887cSAdrian Hunter		return 7
800*ae8b887cSAdrian Hunter
801*ae8b887cSAdrian Hunter	def columnHeader(self, column):
802*ae8b887cSAdrian Hunter		headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
803*ae8b887cSAdrian Hunter		return headers[column]
804*ae8b887cSAdrian Hunter
805*ae8b887cSAdrian Hunter	def columnAlignment(self, column):
806*ae8b887cSAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
807*ae8b887cSAdrian Hunter		return alignment[column]
808*ae8b887cSAdrian Hunter
809*ae8b887cSAdrian Hunter	def DoFindSelect(self, query, match):
810*ae8b887cSAdrian Hunter		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
811*ae8b887cSAdrian Hunter						" FROM calls"
812*ae8b887cSAdrian Hunter						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
813*ae8b887cSAdrian Hunter						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
814*ae8b887cSAdrian Hunter						" WHERE symbols.name" + match +
815*ae8b887cSAdrian Hunter						" ORDER BY comm_id, thread_id, call_time, calls.id")
816*ae8b887cSAdrian Hunter
817*ae8b887cSAdrian Hunter	def FindPath(self, query):
818*ae8b887cSAdrian Hunter		# Turn the query result into a list of ids that the tree view can walk
819*ae8b887cSAdrian Hunter		# to open the tree at the right place.
820*ae8b887cSAdrian Hunter		ids = []
821*ae8b887cSAdrian Hunter		parent_id = query.value(0)
822*ae8b887cSAdrian Hunter		while parent_id:
823*ae8b887cSAdrian Hunter			ids.insert(0, parent_id)
824*ae8b887cSAdrian Hunter			q2 = QSqlQuery(self.glb.db)
825*ae8b887cSAdrian Hunter			QueryExec(q2, "SELECT parent_id"
826*ae8b887cSAdrian Hunter					" FROM calls"
827*ae8b887cSAdrian Hunter					" WHERE id = " + str(parent_id))
828*ae8b887cSAdrian Hunter			if not q2.next():
829*ae8b887cSAdrian Hunter				break
830*ae8b887cSAdrian Hunter			parent_id = q2.value(0)
831*ae8b887cSAdrian Hunter		ids.insert(0, query.value(2))
832*ae8b887cSAdrian Hunter		ids.insert(0, query.value(1))
833*ae8b887cSAdrian Hunter		return ids
834*ae8b887cSAdrian Hunter
835ebd70c7dSAdrian Hunter# Vertical widget layout
836ebd70c7dSAdrian Hunter
837ebd70c7dSAdrian Hunterclass VBox():
838ebd70c7dSAdrian Hunter
839ebd70c7dSAdrian Hunter	def __init__(self, w1, w2, w3=None):
840ebd70c7dSAdrian Hunter		self.vbox = QWidget()
841ebd70c7dSAdrian Hunter		self.vbox.setLayout(QVBoxLayout());
842ebd70c7dSAdrian Hunter
843ebd70c7dSAdrian Hunter		self.vbox.layout().setContentsMargins(0, 0, 0, 0)
844ebd70c7dSAdrian Hunter
845ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w1)
846ebd70c7dSAdrian Hunter		self.vbox.layout().addWidget(w2)
847ebd70c7dSAdrian Hunter		if w3:
848ebd70c7dSAdrian Hunter			self.vbox.layout().addWidget(w3)
849ebd70c7dSAdrian Hunter
850ebd70c7dSAdrian Hunter	def Widget(self):
851ebd70c7dSAdrian Hunter		return self.vbox
852ebd70c7dSAdrian Hunter
853a731cc4cSAdrian Hunter# Tree window base
8541beb5c7bSAdrian Hunter
855a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow):
8561beb5c7bSAdrian Hunter
857a731cc4cSAdrian Hunter	def __init__(self, parent=None):
858a731cc4cSAdrian Hunter		super(TreeWindowBase, self).__init__(parent)
8591beb5c7bSAdrian Hunter
860a731cc4cSAdrian Hunter		self.model = None
861a731cc4cSAdrian Hunter		self.view = None
862a731cc4cSAdrian Hunter		self.find_bar = None
8631beb5c7bSAdrian Hunter
864ebd70c7dSAdrian Hunter	def DisplayFound(self, ids):
865ebd70c7dSAdrian Hunter		if not len(ids):
866ebd70c7dSAdrian Hunter			return False
867ebd70c7dSAdrian Hunter		parent = QModelIndex()
868ebd70c7dSAdrian Hunter		for dbid in ids:
869ebd70c7dSAdrian Hunter			found = False
870ebd70c7dSAdrian Hunter			n = self.model.rowCount(parent)
871ebd70c7dSAdrian Hunter			for row in xrange(n):
872ebd70c7dSAdrian Hunter				child = self.model.index(row, 0, parent)
873ebd70c7dSAdrian Hunter				if child.internalPointer().dbid == dbid:
874ebd70c7dSAdrian Hunter					found = True
875ebd70c7dSAdrian Hunter					self.view.setCurrentIndex(child)
876ebd70c7dSAdrian Hunter					parent = child
877ebd70c7dSAdrian Hunter					break
878ebd70c7dSAdrian Hunter			if not found:
879ebd70c7dSAdrian Hunter				break
880ebd70c7dSAdrian Hunter		return found
881ebd70c7dSAdrian Hunter
882ebd70c7dSAdrian Hunter	def Find(self, value, direction, pattern, context):
883ebd70c7dSAdrian Hunter		self.view.setFocus()
884ebd70c7dSAdrian Hunter		self.find_bar.Busy()
885ebd70c7dSAdrian Hunter		self.model.Find(value, direction, pattern, context, self.FindDone)
886ebd70c7dSAdrian Hunter
887ebd70c7dSAdrian Hunter	def FindDone(self, ids):
888ebd70c7dSAdrian Hunter		found = True
889ebd70c7dSAdrian Hunter		if not self.DisplayFound(ids):
890ebd70c7dSAdrian Hunter			found = False
891ebd70c7dSAdrian Hunter		self.find_bar.Idle()
892ebd70c7dSAdrian Hunter		if not found:
893ebd70c7dSAdrian Hunter			self.find_bar.NotFound()
894ebd70c7dSAdrian Hunter
895a731cc4cSAdrian Hunter
896a731cc4cSAdrian Hunter# Context-sensitive call graph window
897a731cc4cSAdrian Hunter
898a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase):
899a731cc4cSAdrian Hunter
900a731cc4cSAdrian Hunter	def __init__(self, glb, parent=None):
901a731cc4cSAdrian Hunter		super(CallGraphWindow, self).__init__(parent)
902a731cc4cSAdrian Hunter
903a731cc4cSAdrian Hunter		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
904a731cc4cSAdrian Hunter
905a731cc4cSAdrian Hunter		self.view = QTreeView()
906a731cc4cSAdrian Hunter		self.view.setModel(self.model)
907a731cc4cSAdrian Hunter
908a731cc4cSAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
909a731cc4cSAdrian Hunter			self.view.setColumnWidth(c, w)
910a731cc4cSAdrian Hunter
911a731cc4cSAdrian Hunter		self.find_bar = FindBar(self, self)
912a731cc4cSAdrian Hunter
913a731cc4cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
914a731cc4cSAdrian Hunter
915a731cc4cSAdrian Hunter		self.setWidget(self.vbox.Widget())
916a731cc4cSAdrian Hunter
917a731cc4cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
918a731cc4cSAdrian Hunter
919*ae8b887cSAdrian Hunter# Call tree window
920*ae8b887cSAdrian Hunter
921*ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase):
922*ae8b887cSAdrian Hunter
923*ae8b887cSAdrian Hunter	def __init__(self, glb, parent=None):
924*ae8b887cSAdrian Hunter		super(CallTreeWindow, self).__init__(parent)
925*ae8b887cSAdrian Hunter
926*ae8b887cSAdrian Hunter		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
927*ae8b887cSAdrian Hunter
928*ae8b887cSAdrian Hunter		self.view = QTreeView()
929*ae8b887cSAdrian Hunter		self.view.setModel(self.model)
930*ae8b887cSAdrian Hunter
931*ae8b887cSAdrian Hunter		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
932*ae8b887cSAdrian Hunter			self.view.setColumnWidth(c, w)
933*ae8b887cSAdrian Hunter
934*ae8b887cSAdrian Hunter		self.find_bar = FindBar(self, self)
935*ae8b887cSAdrian Hunter
936*ae8b887cSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget())
937*ae8b887cSAdrian Hunter
938*ae8b887cSAdrian Hunter		self.setWidget(self.vbox.Widget())
939*ae8b887cSAdrian Hunter
940*ae8b887cSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
941*ae8b887cSAdrian Hunter
9428392b74bSAdrian Hunter# Child data item  finder
9438392b74bSAdrian Hunter
9448392b74bSAdrian Hunterclass ChildDataItemFinder():
9458392b74bSAdrian Hunter
9468392b74bSAdrian Hunter	def __init__(self, root):
9478392b74bSAdrian Hunter		self.root = root
9488392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
9498392b74bSAdrian Hunter		self.rows = []
9508392b74bSAdrian Hunter		self.pos = 0
9518392b74bSAdrian Hunter
9528392b74bSAdrian Hunter	def FindSelect(self):
9538392b74bSAdrian Hunter		self.rows = []
9548392b74bSAdrian Hunter		if self.pattern:
9558392b74bSAdrian Hunter			pattern = re.compile(self.value)
9568392b74bSAdrian Hunter			for child in self.root.child_items:
9578392b74bSAdrian Hunter				for column_data in child.data:
9588392b74bSAdrian Hunter					if re.search(pattern, str(column_data)) is not None:
9598392b74bSAdrian Hunter						self.rows.append(child.row)
9608392b74bSAdrian Hunter						break
9618392b74bSAdrian Hunter		else:
9628392b74bSAdrian Hunter			for child in self.root.child_items:
9638392b74bSAdrian Hunter				for column_data in child.data:
9648392b74bSAdrian Hunter					if self.value in str(column_data):
9658392b74bSAdrian Hunter						self.rows.append(child.row)
9668392b74bSAdrian Hunter						break
9678392b74bSAdrian Hunter
9688392b74bSAdrian Hunter	def FindValue(self):
9698392b74bSAdrian Hunter		self.pos = 0
9708392b74bSAdrian Hunter		if self.last_value != self.value or self.pattern != self.last_pattern:
9718392b74bSAdrian Hunter			self.FindSelect()
9728392b74bSAdrian Hunter		if not len(self.rows):
9738392b74bSAdrian Hunter			return -1
9748392b74bSAdrian Hunter		return self.rows[self.pos]
9758392b74bSAdrian Hunter
9768392b74bSAdrian Hunter	def FindThread(self):
9778392b74bSAdrian Hunter		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
9788392b74bSAdrian Hunter			row = self.FindValue()
9798392b74bSAdrian Hunter		elif len(self.rows):
9808392b74bSAdrian Hunter			if self.direction > 0:
9818392b74bSAdrian Hunter				self.pos += 1
9828392b74bSAdrian Hunter				if self.pos >= len(self.rows):
9838392b74bSAdrian Hunter					self.pos = 0
9848392b74bSAdrian Hunter			else:
9858392b74bSAdrian Hunter				self.pos -= 1
9868392b74bSAdrian Hunter				if self.pos < 0:
9878392b74bSAdrian Hunter					self.pos = len(self.rows) - 1
9888392b74bSAdrian Hunter			row = self.rows[self.pos]
9898392b74bSAdrian Hunter		else:
9908392b74bSAdrian Hunter			row = -1
9918392b74bSAdrian Hunter		return (True, row)
9928392b74bSAdrian Hunter
9938392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context, callback):
9948392b74bSAdrian Hunter		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
9958392b74bSAdrian Hunter		# Use a thread so the UI is not blocked
9968392b74bSAdrian Hunter		thread = Thread(self.FindThread)
9978392b74bSAdrian Hunter		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
9988392b74bSAdrian Hunter		thread.start()
9998392b74bSAdrian Hunter
10008392b74bSAdrian Hunter	def FindDone(self, thread, callback, row):
10018392b74bSAdrian Hunter		callback(row)
10028392b74bSAdrian Hunter
10038392b74bSAdrian Hunter# Number of database records to fetch in one go
10048392b74bSAdrian Hunter
10058392b74bSAdrian Hunterglb_chunk_sz = 10000
10068392b74bSAdrian Hunter
10078392b74bSAdrian Hunter# size of pickled integer big enough for record size
10088392b74bSAdrian Hunter
10098392b74bSAdrian Hunterglb_nsz = 8
10108392b74bSAdrian Hunter
10118392b74bSAdrian Hunter# Background process for SQL data fetcher
10128392b74bSAdrian Hunter
10138392b74bSAdrian Hunterclass SQLFetcherProcess():
10148392b74bSAdrian Hunter
10158392b74bSAdrian Hunter	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
10168392b74bSAdrian Hunter		# Need a unique connection name
10178392b74bSAdrian Hunter		conn_name = "SQLFetcher" + str(os.getpid())
10188392b74bSAdrian Hunter		self.db, dbname = dbref.Open(conn_name)
10198392b74bSAdrian Hunter		self.sql = sql
10208392b74bSAdrian Hunter		self.buffer = buffer
10218392b74bSAdrian Hunter		self.head = head
10228392b74bSAdrian Hunter		self.tail = tail
10238392b74bSAdrian Hunter		self.fetch_count = fetch_count
10248392b74bSAdrian Hunter		self.fetching_done = fetching_done
10258392b74bSAdrian Hunter		self.process_target = process_target
10268392b74bSAdrian Hunter		self.wait_event = wait_event
10278392b74bSAdrian Hunter		self.fetched_event = fetched_event
10288392b74bSAdrian Hunter		self.prep = prep
10298392b74bSAdrian Hunter		self.query = QSqlQuery(self.db)
10308392b74bSAdrian Hunter		self.query_limit = 0 if "$$last_id$$" in sql else 2
10318392b74bSAdrian Hunter		self.last_id = -1
10328392b74bSAdrian Hunter		self.fetched = 0
10338392b74bSAdrian Hunter		self.more = True
10348392b74bSAdrian Hunter		self.local_head = self.head.value
10358392b74bSAdrian Hunter		self.local_tail = self.tail.value
10368392b74bSAdrian Hunter
10378392b74bSAdrian Hunter	def Select(self):
10388392b74bSAdrian Hunter		if self.query_limit:
10398392b74bSAdrian Hunter			if self.query_limit == 1:
10408392b74bSAdrian Hunter				return
10418392b74bSAdrian Hunter			self.query_limit -= 1
10428392b74bSAdrian Hunter		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
10438392b74bSAdrian Hunter		QueryExec(self.query, stmt)
10448392b74bSAdrian Hunter
10458392b74bSAdrian Hunter	def Next(self):
10468392b74bSAdrian Hunter		if not self.query.next():
10478392b74bSAdrian Hunter			self.Select()
10488392b74bSAdrian Hunter			if not self.query.next():
10498392b74bSAdrian Hunter				return None
10508392b74bSAdrian Hunter		self.last_id = self.query.value(0)
10518392b74bSAdrian Hunter		return self.prep(self.query)
10528392b74bSAdrian Hunter
10538392b74bSAdrian Hunter	def WaitForTarget(self):
10548392b74bSAdrian Hunter		while True:
10558392b74bSAdrian Hunter			self.wait_event.clear()
10568392b74bSAdrian Hunter			target = self.process_target.value
10578392b74bSAdrian Hunter			if target > self.fetched or target < 0:
10588392b74bSAdrian Hunter				break
10598392b74bSAdrian Hunter			self.wait_event.wait()
10608392b74bSAdrian Hunter		return target
10618392b74bSAdrian Hunter
10628392b74bSAdrian Hunter	def HasSpace(self, sz):
10638392b74bSAdrian Hunter		if self.local_tail <= self.local_head:
10648392b74bSAdrian Hunter			space = len(self.buffer) - self.local_head
10658392b74bSAdrian Hunter			if space > sz:
10668392b74bSAdrian Hunter				return True
10678392b74bSAdrian Hunter			if space >= glb_nsz:
10688392b74bSAdrian Hunter				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
10698392b74bSAdrian Hunter				nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL)
10708392b74bSAdrian Hunter				self.buffer[self.local_head : self.local_head + len(nd)] = nd
10718392b74bSAdrian Hunter			self.local_head = 0
10728392b74bSAdrian Hunter		if self.local_tail - self.local_head > sz:
10738392b74bSAdrian Hunter			return True
10748392b74bSAdrian Hunter		return False
10758392b74bSAdrian Hunter
10768392b74bSAdrian Hunter	def WaitForSpace(self, sz):
10778392b74bSAdrian Hunter		if self.HasSpace(sz):
10788392b74bSAdrian Hunter			return
10798392b74bSAdrian Hunter		while True:
10808392b74bSAdrian Hunter			self.wait_event.clear()
10818392b74bSAdrian Hunter			self.local_tail = self.tail.value
10828392b74bSAdrian Hunter			if self.HasSpace(sz):
10838392b74bSAdrian Hunter				return
10848392b74bSAdrian Hunter			self.wait_event.wait()
10858392b74bSAdrian Hunter
10868392b74bSAdrian Hunter	def AddToBuffer(self, obj):
10878392b74bSAdrian Hunter		d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL)
10888392b74bSAdrian Hunter		n = len(d)
10898392b74bSAdrian Hunter		nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL)
10908392b74bSAdrian Hunter		sz = n + glb_nsz
10918392b74bSAdrian Hunter		self.WaitForSpace(sz)
10928392b74bSAdrian Hunter		pos = self.local_head
10938392b74bSAdrian Hunter		self.buffer[pos : pos + len(nd)] = nd
10948392b74bSAdrian Hunter		self.buffer[pos + glb_nsz : pos + sz] = d
10958392b74bSAdrian Hunter		self.local_head += sz
10968392b74bSAdrian Hunter
10978392b74bSAdrian Hunter	def FetchBatch(self, batch_size):
10988392b74bSAdrian Hunter		fetched = 0
10998392b74bSAdrian Hunter		while batch_size > fetched:
11008392b74bSAdrian Hunter			obj = self.Next()
11018392b74bSAdrian Hunter			if obj is None:
11028392b74bSAdrian Hunter				self.more = False
11038392b74bSAdrian Hunter				break
11048392b74bSAdrian Hunter			self.AddToBuffer(obj)
11058392b74bSAdrian Hunter			fetched += 1
11068392b74bSAdrian Hunter		if fetched:
11078392b74bSAdrian Hunter			self.fetched += fetched
11088392b74bSAdrian Hunter			with self.fetch_count.get_lock():
11098392b74bSAdrian Hunter				self.fetch_count.value += fetched
11108392b74bSAdrian Hunter			self.head.value = self.local_head
11118392b74bSAdrian Hunter			self.fetched_event.set()
11128392b74bSAdrian Hunter
11138392b74bSAdrian Hunter	def Run(self):
11148392b74bSAdrian Hunter		while self.more:
11158392b74bSAdrian Hunter			target = self.WaitForTarget()
11168392b74bSAdrian Hunter			if target < 0:
11178392b74bSAdrian Hunter				break
11188392b74bSAdrian Hunter			batch_size = min(glb_chunk_sz, target - self.fetched)
11198392b74bSAdrian Hunter			self.FetchBatch(batch_size)
11208392b74bSAdrian Hunter		self.fetching_done.value = True
11218392b74bSAdrian Hunter		self.fetched_event.set()
11228392b74bSAdrian Hunter
11238392b74bSAdrian Hunterdef SQLFetcherFn(*x):
11248392b74bSAdrian Hunter	process = SQLFetcherProcess(*x)
11258392b74bSAdrian Hunter	process.Run()
11268392b74bSAdrian Hunter
11278392b74bSAdrian Hunter# SQL data fetcher
11288392b74bSAdrian Hunter
11298392b74bSAdrian Hunterclass SQLFetcher(QObject):
11308392b74bSAdrian Hunter
11318392b74bSAdrian Hunter	done = Signal(object)
11328392b74bSAdrian Hunter
11338392b74bSAdrian Hunter	def __init__(self, glb, sql, prep, process_data, parent=None):
11348392b74bSAdrian Hunter		super(SQLFetcher, self).__init__(parent)
11358392b74bSAdrian Hunter		self.process_data = process_data
11368392b74bSAdrian Hunter		self.more = True
11378392b74bSAdrian Hunter		self.target = 0
11388392b74bSAdrian Hunter		self.last_target = 0
11398392b74bSAdrian Hunter		self.fetched = 0
11408392b74bSAdrian Hunter		self.buffer_size = 16 * 1024 * 1024
11418392b74bSAdrian Hunter		self.buffer = Array(c_char, self.buffer_size, lock=False)
11428392b74bSAdrian Hunter		self.head = Value(c_longlong)
11438392b74bSAdrian Hunter		self.tail = Value(c_longlong)
11448392b74bSAdrian Hunter		self.local_tail = 0
11458392b74bSAdrian Hunter		self.fetch_count = Value(c_longlong)
11468392b74bSAdrian Hunter		self.fetching_done = Value(c_bool)
11478392b74bSAdrian Hunter		self.last_count = 0
11488392b74bSAdrian Hunter		self.process_target = Value(c_longlong)
11498392b74bSAdrian Hunter		self.wait_event = Event()
11508392b74bSAdrian Hunter		self.fetched_event = Event()
11518392b74bSAdrian Hunter		glb.AddInstanceToShutdownOnExit(self)
11528392b74bSAdrian 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))
11538392b74bSAdrian Hunter		self.process.start()
11548392b74bSAdrian Hunter		self.thread = Thread(self.Thread)
11558392b74bSAdrian Hunter		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
11568392b74bSAdrian Hunter		self.thread.start()
11578392b74bSAdrian Hunter
11588392b74bSAdrian Hunter	def Shutdown(self):
11598392b74bSAdrian Hunter		# Tell the thread and process to exit
11608392b74bSAdrian Hunter		self.process_target.value = -1
11618392b74bSAdrian Hunter		self.wait_event.set()
11628392b74bSAdrian Hunter		self.more = False
11638392b74bSAdrian Hunter		self.fetching_done.value = True
11648392b74bSAdrian Hunter		self.fetched_event.set()
11658392b74bSAdrian Hunter
11668392b74bSAdrian Hunter	def Thread(self):
11678392b74bSAdrian Hunter		if not self.more:
11688392b74bSAdrian Hunter			return True, 0
11698392b74bSAdrian Hunter		while True:
11708392b74bSAdrian Hunter			self.fetched_event.clear()
11718392b74bSAdrian Hunter			fetch_count = self.fetch_count.value
11728392b74bSAdrian Hunter			if fetch_count != self.last_count:
11738392b74bSAdrian Hunter				break
11748392b74bSAdrian Hunter			if self.fetching_done.value:
11758392b74bSAdrian Hunter				self.more = False
11768392b74bSAdrian Hunter				return True, 0
11778392b74bSAdrian Hunter			self.fetched_event.wait()
11788392b74bSAdrian Hunter		count = fetch_count - self.last_count
11798392b74bSAdrian Hunter		self.last_count = fetch_count
11808392b74bSAdrian Hunter		self.fetched += count
11818392b74bSAdrian Hunter		return False, count
11828392b74bSAdrian Hunter
11838392b74bSAdrian Hunter	def Fetch(self, nr):
11848392b74bSAdrian Hunter		if not self.more:
11858392b74bSAdrian Hunter			# -1 inidcates there are no more
11868392b74bSAdrian Hunter			return -1
11878392b74bSAdrian Hunter		result = self.fetched
11888392b74bSAdrian Hunter		extra = result + nr - self.target
11898392b74bSAdrian Hunter		if extra > 0:
11908392b74bSAdrian Hunter			self.target += extra
11918392b74bSAdrian Hunter			# process_target < 0 indicates shutting down
11928392b74bSAdrian Hunter			if self.process_target.value >= 0:
11938392b74bSAdrian Hunter				self.process_target.value = self.target
11948392b74bSAdrian Hunter			self.wait_event.set()
11958392b74bSAdrian Hunter		return result
11968392b74bSAdrian Hunter
11978392b74bSAdrian Hunter	def RemoveFromBuffer(self):
11988392b74bSAdrian Hunter		pos = self.local_tail
11998392b74bSAdrian Hunter		if len(self.buffer) - pos < glb_nsz:
12008392b74bSAdrian Hunter			pos = 0
12018392b74bSAdrian Hunter		n = cPickle.loads(self.buffer[pos : pos + glb_nsz])
12028392b74bSAdrian Hunter		if n == 0:
12038392b74bSAdrian Hunter			pos = 0
12048392b74bSAdrian Hunter			n = cPickle.loads(self.buffer[0 : glb_nsz])
12058392b74bSAdrian Hunter		pos += glb_nsz
12068392b74bSAdrian Hunter		obj = cPickle.loads(self.buffer[pos : pos + n])
12078392b74bSAdrian Hunter		self.local_tail = pos + n
12088392b74bSAdrian Hunter		return obj
12098392b74bSAdrian Hunter
12108392b74bSAdrian Hunter	def ProcessData(self, count):
12118392b74bSAdrian Hunter		for i in xrange(count):
12128392b74bSAdrian Hunter			obj = self.RemoveFromBuffer()
12138392b74bSAdrian Hunter			self.process_data(obj)
12148392b74bSAdrian Hunter		self.tail.value = self.local_tail
12158392b74bSAdrian Hunter		self.wait_event.set()
12168392b74bSAdrian Hunter		self.done.emit(count)
12178392b74bSAdrian Hunter
12188392b74bSAdrian Hunter# Fetch more records bar
12198392b74bSAdrian Hunter
12208392b74bSAdrian Hunterclass FetchMoreRecordsBar():
12218392b74bSAdrian Hunter
12228392b74bSAdrian Hunter	def __init__(self, model, parent):
12238392b74bSAdrian Hunter		self.model = model
12248392b74bSAdrian Hunter
12258392b74bSAdrian Hunter		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
12268392b74bSAdrian Hunter		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12278392b74bSAdrian Hunter
12288392b74bSAdrian Hunter		self.fetch_count = QSpinBox()
12298392b74bSAdrian Hunter		self.fetch_count.setRange(1, 1000000)
12308392b74bSAdrian Hunter		self.fetch_count.setValue(10)
12318392b74bSAdrian Hunter		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12328392b74bSAdrian Hunter
12338392b74bSAdrian Hunter		self.fetch = QPushButton("Go!")
12348392b74bSAdrian Hunter		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
12358392b74bSAdrian Hunter		self.fetch.released.connect(self.FetchMoreRecords)
12368392b74bSAdrian Hunter
12378392b74bSAdrian Hunter		self.progress = QProgressBar()
12388392b74bSAdrian Hunter		self.progress.setRange(0, 100)
12398392b74bSAdrian Hunter		self.progress.hide()
12408392b74bSAdrian Hunter
12418392b74bSAdrian Hunter		self.done_label = QLabel("All records fetched")
12428392b74bSAdrian Hunter		self.done_label.hide()
12438392b74bSAdrian Hunter
12448392b74bSAdrian Hunter		self.spacer = QLabel("")
12458392b74bSAdrian Hunter
12468392b74bSAdrian Hunter		self.close_button = QToolButton()
12478392b74bSAdrian Hunter		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
12488392b74bSAdrian Hunter		self.close_button.released.connect(self.Deactivate)
12498392b74bSAdrian Hunter
12508392b74bSAdrian Hunter		self.hbox = QHBoxLayout()
12518392b74bSAdrian Hunter		self.hbox.setContentsMargins(0, 0, 0, 0)
12528392b74bSAdrian Hunter
12538392b74bSAdrian Hunter		self.hbox.addWidget(self.label)
12548392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch_count)
12558392b74bSAdrian Hunter		self.hbox.addWidget(self.fetch)
12568392b74bSAdrian Hunter		self.hbox.addWidget(self.spacer)
12578392b74bSAdrian Hunter		self.hbox.addWidget(self.progress)
12588392b74bSAdrian Hunter		self.hbox.addWidget(self.done_label)
12598392b74bSAdrian Hunter		self.hbox.addWidget(self.close_button)
12608392b74bSAdrian Hunter
12618392b74bSAdrian Hunter		self.bar = QWidget()
12628392b74bSAdrian Hunter		self.bar.setLayout(self.hbox);
12638392b74bSAdrian Hunter		self.bar.show()
12648392b74bSAdrian Hunter
12658392b74bSAdrian Hunter		self.in_progress = False
12668392b74bSAdrian Hunter		self.model.progress.connect(self.Progress)
12678392b74bSAdrian Hunter
12688392b74bSAdrian Hunter		self.done = False
12698392b74bSAdrian Hunter
12708392b74bSAdrian Hunter		if not model.HasMoreRecords():
12718392b74bSAdrian Hunter			self.Done()
12728392b74bSAdrian Hunter
12738392b74bSAdrian Hunter	def Widget(self):
12748392b74bSAdrian Hunter		return self.bar
12758392b74bSAdrian Hunter
12768392b74bSAdrian Hunter	def Activate(self):
12778392b74bSAdrian Hunter		self.bar.show()
12788392b74bSAdrian Hunter		self.fetch.setFocus()
12798392b74bSAdrian Hunter
12808392b74bSAdrian Hunter	def Deactivate(self):
12818392b74bSAdrian Hunter		self.bar.hide()
12828392b74bSAdrian Hunter
12838392b74bSAdrian Hunter	def Enable(self, enable):
12848392b74bSAdrian Hunter		self.fetch.setEnabled(enable)
12858392b74bSAdrian Hunter		self.fetch_count.setEnabled(enable)
12868392b74bSAdrian Hunter
12878392b74bSAdrian Hunter	def Busy(self):
12888392b74bSAdrian Hunter		self.Enable(False)
12898392b74bSAdrian Hunter		self.fetch.hide()
12908392b74bSAdrian Hunter		self.spacer.hide()
12918392b74bSAdrian Hunter		self.progress.show()
12928392b74bSAdrian Hunter
12938392b74bSAdrian Hunter	def Idle(self):
12948392b74bSAdrian Hunter		self.in_progress = False
12958392b74bSAdrian Hunter		self.Enable(True)
12968392b74bSAdrian Hunter		self.progress.hide()
12978392b74bSAdrian Hunter		self.fetch.show()
12988392b74bSAdrian Hunter		self.spacer.show()
12998392b74bSAdrian Hunter
13008392b74bSAdrian Hunter	def Target(self):
13018392b74bSAdrian Hunter		return self.fetch_count.value() * glb_chunk_sz
13028392b74bSAdrian Hunter
13038392b74bSAdrian Hunter	def Done(self):
13048392b74bSAdrian Hunter		self.done = True
13058392b74bSAdrian Hunter		self.Idle()
13068392b74bSAdrian Hunter		self.label.hide()
13078392b74bSAdrian Hunter		self.fetch_count.hide()
13088392b74bSAdrian Hunter		self.fetch.hide()
13098392b74bSAdrian Hunter		self.spacer.hide()
13108392b74bSAdrian Hunter		self.done_label.show()
13118392b74bSAdrian Hunter
13128392b74bSAdrian Hunter	def Progress(self, count):
13138392b74bSAdrian Hunter		if self.in_progress:
13148392b74bSAdrian Hunter			if count:
13158392b74bSAdrian Hunter				percent = ((count - self.start) * 100) / self.Target()
13168392b74bSAdrian Hunter				if percent >= 100:
13178392b74bSAdrian Hunter					self.Idle()
13188392b74bSAdrian Hunter				else:
13198392b74bSAdrian Hunter					self.progress.setValue(percent)
13208392b74bSAdrian Hunter		if not count:
13218392b74bSAdrian Hunter			# Count value of zero means no more records
13228392b74bSAdrian Hunter			self.Done()
13238392b74bSAdrian Hunter
13248392b74bSAdrian Hunter	def FetchMoreRecords(self):
13258392b74bSAdrian Hunter		if self.done:
13268392b74bSAdrian Hunter			return
13278392b74bSAdrian Hunter		self.progress.setValue(0)
13288392b74bSAdrian Hunter		self.Busy()
13298392b74bSAdrian Hunter		self.in_progress = True
13308392b74bSAdrian Hunter		self.start = self.model.FetchMoreRecords(self.Target())
13318392b74bSAdrian Hunter
133276099f98SAdrian Hunter# Brance data model level two item
133376099f98SAdrian Hunter
133476099f98SAdrian Hunterclass BranchLevelTwoItem():
133576099f98SAdrian Hunter
133676099f98SAdrian Hunter	def __init__(self, row, text, parent_item):
133776099f98SAdrian Hunter		self.row = row
133876099f98SAdrian Hunter		self.parent_item = parent_item
133976099f98SAdrian Hunter		self.data = [""] * 8
134076099f98SAdrian Hunter		self.data[7] = text
134176099f98SAdrian Hunter		self.level = 2
134276099f98SAdrian Hunter
134376099f98SAdrian Hunter	def getParentItem(self):
134476099f98SAdrian Hunter		return self.parent_item
134576099f98SAdrian Hunter
134676099f98SAdrian Hunter	def getRow(self):
134776099f98SAdrian Hunter		return self.row
134876099f98SAdrian Hunter
134976099f98SAdrian Hunter	def childCount(self):
135076099f98SAdrian Hunter		return 0
135176099f98SAdrian Hunter
135276099f98SAdrian Hunter	def hasChildren(self):
135376099f98SAdrian Hunter		return False
135476099f98SAdrian Hunter
135576099f98SAdrian Hunter	def getData(self, column):
135676099f98SAdrian Hunter		return self.data[column]
135776099f98SAdrian Hunter
135876099f98SAdrian Hunter# Brance data model level one item
135976099f98SAdrian Hunter
136076099f98SAdrian Hunterclass BranchLevelOneItem():
136176099f98SAdrian Hunter
136276099f98SAdrian Hunter	def __init__(self, glb, row, data, parent_item):
136376099f98SAdrian Hunter		self.glb = glb
136476099f98SAdrian Hunter		self.row = row
136576099f98SAdrian Hunter		self.parent_item = parent_item
136676099f98SAdrian Hunter		self.child_count = 0
136776099f98SAdrian Hunter		self.child_items = []
136876099f98SAdrian Hunter		self.data = data[1:]
136976099f98SAdrian Hunter		self.dbid = data[0]
137076099f98SAdrian Hunter		self.level = 1
137176099f98SAdrian Hunter		self.query_done = False
137276099f98SAdrian Hunter
137376099f98SAdrian Hunter	def getChildItem(self, row):
137476099f98SAdrian Hunter		return self.child_items[row]
137576099f98SAdrian Hunter
137676099f98SAdrian Hunter	def getParentItem(self):
137776099f98SAdrian Hunter		return self.parent_item
137876099f98SAdrian Hunter
137976099f98SAdrian Hunter	def getRow(self):
138076099f98SAdrian Hunter		return self.row
138176099f98SAdrian Hunter
138276099f98SAdrian Hunter	def Select(self):
138376099f98SAdrian Hunter		self.query_done = True
138476099f98SAdrian Hunter
138576099f98SAdrian Hunter		if not self.glb.have_disassembler:
138676099f98SAdrian Hunter			return
138776099f98SAdrian Hunter
138876099f98SAdrian Hunter		query = QSqlQuery(self.glb.db)
138976099f98SAdrian Hunter
139076099f98SAdrian Hunter		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
139176099f98SAdrian Hunter				  " FROM samples"
139276099f98SAdrian Hunter				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
139376099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
139476099f98SAdrian Hunter				  " WHERE samples.id = " + str(self.dbid))
139576099f98SAdrian Hunter		if not query.next():
139676099f98SAdrian Hunter			return
139776099f98SAdrian Hunter		cpu = query.value(0)
139876099f98SAdrian Hunter		dso = query.value(1)
139976099f98SAdrian Hunter		sym = query.value(2)
140076099f98SAdrian Hunter		if dso == 0 or sym == 0:
140176099f98SAdrian Hunter			return
140276099f98SAdrian Hunter		off = query.value(3)
140376099f98SAdrian Hunter		short_name = query.value(4)
140476099f98SAdrian Hunter		long_name = query.value(5)
140576099f98SAdrian Hunter		build_id = query.value(6)
140676099f98SAdrian Hunter		sym_start = query.value(7)
140776099f98SAdrian Hunter		ip = query.value(8)
140876099f98SAdrian Hunter
140976099f98SAdrian Hunter		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
141076099f98SAdrian Hunter				  " FROM samples"
141176099f98SAdrian Hunter				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
141276099f98SAdrian Hunter				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
141376099f98SAdrian Hunter				  " ORDER BY samples.id"
141476099f98SAdrian Hunter				  " LIMIT 1")
141576099f98SAdrian Hunter		if not query.next():
141676099f98SAdrian Hunter			return
141776099f98SAdrian Hunter		if query.value(0) != dso:
141876099f98SAdrian Hunter			# Cannot disassemble from one dso to another
141976099f98SAdrian Hunter			return
142076099f98SAdrian Hunter		bsym = query.value(1)
142176099f98SAdrian Hunter		boff = query.value(2)
142276099f98SAdrian Hunter		bsym_start = query.value(3)
142376099f98SAdrian Hunter		if bsym == 0:
142476099f98SAdrian Hunter			return
142576099f98SAdrian Hunter		tot = bsym_start + boff + 1 - sym_start - off
142676099f98SAdrian Hunter		if tot <= 0 or tot > 16384:
142776099f98SAdrian Hunter			return
142876099f98SAdrian Hunter
142976099f98SAdrian Hunter		inst = self.glb.disassembler.Instruction()
143076099f98SAdrian Hunter		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
143176099f98SAdrian Hunter		if not f:
143276099f98SAdrian Hunter			return
143376099f98SAdrian Hunter		mode = 0 if Is64Bit(f) else 1
143476099f98SAdrian Hunter		self.glb.disassembler.SetMode(inst, mode)
143576099f98SAdrian Hunter
143676099f98SAdrian Hunter		buf_sz = tot + 16
143776099f98SAdrian Hunter		buf = create_string_buffer(tot + 16)
143876099f98SAdrian Hunter		f.seek(sym_start + off)
143976099f98SAdrian Hunter		buf.value = f.read(buf_sz)
144076099f98SAdrian Hunter		buf_ptr = addressof(buf)
144176099f98SAdrian Hunter		i = 0
144276099f98SAdrian Hunter		while tot > 0:
144376099f98SAdrian Hunter			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
144476099f98SAdrian Hunter			if cnt:
144576099f98SAdrian Hunter				byte_str = tohex(ip).rjust(16)
144676099f98SAdrian Hunter				for k in xrange(cnt):
144776099f98SAdrian Hunter					byte_str += " %02x" % ord(buf[i])
144876099f98SAdrian Hunter					i += 1
144976099f98SAdrian Hunter				while k < 15:
145076099f98SAdrian Hunter					byte_str += "   "
145176099f98SAdrian Hunter					k += 1
145276099f98SAdrian Hunter				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
145376099f98SAdrian Hunter				self.child_count += 1
145476099f98SAdrian Hunter			else:
145576099f98SAdrian Hunter				return
145676099f98SAdrian Hunter			buf_ptr += cnt
145776099f98SAdrian Hunter			tot -= cnt
145876099f98SAdrian Hunter			buf_sz -= cnt
145976099f98SAdrian Hunter			ip += cnt
146076099f98SAdrian Hunter
146176099f98SAdrian Hunter	def childCount(self):
146276099f98SAdrian Hunter		if not self.query_done:
146376099f98SAdrian Hunter			self.Select()
146476099f98SAdrian Hunter			if not self.child_count:
146576099f98SAdrian Hunter				return -1
146676099f98SAdrian Hunter		return self.child_count
146776099f98SAdrian Hunter
146876099f98SAdrian Hunter	def hasChildren(self):
146976099f98SAdrian Hunter		if not self.query_done:
147076099f98SAdrian Hunter			return True
147176099f98SAdrian Hunter		return self.child_count > 0
147276099f98SAdrian Hunter
147376099f98SAdrian Hunter	def getData(self, column):
147476099f98SAdrian Hunter		return self.data[column]
147576099f98SAdrian Hunter
147676099f98SAdrian Hunter# Brance data model root item
147776099f98SAdrian Hunter
147876099f98SAdrian Hunterclass BranchRootItem():
147976099f98SAdrian Hunter
148076099f98SAdrian Hunter	def __init__(self):
148176099f98SAdrian Hunter		self.child_count = 0
148276099f98SAdrian Hunter		self.child_items = []
148376099f98SAdrian Hunter		self.level = 0
148476099f98SAdrian Hunter
148576099f98SAdrian Hunter	def getChildItem(self, row):
148676099f98SAdrian Hunter		return self.child_items[row]
148776099f98SAdrian Hunter
148876099f98SAdrian Hunter	def getParentItem(self):
148976099f98SAdrian Hunter		return None
149076099f98SAdrian Hunter
149176099f98SAdrian Hunter	def getRow(self):
149276099f98SAdrian Hunter		return 0
149376099f98SAdrian Hunter
149476099f98SAdrian Hunter	def childCount(self):
149576099f98SAdrian Hunter		return self.child_count
149676099f98SAdrian Hunter
149776099f98SAdrian Hunter	def hasChildren(self):
149876099f98SAdrian Hunter		return self.child_count > 0
149976099f98SAdrian Hunter
150076099f98SAdrian Hunter	def getData(self, column):
150176099f98SAdrian Hunter		return ""
150276099f98SAdrian Hunter
150376099f98SAdrian Hunter# Branch data preparation
150476099f98SAdrian Hunter
150576099f98SAdrian Hunterdef BranchDataPrep(query):
150676099f98SAdrian Hunter	data = []
150776099f98SAdrian Hunter	for i in xrange(0, 8):
150876099f98SAdrian Hunter		data.append(query.value(i))
150976099f98SAdrian Hunter	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
151076099f98SAdrian Hunter			" (" + dsoname(query.value(11)) + ")" + " -> " +
151176099f98SAdrian Hunter			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
151276099f98SAdrian Hunter			" (" + dsoname(query.value(15)) + ")")
151376099f98SAdrian Hunter	return data
151476099f98SAdrian Hunter
151576099f98SAdrian Hunter# Branch data model
151676099f98SAdrian Hunter
151776099f98SAdrian Hunterclass BranchModel(TreeModel):
151876099f98SAdrian Hunter
151976099f98SAdrian Hunter	progress = Signal(object)
152076099f98SAdrian Hunter
152176099f98SAdrian Hunter	def __init__(self, glb, event_id, where_clause, parent=None):
1522a448ba23SAdrian Hunter		super(BranchModel, self).__init__(glb, parent)
152376099f98SAdrian Hunter		self.event_id = event_id
152476099f98SAdrian Hunter		self.more = True
152576099f98SAdrian Hunter		self.populated = 0
152676099f98SAdrian Hunter		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
152776099f98SAdrian Hunter			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
152876099f98SAdrian Hunter			" ip, symbols.name, sym_offset, dsos.short_name,"
152976099f98SAdrian Hunter			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
153076099f98SAdrian Hunter			" FROM samples"
153176099f98SAdrian Hunter			" INNER JOIN comms ON comm_id = comms.id"
153276099f98SAdrian Hunter			" INNER JOIN threads ON thread_id = threads.id"
153376099f98SAdrian Hunter			" INNER JOIN branch_types ON branch_type = branch_types.id"
153476099f98SAdrian Hunter			" INNER JOIN symbols ON symbol_id = symbols.id"
153576099f98SAdrian Hunter			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
153676099f98SAdrian Hunter			" INNER JOIN dsos ON samples.dso_id = dsos.id"
153776099f98SAdrian Hunter			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
153876099f98SAdrian Hunter			" WHERE samples.id > $$last_id$$" + where_clause +
153976099f98SAdrian Hunter			" AND evsel_id = " + str(self.event_id) +
154076099f98SAdrian Hunter			" ORDER BY samples.id"
154176099f98SAdrian Hunter			" LIMIT " + str(glb_chunk_sz))
154276099f98SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample)
154376099f98SAdrian Hunter		self.fetcher.done.connect(self.Update)
154476099f98SAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
154576099f98SAdrian Hunter
1546a448ba23SAdrian Hunter	def GetRoot(self):
1547a448ba23SAdrian Hunter		return BranchRootItem()
1548a448ba23SAdrian Hunter
154976099f98SAdrian Hunter	def columnCount(self, parent=None):
155076099f98SAdrian Hunter		return 8
155176099f98SAdrian Hunter
155276099f98SAdrian Hunter	def columnHeader(self, column):
155376099f98SAdrian Hunter		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
155476099f98SAdrian Hunter
155576099f98SAdrian Hunter	def columnFont(self, column):
155676099f98SAdrian Hunter		if column != 7:
155776099f98SAdrian Hunter			return None
155876099f98SAdrian Hunter		return QFont("Monospace")
155976099f98SAdrian Hunter
156076099f98SAdrian Hunter	def DisplayData(self, item, index):
156176099f98SAdrian Hunter		if item.level == 1:
156276099f98SAdrian Hunter			self.FetchIfNeeded(item.row)
156376099f98SAdrian Hunter		return item.getData(index.column())
156476099f98SAdrian Hunter
156576099f98SAdrian Hunter	def AddSample(self, data):
156676099f98SAdrian Hunter		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
156776099f98SAdrian Hunter		self.root.child_items.append(child)
156876099f98SAdrian Hunter		self.populated += 1
156976099f98SAdrian Hunter
157076099f98SAdrian Hunter	def Update(self, fetched):
157176099f98SAdrian Hunter		if not fetched:
157276099f98SAdrian Hunter			self.more = False
157376099f98SAdrian Hunter			self.progress.emit(0)
157476099f98SAdrian Hunter		child_count = self.root.child_count
157576099f98SAdrian Hunter		count = self.populated - child_count
157676099f98SAdrian Hunter		if count > 0:
157776099f98SAdrian Hunter			parent = QModelIndex()
157876099f98SAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
157976099f98SAdrian Hunter			self.insertRows(child_count, count, parent)
158076099f98SAdrian Hunter			self.root.child_count += count
158176099f98SAdrian Hunter			self.endInsertRows()
158276099f98SAdrian Hunter			self.progress.emit(self.root.child_count)
158376099f98SAdrian Hunter
158476099f98SAdrian Hunter	def FetchMoreRecords(self, count):
158576099f98SAdrian Hunter		current = self.root.child_count
158676099f98SAdrian Hunter		if self.more:
158776099f98SAdrian Hunter			self.fetcher.Fetch(count)
158876099f98SAdrian Hunter		else:
158976099f98SAdrian Hunter			self.progress.emit(0)
159076099f98SAdrian Hunter		return current
159176099f98SAdrian Hunter
159276099f98SAdrian Hunter	def HasMoreRecords(self):
159376099f98SAdrian Hunter		return self.more
159476099f98SAdrian Hunter
15950bf0947aSAdrian Hunter# Report Variables
15960bf0947aSAdrian Hunter
15970bf0947aSAdrian Hunterclass ReportVars():
15980bf0947aSAdrian Hunter
1599cd358012SAdrian Hunter	def __init__(self, name = "", where_clause = "", limit = ""):
1600947cc38dSAdrian Hunter		self.name = name
16010bf0947aSAdrian Hunter		self.where_clause = where_clause
1602cd358012SAdrian Hunter		self.limit = limit
16030bf0947aSAdrian Hunter
16040bf0947aSAdrian Hunter	def UniqueId(self):
1605cd358012SAdrian Hunter		return str(self.where_clause + ";" + self.limit)
16060bf0947aSAdrian Hunter
160776099f98SAdrian Hunter# Branch window
160876099f98SAdrian Hunter
160976099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow):
161076099f98SAdrian Hunter
1611947cc38dSAdrian Hunter	def __init__(self, glb, event_id, report_vars, parent=None):
161276099f98SAdrian Hunter		super(BranchWindow, self).__init__(parent)
161376099f98SAdrian Hunter
16140bf0947aSAdrian Hunter		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
161576099f98SAdrian Hunter
16160bf0947aSAdrian Hunter		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
161776099f98SAdrian Hunter
161876099f98SAdrian Hunter		self.view = QTreeView()
161976099f98SAdrian Hunter		self.view.setUniformRowHeights(True)
162076099f98SAdrian Hunter		self.view.setModel(self.model)
162176099f98SAdrian Hunter
162276099f98SAdrian Hunter		self.ResizeColumnsToContents()
162376099f98SAdrian Hunter
162476099f98SAdrian Hunter		self.find_bar = FindBar(self, self, True)
162576099f98SAdrian Hunter
162676099f98SAdrian Hunter		self.finder = ChildDataItemFinder(self.model.root)
162776099f98SAdrian Hunter
162876099f98SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
162976099f98SAdrian Hunter
163076099f98SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
163176099f98SAdrian Hunter
163276099f98SAdrian Hunter		self.setWidget(self.vbox.Widget())
163376099f98SAdrian Hunter
1634947cc38dSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
163576099f98SAdrian Hunter
163676099f98SAdrian Hunter	def ResizeColumnToContents(self, column, n):
163776099f98SAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
163876099f98SAdrian Hunter		# so implement a crude alternative
163976099f98SAdrian Hunter		mm = "MM" if column else "MMMM"
164076099f98SAdrian Hunter		font = self.view.font()
164176099f98SAdrian Hunter		metrics = QFontMetrics(font)
164276099f98SAdrian Hunter		max = 0
164376099f98SAdrian Hunter		for row in xrange(n):
164476099f98SAdrian Hunter			val = self.model.root.child_items[row].data[column]
164576099f98SAdrian Hunter			len = metrics.width(str(val) + mm)
164676099f98SAdrian Hunter			max = len if len > max else max
164776099f98SAdrian Hunter		val = self.model.columnHeader(column)
164876099f98SAdrian Hunter		len = metrics.width(str(val) + mm)
164976099f98SAdrian Hunter		max = len if len > max else max
165076099f98SAdrian Hunter		self.view.setColumnWidth(column, max)
165176099f98SAdrian Hunter
165276099f98SAdrian Hunter	def ResizeColumnsToContents(self):
165376099f98SAdrian Hunter		n = min(self.model.root.child_count, 100)
165476099f98SAdrian Hunter		if n < 1:
165576099f98SAdrian Hunter			# No data yet, so connect a signal to notify when there is
165676099f98SAdrian Hunter			self.model.rowsInserted.connect(self.UpdateColumnWidths)
165776099f98SAdrian Hunter			return
165876099f98SAdrian Hunter		columns = self.model.columnCount()
165976099f98SAdrian Hunter		for i in xrange(columns):
166076099f98SAdrian Hunter			self.ResizeColumnToContents(i, n)
166176099f98SAdrian Hunter
166276099f98SAdrian Hunter	def UpdateColumnWidths(self, *x):
166376099f98SAdrian Hunter		# This only needs to be done once, so disconnect the signal now
166476099f98SAdrian Hunter		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
166576099f98SAdrian Hunter		self.ResizeColumnsToContents()
166676099f98SAdrian Hunter
166776099f98SAdrian Hunter	def Find(self, value, direction, pattern, context):
166876099f98SAdrian Hunter		self.view.setFocus()
166976099f98SAdrian Hunter		self.find_bar.Busy()
167076099f98SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
167176099f98SAdrian Hunter
167276099f98SAdrian Hunter	def FindDone(self, row):
167376099f98SAdrian Hunter		self.find_bar.Idle()
167476099f98SAdrian Hunter		if row >= 0:
167576099f98SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
167676099f98SAdrian Hunter		else:
167776099f98SAdrian Hunter			self.find_bar.NotFound()
167876099f98SAdrian Hunter
16791c3ca1b3SAdrian Hunter# Line edit data item
16801c3ca1b3SAdrian Hunter
16811c3ca1b3SAdrian Hunterclass LineEditDataItem(object):
16821c3ca1b3SAdrian Hunter
1683cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
16841c3ca1b3SAdrian Hunter		self.glb = glb
16851c3ca1b3SAdrian Hunter		self.label = label
16861c3ca1b3SAdrian Hunter		self.placeholder_text = placeholder_text
16871c3ca1b3SAdrian Hunter		self.parent = parent
16881c3ca1b3SAdrian Hunter		self.id = id
16891c3ca1b3SAdrian Hunter
1690cd358012SAdrian Hunter		self.value = default
16911c3ca1b3SAdrian Hunter
1692cd358012SAdrian Hunter		self.widget = QLineEdit(default)
16931c3ca1b3SAdrian Hunter		self.widget.editingFinished.connect(self.Validate)
16941c3ca1b3SAdrian Hunter		self.widget.textChanged.connect(self.Invalidate)
16951c3ca1b3SAdrian Hunter		self.red = False
16961c3ca1b3SAdrian Hunter		self.error = ""
16971c3ca1b3SAdrian Hunter		self.validated = True
16981c3ca1b3SAdrian Hunter
16991c3ca1b3SAdrian Hunter		if placeholder_text:
17001c3ca1b3SAdrian Hunter			self.widget.setPlaceholderText(placeholder_text)
17011c3ca1b3SAdrian Hunter
17021c3ca1b3SAdrian Hunter	def TurnTextRed(self):
17031c3ca1b3SAdrian Hunter		if not self.red:
17041c3ca1b3SAdrian Hunter			palette = QPalette()
17051c3ca1b3SAdrian Hunter			palette.setColor(QPalette.Text,Qt.red)
17061c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
17071c3ca1b3SAdrian Hunter			self.red = True
17081c3ca1b3SAdrian Hunter
17091c3ca1b3SAdrian Hunter	def TurnTextNormal(self):
17101c3ca1b3SAdrian Hunter		if self.red:
17111c3ca1b3SAdrian Hunter			palette = QPalette()
17121c3ca1b3SAdrian Hunter			self.widget.setPalette(palette)
17131c3ca1b3SAdrian Hunter			self.red = False
17141c3ca1b3SAdrian Hunter
17151c3ca1b3SAdrian Hunter	def InvalidValue(self, value):
17161c3ca1b3SAdrian Hunter		self.value = ""
17171c3ca1b3SAdrian Hunter		self.TurnTextRed()
17181c3ca1b3SAdrian Hunter		self.error = self.label + " invalid value '" + value + "'"
17191c3ca1b3SAdrian Hunter		self.parent.ShowMessage(self.error)
17201c3ca1b3SAdrian Hunter
17211c3ca1b3SAdrian Hunter	def Invalidate(self):
17221c3ca1b3SAdrian Hunter		self.validated = False
17231c3ca1b3SAdrian Hunter
17241c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
17251c3ca1b3SAdrian Hunter		self.value = input_string.strip()
17261c3ca1b3SAdrian Hunter
17271c3ca1b3SAdrian Hunter	def Validate(self):
17281c3ca1b3SAdrian Hunter		self.validated = True
17291c3ca1b3SAdrian Hunter		self.error = ""
17301c3ca1b3SAdrian Hunter		self.TurnTextNormal()
17311c3ca1b3SAdrian Hunter		self.parent.ClearMessage()
17321c3ca1b3SAdrian Hunter		input_string = self.widget.text()
17331c3ca1b3SAdrian Hunter		if not len(input_string.strip()):
17341c3ca1b3SAdrian Hunter			self.value = ""
17351c3ca1b3SAdrian Hunter			return
17361c3ca1b3SAdrian Hunter		self.DoValidate(input_string)
17371c3ca1b3SAdrian Hunter
17381c3ca1b3SAdrian Hunter	def IsValid(self):
17391c3ca1b3SAdrian Hunter		if not self.validated:
17401c3ca1b3SAdrian Hunter			self.Validate()
17411c3ca1b3SAdrian Hunter		if len(self.error):
17421c3ca1b3SAdrian Hunter			self.parent.ShowMessage(self.error)
17431c3ca1b3SAdrian Hunter			return False
17441c3ca1b3SAdrian Hunter		return True
17451c3ca1b3SAdrian Hunter
17461c3ca1b3SAdrian Hunter	def IsNumber(self, value):
17471c3ca1b3SAdrian Hunter		try:
17481c3ca1b3SAdrian Hunter			x = int(value)
17491c3ca1b3SAdrian Hunter		except:
17501c3ca1b3SAdrian Hunter			x = 0
17511c3ca1b3SAdrian Hunter		return str(x) == value
17521c3ca1b3SAdrian Hunter
17531c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item
17541c3ca1b3SAdrian Hunter
17551c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
17561c3ca1b3SAdrian Hunter
17571c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
17581c3ca1b3SAdrian Hunter		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
17591c3ca1b3SAdrian Hunter
17601c3ca1b3SAdrian Hunter		self.column_name = column_name
17611c3ca1b3SAdrian Hunter
17621c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
17631c3ca1b3SAdrian Hunter		singles = []
17641c3ca1b3SAdrian Hunter		ranges = []
17651c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
17661c3ca1b3SAdrian Hunter			if "-" in value:
17671c3ca1b3SAdrian Hunter				vrange = value.split("-")
17681c3ca1b3SAdrian Hunter				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
17691c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
17701c3ca1b3SAdrian Hunter				ranges.append(vrange)
17711c3ca1b3SAdrian Hunter			else:
17721c3ca1b3SAdrian Hunter				if not self.IsNumber(value):
17731c3ca1b3SAdrian Hunter					return self.InvalidValue(value)
17741c3ca1b3SAdrian Hunter				singles.append(value)
17751c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
17761c3ca1b3SAdrian Hunter		if len(singles):
17771c3ca1b3SAdrian Hunter			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
17781c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
17791c3ca1b3SAdrian Hunter
1780cd358012SAdrian Hunter# Positive integer dialog data item
1781cd358012SAdrian Hunter
1782cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem):
1783cd358012SAdrian Hunter
1784cd358012SAdrian Hunter	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1785cd358012SAdrian Hunter		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1786cd358012SAdrian Hunter
1787cd358012SAdrian Hunter	def DoValidate(self, input_string):
1788cd358012SAdrian Hunter		if not self.IsNumber(input_string.strip()):
1789cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1790cd358012SAdrian Hunter		value = int(input_string.strip())
1791cd358012SAdrian Hunter		if value <= 0:
1792cd358012SAdrian Hunter			return self.InvalidValue(input_string)
1793cd358012SAdrian Hunter		self.value = str(value)
1794cd358012SAdrian Hunter
17951c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table
17961c3ca1b3SAdrian Hunter
17971c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem):
17981c3ca1b3SAdrian Hunter
17991c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
18001c3ca1b3SAdrian Hunter		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
18011c3ca1b3SAdrian Hunter
18021c3ca1b3SAdrian Hunter		self.table_name = table_name
18031c3ca1b3SAdrian Hunter		self.match_column = match_column
18041c3ca1b3SAdrian Hunter		self.column_name1 = column_name1
18051c3ca1b3SAdrian Hunter		self.column_name2 = column_name2
18061c3ca1b3SAdrian Hunter
18071c3ca1b3SAdrian Hunter	def ValueToIds(self, value):
18081c3ca1b3SAdrian Hunter		ids = []
18091c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
18101c3ca1b3SAdrian Hunter		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
18111c3ca1b3SAdrian Hunter		ret = query.exec_(stmt)
18121c3ca1b3SAdrian Hunter		if ret:
18131c3ca1b3SAdrian Hunter			while query.next():
18141c3ca1b3SAdrian Hunter				ids.append(str(query.value(0)))
18151c3ca1b3SAdrian Hunter		return ids
18161c3ca1b3SAdrian Hunter
18171c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
18181c3ca1b3SAdrian Hunter		all_ids = []
18191c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
18201c3ca1b3SAdrian Hunter			ids = self.ValueToIds(value)
18211c3ca1b3SAdrian Hunter			if len(ids):
18221c3ca1b3SAdrian Hunter				all_ids.extend(ids)
18231c3ca1b3SAdrian Hunter			else:
18241c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
18251c3ca1b3SAdrian Hunter		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
18261c3ca1b3SAdrian Hunter		if self.column_name2:
18271c3ca1b3SAdrian Hunter			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
18281c3ca1b3SAdrian Hunter
18291c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table
18301c3ca1b3SAdrian Hunter
18311c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem):
18321c3ca1b3SAdrian Hunter
18331c3ca1b3SAdrian Hunter	def __init__(self, glb, label, placeholder_text, column_name, parent):
18341c3ca1b3SAdrian Hunter		self.column_name = column_name
18351c3ca1b3SAdrian Hunter
18361c3ca1b3SAdrian Hunter		self.last_id = 0
18371c3ca1b3SAdrian Hunter		self.first_time = 0
18381c3ca1b3SAdrian Hunter		self.last_time = 2 ** 64
18391c3ca1b3SAdrian Hunter
18401c3ca1b3SAdrian Hunter		query = QSqlQuery(glb.db)
18411c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
18421c3ca1b3SAdrian Hunter		if query.next():
18431c3ca1b3SAdrian Hunter			self.last_id = int(query.value(0))
18441c3ca1b3SAdrian Hunter			self.last_time = int(query.value(1))
18451c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
18461c3ca1b3SAdrian Hunter		if query.next():
18471c3ca1b3SAdrian Hunter			self.first_time = int(query.value(0))
18481c3ca1b3SAdrian Hunter		if placeholder_text:
18491c3ca1b3SAdrian Hunter			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
18501c3ca1b3SAdrian Hunter
18511c3ca1b3SAdrian Hunter		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
18521c3ca1b3SAdrian Hunter
18531c3ca1b3SAdrian Hunter	def IdBetween(self, query, lower_id, higher_id, order):
18541c3ca1b3SAdrian Hunter		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
18551c3ca1b3SAdrian Hunter		if query.next():
18561c3ca1b3SAdrian Hunter			return True, int(query.value(0))
18571c3ca1b3SAdrian Hunter		else:
18581c3ca1b3SAdrian Hunter			return False, 0
18591c3ca1b3SAdrian Hunter
18601c3ca1b3SAdrian Hunter	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
18611c3ca1b3SAdrian Hunter		query = QSqlQuery(self.glb.db)
18621c3ca1b3SAdrian Hunter		while True:
18631c3ca1b3SAdrian Hunter			next_id = int((lower_id + higher_id) / 2)
18641c3ca1b3SAdrian Hunter			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
18651c3ca1b3SAdrian Hunter			if not query.next():
18661c3ca1b3SAdrian Hunter				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
18671c3ca1b3SAdrian Hunter				if not ok:
18681c3ca1b3SAdrian Hunter					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
18691c3ca1b3SAdrian Hunter					if not ok:
18701c3ca1b3SAdrian Hunter						return str(higher_id)
18711c3ca1b3SAdrian Hunter				next_id = dbid
18721c3ca1b3SAdrian Hunter				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
18731c3ca1b3SAdrian Hunter			next_time = int(query.value(0))
18741c3ca1b3SAdrian Hunter			if get_floor:
18751c3ca1b3SAdrian Hunter				if target_time > next_time:
18761c3ca1b3SAdrian Hunter					lower_id = next_id
18771c3ca1b3SAdrian Hunter				else:
18781c3ca1b3SAdrian Hunter					higher_id = next_id
18791c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
18801c3ca1b3SAdrian Hunter					return str(higher_id)
18811c3ca1b3SAdrian Hunter			else:
18821c3ca1b3SAdrian Hunter				if target_time >= next_time:
18831c3ca1b3SAdrian Hunter					lower_id = next_id
18841c3ca1b3SAdrian Hunter				else:
18851c3ca1b3SAdrian Hunter					higher_id = next_id
18861c3ca1b3SAdrian Hunter				if higher_id <= lower_id + 1:
18871c3ca1b3SAdrian Hunter					return str(lower_id)
18881c3ca1b3SAdrian Hunter
18891c3ca1b3SAdrian Hunter	def ConvertRelativeTime(self, val):
18901c3ca1b3SAdrian Hunter		mult = 1
18911c3ca1b3SAdrian Hunter		suffix = val[-2:]
18921c3ca1b3SAdrian Hunter		if suffix == "ms":
18931c3ca1b3SAdrian Hunter			mult = 1000000
18941c3ca1b3SAdrian Hunter		elif suffix == "us":
18951c3ca1b3SAdrian Hunter			mult = 1000
18961c3ca1b3SAdrian Hunter		elif suffix == "ns":
18971c3ca1b3SAdrian Hunter			mult = 1
18981c3ca1b3SAdrian Hunter		else:
18991c3ca1b3SAdrian Hunter			return val
19001c3ca1b3SAdrian Hunter		val = val[:-2].strip()
19011c3ca1b3SAdrian Hunter		if not self.IsNumber(val):
19021c3ca1b3SAdrian Hunter			return val
19031c3ca1b3SAdrian Hunter		val = int(val) * mult
19041c3ca1b3SAdrian Hunter		if val >= 0:
19051c3ca1b3SAdrian Hunter			val += self.first_time
19061c3ca1b3SAdrian Hunter		else:
19071c3ca1b3SAdrian Hunter			val += self.last_time
19081c3ca1b3SAdrian Hunter		return str(val)
19091c3ca1b3SAdrian Hunter
19101c3ca1b3SAdrian Hunter	def ConvertTimeRange(self, vrange):
19111c3ca1b3SAdrian Hunter		if vrange[0] == "":
19121c3ca1b3SAdrian Hunter			vrange[0] = str(self.first_time)
19131c3ca1b3SAdrian Hunter		if vrange[1] == "":
19141c3ca1b3SAdrian Hunter			vrange[1] = str(self.last_time)
19151c3ca1b3SAdrian Hunter		vrange[0] = self.ConvertRelativeTime(vrange[0])
19161c3ca1b3SAdrian Hunter		vrange[1] = self.ConvertRelativeTime(vrange[1])
19171c3ca1b3SAdrian Hunter		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
19181c3ca1b3SAdrian Hunter			return False
19191c3ca1b3SAdrian Hunter		beg_range = max(int(vrange[0]), self.first_time)
19201c3ca1b3SAdrian Hunter		end_range = min(int(vrange[1]), self.last_time)
19211c3ca1b3SAdrian Hunter		if beg_range > self.last_time or end_range < self.first_time:
19221c3ca1b3SAdrian Hunter			return False
19231c3ca1b3SAdrian Hunter		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
19241c3ca1b3SAdrian Hunter		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
19251c3ca1b3SAdrian Hunter		return True
19261c3ca1b3SAdrian Hunter
19271c3ca1b3SAdrian Hunter	def AddTimeRange(self, value, ranges):
19281c3ca1b3SAdrian Hunter		n = value.count("-")
19291c3ca1b3SAdrian Hunter		if n == 1:
19301c3ca1b3SAdrian Hunter			pass
19311c3ca1b3SAdrian Hunter		elif n == 2:
19321c3ca1b3SAdrian Hunter			if value.split("-")[1].strip() == "":
19331c3ca1b3SAdrian Hunter				n = 1
19341c3ca1b3SAdrian Hunter		elif n == 3:
19351c3ca1b3SAdrian Hunter			n = 2
19361c3ca1b3SAdrian Hunter		else:
19371c3ca1b3SAdrian Hunter			return False
19381c3ca1b3SAdrian Hunter		pos = findnth(value, "-", n)
19391c3ca1b3SAdrian Hunter		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
19401c3ca1b3SAdrian Hunter		if self.ConvertTimeRange(vrange):
19411c3ca1b3SAdrian Hunter			ranges.append(vrange)
19421c3ca1b3SAdrian Hunter			return True
19431c3ca1b3SAdrian Hunter		return False
19441c3ca1b3SAdrian Hunter
19451c3ca1b3SAdrian Hunter	def DoValidate(self, input_string):
19461c3ca1b3SAdrian Hunter		ranges = []
19471c3ca1b3SAdrian Hunter		for value in [x.strip() for x in input_string.split(",")]:
19481c3ca1b3SAdrian Hunter			if not self.AddTimeRange(value, ranges):
19491c3ca1b3SAdrian Hunter				return self.InvalidValue(value)
19501c3ca1b3SAdrian Hunter		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
19511c3ca1b3SAdrian Hunter		self.value = " OR ".join(ranges)
19521c3ca1b3SAdrian Hunter
19530924cd68SAdrian Hunter# Report Dialog Base
1954210cf1f9SAdrian Hunter
19550924cd68SAdrian Hunterclass ReportDialogBase(QDialog):
1956210cf1f9SAdrian Hunter
19570924cd68SAdrian Hunter	def __init__(self, glb, title, items, partial, parent=None):
19580924cd68SAdrian Hunter		super(ReportDialogBase, self).__init__(parent)
1959210cf1f9SAdrian Hunter
1960210cf1f9SAdrian Hunter		self.glb = glb
1961210cf1f9SAdrian Hunter
19620bf0947aSAdrian Hunter		self.report_vars = ReportVars()
1963210cf1f9SAdrian Hunter
19640924cd68SAdrian Hunter		self.setWindowTitle(title)
1965210cf1f9SAdrian Hunter		self.setMinimumWidth(600)
1966210cf1f9SAdrian Hunter
19671c3ca1b3SAdrian Hunter		self.data_items = [x(glb, self) for x in items]
1968210cf1f9SAdrian Hunter
19690924cd68SAdrian Hunter		self.partial = partial
19700924cd68SAdrian Hunter
1971210cf1f9SAdrian Hunter		self.grid = QGridLayout()
1972210cf1f9SAdrian Hunter
1973210cf1f9SAdrian Hunter		for row in xrange(len(self.data_items)):
1974210cf1f9SAdrian Hunter			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1975210cf1f9SAdrian Hunter			self.grid.addWidget(self.data_items[row].widget, row, 1)
1976210cf1f9SAdrian Hunter
1977210cf1f9SAdrian Hunter		self.status = QLabel()
1978210cf1f9SAdrian Hunter
1979210cf1f9SAdrian Hunter		self.ok_button = QPushButton("Ok", self)
1980210cf1f9SAdrian Hunter		self.ok_button.setDefault(True)
1981210cf1f9SAdrian Hunter		self.ok_button.released.connect(self.Ok)
1982210cf1f9SAdrian Hunter		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1983210cf1f9SAdrian Hunter
1984210cf1f9SAdrian Hunter		self.cancel_button = QPushButton("Cancel", self)
1985210cf1f9SAdrian Hunter		self.cancel_button.released.connect(self.reject)
1986210cf1f9SAdrian Hunter		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1987210cf1f9SAdrian Hunter
1988210cf1f9SAdrian Hunter		self.hbox = QHBoxLayout()
1989210cf1f9SAdrian Hunter		#self.hbox.addStretch()
1990210cf1f9SAdrian Hunter		self.hbox.addWidget(self.status)
1991210cf1f9SAdrian Hunter		self.hbox.addWidget(self.ok_button)
1992210cf1f9SAdrian Hunter		self.hbox.addWidget(self.cancel_button)
1993210cf1f9SAdrian Hunter
1994210cf1f9SAdrian Hunter		self.vbox = QVBoxLayout()
1995210cf1f9SAdrian Hunter		self.vbox.addLayout(self.grid)
1996210cf1f9SAdrian Hunter		self.vbox.addLayout(self.hbox)
1997210cf1f9SAdrian Hunter
1998210cf1f9SAdrian Hunter		self.setLayout(self.vbox);
1999210cf1f9SAdrian Hunter
2000210cf1f9SAdrian Hunter	def Ok(self):
20010bf0947aSAdrian Hunter		vars = self.report_vars
20021c3ca1b3SAdrian Hunter		for d in self.data_items:
20031c3ca1b3SAdrian Hunter			if d.id == "REPORTNAME":
20041c3ca1b3SAdrian Hunter				vars.name = d.value
2005947cc38dSAdrian Hunter		if not vars.name:
2006210cf1f9SAdrian Hunter			self.ShowMessage("Report name is required")
2007210cf1f9SAdrian Hunter			return
2008210cf1f9SAdrian Hunter		for d in self.data_items:
2009210cf1f9SAdrian Hunter			if not d.IsValid():
2010210cf1f9SAdrian Hunter				return
2011210cf1f9SAdrian Hunter		for d in self.data_items[1:]:
2012cd358012SAdrian Hunter			if d.id == "LIMIT":
2013cd358012SAdrian Hunter				vars.limit = d.value
2014cd358012SAdrian Hunter			elif len(d.value):
20150bf0947aSAdrian Hunter				if len(vars.where_clause):
20160bf0947aSAdrian Hunter					vars.where_clause += " AND "
20170bf0947aSAdrian Hunter				vars.where_clause += d.value
20180bf0947aSAdrian Hunter		if len(vars.where_clause):
20190924cd68SAdrian Hunter			if self.partial:
20200bf0947aSAdrian Hunter				vars.where_clause = " AND ( " + vars.where_clause + " ) "
2021210cf1f9SAdrian Hunter			else:
20220bf0947aSAdrian Hunter				vars.where_clause = " WHERE " + vars.where_clause + " "
2023210cf1f9SAdrian Hunter		self.accept()
2024210cf1f9SAdrian Hunter
2025210cf1f9SAdrian Hunter	def ShowMessage(self, msg):
2026210cf1f9SAdrian Hunter		self.status.setText("<font color=#FF0000>" + msg)
2027210cf1f9SAdrian Hunter
2028210cf1f9SAdrian Hunter	def ClearMessage(self):
2029210cf1f9SAdrian Hunter		self.status.setText("")
2030210cf1f9SAdrian Hunter
20310924cd68SAdrian Hunter# Selected branch report creation dialog
20320924cd68SAdrian Hunter
20330924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase):
20340924cd68SAdrian Hunter
20350924cd68SAdrian Hunter	def __init__(self, glb, parent=None):
20360924cd68SAdrian Hunter		title = "Selected Branches"
20371c3ca1b3SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
20381c3ca1b3SAdrian Hunter			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
20391c3ca1b3SAdrian Hunter			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
20401c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
20411c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
20421c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
20431c3ca1b3SAdrian 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),
20441c3ca1b3SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
20451c3ca1b3SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
20460924cd68SAdrian Hunter		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
20470924cd68SAdrian Hunter
204876099f98SAdrian Hunter# Event list
204976099f98SAdrian Hunter
205076099f98SAdrian Hunterdef GetEventList(db):
205176099f98SAdrian Hunter	events = []
205276099f98SAdrian Hunter	query = QSqlQuery(db)
205376099f98SAdrian Hunter	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
205476099f98SAdrian Hunter	while query.next():
205576099f98SAdrian Hunter		events.append(query.value(0))
205676099f98SAdrian Hunter	return events
205776099f98SAdrian Hunter
2058655cb952SAdrian Hunter# Is a table selectable
2059655cb952SAdrian Hunter
2060*ae8b887cSAdrian Hunterdef IsSelectable(db, table, sql = ""):
2061655cb952SAdrian Hunter	query = QSqlQuery(db)
2062655cb952SAdrian Hunter	try:
2063*ae8b887cSAdrian Hunter		QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1")
2064655cb952SAdrian Hunter	except:
2065655cb952SAdrian Hunter		return False
2066655cb952SAdrian Hunter	return True
2067655cb952SAdrian Hunter
20688392b74bSAdrian Hunter# SQL data preparation
20698392b74bSAdrian Hunter
20708392b74bSAdrian Hunterdef SQLTableDataPrep(query, count):
20718392b74bSAdrian Hunter	data = []
20728392b74bSAdrian Hunter	for i in xrange(count):
20738392b74bSAdrian Hunter		data.append(query.value(i))
20748392b74bSAdrian Hunter	return data
20758392b74bSAdrian Hunter
20768392b74bSAdrian Hunter# SQL table data model item
20778392b74bSAdrian Hunter
20788392b74bSAdrian Hunterclass SQLTableItem():
20798392b74bSAdrian Hunter
20808392b74bSAdrian Hunter	def __init__(self, row, data):
20818392b74bSAdrian Hunter		self.row = row
20828392b74bSAdrian Hunter		self.data = data
20838392b74bSAdrian Hunter
20848392b74bSAdrian Hunter	def getData(self, column):
20858392b74bSAdrian Hunter		return self.data[column]
20868392b74bSAdrian Hunter
20878392b74bSAdrian Hunter# SQL table data model
20888392b74bSAdrian Hunter
20898392b74bSAdrian Hunterclass SQLTableModel(TableModel):
20908392b74bSAdrian Hunter
20918392b74bSAdrian Hunter	progress = Signal(object)
20928392b74bSAdrian Hunter
20938c90fef9SAdrian Hunter	def __init__(self, glb, sql, column_headers, parent=None):
20948392b74bSAdrian Hunter		super(SQLTableModel, self).__init__(parent)
20958392b74bSAdrian Hunter		self.glb = glb
20968392b74bSAdrian Hunter		self.more = True
20978392b74bSAdrian Hunter		self.populated = 0
20988c90fef9SAdrian Hunter		self.column_headers = column_headers
20998c90fef9SAdrian Hunter		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample)
21008392b74bSAdrian Hunter		self.fetcher.done.connect(self.Update)
21018392b74bSAdrian Hunter		self.fetcher.Fetch(glb_chunk_sz)
21028392b74bSAdrian Hunter
21038392b74bSAdrian Hunter	def DisplayData(self, item, index):
21048392b74bSAdrian Hunter		self.FetchIfNeeded(item.row)
21058392b74bSAdrian Hunter		return item.getData(index.column())
21068392b74bSAdrian Hunter
21078392b74bSAdrian Hunter	def AddSample(self, data):
21088392b74bSAdrian Hunter		child = SQLTableItem(self.populated, data)
21098392b74bSAdrian Hunter		self.child_items.append(child)
21108392b74bSAdrian Hunter		self.populated += 1
21118392b74bSAdrian Hunter
21128392b74bSAdrian Hunter	def Update(self, fetched):
21138392b74bSAdrian Hunter		if not fetched:
21148392b74bSAdrian Hunter			self.more = False
21158392b74bSAdrian Hunter			self.progress.emit(0)
21168392b74bSAdrian Hunter		child_count = self.child_count
21178392b74bSAdrian Hunter		count = self.populated - child_count
21188392b74bSAdrian Hunter		if count > 0:
21198392b74bSAdrian Hunter			parent = QModelIndex()
21208392b74bSAdrian Hunter			self.beginInsertRows(parent, child_count, child_count + count - 1)
21218392b74bSAdrian Hunter			self.insertRows(child_count, count, parent)
21228392b74bSAdrian Hunter			self.child_count += count
21238392b74bSAdrian Hunter			self.endInsertRows()
21248392b74bSAdrian Hunter			self.progress.emit(self.child_count)
21258392b74bSAdrian Hunter
21268392b74bSAdrian Hunter	def FetchMoreRecords(self, count):
21278392b74bSAdrian Hunter		current = self.child_count
21288392b74bSAdrian Hunter		if self.more:
21298392b74bSAdrian Hunter			self.fetcher.Fetch(count)
21308392b74bSAdrian Hunter		else:
21318392b74bSAdrian Hunter			self.progress.emit(0)
21328392b74bSAdrian Hunter		return current
21338392b74bSAdrian Hunter
21348392b74bSAdrian Hunter	def HasMoreRecords(self):
21358392b74bSAdrian Hunter		return self.more
21368392b74bSAdrian Hunter
21378c90fef9SAdrian Hunter	def columnCount(self, parent=None):
21388c90fef9SAdrian Hunter		return len(self.column_headers)
21398c90fef9SAdrian Hunter
21408c90fef9SAdrian Hunter	def columnHeader(self, column):
21418c90fef9SAdrian Hunter		return self.column_headers[column]
21428c90fef9SAdrian Hunter
21438392b74bSAdrian Hunter# SQL automatic table data model
21448392b74bSAdrian Hunter
21458392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel):
21468392b74bSAdrian Hunter
21478392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
21488392b74bSAdrian Hunter		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
21498392b74bSAdrian Hunter		if table_name == "comm_threads_view":
21508392b74bSAdrian Hunter			# For now, comm_threads_view has no id column
21518392b74bSAdrian Hunter			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
21528c90fef9SAdrian Hunter		column_headers = []
21538392b74bSAdrian Hunter		query = QSqlQuery(glb.db)
21548392b74bSAdrian Hunter		if glb.dbref.is_sqlite3:
21558392b74bSAdrian Hunter			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
21568392b74bSAdrian Hunter			while query.next():
21578c90fef9SAdrian Hunter				column_headers.append(query.value(1))
21588392b74bSAdrian Hunter			if table_name == "sqlite_master":
21598392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21608392b74bSAdrian Hunter		else:
21618392b74bSAdrian Hunter			if table_name[:19] == "information_schema.":
21628392b74bSAdrian Hunter				sql = "SELECT * FROM " + table_name
21638392b74bSAdrian Hunter				select_table_name = table_name[19:]
21648392b74bSAdrian Hunter				schema = "information_schema"
21658392b74bSAdrian Hunter			else:
21668392b74bSAdrian Hunter				select_table_name = table_name
21678392b74bSAdrian Hunter				schema = "public"
21688392b74bSAdrian Hunter			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
21698392b74bSAdrian Hunter			while query.next():
21708c90fef9SAdrian Hunter				column_headers.append(query.value(0))
21718c90fef9SAdrian Hunter		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
21728392b74bSAdrian Hunter
21738392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents
21748392b74bSAdrian Hunter
21758392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject):
21768392b74bSAdrian Hunter
21778392b74bSAdrian Hunter	def __init__(self, parent=None):
21788392b74bSAdrian Hunter		super(ResizeColumnsToContentsBase, self).__init__(parent)
21798392b74bSAdrian Hunter
21808392b74bSAdrian Hunter	def ResizeColumnToContents(self, column, n):
21818392b74bSAdrian Hunter		# Using the view's resizeColumnToContents() here is extrememly slow
21828392b74bSAdrian Hunter		# so implement a crude alternative
21838392b74bSAdrian Hunter		font = self.view.font()
21848392b74bSAdrian Hunter		metrics = QFontMetrics(font)
21858392b74bSAdrian Hunter		max = 0
21868392b74bSAdrian Hunter		for row in xrange(n):
21878392b74bSAdrian Hunter			val = self.data_model.child_items[row].data[column]
21888392b74bSAdrian Hunter			len = metrics.width(str(val) + "MM")
21898392b74bSAdrian Hunter			max = len if len > max else max
21908392b74bSAdrian Hunter		val = self.data_model.columnHeader(column)
21918392b74bSAdrian Hunter		len = metrics.width(str(val) + "MM")
21928392b74bSAdrian Hunter		max = len if len > max else max
21938392b74bSAdrian Hunter		self.view.setColumnWidth(column, max)
21948392b74bSAdrian Hunter
21958392b74bSAdrian Hunter	def ResizeColumnsToContents(self):
21968392b74bSAdrian Hunter		n = min(self.data_model.child_count, 100)
21978392b74bSAdrian Hunter		if n < 1:
21988392b74bSAdrian Hunter			# No data yet, so connect a signal to notify when there is
21998392b74bSAdrian Hunter			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
22008392b74bSAdrian Hunter			return
22018392b74bSAdrian Hunter		columns = self.data_model.columnCount()
22028392b74bSAdrian Hunter		for i in xrange(columns):
22038392b74bSAdrian Hunter			self.ResizeColumnToContents(i, n)
22048392b74bSAdrian Hunter
22058392b74bSAdrian Hunter	def UpdateColumnWidths(self, *x):
22068392b74bSAdrian Hunter		# This only needs to be done once, so disconnect the signal now
22078392b74bSAdrian Hunter		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
22088392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22098392b74bSAdrian Hunter
22108392b74bSAdrian Hunter# Table window
22118392b74bSAdrian Hunter
22128392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
22138392b74bSAdrian Hunter
22148392b74bSAdrian Hunter	def __init__(self, glb, table_name, parent=None):
22158392b74bSAdrian Hunter		super(TableWindow, self).__init__(parent)
22168392b74bSAdrian Hunter
22178392b74bSAdrian Hunter		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
22188392b74bSAdrian Hunter
22198392b74bSAdrian Hunter		self.model = QSortFilterProxyModel()
22208392b74bSAdrian Hunter		self.model.setSourceModel(self.data_model)
22218392b74bSAdrian Hunter
22228392b74bSAdrian Hunter		self.view = QTableView()
22238392b74bSAdrian Hunter		self.view.setModel(self.model)
22248392b74bSAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
22258392b74bSAdrian Hunter		self.view.verticalHeader().setVisible(False)
22268392b74bSAdrian Hunter		self.view.sortByColumn(-1, Qt.AscendingOrder)
22278392b74bSAdrian Hunter		self.view.setSortingEnabled(True)
22288392b74bSAdrian Hunter
22298392b74bSAdrian Hunter		self.ResizeColumnsToContents()
22308392b74bSAdrian Hunter
22318392b74bSAdrian Hunter		self.find_bar = FindBar(self, self, True)
22328392b74bSAdrian Hunter
22338392b74bSAdrian Hunter		self.finder = ChildDataItemFinder(self.data_model)
22348392b74bSAdrian Hunter
22358392b74bSAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
22368392b74bSAdrian Hunter
22378392b74bSAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
22388392b74bSAdrian Hunter
22398392b74bSAdrian Hunter		self.setWidget(self.vbox.Widget())
22408392b74bSAdrian Hunter
22418392b74bSAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
22428392b74bSAdrian Hunter
22438392b74bSAdrian Hunter	def Find(self, value, direction, pattern, context):
22448392b74bSAdrian Hunter		self.view.setFocus()
22458392b74bSAdrian Hunter		self.find_bar.Busy()
22468392b74bSAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
22478392b74bSAdrian Hunter
22488392b74bSAdrian Hunter	def FindDone(self, row):
22498392b74bSAdrian Hunter		self.find_bar.Idle()
22508392b74bSAdrian Hunter		if row >= 0:
225135fa1ceeSAdrian Hunter			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
22528392b74bSAdrian Hunter		else:
22538392b74bSAdrian Hunter			self.find_bar.NotFound()
22548392b74bSAdrian Hunter
22558392b74bSAdrian Hunter# Table list
22568392b74bSAdrian Hunter
22578392b74bSAdrian Hunterdef GetTableList(glb):
22588392b74bSAdrian Hunter	tables = []
22598392b74bSAdrian Hunter	query = QSqlQuery(glb.db)
22608392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22618392b74bSAdrian Hunter		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
22628392b74bSAdrian Hunter	else:
22638392b74bSAdrian 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")
22648392b74bSAdrian Hunter	while query.next():
22658392b74bSAdrian Hunter		tables.append(query.value(0))
22668392b74bSAdrian Hunter	if glb.dbref.is_sqlite3:
22678392b74bSAdrian Hunter		tables.append("sqlite_master")
22688392b74bSAdrian Hunter	else:
22698392b74bSAdrian Hunter		tables.append("information_schema.tables")
22708392b74bSAdrian Hunter		tables.append("information_schema.views")
22718392b74bSAdrian Hunter		tables.append("information_schema.columns")
22728392b74bSAdrian Hunter	return tables
22738392b74bSAdrian Hunter
2274cd358012SAdrian Hunter# Top Calls data model
2275cd358012SAdrian Hunter
2276cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel):
2277cd358012SAdrian Hunter
2278cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2279cd358012SAdrian Hunter		text = ""
2280cd358012SAdrian Hunter		if not glb.dbref.is_sqlite3:
2281cd358012SAdrian Hunter			text = "::text"
2282cd358012SAdrian Hunter		limit = ""
2283cd358012SAdrian Hunter		if len(report_vars.limit):
2284cd358012SAdrian Hunter			limit = " LIMIT " + report_vars.limit
2285cd358012SAdrian Hunter		sql = ("SELECT comm, pid, tid, name,"
2286cd358012SAdrian Hunter			" CASE"
2287cd358012SAdrian Hunter			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2288cd358012SAdrian Hunter			" ELSE short_name"
2289cd358012SAdrian Hunter			" END AS dso,"
2290cd358012SAdrian Hunter			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2291cd358012SAdrian Hunter			" CASE"
2292cd358012SAdrian Hunter			" WHEN (calls.flags = 1) THEN 'no call'" + text +
2293cd358012SAdrian Hunter			" WHEN (calls.flags = 2) THEN 'no return'" + text +
2294cd358012SAdrian Hunter			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2295cd358012SAdrian Hunter			" ELSE ''" + text +
2296cd358012SAdrian Hunter			" END AS flags"
2297cd358012SAdrian Hunter			" FROM calls"
2298cd358012SAdrian Hunter			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2299cd358012SAdrian Hunter			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2300cd358012SAdrian Hunter			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
2301cd358012SAdrian Hunter			" INNER JOIN comms ON calls.comm_id = comms.id"
2302cd358012SAdrian Hunter			" INNER JOIN threads ON calls.thread_id = threads.id" +
2303cd358012SAdrian Hunter			report_vars.where_clause +
2304cd358012SAdrian Hunter			" ORDER BY elapsed_time DESC" +
2305cd358012SAdrian Hunter			limit
2306cd358012SAdrian Hunter			)
2307cd358012SAdrian Hunter		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2308cd358012SAdrian Hunter		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2309cd358012SAdrian Hunter		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2310cd358012SAdrian Hunter
2311cd358012SAdrian Hunter	def columnAlignment(self, column):
2312cd358012SAdrian Hunter		return self.alignment[column]
2313cd358012SAdrian Hunter
2314cd358012SAdrian Hunter# Top Calls report creation dialog
2315cd358012SAdrian Hunter
2316cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase):
2317cd358012SAdrian Hunter
2318cd358012SAdrian Hunter	def __init__(self, glb, parent=None):
2319cd358012SAdrian Hunter		title = "Top Calls by Elapsed Time"
2320cd358012SAdrian Hunter		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2321cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2322cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2323cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2324cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2325cd358012SAdrian Hunter			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2326cd358012SAdrian Hunter			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2327cd358012SAdrian Hunter			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2328cd358012SAdrian Hunter		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2329cd358012SAdrian Hunter
2330cd358012SAdrian Hunter# Top Calls window
2331cd358012SAdrian Hunter
2332cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2333cd358012SAdrian Hunter
2334cd358012SAdrian Hunter	def __init__(self, glb, report_vars, parent=None):
2335cd358012SAdrian Hunter		super(TopCallsWindow, self).__init__(parent)
2336cd358012SAdrian Hunter
2337cd358012SAdrian Hunter		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2338cd358012SAdrian Hunter		self.model = self.data_model
2339cd358012SAdrian Hunter
2340cd358012SAdrian Hunter		self.view = QTableView()
2341cd358012SAdrian Hunter		self.view.setModel(self.model)
2342cd358012SAdrian Hunter		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2343cd358012SAdrian Hunter		self.view.verticalHeader().setVisible(False)
2344cd358012SAdrian Hunter
2345cd358012SAdrian Hunter		self.ResizeColumnsToContents()
2346cd358012SAdrian Hunter
2347cd358012SAdrian Hunter		self.find_bar = FindBar(self, self, True)
2348cd358012SAdrian Hunter
2349cd358012SAdrian Hunter		self.finder = ChildDataItemFinder(self.model)
2350cd358012SAdrian Hunter
2351cd358012SAdrian Hunter		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2352cd358012SAdrian Hunter
2353cd358012SAdrian Hunter		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2354cd358012SAdrian Hunter
2355cd358012SAdrian Hunter		self.setWidget(self.vbox.Widget())
2356cd358012SAdrian Hunter
2357cd358012SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2358cd358012SAdrian Hunter
2359cd358012SAdrian Hunter	def Find(self, value, direction, pattern, context):
2360cd358012SAdrian Hunter		self.view.setFocus()
2361cd358012SAdrian Hunter		self.find_bar.Busy()
2362cd358012SAdrian Hunter		self.finder.Find(value, direction, pattern, context, self.FindDone)
2363cd358012SAdrian Hunter
2364cd358012SAdrian Hunter	def FindDone(self, row):
2365cd358012SAdrian Hunter		self.find_bar.Idle()
2366cd358012SAdrian Hunter		if row >= 0:
2367cd358012SAdrian Hunter			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2368cd358012SAdrian Hunter		else:
2369cd358012SAdrian Hunter			self.find_bar.NotFound()
2370cd358012SAdrian Hunter
23711beb5c7bSAdrian Hunter# Action Definition
23721beb5c7bSAdrian Hunter
23731beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None):
23741beb5c7bSAdrian Hunter	action = QAction(label, parent)
23751beb5c7bSAdrian Hunter	if shortcut != None:
23761beb5c7bSAdrian Hunter		action.setShortcuts(shortcut)
23771beb5c7bSAdrian Hunter	action.setStatusTip(tip)
23781beb5c7bSAdrian Hunter	action.triggered.connect(callback)
23791beb5c7bSAdrian Hunter	return action
23801beb5c7bSAdrian Hunter
23811beb5c7bSAdrian Hunter# Typical application actions
23821beb5c7bSAdrian Hunter
23831beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None):
23841beb5c7bSAdrian Hunter	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
23851beb5c7bSAdrian Hunter
23861beb5c7bSAdrian Hunter# Typical MDI actions
23871beb5c7bSAdrian Hunter
23881beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area):
23891beb5c7bSAdrian Hunter	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
23901beb5c7bSAdrian Hunter
23911beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area):
23921beb5c7bSAdrian Hunter	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
23931beb5c7bSAdrian Hunter
23941beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area):
23951beb5c7bSAdrian Hunter	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
23961beb5c7bSAdrian Hunter
23971beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area):
23981beb5c7bSAdrian Hunter	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
23991beb5c7bSAdrian Hunter
24001beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area):
24011beb5c7bSAdrian Hunter	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
24021beb5c7bSAdrian Hunter
24031beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area):
24041beb5c7bSAdrian Hunter	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
24051beb5c7bSAdrian Hunter
24061beb5c7bSAdrian Hunter# Typical MDI window menu
24071beb5c7bSAdrian Hunter
24081beb5c7bSAdrian Hunterclass WindowMenu():
24091beb5c7bSAdrian Hunter
24101beb5c7bSAdrian Hunter	def __init__(self, mdi_area, menu):
24111beb5c7bSAdrian Hunter		self.mdi_area = mdi_area
24121beb5c7bSAdrian Hunter		self.window_menu = menu.addMenu("&Windows")
24131beb5c7bSAdrian Hunter		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
24141beb5c7bSAdrian Hunter		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
24151beb5c7bSAdrian Hunter		self.tile_windows = CreateTileWindowsAction(mdi_area)
24161beb5c7bSAdrian Hunter		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
24171beb5c7bSAdrian Hunter		self.next_window = CreateNextWindowAction(mdi_area)
24181beb5c7bSAdrian Hunter		self.previous_window = CreatePreviousWindowAction(mdi_area)
24191beb5c7bSAdrian Hunter		self.window_menu.aboutToShow.connect(self.Update)
24201beb5c7bSAdrian Hunter
24211beb5c7bSAdrian Hunter	def Update(self):
24221beb5c7bSAdrian Hunter		self.window_menu.clear()
24231beb5c7bSAdrian Hunter		sub_window_count = len(self.mdi_area.subWindowList())
24241beb5c7bSAdrian Hunter		have_sub_windows = sub_window_count != 0
24251beb5c7bSAdrian Hunter		self.close_active_window.setEnabled(have_sub_windows)
24261beb5c7bSAdrian Hunter		self.close_all_windows.setEnabled(have_sub_windows)
24271beb5c7bSAdrian Hunter		self.tile_windows.setEnabled(have_sub_windows)
24281beb5c7bSAdrian Hunter		self.cascade_windows.setEnabled(have_sub_windows)
24291beb5c7bSAdrian Hunter		self.next_window.setEnabled(have_sub_windows)
24301beb5c7bSAdrian Hunter		self.previous_window.setEnabled(have_sub_windows)
24311beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_active_window)
24321beb5c7bSAdrian Hunter		self.window_menu.addAction(self.close_all_windows)
24331beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24341beb5c7bSAdrian Hunter		self.window_menu.addAction(self.tile_windows)
24351beb5c7bSAdrian Hunter		self.window_menu.addAction(self.cascade_windows)
24361beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24371beb5c7bSAdrian Hunter		self.window_menu.addAction(self.next_window)
24381beb5c7bSAdrian Hunter		self.window_menu.addAction(self.previous_window)
24391beb5c7bSAdrian Hunter		if sub_window_count == 0:
24401beb5c7bSAdrian Hunter			return
24411beb5c7bSAdrian Hunter		self.window_menu.addSeparator()
24421beb5c7bSAdrian Hunter		nr = 1
24431beb5c7bSAdrian Hunter		for sub_window in self.mdi_area.subWindowList():
24441beb5c7bSAdrian Hunter			label = str(nr) + " " + sub_window.name
24451beb5c7bSAdrian Hunter			if nr < 10:
24461beb5c7bSAdrian Hunter				label = "&" + label
24471beb5c7bSAdrian Hunter			action = self.window_menu.addAction(label)
24481beb5c7bSAdrian Hunter			action.setCheckable(True)
24491beb5c7bSAdrian Hunter			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
24501beb5c7bSAdrian Hunter			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
24511beb5c7bSAdrian Hunter			self.window_menu.addAction(action)
24521beb5c7bSAdrian Hunter			nr += 1
24531beb5c7bSAdrian Hunter
24541beb5c7bSAdrian Hunter	def setActiveSubWindow(self, nr):
24551beb5c7bSAdrian Hunter		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
24561beb5c7bSAdrian Hunter
245765b24292SAdrian Hunter# Help text
245865b24292SAdrian Hunter
245965b24292SAdrian Hunterglb_help_text = """
246065b24292SAdrian Hunter<h1>Contents</h1>
246165b24292SAdrian Hunter<style>
246265b24292SAdrian Hunterp.c1 {
246365b24292SAdrian Hunter    text-indent: 40px;
246465b24292SAdrian Hunter}
246565b24292SAdrian Hunterp.c2 {
246665b24292SAdrian Hunter    text-indent: 80px;
246765b24292SAdrian Hunter}
246865b24292SAdrian Hunter}
246965b24292SAdrian Hunter</style>
247065b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p>
247165b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2472*ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
2473*ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p>
2474*ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
2475*ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
247665b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p>
247765b24292SAdrian Hunter<h1 id=reports>1. Reports</h1>
247865b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
247965b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive
248065b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column
248165b24292SAdrian Hunterwidths to suit will display something like:
248265b24292SAdrian Hunter<pre>
248365b24292SAdrian Hunter                                         Call Graph: pt_example
248465b24292SAdrian HunterCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
248565b24292SAdrian Hunterv- ls
248665b24292SAdrian Hunter    v- 2638:2638
248765b24292SAdrian Hunter        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
248865b24292SAdrian Hunter          |- unknown               unknown       1        13198     0.1              1              0.0
248965b24292SAdrian Hunter          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
249065b24292SAdrian Hunter          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
249165b24292SAdrian Hunter          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
249265b24292SAdrian Hunter             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
249365b24292SAdrian Hunter             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
249465b24292SAdrian Hunter             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
249565b24292SAdrian Hunter             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
249665b24292SAdrian Hunter             v- main               ls            1      8182043    99.6         180254             99.9
249765b24292SAdrian Hunter</pre>
249865b24292SAdrian Hunter<h3>Points to note:</h3>
249965b24292SAdrian Hunter<ul>
250065b24292SAdrian Hunter<li>The top level is a command name (comm)</li>
250165b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li>
250265b24292SAdrian Hunter<li>Subsequent levels are functions</li>
250365b24292SAdrian Hunter<li>'Count' is the number of calls</li>
250465b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li>
250565b24292SAdrian Hunter<li>Percentages are relative to the level above</li>
250665b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls
250765b24292SAdrian Hunter</ul>
250865b24292SAdrian Hunter<h3>Find</h3>
250965b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
251065b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters.
2511*ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2>
2512*ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
2513*ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
2514*ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2>
251565b24292SAdrian HunterThe All branches report displays all branches in chronological order.
251665b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
251765b24292SAdrian Hunter<h3>Disassembly</h3>
251865b24292SAdrian HunterOpen a branch to display disassembly. This only works if:
251965b24292SAdrian Hunter<ol>
252065b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
252165b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
252265b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
252365b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
252465b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
252565b24292SAdrian Hunter</ol>
252665b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4>
252765b24292SAdrian HunterTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
252865b24292SAdrian Hunter<pre>
252965b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild
253065b24292SAdrian Huntergit clone https://github.com/intelxed/xed
253165b24292SAdrian Huntercd xed
253265b24292SAdrian Hunter./mfile.py --share
253365b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install
253465b24292SAdrian Huntersudo ldconfig
253565b24292SAdrian Hunter</pre>
253665b24292SAdrian Hunter<h3>Find</h3>
253765b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
253865b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
253965b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
2540*ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2>
254165b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
254265b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together.
2543*ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3>
254465b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
254565b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace.  Examples:
254665b24292SAdrian Hunter<pre>
254765b24292SAdrian Hunter	81073085947329-81073085958238	From 81073085947329 to 81073085958238
254865b24292SAdrian Hunter	100us-200us		From 100us to 200us
254965b24292SAdrian Hunter	10ms-			From 10ms to the end
255065b24292SAdrian Hunter	-100ns			The first 100ns
255165b24292SAdrian Hunter	-10ms-			The last 10ms
255265b24292SAdrian Hunter</pre>
255365b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2554*ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
2555cd358012SAdrian 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.
2556cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2557cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
255865b24292SAdrian Hunter<h1 id=tables>2. Tables</h1>
255965b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view
256065b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched
256165b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
256265b24292SAdrian Hunterbut that can be slow for large tables.
256365b24292SAdrian Hunter<p>There are also tables of database meta-information.
256465b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included.
256565b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included.
256665b24292SAdrian Hunter<h3>Find</h3>
256765b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
256865b24292SAdrian HunterRefer to Python documentation for the regular expression syntax.
256965b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched.
257035fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
257135fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order.
257265b24292SAdrian Hunter"""
257365b24292SAdrian Hunter
257465b24292SAdrian Hunter# Help window
257565b24292SAdrian Hunter
257665b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow):
257765b24292SAdrian Hunter
257865b24292SAdrian Hunter	def __init__(self, glb, parent=None):
257965b24292SAdrian Hunter		super(HelpWindow, self).__init__(parent)
258065b24292SAdrian Hunter
258165b24292SAdrian Hunter		self.text = QTextBrowser()
258265b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
258365b24292SAdrian Hunter		self.text.setReadOnly(True)
258465b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
258565b24292SAdrian Hunter
258665b24292SAdrian Hunter		self.setWidget(self.text)
258765b24292SAdrian Hunter
258865b24292SAdrian Hunter		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
258965b24292SAdrian Hunter
259065b24292SAdrian Hunter# Main window that only displays the help text
259165b24292SAdrian Hunter
259265b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow):
259365b24292SAdrian Hunter
259465b24292SAdrian Hunter	def __init__(self, parent=None):
259565b24292SAdrian Hunter		super(HelpOnlyWindow, self).__init__(parent)
259665b24292SAdrian Hunter
259765b24292SAdrian Hunter		self.setMinimumSize(200, 100)
259865b24292SAdrian Hunter		self.resize(800, 600)
259965b24292SAdrian Hunter		self.setWindowTitle("Exported SQL Viewer Help")
260065b24292SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
260165b24292SAdrian Hunter
260265b24292SAdrian Hunter		self.text = QTextBrowser()
260365b24292SAdrian Hunter		self.text.setHtml(glb_help_text)
260465b24292SAdrian Hunter		self.text.setReadOnly(True)
260565b24292SAdrian Hunter		self.text.setOpenExternalLinks(True)
260665b24292SAdrian Hunter
260765b24292SAdrian Hunter		self.setCentralWidget(self.text)
260865b24292SAdrian Hunter
260982f68e28SAdrian Hunter# Font resize
261082f68e28SAdrian Hunter
261182f68e28SAdrian Hunterdef ResizeFont(widget, diff):
261282f68e28SAdrian Hunter	font = widget.font()
261382f68e28SAdrian Hunter	sz = font.pointSize()
261482f68e28SAdrian Hunter	font.setPointSize(sz + diff)
261582f68e28SAdrian Hunter	widget.setFont(font)
261682f68e28SAdrian Hunter
261782f68e28SAdrian Hunterdef ShrinkFont(widget):
261882f68e28SAdrian Hunter	ResizeFont(widget, -1)
261982f68e28SAdrian Hunter
262082f68e28SAdrian Hunterdef EnlargeFont(widget):
262182f68e28SAdrian Hunter	ResizeFont(widget, 1)
262282f68e28SAdrian Hunter
26231beb5c7bSAdrian Hunter# Unique name for sub-windows
26241beb5c7bSAdrian Hunter
26251beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr):
26261beb5c7bSAdrian Hunter	if nr > 1:
26271beb5c7bSAdrian Hunter		name += " <" + str(nr) + ">"
26281beb5c7bSAdrian Hunter	return name
26291beb5c7bSAdrian Hunter
26301beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name):
26311beb5c7bSAdrian Hunter	nr = 1
26321beb5c7bSAdrian Hunter	while True:
26331beb5c7bSAdrian Hunter		unique_name = NumberedWindowName(name, nr)
26341beb5c7bSAdrian Hunter		ok = True
26351beb5c7bSAdrian Hunter		for sub_window in mdi_area.subWindowList():
26361beb5c7bSAdrian Hunter			if sub_window.name == unique_name:
26371beb5c7bSAdrian Hunter				ok = False
26381beb5c7bSAdrian Hunter				break
26391beb5c7bSAdrian Hunter		if ok:
26401beb5c7bSAdrian Hunter			return unique_name
26411beb5c7bSAdrian Hunter		nr += 1
26421beb5c7bSAdrian Hunter
26431beb5c7bSAdrian Hunter# Add a sub-window
26441beb5c7bSAdrian Hunter
26451beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name):
26461beb5c7bSAdrian Hunter	unique_name = UniqueSubWindowName(mdi_area, name)
26471beb5c7bSAdrian Hunter	sub_window.setMinimumSize(200, 100)
26481beb5c7bSAdrian Hunter	sub_window.resize(800, 600)
26491beb5c7bSAdrian Hunter	sub_window.setWindowTitle(unique_name)
26501beb5c7bSAdrian Hunter	sub_window.setAttribute(Qt.WA_DeleteOnClose)
26511beb5c7bSAdrian Hunter	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
26521beb5c7bSAdrian Hunter	sub_window.name = unique_name
26531beb5c7bSAdrian Hunter	mdi_area.addSubWindow(sub_window)
26541beb5c7bSAdrian Hunter	sub_window.show()
26551beb5c7bSAdrian Hunter
2656031c2a00SAdrian Hunter# Main window
2657031c2a00SAdrian Hunter
2658031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
2659031c2a00SAdrian Hunter
2660031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
2661031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
2662031c2a00SAdrian Hunter
2663031c2a00SAdrian Hunter		self.glb = glb
2664031c2a00SAdrian Hunter
26651beb5c7bSAdrian Hunter		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
2666031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
2667031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
2668031c2a00SAdrian Hunter
26691beb5c7bSAdrian Hunter		self.mdi_area = QMdiArea()
26701beb5c7bSAdrian Hunter		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
26711beb5c7bSAdrian Hunter		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
2672031c2a00SAdrian Hunter
26731beb5c7bSAdrian Hunter		self.setCentralWidget(self.mdi_area)
2674031c2a00SAdrian Hunter
26751beb5c7bSAdrian Hunter		menu = self.menuBar()
2676031c2a00SAdrian Hunter
26771beb5c7bSAdrian Hunter		file_menu = menu.addMenu("&File")
26781beb5c7bSAdrian Hunter		file_menu.addAction(CreateExitAction(glb.app, self))
26791beb5c7bSAdrian Hunter
2680ebd70c7dSAdrian Hunter		edit_menu = menu.addMenu("&Edit")
2681ebd70c7dSAdrian Hunter		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
26828392b74bSAdrian Hunter		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
268382f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
268482f68e28SAdrian Hunter		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2685ebd70c7dSAdrian Hunter
26861beb5c7bSAdrian Hunter		reports_menu = menu.addMenu("&Reports")
2687655cb952SAdrian Hunter		if IsSelectable(glb.db, "calls"):
26881beb5c7bSAdrian Hunter			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
26891beb5c7bSAdrian Hunter
2690*ae8b887cSAdrian Hunter		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
2691*ae8b887cSAdrian Hunter			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
2692*ae8b887cSAdrian Hunter
269376099f98SAdrian Hunter		self.EventMenu(GetEventList(glb.db), reports_menu)
269476099f98SAdrian Hunter
2695cd358012SAdrian Hunter		if IsSelectable(glb.db, "calls"):
2696cd358012SAdrian Hunter			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
2697cd358012SAdrian Hunter
26988392b74bSAdrian Hunter		self.TableMenu(GetTableList(glb), menu)
26998392b74bSAdrian Hunter
27001beb5c7bSAdrian Hunter		self.window_menu = WindowMenu(self.mdi_area, menu)
27011beb5c7bSAdrian Hunter
270265b24292SAdrian Hunter		help_menu = menu.addMenu("&Help")
270365b24292SAdrian Hunter		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
270465b24292SAdrian Hunter
2705ebd70c7dSAdrian Hunter	def Find(self):
2706ebd70c7dSAdrian Hunter		win = self.mdi_area.activeSubWindow()
2707ebd70c7dSAdrian Hunter		if win:
2708ebd70c7dSAdrian Hunter			try:
2709ebd70c7dSAdrian Hunter				win.find_bar.Activate()
2710ebd70c7dSAdrian Hunter			except:
2711ebd70c7dSAdrian Hunter				pass
2712ebd70c7dSAdrian Hunter
27138392b74bSAdrian Hunter	def FetchMoreRecords(self):
27148392b74bSAdrian Hunter		win = self.mdi_area.activeSubWindow()
27158392b74bSAdrian Hunter		if win:
27168392b74bSAdrian Hunter			try:
27178392b74bSAdrian Hunter				win.fetch_bar.Activate()
27188392b74bSAdrian Hunter			except:
27198392b74bSAdrian Hunter				pass
27208392b74bSAdrian Hunter
272182f68e28SAdrian Hunter	def ShrinkFont(self):
272282f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
272382f68e28SAdrian Hunter		ShrinkFont(win.view)
272482f68e28SAdrian Hunter
272582f68e28SAdrian Hunter	def EnlargeFont(self):
272682f68e28SAdrian Hunter		win = self.mdi_area.activeSubWindow()
272782f68e28SAdrian Hunter		EnlargeFont(win.view)
272882f68e28SAdrian Hunter
272976099f98SAdrian Hunter	def EventMenu(self, events, reports_menu):
273076099f98SAdrian Hunter		branches_events = 0
273176099f98SAdrian Hunter		for event in events:
273276099f98SAdrian Hunter			event = event.split(":")[0]
273376099f98SAdrian Hunter			if event == "branches":
273476099f98SAdrian Hunter				branches_events += 1
273576099f98SAdrian Hunter		dbid = 0
273676099f98SAdrian Hunter		for event in events:
273776099f98SAdrian Hunter			dbid += 1
273876099f98SAdrian Hunter			event = event.split(":")[0]
273976099f98SAdrian Hunter			if event == "branches":
274076099f98SAdrian Hunter				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
274176099f98SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2742210cf1f9SAdrian Hunter				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2743210cf1f9SAdrian Hunter				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
274476099f98SAdrian Hunter
27458392b74bSAdrian Hunter	def TableMenu(self, tables, menu):
27468392b74bSAdrian Hunter		table_menu = menu.addMenu("&Tables")
27478392b74bSAdrian Hunter		for table in tables:
27488392b74bSAdrian Hunter			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
27498392b74bSAdrian Hunter
27501beb5c7bSAdrian Hunter	def NewCallGraph(self):
27511beb5c7bSAdrian Hunter		CallGraphWindow(self.glb, self)
2752031c2a00SAdrian Hunter
2753*ae8b887cSAdrian Hunter	def NewCallTree(self):
2754*ae8b887cSAdrian Hunter		CallTreeWindow(self.glb, self)
2755*ae8b887cSAdrian Hunter
2756cd358012SAdrian Hunter	def NewTopCalls(self):
2757cd358012SAdrian Hunter		dialog = TopCallsDialog(self.glb, self)
2758cd358012SAdrian Hunter		ret = dialog.exec_()
2759cd358012SAdrian Hunter		if ret:
2760cd358012SAdrian Hunter			TopCallsWindow(self.glb, dialog.report_vars, self)
2761cd358012SAdrian Hunter
276276099f98SAdrian Hunter	def NewBranchView(self, event_id):
2763947cc38dSAdrian Hunter		BranchWindow(self.glb, event_id, ReportVars(), self)
276476099f98SAdrian Hunter
2765210cf1f9SAdrian Hunter	def NewSelectedBranchView(self, event_id):
2766210cf1f9SAdrian Hunter		dialog = SelectedBranchDialog(self.glb, self)
2767210cf1f9SAdrian Hunter		ret = dialog.exec_()
2768210cf1f9SAdrian Hunter		if ret:
2769947cc38dSAdrian Hunter			BranchWindow(self.glb, event_id, dialog.report_vars, self)
2770210cf1f9SAdrian Hunter
27718392b74bSAdrian Hunter	def NewTableView(self, table_name):
27728392b74bSAdrian Hunter		TableWindow(self.glb, table_name, self)
27738392b74bSAdrian Hunter
277465b24292SAdrian Hunter	def Help(self):
277565b24292SAdrian Hunter		HelpWindow(self.glb, self)
277665b24292SAdrian Hunter
277776099f98SAdrian Hunter# XED Disassembler
277876099f98SAdrian Hunter
277976099f98SAdrian Hunterclass xed_state_t(Structure):
278076099f98SAdrian Hunter
278176099f98SAdrian Hunter	_fields_ = [
278276099f98SAdrian Hunter		("mode", c_int),
278376099f98SAdrian Hunter		("width", c_int)
278476099f98SAdrian Hunter	]
278576099f98SAdrian Hunter
278676099f98SAdrian Hunterclass XEDInstruction():
278776099f98SAdrian Hunter
278876099f98SAdrian Hunter	def __init__(self, libxed):
278976099f98SAdrian Hunter		# Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion
279076099f98SAdrian Hunter		xedd_t = c_byte * 512
279176099f98SAdrian Hunter		self.xedd = xedd_t()
279276099f98SAdrian Hunter		self.xedp = addressof(self.xedd)
279376099f98SAdrian Hunter		libxed.xed_decoded_inst_zero(self.xedp)
279476099f98SAdrian Hunter		self.state = xed_state_t()
279576099f98SAdrian Hunter		self.statep = addressof(self.state)
279676099f98SAdrian Hunter		# Buffer for disassembled instruction text
279776099f98SAdrian Hunter		self.buffer = create_string_buffer(256)
279876099f98SAdrian Hunter		self.bufferp = addressof(self.buffer)
279976099f98SAdrian Hunter
280076099f98SAdrian Hunterclass LibXED():
280176099f98SAdrian Hunter
280276099f98SAdrian Hunter	def __init__(self):
28035ed4419dSAdrian Hunter		try:
280476099f98SAdrian Hunter			self.libxed = CDLL("libxed.so")
28055ed4419dSAdrian Hunter		except:
28065ed4419dSAdrian Hunter			self.libxed = None
28075ed4419dSAdrian Hunter		if not self.libxed:
28085ed4419dSAdrian Hunter			self.libxed = CDLL("/usr/local/lib/libxed.so")
280976099f98SAdrian Hunter
281076099f98SAdrian Hunter		self.xed_tables_init = self.libxed.xed_tables_init
281176099f98SAdrian Hunter		self.xed_tables_init.restype = None
281276099f98SAdrian Hunter		self.xed_tables_init.argtypes = []
281376099f98SAdrian Hunter
281476099f98SAdrian Hunter		self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero
281576099f98SAdrian Hunter		self.xed_decoded_inst_zero.restype = None
281676099f98SAdrian Hunter		self.xed_decoded_inst_zero.argtypes = [ c_void_p ]
281776099f98SAdrian Hunter
281876099f98SAdrian Hunter		self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode
281976099f98SAdrian Hunter		self.xed_operand_values_set_mode.restype = None
282076099f98SAdrian Hunter		self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ]
282176099f98SAdrian Hunter
282276099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode
282376099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.restype = None
282476099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ]
282576099f98SAdrian Hunter
282676099f98SAdrian Hunter		self.xed_decode = self.libxed.xed_decode
282776099f98SAdrian Hunter		self.xed_decode.restype = c_int
282876099f98SAdrian Hunter		self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ]
282976099f98SAdrian Hunter
283076099f98SAdrian Hunter		self.xed_format_context = self.libxed.xed_format_context
283176099f98SAdrian Hunter		self.xed_format_context.restype = c_uint
283276099f98SAdrian Hunter		self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ]
283376099f98SAdrian Hunter
283476099f98SAdrian Hunter		self.xed_tables_init()
283576099f98SAdrian Hunter
283676099f98SAdrian Hunter	def Instruction(self):
283776099f98SAdrian Hunter		return XEDInstruction(self)
283876099f98SAdrian Hunter
283976099f98SAdrian Hunter	def SetMode(self, inst, mode):
284076099f98SAdrian Hunter		if mode:
284176099f98SAdrian Hunter			inst.state.mode = 4 # 32-bit
284276099f98SAdrian Hunter			inst.state.width = 4 # 4 bytes
284376099f98SAdrian Hunter		else:
284476099f98SAdrian Hunter			inst.state.mode = 1 # 64-bit
284576099f98SAdrian Hunter			inst.state.width = 8 # 8 bytes
284676099f98SAdrian Hunter		self.xed_operand_values_set_mode(inst.xedp, inst.statep)
284776099f98SAdrian Hunter
284876099f98SAdrian Hunter	def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip):
284976099f98SAdrian Hunter		self.xed_decoded_inst_zero_keep_mode(inst.xedp)
285076099f98SAdrian Hunter		err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt)
285176099f98SAdrian Hunter		if err:
285276099f98SAdrian Hunter			return 0, ""
285376099f98SAdrian Hunter		# Use AT&T mode (2), alternative is Intel (3)
285476099f98SAdrian Hunter		ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0)
285576099f98SAdrian Hunter		if not ok:
285676099f98SAdrian Hunter			return 0, ""
285776099f98SAdrian Hunter		# Return instruction length and the disassembled instruction text
285876099f98SAdrian Hunter		# For now, assume the length is in byte 166
285976099f98SAdrian Hunter		return inst.xedd[166], inst.buffer.value
286076099f98SAdrian Hunter
286176099f98SAdrian Hunterdef TryOpen(file_name):
286276099f98SAdrian Hunter	try:
286376099f98SAdrian Hunter		return open(file_name, "rb")
286476099f98SAdrian Hunter	except:
286576099f98SAdrian Hunter		return None
286676099f98SAdrian Hunter
286776099f98SAdrian Hunterdef Is64Bit(f):
286876099f98SAdrian Hunter	result = sizeof(c_void_p)
286976099f98SAdrian Hunter	# ELF support only
287076099f98SAdrian Hunter	pos = f.tell()
287176099f98SAdrian Hunter	f.seek(0)
287276099f98SAdrian Hunter	header = f.read(7)
287376099f98SAdrian Hunter	f.seek(pos)
287476099f98SAdrian Hunter	magic = header[0:4]
287576099f98SAdrian Hunter	eclass = ord(header[4])
287676099f98SAdrian Hunter	encoding = ord(header[5])
287776099f98SAdrian Hunter	version = ord(header[6])
287876099f98SAdrian Hunter	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
287976099f98SAdrian Hunter		result = True if eclass == 2 else False
288076099f98SAdrian Hunter	return result
288176099f98SAdrian Hunter
2882031c2a00SAdrian Hunter# Global data
2883031c2a00SAdrian Hunter
2884031c2a00SAdrian Hunterclass Glb():
2885031c2a00SAdrian Hunter
2886031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
2887031c2a00SAdrian Hunter		self.dbref = dbref
2888031c2a00SAdrian Hunter		self.db = db
2889031c2a00SAdrian Hunter		self.dbname = dbname
289076099f98SAdrian Hunter		self.home_dir = os.path.expanduser("~")
289176099f98SAdrian Hunter		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
289276099f98SAdrian Hunter		if self.buildid_dir:
289376099f98SAdrian Hunter			self.buildid_dir += "/.build-id/"
289476099f98SAdrian Hunter		else:
289576099f98SAdrian Hunter			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
2896031c2a00SAdrian Hunter		self.app = None
2897031c2a00SAdrian Hunter		self.mainwindow = None
28988392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit = weakref.WeakSet()
289976099f98SAdrian Hunter		try:
290076099f98SAdrian Hunter			self.disassembler = LibXED()
290176099f98SAdrian Hunter			self.have_disassembler = True
290276099f98SAdrian Hunter		except:
290376099f98SAdrian Hunter			self.have_disassembler = False
290476099f98SAdrian Hunter
290576099f98SAdrian Hunter	def FileFromBuildId(self, build_id):
290676099f98SAdrian Hunter		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
290776099f98SAdrian Hunter		return TryOpen(file_name)
290876099f98SAdrian Hunter
290976099f98SAdrian Hunter	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
291076099f98SAdrian Hunter		# Assume current machine i.e. no support for virtualization
291176099f98SAdrian Hunter		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
291276099f98SAdrian Hunter			file_name = os.getenv("PERF_KCORE")
291376099f98SAdrian Hunter			f = TryOpen(file_name) if file_name else None
291476099f98SAdrian Hunter			if f:
291576099f98SAdrian Hunter				return f
291676099f98SAdrian Hunter			# For now, no special handling if long_name is /proc/kcore
291776099f98SAdrian Hunter			f = TryOpen(long_name)
291876099f98SAdrian Hunter			if f:
291976099f98SAdrian Hunter				return f
292076099f98SAdrian Hunter		f = self.FileFromBuildId(build_id)
292176099f98SAdrian Hunter		if f:
292276099f98SAdrian Hunter			return f
292376099f98SAdrian Hunter		return None
29248392b74bSAdrian Hunter
29258392b74bSAdrian Hunter	def AddInstanceToShutdownOnExit(self, instance):
29268392b74bSAdrian Hunter		self.instances_to_shutdown_on_exit.add(instance)
29278392b74bSAdrian Hunter
29288392b74bSAdrian Hunter	# Shutdown any background processes or threads
29298392b74bSAdrian Hunter	def ShutdownInstances(self):
29308392b74bSAdrian Hunter		for x in self.instances_to_shutdown_on_exit:
29318392b74bSAdrian Hunter			try:
29328392b74bSAdrian Hunter				x.Shutdown()
29338392b74bSAdrian Hunter			except:
29348392b74bSAdrian Hunter				pass
2935031c2a00SAdrian Hunter
2936031c2a00SAdrian Hunter# Database reference
2937031c2a00SAdrian Hunter
2938031c2a00SAdrian Hunterclass DBRef():
2939031c2a00SAdrian Hunter
2940031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
2941031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
2942031c2a00SAdrian Hunter		self.dbname = dbname
2943031c2a00SAdrian Hunter
2944031c2a00SAdrian Hunter	def Open(self, connection_name):
2945031c2a00SAdrian Hunter		dbname = self.dbname
2946031c2a00SAdrian Hunter		if self.is_sqlite3:
2947031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
2948031c2a00SAdrian Hunter		else:
2949031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
2950031c2a00SAdrian Hunter			opts = dbname.split()
2951031c2a00SAdrian Hunter			for opt in opts:
2952031c2a00SAdrian Hunter				if "=" in opt:
2953031c2a00SAdrian Hunter					opt = opt.split("=")
2954031c2a00SAdrian Hunter					if opt[0] == "hostname":
2955031c2a00SAdrian Hunter						db.setHostName(opt[1])
2956031c2a00SAdrian Hunter					elif opt[0] == "port":
2957031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
2958031c2a00SAdrian Hunter					elif opt[0] == "username":
2959031c2a00SAdrian Hunter						db.setUserName(opt[1])
2960031c2a00SAdrian Hunter					elif opt[0] == "password":
2961031c2a00SAdrian Hunter						db.setPassword(opt[1])
2962031c2a00SAdrian Hunter					elif opt[0] == "dbname":
2963031c2a00SAdrian Hunter						dbname = opt[1]
2964031c2a00SAdrian Hunter				else:
2965031c2a00SAdrian Hunter					dbname = opt
2966031c2a00SAdrian Hunter
2967031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
2968031c2a00SAdrian Hunter		if not db.open():
2969031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
2970031c2a00SAdrian Hunter		return db, dbname
2971031c2a00SAdrian Hunter
2972031c2a00SAdrian Hunter# Main
2973031c2a00SAdrian Hunter
2974031c2a00SAdrian Hunterdef Main():
2975031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
297665b24292SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}"
2977031c2a00SAdrian Hunter		raise Exception("Too few arguments")
2978031c2a00SAdrian Hunter
2979031c2a00SAdrian Hunter	dbname = sys.argv[1]
298065b24292SAdrian Hunter	if dbname == "--help-only":
298165b24292SAdrian Hunter		app = QApplication(sys.argv)
298265b24292SAdrian Hunter		mainwindow = HelpOnlyWindow()
298365b24292SAdrian Hunter		mainwindow.show()
298465b24292SAdrian Hunter		err = app.exec_()
298565b24292SAdrian Hunter		sys.exit(err)
2986031c2a00SAdrian Hunter
2987031c2a00SAdrian Hunter	is_sqlite3 = False
2988031c2a00SAdrian Hunter	try:
2989031c2a00SAdrian Hunter		f = open(dbname)
2990031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
2991031c2a00SAdrian Hunter			is_sqlite3 = True
2992031c2a00SAdrian Hunter		f.close()
2993031c2a00SAdrian Hunter	except:
2994031c2a00SAdrian Hunter		pass
2995031c2a00SAdrian Hunter
2996031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
2997031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
2998031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
2999031c2a00SAdrian Hunter	app = QApplication(sys.argv)
3000031c2a00SAdrian Hunter	glb.app = app
3001031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
3002031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
3003031c2a00SAdrian Hunter	mainwindow.show()
3004031c2a00SAdrian Hunter	err = app.exec_()
30058392b74bSAdrian Hunter	glb.ShutdownInstances()
3006031c2a00SAdrian Hunter	db.close()
3007031c2a00SAdrian Hunter	sys.exit(err)
3008031c2a00SAdrian Hunter
3009031c2a00SAdrian Hunterif __name__ == "__main__":
3010031c2a00SAdrian Hunter	Main()
3011