162306a36Sopenharmony_ci#!/usr/bin/env python
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci# exported-sql-viewer.py: view data from sql database
462306a36Sopenharmony_ci# Copyright (c) 2014-2018, Intel Corporation.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci# To use this script you will need to have exported data using either the
762306a36Sopenharmony_ci# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
862306a36Sopenharmony_ci# scripts for details.
962306a36Sopenharmony_ci#
1062306a36Sopenharmony_ci# Following on from the example in the export scripts, a
1162306a36Sopenharmony_ci# call-graph can be displayed for the pt_example database like this:
1262306a36Sopenharmony_ci#
1362306a36Sopenharmony_ci#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
1462306a36Sopenharmony_ci#
1562306a36Sopenharmony_ci# Note that for PostgreSQL, this script supports connecting to remote databases
1662306a36Sopenharmony_ci# by setting hostname, port, username, password, and dbname e.g.
1762306a36Sopenharmony_ci#
1862306a36Sopenharmony_ci#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
1962306a36Sopenharmony_ci#
2062306a36Sopenharmony_ci# The result is a GUI window with a tree representing a context-sensitive
2162306a36Sopenharmony_ci# call-graph.  Expanding a couple of levels of the tree and adjusting column
2262306a36Sopenharmony_ci# widths to suit will display something like:
2362306a36Sopenharmony_ci#
2462306a36Sopenharmony_ci#                                         Call Graph: pt_example
2562306a36Sopenharmony_ci# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
2662306a36Sopenharmony_ci# v- ls
2762306a36Sopenharmony_ci#     v- 2638:2638
2862306a36Sopenharmony_ci#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
2962306a36Sopenharmony_ci#           |- unknown               unknown       1        13198     0.1              1              0.0
3062306a36Sopenharmony_ci#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
3162306a36Sopenharmony_ci#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
3262306a36Sopenharmony_ci#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
3362306a36Sopenharmony_ci#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
3462306a36Sopenharmony_ci#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
3562306a36Sopenharmony_ci#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
3662306a36Sopenharmony_ci#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
3762306a36Sopenharmony_ci#              v- main               ls            1      8182043    99.6         180254             99.9
3862306a36Sopenharmony_ci#
3962306a36Sopenharmony_ci# Points to note:
4062306a36Sopenharmony_ci#	The top level is a command name (comm)
4162306a36Sopenharmony_ci#	The next level is a thread (pid:tid)
4262306a36Sopenharmony_ci#	Subsequent levels are functions
4362306a36Sopenharmony_ci#	'Count' is the number of calls
4462306a36Sopenharmony_ci#	'Time' is the elapsed time until the function returns
4562306a36Sopenharmony_ci#	Percentages are relative to the level above
4662306a36Sopenharmony_ci#	'Branch Count' is the total number of branches for that function and all
4762306a36Sopenharmony_ci#       functions that it calls
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci# There is also a "All branches" report, which displays branches and
5062306a36Sopenharmony_ci# possibly disassembly.  However, presently, the only supported disassembler is
5162306a36Sopenharmony_ci# Intel XED, and additionally the object code must be present in perf build ID
5262306a36Sopenharmony_ci# cache. To use Intel XED, libxed.so must be present. To build and install
5362306a36Sopenharmony_ci# libxed.so:
5462306a36Sopenharmony_ci#            git clone https://github.com/intelxed/mbuild.git mbuild
5562306a36Sopenharmony_ci#            git clone https://github.com/intelxed/xed
5662306a36Sopenharmony_ci#            cd xed
5762306a36Sopenharmony_ci#            ./mfile.py --share
5862306a36Sopenharmony_ci#            sudo ./mfile.py --prefix=/usr/local install
5962306a36Sopenharmony_ci#            sudo ldconfig
6062306a36Sopenharmony_ci#
6162306a36Sopenharmony_ci# Example report:
6262306a36Sopenharmony_ci#
6362306a36Sopenharmony_ci# Time           CPU  Command  PID    TID    Branch Type            In Tx  Branch
6462306a36Sopenharmony_ci# 8107675239590  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6562306a36Sopenharmony_ci#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6662306a36Sopenharmony_ci# 8107675239899  2    ls       22011  22011  hardware interrupt     No         7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
6762306a36Sopenharmony_ci# 8107675241900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)
6862306a36Sopenharmony_ci#                                                                              7fab593ea260 48 89 e7                                        mov %rsp, %rdi
6962306a36Sopenharmony_ci#                                                                              7fab593ea263 e8 c8 06 00 00                                  callq  0x7fab593ea930
7062306a36Sopenharmony_ci# 8107675241900  2    ls       22011  22011  call                   No         7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)
7162306a36Sopenharmony_ci#                                                                              7fab593ea930 55                                              pushq  %rbp
7262306a36Sopenharmony_ci#                                                                              7fab593ea931 48 89 e5                                        mov %rsp, %rbp
7362306a36Sopenharmony_ci#                                                                              7fab593ea934 41 57                                           pushq  %r15
7462306a36Sopenharmony_ci#                                                                              7fab593ea936 41 56                                           pushq  %r14
7562306a36Sopenharmony_ci#                                                                              7fab593ea938 41 55                                           pushq  %r13
7662306a36Sopenharmony_ci#                                                                              7fab593ea93a 41 54                                           pushq  %r12
7762306a36Sopenharmony_ci#                                                                              7fab593ea93c 53                                              pushq  %rbx
7862306a36Sopenharmony_ci#                                                                              7fab593ea93d 48 89 fb                                        mov %rdi, %rbx
7962306a36Sopenharmony_ci#                                                                              7fab593ea940 48 83 ec 68                                     sub $0x68, %rsp
8062306a36Sopenharmony_ci#                                                                              7fab593ea944 0f 31                                           rdtsc
8162306a36Sopenharmony_ci#                                                                              7fab593ea946 48 c1 e2 20                                     shl $0x20, %rdx
8262306a36Sopenharmony_ci#                                                                              7fab593ea94a 89 c0                                           mov %eax, %eax
8362306a36Sopenharmony_ci#                                                                              7fab593ea94c 48 09 c2                                        or %rax, %rdx
8462306a36Sopenharmony_ci#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8562306a36Sopenharmony_ci# 8107675242232  2    ls       22011  22011  hardware interrupt     No         7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
8662306a36Sopenharmony_ci# 8107675242900  2    ls       22011  22011  return from interrupt  No     ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)
8762306a36Sopenharmony_ci#                                                                              7fab593ea94f 48 8b 05 1a 15 22 00                            movq  0x22151a(%rip), %rax
8862306a36Sopenharmony_ci#                                                                              7fab593ea956 48 89 15 3b 13 22 00                            movq  %rdx, 0x22133b(%rip)
8962306a36Sopenharmony_ci# 8107675243232  2    ls       22011  22011  hardware interrupt     No         7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cifrom __future__ import print_function
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciimport sys
9462306a36Sopenharmony_ci# Only change warnings if the python -W option was not used
9562306a36Sopenharmony_ciif not sys.warnoptions:
9662306a36Sopenharmony_ci	import warnings
9762306a36Sopenharmony_ci	# PySide2 causes deprecation warnings, ignore them.
9862306a36Sopenharmony_ci	warnings.filterwarnings("ignore", category=DeprecationWarning)
9962306a36Sopenharmony_ciimport argparse
10062306a36Sopenharmony_ciimport weakref
10162306a36Sopenharmony_ciimport threading
10262306a36Sopenharmony_ciimport string
10362306a36Sopenharmony_citry:
10462306a36Sopenharmony_ci	# Python2
10562306a36Sopenharmony_ci	import cPickle as pickle
10662306a36Sopenharmony_ci	# size of pickled integer big enough for record size
10762306a36Sopenharmony_ci	glb_nsz = 8
10862306a36Sopenharmony_ciexcept ImportError:
10962306a36Sopenharmony_ci	import pickle
11062306a36Sopenharmony_ci	glb_nsz = 16
11162306a36Sopenharmony_ciimport re
11262306a36Sopenharmony_ciimport os
11362306a36Sopenharmony_ciimport random
11462306a36Sopenharmony_ciimport copy
11562306a36Sopenharmony_ciimport math
11662306a36Sopenharmony_cifrom libxed import LibXED
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cipyside_version_1 = True
11962306a36Sopenharmony_ciif not "--pyside-version-1" in sys.argv:
12062306a36Sopenharmony_ci	try:
12162306a36Sopenharmony_ci		from PySide2.QtCore import *
12262306a36Sopenharmony_ci		from PySide2.QtGui import *
12362306a36Sopenharmony_ci		from PySide2.QtSql import *
12462306a36Sopenharmony_ci		from PySide2.QtWidgets import *
12562306a36Sopenharmony_ci		pyside_version_1 = False
12662306a36Sopenharmony_ci	except:
12762306a36Sopenharmony_ci		pass
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciif pyside_version_1:
13062306a36Sopenharmony_ci	from PySide.QtCore import *
13162306a36Sopenharmony_ci	from PySide.QtGui import *
13262306a36Sopenharmony_ci	from PySide.QtSql import *
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cifrom decimal import Decimal, ROUND_HALF_UP
13562306a36Sopenharmony_cifrom ctypes import CDLL, Structure, create_string_buffer, addressof, sizeof, \
13662306a36Sopenharmony_ci		   c_void_p, c_bool, c_byte, c_char, c_int, c_uint, c_longlong, c_ulonglong
13762306a36Sopenharmony_cifrom multiprocessing import Process, Array, Value, Event
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci# xrange is range in Python3
14062306a36Sopenharmony_citry:
14162306a36Sopenharmony_ci	xrange
14262306a36Sopenharmony_ciexcept NameError:
14362306a36Sopenharmony_ci	xrange = range
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cidef printerr(*args, **keyword_args):
14662306a36Sopenharmony_ci	print(*args, file=sys.stderr, **keyword_args)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci# Data formatting helpers
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cidef tohex(ip):
15162306a36Sopenharmony_ci	if ip < 0:
15262306a36Sopenharmony_ci		ip += 1 << 64
15362306a36Sopenharmony_ci	return "%x" % ip
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cidef offstr(offset):
15662306a36Sopenharmony_ci	if offset:
15762306a36Sopenharmony_ci		return "+0x%x" % offset
15862306a36Sopenharmony_ci	return ""
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cidef dsoname(name):
16162306a36Sopenharmony_ci	if name == "[kernel.kallsyms]":
16262306a36Sopenharmony_ci		return "[kernel]"
16362306a36Sopenharmony_ci	return name
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cidef findnth(s, sub, n, offs=0):
16662306a36Sopenharmony_ci	pos = s.find(sub)
16762306a36Sopenharmony_ci	if pos < 0:
16862306a36Sopenharmony_ci		return pos
16962306a36Sopenharmony_ci	if n <= 1:
17062306a36Sopenharmony_ci		return offs + pos
17162306a36Sopenharmony_ci	return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci# Percent to one decimal place
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cidef PercentToOneDP(n, d):
17662306a36Sopenharmony_ci	if not d:
17762306a36Sopenharmony_ci		return "0.0"
17862306a36Sopenharmony_ci	x = (n * Decimal(100)) / d
17962306a36Sopenharmony_ci	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci# Helper for queries that must not fail
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cidef QueryExec(query, stmt):
18462306a36Sopenharmony_ci	ret = query.exec_(stmt)
18562306a36Sopenharmony_ci	if not ret:
18662306a36Sopenharmony_ci		raise Exception("Query failed: " + query.lastError().text())
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci# Background thread
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciclass Thread(QThread):
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	done = Signal(object)
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	def __init__(self, task, param=None, parent=None):
19562306a36Sopenharmony_ci		super(Thread, self).__init__(parent)
19662306a36Sopenharmony_ci		self.task = task
19762306a36Sopenharmony_ci		self.param = param
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	def run(self):
20062306a36Sopenharmony_ci		while True:
20162306a36Sopenharmony_ci			if self.param is None:
20262306a36Sopenharmony_ci				done, result = self.task()
20362306a36Sopenharmony_ci			else:
20462306a36Sopenharmony_ci				done, result = self.task(self.param)
20562306a36Sopenharmony_ci			self.done.emit(result)
20662306a36Sopenharmony_ci			if done:
20762306a36Sopenharmony_ci				break
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci# Tree data model
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciclass TreeModel(QAbstractItemModel):
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	def __init__(self, glb, params, parent=None):
21462306a36Sopenharmony_ci		super(TreeModel, self).__init__(parent)
21562306a36Sopenharmony_ci		self.glb = glb
21662306a36Sopenharmony_ci		self.params = params
21762306a36Sopenharmony_ci		self.root = self.GetRoot()
21862306a36Sopenharmony_ci		self.last_row_read = 0
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	def Item(self, parent):
22162306a36Sopenharmony_ci		if parent.isValid():
22262306a36Sopenharmony_ci			return parent.internalPointer()
22362306a36Sopenharmony_ci		else:
22462306a36Sopenharmony_ci			return self.root
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	def rowCount(self, parent):
22762306a36Sopenharmony_ci		result = self.Item(parent).childCount()
22862306a36Sopenharmony_ci		if result < 0:
22962306a36Sopenharmony_ci			result = 0
23062306a36Sopenharmony_ci			self.dataChanged.emit(parent, parent)
23162306a36Sopenharmony_ci		return result
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	def hasChildren(self, parent):
23462306a36Sopenharmony_ci		return self.Item(parent).hasChildren()
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	def headerData(self, section, orientation, role):
23762306a36Sopenharmony_ci		if role == Qt.TextAlignmentRole:
23862306a36Sopenharmony_ci			return self.columnAlignment(section)
23962306a36Sopenharmony_ci		if role != Qt.DisplayRole:
24062306a36Sopenharmony_ci			return None
24162306a36Sopenharmony_ci		if orientation != Qt.Horizontal:
24262306a36Sopenharmony_ci			return None
24362306a36Sopenharmony_ci		return self.columnHeader(section)
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	def parent(self, child):
24662306a36Sopenharmony_ci		child_item = child.internalPointer()
24762306a36Sopenharmony_ci		if child_item is self.root:
24862306a36Sopenharmony_ci			return QModelIndex()
24962306a36Sopenharmony_ci		parent_item = child_item.getParentItem()
25062306a36Sopenharmony_ci		return self.createIndex(parent_item.getRow(), 0, parent_item)
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	def index(self, row, column, parent):
25362306a36Sopenharmony_ci		child_item = self.Item(parent).getChildItem(row)
25462306a36Sopenharmony_ci		return self.createIndex(row, column, child_item)
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	def DisplayData(self, item, index):
25762306a36Sopenharmony_ci		return item.getData(index.column())
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	def FetchIfNeeded(self, row):
26062306a36Sopenharmony_ci		if row > self.last_row_read:
26162306a36Sopenharmony_ci			self.last_row_read = row
26262306a36Sopenharmony_ci			if row + 10 >= self.root.child_count:
26362306a36Sopenharmony_ci				self.fetcher.Fetch(glb_chunk_sz)
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	def columnAlignment(self, column):
26662306a36Sopenharmony_ci		return Qt.AlignLeft
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	def columnFont(self, column):
26962306a36Sopenharmony_ci		return None
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	def data(self, index, role):
27262306a36Sopenharmony_ci		if role == Qt.TextAlignmentRole:
27362306a36Sopenharmony_ci			return self.columnAlignment(index.column())
27462306a36Sopenharmony_ci		if role == Qt.FontRole:
27562306a36Sopenharmony_ci			return self.columnFont(index.column())
27662306a36Sopenharmony_ci		if role != Qt.DisplayRole:
27762306a36Sopenharmony_ci			return None
27862306a36Sopenharmony_ci		item = index.internalPointer()
27962306a36Sopenharmony_ci		return self.DisplayData(item, index)
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci# Table data model
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciclass TableModel(QAbstractTableModel):
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	def __init__(self, parent=None):
28662306a36Sopenharmony_ci		super(TableModel, self).__init__(parent)
28762306a36Sopenharmony_ci		self.child_count = 0
28862306a36Sopenharmony_ci		self.child_items = []
28962306a36Sopenharmony_ci		self.last_row_read = 0
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	def Item(self, parent):
29262306a36Sopenharmony_ci		if parent.isValid():
29362306a36Sopenharmony_ci			return parent.internalPointer()
29462306a36Sopenharmony_ci		else:
29562306a36Sopenharmony_ci			return self
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	def rowCount(self, parent):
29862306a36Sopenharmony_ci		return self.child_count
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	def headerData(self, section, orientation, role):
30162306a36Sopenharmony_ci		if role == Qt.TextAlignmentRole:
30262306a36Sopenharmony_ci			return self.columnAlignment(section)
30362306a36Sopenharmony_ci		if role != Qt.DisplayRole:
30462306a36Sopenharmony_ci			return None
30562306a36Sopenharmony_ci		if orientation != Qt.Horizontal:
30662306a36Sopenharmony_ci			return None
30762306a36Sopenharmony_ci		return self.columnHeader(section)
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	def index(self, row, column, parent):
31062306a36Sopenharmony_ci		return self.createIndex(row, column, self.child_items[row])
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	def DisplayData(self, item, index):
31362306a36Sopenharmony_ci		return item.getData(index.column())
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	def FetchIfNeeded(self, row):
31662306a36Sopenharmony_ci		if row > self.last_row_read:
31762306a36Sopenharmony_ci			self.last_row_read = row
31862306a36Sopenharmony_ci			if row + 10 >= self.child_count:
31962306a36Sopenharmony_ci				self.fetcher.Fetch(glb_chunk_sz)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	def columnAlignment(self, column):
32262306a36Sopenharmony_ci		return Qt.AlignLeft
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	def columnFont(self, column):
32562306a36Sopenharmony_ci		return None
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	def data(self, index, role):
32862306a36Sopenharmony_ci		if role == Qt.TextAlignmentRole:
32962306a36Sopenharmony_ci			return self.columnAlignment(index.column())
33062306a36Sopenharmony_ci		if role == Qt.FontRole:
33162306a36Sopenharmony_ci			return self.columnFont(index.column())
33262306a36Sopenharmony_ci		if role != Qt.DisplayRole:
33362306a36Sopenharmony_ci			return None
33462306a36Sopenharmony_ci		item = index.internalPointer()
33562306a36Sopenharmony_ci		return self.DisplayData(item, index)
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci# Model cache
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cimodel_cache = weakref.WeakValueDictionary()
34062306a36Sopenharmony_cimodel_cache_lock = threading.Lock()
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cidef LookupCreateModel(model_name, create_fn):
34362306a36Sopenharmony_ci	model_cache_lock.acquire()
34462306a36Sopenharmony_ci	try:
34562306a36Sopenharmony_ci		model = model_cache[model_name]
34662306a36Sopenharmony_ci	except:
34762306a36Sopenharmony_ci		model = None
34862306a36Sopenharmony_ci	if model is None:
34962306a36Sopenharmony_ci		model = create_fn()
35062306a36Sopenharmony_ci		model_cache[model_name] = model
35162306a36Sopenharmony_ci	model_cache_lock.release()
35262306a36Sopenharmony_ci	return model
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cidef LookupModel(model_name):
35562306a36Sopenharmony_ci	model_cache_lock.acquire()
35662306a36Sopenharmony_ci	try:
35762306a36Sopenharmony_ci		model = model_cache[model_name]
35862306a36Sopenharmony_ci	except:
35962306a36Sopenharmony_ci		model = None
36062306a36Sopenharmony_ci	model_cache_lock.release()
36162306a36Sopenharmony_ci	return model
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci# Find bar
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciclass FindBar():
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	def __init__(self, parent, finder, is_reg_expr=False):
36862306a36Sopenharmony_ci		self.finder = finder
36962306a36Sopenharmony_ci		self.context = []
37062306a36Sopenharmony_ci		self.last_value = None
37162306a36Sopenharmony_ci		self.last_pattern = None
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		label = QLabel("Find:")
37462306a36Sopenharmony_ci		label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		self.textbox = QComboBox()
37762306a36Sopenharmony_ci		self.textbox.setEditable(True)
37862306a36Sopenharmony_ci		self.textbox.currentIndexChanged.connect(self.ValueChanged)
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		self.progress = QProgressBar()
38162306a36Sopenharmony_ci		self.progress.setRange(0, 0)
38262306a36Sopenharmony_ci		self.progress.hide()
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if is_reg_expr:
38562306a36Sopenharmony_ci			self.pattern = QCheckBox("Regular Expression")
38662306a36Sopenharmony_ci		else:
38762306a36Sopenharmony_ci			self.pattern = QCheckBox("Pattern")
38862306a36Sopenharmony_ci		self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		self.next_button = QToolButton()
39162306a36Sopenharmony_ci		self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown))
39262306a36Sopenharmony_ci		self.next_button.released.connect(lambda: self.NextPrev(1))
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		self.prev_button = QToolButton()
39562306a36Sopenharmony_ci		self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp))
39662306a36Sopenharmony_ci		self.prev_button.released.connect(lambda: self.NextPrev(-1))
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		self.close_button = QToolButton()
39962306a36Sopenharmony_ci		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
40062306a36Sopenharmony_ci		self.close_button.released.connect(self.Deactivate)
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		self.hbox = QHBoxLayout()
40362306a36Sopenharmony_ci		self.hbox.setContentsMargins(0, 0, 0, 0)
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		self.hbox.addWidget(label)
40662306a36Sopenharmony_ci		self.hbox.addWidget(self.textbox)
40762306a36Sopenharmony_ci		self.hbox.addWidget(self.progress)
40862306a36Sopenharmony_ci		self.hbox.addWidget(self.pattern)
40962306a36Sopenharmony_ci		self.hbox.addWidget(self.next_button)
41062306a36Sopenharmony_ci		self.hbox.addWidget(self.prev_button)
41162306a36Sopenharmony_ci		self.hbox.addWidget(self.close_button)
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		self.bar = QWidget()
41462306a36Sopenharmony_ci		self.bar.setLayout(self.hbox)
41562306a36Sopenharmony_ci		self.bar.hide()
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	def Widget(self):
41862306a36Sopenharmony_ci		return self.bar
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	def Activate(self):
42162306a36Sopenharmony_ci		self.bar.show()
42262306a36Sopenharmony_ci		self.textbox.lineEdit().selectAll()
42362306a36Sopenharmony_ci		self.textbox.setFocus()
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	def Deactivate(self):
42662306a36Sopenharmony_ci		self.bar.hide()
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	def Busy(self):
42962306a36Sopenharmony_ci		self.textbox.setEnabled(False)
43062306a36Sopenharmony_ci		self.pattern.hide()
43162306a36Sopenharmony_ci		self.next_button.hide()
43262306a36Sopenharmony_ci		self.prev_button.hide()
43362306a36Sopenharmony_ci		self.progress.show()
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	def Idle(self):
43662306a36Sopenharmony_ci		self.textbox.setEnabled(True)
43762306a36Sopenharmony_ci		self.progress.hide()
43862306a36Sopenharmony_ci		self.pattern.show()
43962306a36Sopenharmony_ci		self.next_button.show()
44062306a36Sopenharmony_ci		self.prev_button.show()
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	def Find(self, direction):
44362306a36Sopenharmony_ci		value = self.textbox.currentText()
44462306a36Sopenharmony_ci		pattern = self.pattern.isChecked()
44562306a36Sopenharmony_ci		self.last_value = value
44662306a36Sopenharmony_ci		self.last_pattern = pattern
44762306a36Sopenharmony_ci		self.finder.Find(value, direction, pattern, self.context)
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	def ValueChanged(self):
45062306a36Sopenharmony_ci		value = self.textbox.currentText()
45162306a36Sopenharmony_ci		pattern = self.pattern.isChecked()
45262306a36Sopenharmony_ci		index = self.textbox.currentIndex()
45362306a36Sopenharmony_ci		data = self.textbox.itemData(index)
45462306a36Sopenharmony_ci		# Store the pattern in the combo box to keep it with the text value
45562306a36Sopenharmony_ci		if data == None:
45662306a36Sopenharmony_ci			self.textbox.setItemData(index, pattern)
45762306a36Sopenharmony_ci		else:
45862306a36Sopenharmony_ci			self.pattern.setChecked(data)
45962306a36Sopenharmony_ci		self.Find(0)
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	def NextPrev(self, direction):
46262306a36Sopenharmony_ci		value = self.textbox.currentText()
46362306a36Sopenharmony_ci		pattern = self.pattern.isChecked()
46462306a36Sopenharmony_ci		if value != self.last_value:
46562306a36Sopenharmony_ci			index = self.textbox.findText(value)
46662306a36Sopenharmony_ci			# Allow for a button press before the value has been added to the combo box
46762306a36Sopenharmony_ci			if index < 0:
46862306a36Sopenharmony_ci				index = self.textbox.count()
46962306a36Sopenharmony_ci				self.textbox.addItem(value, pattern)
47062306a36Sopenharmony_ci				self.textbox.setCurrentIndex(index)
47162306a36Sopenharmony_ci				return
47262306a36Sopenharmony_ci			else:
47362306a36Sopenharmony_ci				self.textbox.setItemData(index, pattern)
47462306a36Sopenharmony_ci		elif pattern != self.last_pattern:
47562306a36Sopenharmony_ci			# Keep the pattern recorded in the combo box up to date
47662306a36Sopenharmony_ci			index = self.textbox.currentIndex()
47762306a36Sopenharmony_ci			self.textbox.setItemData(index, pattern)
47862306a36Sopenharmony_ci		self.Find(direction)
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	def NotFound(self):
48162306a36Sopenharmony_ci		QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found")
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci# Context-sensitive call graph data model item base
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciclass CallGraphLevelItemBase(object):
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	def __init__(self, glb, params, row, parent_item):
48862306a36Sopenharmony_ci		self.glb = glb
48962306a36Sopenharmony_ci		self.params = params
49062306a36Sopenharmony_ci		self.row = row
49162306a36Sopenharmony_ci		self.parent_item = parent_item
49262306a36Sopenharmony_ci		self.query_done = False
49362306a36Sopenharmony_ci		self.child_count = 0
49462306a36Sopenharmony_ci		self.child_items = []
49562306a36Sopenharmony_ci		if parent_item:
49662306a36Sopenharmony_ci			self.level = parent_item.level + 1
49762306a36Sopenharmony_ci		else:
49862306a36Sopenharmony_ci			self.level = 0
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	def getChildItem(self, row):
50162306a36Sopenharmony_ci		return self.child_items[row]
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	def getParentItem(self):
50462306a36Sopenharmony_ci		return self.parent_item
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	def getRow(self):
50762306a36Sopenharmony_ci		return self.row
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	def childCount(self):
51062306a36Sopenharmony_ci		if not self.query_done:
51162306a36Sopenharmony_ci			self.Select()
51262306a36Sopenharmony_ci			if not self.child_count:
51362306a36Sopenharmony_ci				return -1
51462306a36Sopenharmony_ci		return self.child_count
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	def hasChildren(self):
51762306a36Sopenharmony_ci		if not self.query_done:
51862306a36Sopenharmony_ci			return True
51962306a36Sopenharmony_ci		return self.child_count > 0
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	def getData(self, column):
52262306a36Sopenharmony_ci		return self.data[column]
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci# Context-sensitive call graph data model level 2+ item base
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ciclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
52962306a36Sopenharmony_ci		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
53062306a36Sopenharmony_ci		self.comm_id = comm_id
53162306a36Sopenharmony_ci		self.thread_id = thread_id
53262306a36Sopenharmony_ci		self.call_path_id = call_path_id
53362306a36Sopenharmony_ci		self.insn_cnt = insn_cnt
53462306a36Sopenharmony_ci		self.cyc_cnt = cyc_cnt
53562306a36Sopenharmony_ci		self.branch_count = branch_count
53662306a36Sopenharmony_ci		self.time = time
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	def Select(self):
53962306a36Sopenharmony_ci		self.query_done = True
54062306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
54162306a36Sopenharmony_ci		if self.params.have_ipc:
54262306a36Sopenharmony_ci			ipc_str = ", SUM(insn_count), SUM(cyc_count)"
54362306a36Sopenharmony_ci		else:
54462306a36Sopenharmony_ci			ipc_str = ""
54562306a36Sopenharmony_ci		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
54662306a36Sopenharmony_ci					" FROM calls"
54762306a36Sopenharmony_ci					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
54862306a36Sopenharmony_ci					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
54962306a36Sopenharmony_ci					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
55062306a36Sopenharmony_ci					" WHERE parent_call_path_id = " + str(self.call_path_id) +
55162306a36Sopenharmony_ci					" AND comm_id = " + str(self.comm_id) +
55262306a36Sopenharmony_ci					" AND thread_id = " + str(self.thread_id) +
55362306a36Sopenharmony_ci					" GROUP BY call_path_id, name, short_name"
55462306a36Sopenharmony_ci					" ORDER BY call_path_id")
55562306a36Sopenharmony_ci		while query.next():
55662306a36Sopenharmony_ci			if self.params.have_ipc:
55762306a36Sopenharmony_ci				insn_cnt = int(query.value(5))
55862306a36Sopenharmony_ci				cyc_cnt = int(query.value(6))
55962306a36Sopenharmony_ci				branch_count = int(query.value(7))
56062306a36Sopenharmony_ci			else:
56162306a36Sopenharmony_ci				insn_cnt = 0
56262306a36Sopenharmony_ci				cyc_cnt = 0
56362306a36Sopenharmony_ci				branch_count = int(query.value(5))
56462306a36Sopenharmony_ci			child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
56562306a36Sopenharmony_ci			self.child_items.append(child_item)
56662306a36Sopenharmony_ci			self.child_count += 1
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci# Context-sensitive call graph data model level three item
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ciclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
57362306a36Sopenharmony_ci		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
57462306a36Sopenharmony_ci		dso = dsoname(dso)
57562306a36Sopenharmony_ci		if self.params.have_ipc:
57662306a36Sopenharmony_ci			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
57762306a36Sopenharmony_ci			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
57862306a36Sopenharmony_ci			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
57962306a36Sopenharmony_ci			ipc = CalcIPC(cyc_cnt, insn_cnt)
58062306a36Sopenharmony_ci			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
58162306a36Sopenharmony_ci		else:
58262306a36Sopenharmony_ci			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
58362306a36Sopenharmony_ci		self.dbid = call_path_id
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci# Context-sensitive call graph data model level two item
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ciclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
59062306a36Sopenharmony_ci		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
59162306a36Sopenharmony_ci		if self.params.have_ipc:
59262306a36Sopenharmony_ci			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
59362306a36Sopenharmony_ci		else:
59462306a36Sopenharmony_ci			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
59562306a36Sopenharmony_ci		self.dbid = thread_id
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	def Select(self):
59862306a36Sopenharmony_ci		super(CallGraphLevelTwoItem, self).Select()
59962306a36Sopenharmony_ci		for child_item in self.child_items:
60062306a36Sopenharmony_ci			self.time += child_item.time
60162306a36Sopenharmony_ci			self.insn_cnt += child_item.insn_cnt
60262306a36Sopenharmony_ci			self.cyc_cnt += child_item.cyc_cnt
60362306a36Sopenharmony_ci			self.branch_count += child_item.branch_count
60462306a36Sopenharmony_ci		for child_item in self.child_items:
60562306a36Sopenharmony_ci			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
60662306a36Sopenharmony_ci			if self.params.have_ipc:
60762306a36Sopenharmony_ci				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
60862306a36Sopenharmony_ci				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
60962306a36Sopenharmony_ci				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
61062306a36Sopenharmony_ci			else:
61162306a36Sopenharmony_ci				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci# Context-sensitive call graph data model level one item
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ciclass CallGraphLevelOneItem(CallGraphLevelItemBase):
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, comm, parent_item):
61862306a36Sopenharmony_ci		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
61962306a36Sopenharmony_ci		if self.params.have_ipc:
62062306a36Sopenharmony_ci			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
62162306a36Sopenharmony_ci		else:
62262306a36Sopenharmony_ci			self.data = [comm, "", "", "", "", "", ""]
62362306a36Sopenharmony_ci		self.dbid = comm_id
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	def Select(self):
62662306a36Sopenharmony_ci		self.query_done = True
62762306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
62862306a36Sopenharmony_ci		QueryExec(query, "SELECT thread_id, pid, tid"
62962306a36Sopenharmony_ci					" FROM comm_threads"
63062306a36Sopenharmony_ci					" INNER JOIN threads ON thread_id = threads.id"
63162306a36Sopenharmony_ci					" WHERE comm_id = " + str(self.dbid))
63262306a36Sopenharmony_ci		while query.next():
63362306a36Sopenharmony_ci			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
63462306a36Sopenharmony_ci			self.child_items.append(child_item)
63562306a36Sopenharmony_ci			self.child_count += 1
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci# Context-sensitive call graph data model root item
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciclass CallGraphRootItem(CallGraphLevelItemBase):
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	def __init__(self, glb, params):
64262306a36Sopenharmony_ci		super(CallGraphRootItem, self).__init__(glb, params, 0, None)
64362306a36Sopenharmony_ci		self.dbid = 0
64462306a36Sopenharmony_ci		self.query_done = True
64562306a36Sopenharmony_ci		if_has_calls = ""
64662306a36Sopenharmony_ci		if IsSelectable(glb.db, "comms", columns = "has_calls"):
64762306a36Sopenharmony_ci			if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE
64862306a36Sopenharmony_ci		query = QSqlQuery(glb.db)
64962306a36Sopenharmony_ci		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
65062306a36Sopenharmony_ci		while query.next():
65162306a36Sopenharmony_ci			if not query.value(0):
65262306a36Sopenharmony_ci				continue
65362306a36Sopenharmony_ci			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
65462306a36Sopenharmony_ci			self.child_items.append(child_item)
65562306a36Sopenharmony_ci			self.child_count += 1
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci# Call graph model parameters
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ciclass CallGraphModelParams():
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
66262306a36Sopenharmony_ci		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci# Context-sensitive call graph data model base
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ciclass CallGraphModelBase(TreeModel):
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
66962306a36Sopenharmony_ci		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	def FindSelect(self, value, pattern, query):
67262306a36Sopenharmony_ci		if pattern:
67362306a36Sopenharmony_ci			# postgresql and sqlite pattern patching differences:
67462306a36Sopenharmony_ci			#   postgresql LIKE is case sensitive but sqlite LIKE is not
67562306a36Sopenharmony_ci			#   postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not
67662306a36Sopenharmony_ci			#   postgresql supports ILIKE which is case insensitive
67762306a36Sopenharmony_ci			#   sqlite supports GLOB (text only) which uses * and ? and is case sensitive
67862306a36Sopenharmony_ci			if not self.glb.dbref.is_sqlite3:
67962306a36Sopenharmony_ci				# Escape % and _
68062306a36Sopenharmony_ci				s = value.replace("%", "\%")
68162306a36Sopenharmony_ci				s = s.replace("_", "\_")
68262306a36Sopenharmony_ci				# Translate * and ? into SQL LIKE pattern characters % and _
68362306a36Sopenharmony_ci				trans = string.maketrans("*?", "%_")
68462306a36Sopenharmony_ci				match = " LIKE '" + str(s).translate(trans) + "'"
68562306a36Sopenharmony_ci			else:
68662306a36Sopenharmony_ci				match = " GLOB '" + str(value) + "'"
68762306a36Sopenharmony_ci		else:
68862306a36Sopenharmony_ci			match = " = '" + str(value) + "'"
68962306a36Sopenharmony_ci		self.DoFindSelect(query, match)
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	def Found(self, query, found):
69262306a36Sopenharmony_ci		if found:
69362306a36Sopenharmony_ci			return self.FindPath(query)
69462306a36Sopenharmony_ci		return []
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	def FindValue(self, value, pattern, query, last_value, last_pattern):
69762306a36Sopenharmony_ci		if last_value == value and pattern == last_pattern:
69862306a36Sopenharmony_ci			found = query.first()
69962306a36Sopenharmony_ci		else:
70062306a36Sopenharmony_ci			self.FindSelect(value, pattern, query)
70162306a36Sopenharmony_ci			found = query.next()
70262306a36Sopenharmony_ci		return self.Found(query, found)
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	def FindNext(self, query):
70562306a36Sopenharmony_ci		found = query.next()
70662306a36Sopenharmony_ci		if not found:
70762306a36Sopenharmony_ci			found = query.first()
70862306a36Sopenharmony_ci		return self.Found(query, found)
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	def FindPrev(self, query):
71162306a36Sopenharmony_ci		found = query.previous()
71262306a36Sopenharmony_ci		if not found:
71362306a36Sopenharmony_ci			found = query.last()
71462306a36Sopenharmony_ci		return self.Found(query, found)
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	def FindThread(self, c):
71762306a36Sopenharmony_ci		if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern:
71862306a36Sopenharmony_ci			ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern)
71962306a36Sopenharmony_ci		elif c.direction > 0:
72062306a36Sopenharmony_ci			ids = self.FindNext(c.query)
72162306a36Sopenharmony_ci		else:
72262306a36Sopenharmony_ci			ids = self.FindPrev(c.query)
72362306a36Sopenharmony_ci		return (True, ids)
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context, callback):
72662306a36Sopenharmony_ci		class Context():
72762306a36Sopenharmony_ci			def __init__(self, *x):
72862306a36Sopenharmony_ci				self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x
72962306a36Sopenharmony_ci			def Update(self, *x):
73062306a36Sopenharmony_ci				self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern)
73162306a36Sopenharmony_ci		if len(context):
73262306a36Sopenharmony_ci			context[0].Update(value, direction, pattern)
73362306a36Sopenharmony_ci		else:
73462306a36Sopenharmony_ci			context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None))
73562306a36Sopenharmony_ci		# Use a thread so the UI is not blocked during the SELECT
73662306a36Sopenharmony_ci		thread = Thread(self.FindThread, context[0])
73762306a36Sopenharmony_ci		thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection)
73862306a36Sopenharmony_ci		thread.start()
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	def FindDone(self, thread, callback, ids):
74162306a36Sopenharmony_ci		callback(ids)
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci# Context-sensitive call graph data model
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ciclass CallGraphModel(CallGraphModelBase):
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
74862306a36Sopenharmony_ci		super(CallGraphModel, self).__init__(glb, parent)
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	def GetRoot(self):
75162306a36Sopenharmony_ci		return CallGraphRootItem(self.glb, self.params)
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	def columnCount(self, parent=None):
75462306a36Sopenharmony_ci		if self.params.have_ipc:
75562306a36Sopenharmony_ci			return 12
75662306a36Sopenharmony_ci		else:
75762306a36Sopenharmony_ci			return 7
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	def columnHeader(self, column):
76062306a36Sopenharmony_ci		if self.params.have_ipc:
76162306a36Sopenharmony_ci			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
76262306a36Sopenharmony_ci		else:
76362306a36Sopenharmony_ci			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
76462306a36Sopenharmony_ci		return headers[column]
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	def columnAlignment(self, column):
76762306a36Sopenharmony_ci		if self.params.have_ipc:
76862306a36Sopenharmony_ci			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
76962306a36Sopenharmony_ci		else:
77062306a36Sopenharmony_ci			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
77162306a36Sopenharmony_ci		return alignment[column]
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	def DoFindSelect(self, query, match):
77462306a36Sopenharmony_ci		QueryExec(query, "SELECT call_path_id, comm_id, thread_id"
77562306a36Sopenharmony_ci						" FROM calls"
77662306a36Sopenharmony_ci						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
77762306a36Sopenharmony_ci						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
77862306a36Sopenharmony_ci						" WHERE calls.id <> 0"
77962306a36Sopenharmony_ci						" AND symbols.name" + match +
78062306a36Sopenharmony_ci						" GROUP BY comm_id, thread_id, call_path_id"
78162306a36Sopenharmony_ci						" ORDER BY comm_id, thread_id, call_path_id")
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	def FindPath(self, query):
78462306a36Sopenharmony_ci		# Turn the query result into a list of ids that the tree view can walk
78562306a36Sopenharmony_ci		# to open the tree at the right place.
78662306a36Sopenharmony_ci		ids = []
78762306a36Sopenharmony_ci		parent_id = query.value(0)
78862306a36Sopenharmony_ci		while parent_id:
78962306a36Sopenharmony_ci			ids.insert(0, parent_id)
79062306a36Sopenharmony_ci			q2 = QSqlQuery(self.glb.db)
79162306a36Sopenharmony_ci			QueryExec(q2, "SELECT parent_id"
79262306a36Sopenharmony_ci					" FROM call_paths"
79362306a36Sopenharmony_ci					" WHERE id = " + str(parent_id))
79462306a36Sopenharmony_ci			if not q2.next():
79562306a36Sopenharmony_ci				break
79662306a36Sopenharmony_ci			parent_id = q2.value(0)
79762306a36Sopenharmony_ci		# The call path root is not used
79862306a36Sopenharmony_ci		if ids[0] == 1:
79962306a36Sopenharmony_ci			del ids[0]
80062306a36Sopenharmony_ci		ids.insert(0, query.value(2))
80162306a36Sopenharmony_ci		ids.insert(0, query.value(1))
80262306a36Sopenharmony_ci		return ids
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci# Call tree data model level 2+ item base
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ciclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
80962306a36Sopenharmony_ci		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
81062306a36Sopenharmony_ci		self.comm_id = comm_id
81162306a36Sopenharmony_ci		self.thread_id = thread_id
81262306a36Sopenharmony_ci		self.calls_id = calls_id
81362306a36Sopenharmony_ci		self.call_time = call_time
81462306a36Sopenharmony_ci		self.time = time
81562306a36Sopenharmony_ci		self.insn_cnt = insn_cnt
81662306a36Sopenharmony_ci		self.cyc_cnt = cyc_cnt
81762306a36Sopenharmony_ci		self.branch_count = branch_count
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	def Select(self):
82062306a36Sopenharmony_ci		self.query_done = True
82162306a36Sopenharmony_ci		if self.calls_id == 0:
82262306a36Sopenharmony_ci			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
82362306a36Sopenharmony_ci		else:
82462306a36Sopenharmony_ci			comm_thread = ""
82562306a36Sopenharmony_ci		if self.params.have_ipc:
82662306a36Sopenharmony_ci			ipc_str = ", insn_count, cyc_count"
82762306a36Sopenharmony_ci		else:
82862306a36Sopenharmony_ci			ipc_str = ""
82962306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
83062306a36Sopenharmony_ci		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
83162306a36Sopenharmony_ci					" FROM calls"
83262306a36Sopenharmony_ci					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
83362306a36Sopenharmony_ci					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
83462306a36Sopenharmony_ci					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
83562306a36Sopenharmony_ci					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
83662306a36Sopenharmony_ci					" ORDER BY call_time, calls.id")
83762306a36Sopenharmony_ci		while query.next():
83862306a36Sopenharmony_ci			if self.params.have_ipc:
83962306a36Sopenharmony_ci				insn_cnt = int(query.value(5))
84062306a36Sopenharmony_ci				cyc_cnt = int(query.value(6))
84162306a36Sopenharmony_ci				branch_count = int(query.value(7))
84262306a36Sopenharmony_ci			else:
84362306a36Sopenharmony_ci				insn_cnt = 0
84462306a36Sopenharmony_ci				cyc_cnt = 0
84562306a36Sopenharmony_ci				branch_count = int(query.value(5))
84662306a36Sopenharmony_ci			child_item = CallTreeLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
84762306a36Sopenharmony_ci			self.child_items.append(child_item)
84862306a36Sopenharmony_ci			self.child_count += 1
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci# Call tree data model level three item
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ciclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item):
85562306a36Sopenharmony_ci		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item)
85662306a36Sopenharmony_ci		dso = dsoname(dso)
85762306a36Sopenharmony_ci		if self.params.have_ipc:
85862306a36Sopenharmony_ci			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
85962306a36Sopenharmony_ci			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
86062306a36Sopenharmony_ci			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
86162306a36Sopenharmony_ci			ipc = CalcIPC(cyc_cnt, insn_cnt)
86262306a36Sopenharmony_ci			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
86362306a36Sopenharmony_ci		else:
86462306a36Sopenharmony_ci			self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
86562306a36Sopenharmony_ci		self.dbid = calls_id
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci# Call tree data model level two item
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ciclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
87262306a36Sopenharmony_ci		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, 0, parent_item)
87362306a36Sopenharmony_ci		if self.params.have_ipc:
87462306a36Sopenharmony_ci			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
87562306a36Sopenharmony_ci		else:
87662306a36Sopenharmony_ci			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
87762306a36Sopenharmony_ci		self.dbid = thread_id
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	def Select(self):
88062306a36Sopenharmony_ci		super(CallTreeLevelTwoItem, self).Select()
88162306a36Sopenharmony_ci		for child_item in self.child_items:
88262306a36Sopenharmony_ci			self.time += child_item.time
88362306a36Sopenharmony_ci			self.insn_cnt += child_item.insn_cnt
88462306a36Sopenharmony_ci			self.cyc_cnt += child_item.cyc_cnt
88562306a36Sopenharmony_ci			self.branch_count += child_item.branch_count
88662306a36Sopenharmony_ci		for child_item in self.child_items:
88762306a36Sopenharmony_ci			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
88862306a36Sopenharmony_ci			if self.params.have_ipc:
88962306a36Sopenharmony_ci				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
89062306a36Sopenharmony_ci				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
89162306a36Sopenharmony_ci				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
89262306a36Sopenharmony_ci			else:
89362306a36Sopenharmony_ci				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci# Call tree data model level one item
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ciclass CallTreeLevelOneItem(CallGraphLevelItemBase):
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	def __init__(self, glb, params, row, comm_id, comm, parent_item):
90062306a36Sopenharmony_ci		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
90162306a36Sopenharmony_ci		if self.params.have_ipc:
90262306a36Sopenharmony_ci			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
90362306a36Sopenharmony_ci		else:
90462306a36Sopenharmony_ci			self.data = [comm, "", "", "", "", "", ""]
90562306a36Sopenharmony_ci		self.dbid = comm_id
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	def Select(self):
90862306a36Sopenharmony_ci		self.query_done = True
90962306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
91062306a36Sopenharmony_ci		QueryExec(query, "SELECT thread_id, pid, tid"
91162306a36Sopenharmony_ci					" FROM comm_threads"
91262306a36Sopenharmony_ci					" INNER JOIN threads ON thread_id = threads.id"
91362306a36Sopenharmony_ci					" WHERE comm_id = " + str(self.dbid))
91462306a36Sopenharmony_ci		while query.next():
91562306a36Sopenharmony_ci			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
91662306a36Sopenharmony_ci			self.child_items.append(child_item)
91762306a36Sopenharmony_ci			self.child_count += 1
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci# Call tree data model root item
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ciclass CallTreeRootItem(CallGraphLevelItemBase):
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	def __init__(self, glb, params):
92462306a36Sopenharmony_ci		super(CallTreeRootItem, self).__init__(glb, params, 0, None)
92562306a36Sopenharmony_ci		self.dbid = 0
92662306a36Sopenharmony_ci		self.query_done = True
92762306a36Sopenharmony_ci		if_has_calls = ""
92862306a36Sopenharmony_ci		if IsSelectable(glb.db, "comms", columns = "has_calls"):
92962306a36Sopenharmony_ci			if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE
93062306a36Sopenharmony_ci		query = QSqlQuery(glb.db)
93162306a36Sopenharmony_ci		QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
93262306a36Sopenharmony_ci		while query.next():
93362306a36Sopenharmony_ci			if not query.value(0):
93462306a36Sopenharmony_ci				continue
93562306a36Sopenharmony_ci			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
93662306a36Sopenharmony_ci			self.child_items.append(child_item)
93762306a36Sopenharmony_ci			self.child_count += 1
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci# Call Tree data model
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ciclass CallTreeModel(CallGraphModelBase):
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
94462306a36Sopenharmony_ci		super(CallTreeModel, self).__init__(glb, parent)
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	def GetRoot(self):
94762306a36Sopenharmony_ci		return CallTreeRootItem(self.glb, self.params)
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	def columnCount(self, parent=None):
95062306a36Sopenharmony_ci		if self.params.have_ipc:
95162306a36Sopenharmony_ci			return 12
95262306a36Sopenharmony_ci		else:
95362306a36Sopenharmony_ci			return 7
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	def columnHeader(self, column):
95662306a36Sopenharmony_ci		if self.params.have_ipc:
95762306a36Sopenharmony_ci			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
95862306a36Sopenharmony_ci		else:
95962306a36Sopenharmony_ci			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
96062306a36Sopenharmony_ci		return headers[column]
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	def columnAlignment(self, column):
96362306a36Sopenharmony_ci		if self.params.have_ipc:
96462306a36Sopenharmony_ci			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
96562306a36Sopenharmony_ci		else:
96662306a36Sopenharmony_ci			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
96762306a36Sopenharmony_ci		return alignment[column]
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	def DoFindSelect(self, query, match):
97062306a36Sopenharmony_ci		QueryExec(query, "SELECT calls.id, comm_id, thread_id"
97162306a36Sopenharmony_ci						" FROM calls"
97262306a36Sopenharmony_ci						" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
97362306a36Sopenharmony_ci						" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
97462306a36Sopenharmony_ci						" WHERE calls.id <> 0"
97562306a36Sopenharmony_ci						" AND symbols.name" + match +
97662306a36Sopenharmony_ci						" ORDER BY comm_id, thread_id, call_time, calls.id")
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	def FindPath(self, query):
97962306a36Sopenharmony_ci		# Turn the query result into a list of ids that the tree view can walk
98062306a36Sopenharmony_ci		# to open the tree at the right place.
98162306a36Sopenharmony_ci		ids = []
98262306a36Sopenharmony_ci		parent_id = query.value(0)
98362306a36Sopenharmony_ci		while parent_id:
98462306a36Sopenharmony_ci			ids.insert(0, parent_id)
98562306a36Sopenharmony_ci			q2 = QSqlQuery(self.glb.db)
98662306a36Sopenharmony_ci			QueryExec(q2, "SELECT parent_id"
98762306a36Sopenharmony_ci					" FROM calls"
98862306a36Sopenharmony_ci					" WHERE id = " + str(parent_id))
98962306a36Sopenharmony_ci			if not q2.next():
99062306a36Sopenharmony_ci				break
99162306a36Sopenharmony_ci			parent_id = q2.value(0)
99262306a36Sopenharmony_ci		ids.insert(0, query.value(2))
99362306a36Sopenharmony_ci		ids.insert(0, query.value(1))
99462306a36Sopenharmony_ci		return ids
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci# Vertical layout
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ciclass HBoxLayout(QHBoxLayout):
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	def __init__(self, *children):
100162306a36Sopenharmony_ci		super(HBoxLayout, self).__init__()
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		self.layout().setContentsMargins(0, 0, 0, 0)
100462306a36Sopenharmony_ci		for child in children:
100562306a36Sopenharmony_ci			if child.isWidgetType():
100662306a36Sopenharmony_ci				self.layout().addWidget(child)
100762306a36Sopenharmony_ci			else:
100862306a36Sopenharmony_ci				self.layout().addLayout(child)
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci# Horizontal layout
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ciclass VBoxLayout(QVBoxLayout):
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	def __init__(self, *children):
101562306a36Sopenharmony_ci		super(VBoxLayout, self).__init__()
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		self.layout().setContentsMargins(0, 0, 0, 0)
101862306a36Sopenharmony_ci		for child in children:
101962306a36Sopenharmony_ci			if child.isWidgetType():
102062306a36Sopenharmony_ci				self.layout().addWidget(child)
102162306a36Sopenharmony_ci			else:
102262306a36Sopenharmony_ci				self.layout().addLayout(child)
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci# Vertical layout widget
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ciclass VBox():
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	def __init__(self, *children):
102962306a36Sopenharmony_ci		self.vbox = QWidget()
103062306a36Sopenharmony_ci		self.vbox.setLayout(VBoxLayout(*children))
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	def Widget(self):
103362306a36Sopenharmony_ci		return self.vbox
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci# Tree window base
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ciclass TreeWindowBase(QMdiSubWindow):
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	def __init__(self, parent=None):
104062306a36Sopenharmony_ci		super(TreeWindowBase, self).__init__(parent)
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		self.model = None
104362306a36Sopenharmony_ci		self.find_bar = None
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		self.view = QTreeView()
104662306a36Sopenharmony_ci		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
104762306a36Sopenharmony_ci		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		self.context_menu = TreeContextMenu(self.view)
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	def DisplayFound(self, ids):
105262306a36Sopenharmony_ci		if not len(ids):
105362306a36Sopenharmony_ci			return False
105462306a36Sopenharmony_ci		parent = QModelIndex()
105562306a36Sopenharmony_ci		for dbid in ids:
105662306a36Sopenharmony_ci			found = False
105762306a36Sopenharmony_ci			n = self.model.rowCount(parent)
105862306a36Sopenharmony_ci			for row in xrange(n):
105962306a36Sopenharmony_ci				child = self.model.index(row, 0, parent)
106062306a36Sopenharmony_ci				if child.internalPointer().dbid == dbid:
106162306a36Sopenharmony_ci					found = True
106262306a36Sopenharmony_ci					self.view.setExpanded(parent, True)
106362306a36Sopenharmony_ci					self.view.setCurrentIndex(child)
106462306a36Sopenharmony_ci					parent = child
106562306a36Sopenharmony_ci					break
106662306a36Sopenharmony_ci			if not found:
106762306a36Sopenharmony_ci				break
106862306a36Sopenharmony_ci		return found
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context):
107162306a36Sopenharmony_ci		self.view.setFocus()
107262306a36Sopenharmony_ci		self.find_bar.Busy()
107362306a36Sopenharmony_ci		self.model.Find(value, direction, pattern, context, self.FindDone)
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	def FindDone(self, ids):
107662306a36Sopenharmony_ci		found = True
107762306a36Sopenharmony_ci		if not self.DisplayFound(ids):
107862306a36Sopenharmony_ci			found = False
107962306a36Sopenharmony_ci		self.find_bar.Idle()
108062306a36Sopenharmony_ci		if not found:
108162306a36Sopenharmony_ci			self.find_bar.NotFound()
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci# Context-sensitive call graph window
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ciclass CallGraphWindow(TreeWindowBase):
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
108962306a36Sopenharmony_ci		super(CallGraphWindow, self).__init__(parent)
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		self.view.setModel(self.model)
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
109662306a36Sopenharmony_ci			self.view.setColumnWidth(c, w)
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		self.find_bar = FindBar(self, self)
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		self.vbox = VBox(self.view, self.find_bar.Widget())
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		self.setWidget(self.vbox.Widget())
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci# Call tree window
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ciclass CallTreeWindow(TreeWindowBase):
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	def __init__(self, glb, parent=None, thread_at_time=None):
111162306a36Sopenharmony_ci		super(CallTreeWindow, self).__init__(parent)
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x))
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		self.view.setModel(self.model)
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)):
111862306a36Sopenharmony_ci			self.view.setColumnWidth(c, w)
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		self.find_bar = FindBar(self, self)
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		self.vbox = VBox(self.view, self.find_bar.Widget())
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		self.setWidget(self.vbox.Widget())
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree")
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		if thread_at_time:
112962306a36Sopenharmony_ci			self.DisplayThreadAtTime(*thread_at_time)
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	def DisplayThreadAtTime(self, comm_id, thread_id, time):
113262306a36Sopenharmony_ci		parent = QModelIndex()
113362306a36Sopenharmony_ci		for dbid in (comm_id, thread_id):
113462306a36Sopenharmony_ci			found = False
113562306a36Sopenharmony_ci			n = self.model.rowCount(parent)
113662306a36Sopenharmony_ci			for row in xrange(n):
113762306a36Sopenharmony_ci				child = self.model.index(row, 0, parent)
113862306a36Sopenharmony_ci				if child.internalPointer().dbid == dbid:
113962306a36Sopenharmony_ci					found = True
114062306a36Sopenharmony_ci					self.view.setExpanded(parent, True)
114162306a36Sopenharmony_ci					self.view.setCurrentIndex(child)
114262306a36Sopenharmony_ci					parent = child
114362306a36Sopenharmony_ci					break
114462306a36Sopenharmony_ci			if not found:
114562306a36Sopenharmony_ci				return
114662306a36Sopenharmony_ci		found = False
114762306a36Sopenharmony_ci		while True:
114862306a36Sopenharmony_ci			n = self.model.rowCount(parent)
114962306a36Sopenharmony_ci			if not n:
115062306a36Sopenharmony_ci				return
115162306a36Sopenharmony_ci			last_child = None
115262306a36Sopenharmony_ci			for row in xrange(n):
115362306a36Sopenharmony_ci				self.view.setExpanded(parent, True)
115462306a36Sopenharmony_ci				child = self.model.index(row, 0, parent)
115562306a36Sopenharmony_ci				child_call_time = child.internalPointer().call_time
115662306a36Sopenharmony_ci				if child_call_time < time:
115762306a36Sopenharmony_ci					last_child = child
115862306a36Sopenharmony_ci				elif child_call_time == time:
115962306a36Sopenharmony_ci					self.view.setCurrentIndex(child)
116062306a36Sopenharmony_ci					return
116162306a36Sopenharmony_ci				elif child_call_time > time:
116262306a36Sopenharmony_ci					break
116362306a36Sopenharmony_ci			if not last_child:
116462306a36Sopenharmony_ci				if not found:
116562306a36Sopenharmony_ci					child = self.model.index(0, 0, parent)
116662306a36Sopenharmony_ci					self.view.setExpanded(parent, True)
116762306a36Sopenharmony_ci					self.view.setCurrentIndex(child)
116862306a36Sopenharmony_ci				return
116962306a36Sopenharmony_ci			found = True
117062306a36Sopenharmony_ci			self.view.setExpanded(parent, True)
117162306a36Sopenharmony_ci			self.view.setCurrentIndex(last_child)
117262306a36Sopenharmony_ci			parent = last_child
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci# ExecComm() gets the comm_id of the command string that was set when the process exec'd i.e. the program name
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cidef ExecComm(db, thread_id, time):
117762306a36Sopenharmony_ci	query = QSqlQuery(db)
117862306a36Sopenharmony_ci	QueryExec(query, "SELECT comm_threads.comm_id, comms.c_time, comms.exec_flag"
117962306a36Sopenharmony_ci				" FROM comm_threads"
118062306a36Sopenharmony_ci				" INNER JOIN comms ON comms.id = comm_threads.comm_id"
118162306a36Sopenharmony_ci				" WHERE comm_threads.thread_id = " + str(thread_id) +
118262306a36Sopenharmony_ci				" ORDER BY comms.c_time, comms.id")
118362306a36Sopenharmony_ci	first = None
118462306a36Sopenharmony_ci	last = None
118562306a36Sopenharmony_ci	while query.next():
118662306a36Sopenharmony_ci		if first is None:
118762306a36Sopenharmony_ci			first = query.value(0)
118862306a36Sopenharmony_ci		if query.value(2) and Decimal(query.value(1)) <= Decimal(time):
118962306a36Sopenharmony_ci			last = query.value(0)
119062306a36Sopenharmony_ci	if not(last is None):
119162306a36Sopenharmony_ci		return last
119262306a36Sopenharmony_ci	return first
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci# Container for (x, y) data
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ciclass XY():
119762306a36Sopenharmony_ci	def __init__(self, x=0, y=0):
119862306a36Sopenharmony_ci		self.x = x
119962306a36Sopenharmony_ci		self.y = y
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	def __str__(self):
120262306a36Sopenharmony_ci		return "XY({}, {})".format(str(self.x), str(self.y))
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci# Container for sub-range data
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ciclass Subrange():
120762306a36Sopenharmony_ci	def __init__(self, lo=0, hi=0):
120862306a36Sopenharmony_ci		self.lo = lo
120962306a36Sopenharmony_ci		self.hi = hi
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	def __str__(self):
121262306a36Sopenharmony_ci		return "Subrange({}, {})".format(str(self.lo), str(self.hi))
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci# Graph data region base class
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ciclass GraphDataRegion(object):
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	def __init__(self, key, title = "", ordinal = ""):
121962306a36Sopenharmony_ci		self.key = key
122062306a36Sopenharmony_ci		self.title = title
122162306a36Sopenharmony_ci		self.ordinal = ordinal
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci# Function to sort GraphDataRegion
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cidef GraphDataRegionOrdinal(data_region):
122662306a36Sopenharmony_ci	return data_region.ordinal
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci# Attributes for a graph region
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ciclass GraphRegionAttribute():
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	def __init__(self, colour):
123362306a36Sopenharmony_ci		self.colour = colour
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci# Switch graph data region represents a task
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciclass SwitchGraphDataRegion(GraphDataRegion):
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	def __init__(self, key, exec_comm_id, pid, tid, comm, thread_id, comm_id):
124062306a36Sopenharmony_ci		super(SwitchGraphDataRegion, self).__init__(key)
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		self.title = str(pid) + " / " + str(tid) + " " + comm
124362306a36Sopenharmony_ci		# Order graph legend within exec comm by pid / tid / time
124462306a36Sopenharmony_ci		self.ordinal = str(pid).rjust(16) + str(exec_comm_id).rjust(8) + str(tid).rjust(16)
124562306a36Sopenharmony_ci		self.exec_comm_id = exec_comm_id
124662306a36Sopenharmony_ci		self.pid = pid
124762306a36Sopenharmony_ci		self.tid = tid
124862306a36Sopenharmony_ci		self.comm = comm
124962306a36Sopenharmony_ci		self.thread_id = thread_id
125062306a36Sopenharmony_ci		self.comm_id = comm_id
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci# Graph data point
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ciclass GraphDataPoint():
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	def __init__(self, data, index, x, y, altx=None, alty=None, hregion=None, vregion=None):
125762306a36Sopenharmony_ci		self.data = data
125862306a36Sopenharmony_ci		self.index = index
125962306a36Sopenharmony_ci		self.x = x
126062306a36Sopenharmony_ci		self.y = y
126162306a36Sopenharmony_ci		self.altx = altx
126262306a36Sopenharmony_ci		self.alty = alty
126362306a36Sopenharmony_ci		self.hregion = hregion
126462306a36Sopenharmony_ci		self.vregion = vregion
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci# Graph data (single graph) base class
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ciclass GraphData(object):
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	def __init__(self, collection, xbase=Decimal(0), ybase=Decimal(0)):
127162306a36Sopenharmony_ci		self.collection = collection
127262306a36Sopenharmony_ci		self.points = []
127362306a36Sopenharmony_ci		self.xbase = xbase
127462306a36Sopenharmony_ci		self.ybase = ybase
127562306a36Sopenharmony_ci		self.title = ""
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	def AddPoint(self, x, y, altx=None, alty=None, hregion=None, vregion=None):
127862306a36Sopenharmony_ci		index = len(self.points)
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci		x = float(Decimal(x) - self.xbase)
128162306a36Sopenharmony_ci		y = float(Decimal(y) - self.ybase)
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		self.points.append(GraphDataPoint(self, index, x, y, altx, alty, hregion, vregion))
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	def XToData(self, x):
128662306a36Sopenharmony_ci		return Decimal(x) + self.xbase
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	def YToData(self, y):
128962306a36Sopenharmony_ci		return Decimal(y) + self.ybase
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci# Switch graph data (for one CPU)
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ciclass SwitchGraphData(GraphData):
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	def __init__(self, db, collection, cpu, xbase):
129662306a36Sopenharmony_ci		super(SwitchGraphData, self).__init__(collection, xbase)
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		self.cpu = cpu
129962306a36Sopenharmony_ci		self.title = "CPU " + str(cpu)
130062306a36Sopenharmony_ci		self.SelectSwitches(db)
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	def SelectComms(self, db, thread_id, last_comm_id, start_time, end_time):
130362306a36Sopenharmony_ci		query = QSqlQuery(db)
130462306a36Sopenharmony_ci		QueryExec(query, "SELECT id, c_time"
130562306a36Sopenharmony_ci					" FROM comms"
130662306a36Sopenharmony_ci					" WHERE c_thread_id = " + str(thread_id) +
130762306a36Sopenharmony_ci					"   AND exec_flag = " + self.collection.glb.dbref.TRUE +
130862306a36Sopenharmony_ci					"   AND c_time >= " + str(start_time) +
130962306a36Sopenharmony_ci					"   AND c_time <= " + str(end_time) +
131062306a36Sopenharmony_ci					" ORDER BY c_time, id")
131162306a36Sopenharmony_ci		while query.next():
131262306a36Sopenharmony_ci			comm_id = query.value(0)
131362306a36Sopenharmony_ci			if comm_id == last_comm_id:
131462306a36Sopenharmony_ci				continue
131562306a36Sopenharmony_ci			time = query.value(1)
131662306a36Sopenharmony_ci			hregion = self.HRegion(db, thread_id, comm_id, time)
131762306a36Sopenharmony_ci			self.AddPoint(time, 1000, None, None, hregion)
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	def SelectSwitches(self, db):
132062306a36Sopenharmony_ci		last_time = None
132162306a36Sopenharmony_ci		last_comm_id = None
132262306a36Sopenharmony_ci		last_thread_id = None
132362306a36Sopenharmony_ci		query = QSqlQuery(db)
132462306a36Sopenharmony_ci		QueryExec(query, "SELECT time, thread_out_id, thread_in_id, comm_out_id, comm_in_id, flags"
132562306a36Sopenharmony_ci					" FROM context_switches"
132662306a36Sopenharmony_ci					" WHERE machine_id = " + str(self.collection.machine_id) +
132762306a36Sopenharmony_ci					"   AND cpu = " + str(self.cpu) +
132862306a36Sopenharmony_ci					" ORDER BY time, id")
132962306a36Sopenharmony_ci		while query.next():
133062306a36Sopenharmony_ci			flags = int(query.value(5))
133162306a36Sopenharmony_ci			if flags & 1:
133262306a36Sopenharmony_ci				# Schedule-out: detect and add exec's
133362306a36Sopenharmony_ci				if last_thread_id == query.value(1) and last_comm_id is not None and last_comm_id != query.value(3):
133462306a36Sopenharmony_ci					self.SelectComms(db, last_thread_id, last_comm_id, last_time, query.value(0))
133562306a36Sopenharmony_ci				continue
133662306a36Sopenharmony_ci			# Schedule-in: add data point
133762306a36Sopenharmony_ci			if len(self.points) == 0:
133862306a36Sopenharmony_ci				start_time = self.collection.glb.StartTime(self.collection.machine_id)
133962306a36Sopenharmony_ci				hregion = self.HRegion(db, query.value(1), query.value(3), start_time)
134062306a36Sopenharmony_ci				self.AddPoint(start_time, 1000, None, None, hregion)
134162306a36Sopenharmony_ci			time = query.value(0)
134262306a36Sopenharmony_ci			comm_id = query.value(4)
134362306a36Sopenharmony_ci			thread_id = query.value(2)
134462306a36Sopenharmony_ci			hregion = self.HRegion(db, thread_id, comm_id, time)
134562306a36Sopenharmony_ci			self.AddPoint(time, 1000, None, None, hregion)
134662306a36Sopenharmony_ci			last_time = time
134762306a36Sopenharmony_ci			last_comm_id = comm_id
134862306a36Sopenharmony_ci			last_thread_id = thread_id
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	def NewHRegion(self, db, key, thread_id, comm_id, time):
135162306a36Sopenharmony_ci		exec_comm_id = ExecComm(db, thread_id, time)
135262306a36Sopenharmony_ci		query = QSqlQuery(db)
135362306a36Sopenharmony_ci		QueryExec(query, "SELECT pid, tid FROM threads WHERE id = " + str(thread_id))
135462306a36Sopenharmony_ci		if query.next():
135562306a36Sopenharmony_ci			pid = query.value(0)
135662306a36Sopenharmony_ci			tid = query.value(1)
135762306a36Sopenharmony_ci		else:
135862306a36Sopenharmony_ci			pid = -1
135962306a36Sopenharmony_ci			tid = -1
136062306a36Sopenharmony_ci		query = QSqlQuery(db)
136162306a36Sopenharmony_ci		QueryExec(query, "SELECT comm FROM comms WHERE id = " + str(comm_id))
136262306a36Sopenharmony_ci		if query.next():
136362306a36Sopenharmony_ci			comm = query.value(0)
136462306a36Sopenharmony_ci		else:
136562306a36Sopenharmony_ci			comm = ""
136662306a36Sopenharmony_ci		return SwitchGraphDataRegion(key, exec_comm_id, pid, tid, comm, thread_id, comm_id)
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	def HRegion(self, db, thread_id, comm_id, time):
136962306a36Sopenharmony_ci		key = str(thread_id) + ":" + str(comm_id)
137062306a36Sopenharmony_ci		hregion = self.collection.LookupHRegion(key)
137162306a36Sopenharmony_ci		if hregion is None:
137262306a36Sopenharmony_ci			hregion = self.NewHRegion(db, key, thread_id, comm_id, time)
137362306a36Sopenharmony_ci			self.collection.AddHRegion(key, hregion)
137462306a36Sopenharmony_ci		return hregion
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci# Graph data collection (multiple related graphs) base class
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ciclass GraphDataCollection(object):
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	def __init__(self, glb):
138162306a36Sopenharmony_ci		self.glb = glb
138262306a36Sopenharmony_ci		self.data = []
138362306a36Sopenharmony_ci		self.hregions = {}
138462306a36Sopenharmony_ci		self.xrangelo = None
138562306a36Sopenharmony_ci		self.xrangehi = None
138662306a36Sopenharmony_ci		self.yrangelo = None
138762306a36Sopenharmony_ci		self.yrangehi = None
138862306a36Sopenharmony_ci		self.dp = XY(0, 0)
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	def AddGraphData(self, data):
139162306a36Sopenharmony_ci		self.data.append(data)
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	def LookupHRegion(self, key):
139462306a36Sopenharmony_ci		if key in self.hregions:
139562306a36Sopenharmony_ci			return self.hregions[key]
139662306a36Sopenharmony_ci		return None
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	def AddHRegion(self, key, hregion):
139962306a36Sopenharmony_ci		self.hregions[key] = hregion
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci# Switch graph data collection (SwitchGraphData for each CPU)
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ciclass SwitchGraphDataCollection(GraphDataCollection):
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	def __init__(self, glb, db, machine_id):
140662306a36Sopenharmony_ci		super(SwitchGraphDataCollection, self).__init__(glb)
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		self.machine_id = machine_id
140962306a36Sopenharmony_ci		self.cpus = self.SelectCPUs(db)
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		self.xrangelo = glb.StartTime(machine_id)
141262306a36Sopenharmony_ci		self.xrangehi = glb.FinishTime(machine_id)
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		self.yrangelo = Decimal(0)
141562306a36Sopenharmony_ci		self.yrangehi = Decimal(1000)
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		for cpu in self.cpus:
141862306a36Sopenharmony_ci			self.AddGraphData(SwitchGraphData(db, self, cpu, self.xrangelo))
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	def SelectCPUs(self, db):
142162306a36Sopenharmony_ci		cpus = []
142262306a36Sopenharmony_ci		query = QSqlQuery(db)
142362306a36Sopenharmony_ci		QueryExec(query, "SELECT DISTINCT cpu"
142462306a36Sopenharmony_ci					" FROM context_switches"
142562306a36Sopenharmony_ci					" WHERE machine_id = " + str(self.machine_id))
142662306a36Sopenharmony_ci		while query.next():
142762306a36Sopenharmony_ci			cpus.append(int(query.value(0)))
142862306a36Sopenharmony_ci		return sorted(cpus)
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci# Switch graph data graphics item displays the graphed data
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ciclass SwitchGraphDataGraphicsItem(QGraphicsItem):
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	def __init__(self, data, graph_width, graph_height, attrs, event_handler, parent=None):
143562306a36Sopenharmony_ci		super(SwitchGraphDataGraphicsItem, self).__init__(parent)
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		self.data = data
143862306a36Sopenharmony_ci		self.graph_width = graph_width
143962306a36Sopenharmony_ci		self.graph_height = graph_height
144062306a36Sopenharmony_ci		self.attrs = attrs
144162306a36Sopenharmony_ci		self.event_handler = event_handler
144262306a36Sopenharmony_ci		self.setAcceptHoverEvents(True)
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	def boundingRect(self):
144562306a36Sopenharmony_ci		return QRectF(0, 0, self.graph_width, self.graph_height)
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	def PaintPoint(self, painter, last, x):
144862306a36Sopenharmony_ci		if not(last is None or last.hregion.pid == 0 or x < self.attrs.subrange.x.lo):
144962306a36Sopenharmony_ci			if last.x < self.attrs.subrange.x.lo:
145062306a36Sopenharmony_ci				x0 = self.attrs.subrange.x.lo
145162306a36Sopenharmony_ci			else:
145262306a36Sopenharmony_ci				x0 = last.x
145362306a36Sopenharmony_ci			if x > self.attrs.subrange.x.hi:
145462306a36Sopenharmony_ci				x1 = self.attrs.subrange.x.hi
145562306a36Sopenharmony_ci			else:
145662306a36Sopenharmony_ci				x1 = x - 1
145762306a36Sopenharmony_ci			x0 = self.attrs.XToPixel(x0)
145862306a36Sopenharmony_ci			x1 = self.attrs.XToPixel(x1)
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci			y0 = self.attrs.YToPixel(last.y)
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci			colour = self.attrs.region_attributes[last.hregion.key].colour
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci			width = x1 - x0 + 1
146562306a36Sopenharmony_ci			if width < 2:
146662306a36Sopenharmony_ci				painter.setPen(colour)
146762306a36Sopenharmony_ci				painter.drawLine(x0, self.graph_height - y0, x0, self.graph_height)
146862306a36Sopenharmony_ci			else:
146962306a36Sopenharmony_ci				painter.fillRect(x0, self.graph_height - y0, width, self.graph_height - 1, colour)
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	def paint(self, painter, option, widget):
147262306a36Sopenharmony_ci		last = None
147362306a36Sopenharmony_ci		for point in self.data.points:
147462306a36Sopenharmony_ci			self.PaintPoint(painter, last, point.x)
147562306a36Sopenharmony_ci			if point.x > self.attrs.subrange.x.hi:
147662306a36Sopenharmony_ci				break;
147762306a36Sopenharmony_ci			last = point
147862306a36Sopenharmony_ci		self.PaintPoint(painter, last, self.attrs.subrange.x.hi + 1)
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	def BinarySearchPoint(self, target):
148162306a36Sopenharmony_ci		lower_pos = 0
148262306a36Sopenharmony_ci		higher_pos = len(self.data.points)
148362306a36Sopenharmony_ci		while True:
148462306a36Sopenharmony_ci			pos = int((lower_pos + higher_pos) / 2)
148562306a36Sopenharmony_ci			val = self.data.points[pos].x
148662306a36Sopenharmony_ci			if target >= val:
148762306a36Sopenharmony_ci				lower_pos = pos
148862306a36Sopenharmony_ci			else:
148962306a36Sopenharmony_ci				higher_pos = pos
149062306a36Sopenharmony_ci			if higher_pos <= lower_pos + 1:
149162306a36Sopenharmony_ci				return lower_pos
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	def XPixelToData(self, x):
149462306a36Sopenharmony_ci		x = self.attrs.PixelToX(x)
149562306a36Sopenharmony_ci		if x < self.data.points[0].x:
149662306a36Sopenharmony_ci			x = 0
149762306a36Sopenharmony_ci			pos = 0
149862306a36Sopenharmony_ci			low = True
149962306a36Sopenharmony_ci		else:
150062306a36Sopenharmony_ci			pos = self.BinarySearchPoint(x)
150162306a36Sopenharmony_ci			low = False
150262306a36Sopenharmony_ci		return (low, pos, self.data.XToData(x))
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	def EventToData(self, event):
150562306a36Sopenharmony_ci		no_data = (None,) * 4
150662306a36Sopenharmony_ci		if len(self.data.points) < 1:
150762306a36Sopenharmony_ci			return no_data
150862306a36Sopenharmony_ci		x = event.pos().x()
150962306a36Sopenharmony_ci		if x < 0:
151062306a36Sopenharmony_ci			return no_data
151162306a36Sopenharmony_ci		low0, pos0, time_from = self.XPixelToData(x)
151262306a36Sopenharmony_ci		low1, pos1, time_to = self.XPixelToData(x + 1)
151362306a36Sopenharmony_ci		hregions = set()
151462306a36Sopenharmony_ci		hregion_times = []
151562306a36Sopenharmony_ci		if not low1:
151662306a36Sopenharmony_ci			for i in xrange(pos0, pos1 + 1):
151762306a36Sopenharmony_ci				hregion = self.data.points[i].hregion
151862306a36Sopenharmony_ci				hregions.add(hregion)
151962306a36Sopenharmony_ci				if i == pos0:
152062306a36Sopenharmony_ci					time = time_from
152162306a36Sopenharmony_ci				else:
152262306a36Sopenharmony_ci					time = self.data.XToData(self.data.points[i].x)
152362306a36Sopenharmony_ci				hregion_times.append((hregion, time))
152462306a36Sopenharmony_ci		return (time_from, time_to, hregions, hregion_times)
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	def hoverMoveEvent(self, event):
152762306a36Sopenharmony_ci		time_from, time_to, hregions, hregion_times = self.EventToData(event)
152862306a36Sopenharmony_ci		if time_from is not None:
152962306a36Sopenharmony_ci			self.event_handler.PointEvent(self.data.cpu, time_from, time_to, hregions)
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	def hoverLeaveEvent(self, event):
153262306a36Sopenharmony_ci		self.event_handler.NoPointEvent()
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	def mousePressEvent(self, event):
153562306a36Sopenharmony_ci		if event.button() != Qt.RightButton:
153662306a36Sopenharmony_ci			super(SwitchGraphDataGraphicsItem, self).mousePressEvent(event)
153762306a36Sopenharmony_ci			return
153862306a36Sopenharmony_ci		time_from, time_to, hregions, hregion_times = self.EventToData(event)
153962306a36Sopenharmony_ci		if hregion_times:
154062306a36Sopenharmony_ci			self.event_handler.RightClickEvent(self.data.cpu, hregion_times, event.screenPos())
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci# X-axis graphics item
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ciclass XAxisGraphicsItem(QGraphicsItem):
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	def __init__(self, width, parent=None):
154762306a36Sopenharmony_ci		super(XAxisGraphicsItem, self).__init__(parent)
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci		self.width = width
155062306a36Sopenharmony_ci		self.max_mark_sz = 4
155162306a36Sopenharmony_ci		self.height = self.max_mark_sz + 1
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	def boundingRect(self):
155462306a36Sopenharmony_ci		return QRectF(0, 0, self.width, self.height)
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	def Step(self):
155762306a36Sopenharmony_ci		attrs = self.parentItem().attrs
155862306a36Sopenharmony_ci		subrange = attrs.subrange.x
155962306a36Sopenharmony_ci		t = subrange.hi - subrange.lo
156062306a36Sopenharmony_ci		s = (3.0 * t) / self.width
156162306a36Sopenharmony_ci		n = 1.0
156262306a36Sopenharmony_ci		while s > n:
156362306a36Sopenharmony_ci			n = n * 10.0
156462306a36Sopenharmony_ci		return n
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	def PaintMarks(self, painter, at_y, lo, hi, step, i):
156762306a36Sopenharmony_ci		attrs = self.parentItem().attrs
156862306a36Sopenharmony_ci		x = lo
156962306a36Sopenharmony_ci		while x <= hi:
157062306a36Sopenharmony_ci			xp = attrs.XToPixel(x)
157162306a36Sopenharmony_ci			if i % 10:
157262306a36Sopenharmony_ci				if i % 5:
157362306a36Sopenharmony_ci					sz = 1
157462306a36Sopenharmony_ci				else:
157562306a36Sopenharmony_ci					sz = 2
157662306a36Sopenharmony_ci			else:
157762306a36Sopenharmony_ci				sz = self.max_mark_sz
157862306a36Sopenharmony_ci				i = 0
157962306a36Sopenharmony_ci			painter.drawLine(xp, at_y, xp, at_y + sz)
158062306a36Sopenharmony_ci			x += step
158162306a36Sopenharmony_ci			i += 1
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	def paint(self, painter, option, widget):
158462306a36Sopenharmony_ci		# Using QPainter::drawLine(int x1, int y1, int x2, int y2) so x2 = width -1
158562306a36Sopenharmony_ci		painter.drawLine(0, 0, self.width - 1, 0)
158662306a36Sopenharmony_ci		n = self.Step()
158762306a36Sopenharmony_ci		attrs = self.parentItem().attrs
158862306a36Sopenharmony_ci		subrange = attrs.subrange.x
158962306a36Sopenharmony_ci		if subrange.lo:
159062306a36Sopenharmony_ci			x_offset = n - (subrange.lo % n)
159162306a36Sopenharmony_ci		else:
159262306a36Sopenharmony_ci			x_offset = 0.0
159362306a36Sopenharmony_ci		x = subrange.lo + x_offset
159462306a36Sopenharmony_ci		i = (x / n) % 10
159562306a36Sopenharmony_ci		self.PaintMarks(painter, 0, x, subrange.hi, n, i)
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	def ScaleDimensions(self):
159862306a36Sopenharmony_ci		n = self.Step()
159962306a36Sopenharmony_ci		attrs = self.parentItem().attrs
160062306a36Sopenharmony_ci		lo = attrs.subrange.x.lo
160162306a36Sopenharmony_ci		hi = (n * 10.0) + lo
160262306a36Sopenharmony_ci		width = attrs.XToPixel(hi)
160362306a36Sopenharmony_ci		if width > 500:
160462306a36Sopenharmony_ci			width = 0
160562306a36Sopenharmony_ci		return (n, lo, hi, width)
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	def PaintScale(self, painter, at_x, at_y):
160862306a36Sopenharmony_ci		n, lo, hi, width = self.ScaleDimensions()
160962306a36Sopenharmony_ci		if not width:
161062306a36Sopenharmony_ci			return
161162306a36Sopenharmony_ci		painter.drawLine(at_x, at_y, at_x + width, at_y)
161262306a36Sopenharmony_ci		self.PaintMarks(painter, at_y, lo, hi, n, 0)
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	def ScaleWidth(self):
161562306a36Sopenharmony_ci		n, lo, hi, width = self.ScaleDimensions()
161662306a36Sopenharmony_ci		return width
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	def ScaleHeight(self):
161962306a36Sopenharmony_ci		return self.height
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	def ScaleUnit(self):
162262306a36Sopenharmony_ci		return self.Step() * 10
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci# Scale graphics item base class
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ciclass ScaleGraphicsItem(QGraphicsItem):
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	def __init__(self, axis, parent=None):
162962306a36Sopenharmony_ci		super(ScaleGraphicsItem, self).__init__(parent)
163062306a36Sopenharmony_ci		self.axis = axis
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	def boundingRect(self):
163362306a36Sopenharmony_ci		scale_width = self.axis.ScaleWidth()
163462306a36Sopenharmony_ci		if not scale_width:
163562306a36Sopenharmony_ci			return QRectF()
163662306a36Sopenharmony_ci		return QRectF(0, 0, self.axis.ScaleWidth() + 100, self.axis.ScaleHeight())
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	def paint(self, painter, option, widget):
163962306a36Sopenharmony_ci		scale_width = self.axis.ScaleWidth()
164062306a36Sopenharmony_ci		if not scale_width:
164162306a36Sopenharmony_ci			return
164262306a36Sopenharmony_ci		self.axis.PaintScale(painter, 0, 5)
164362306a36Sopenharmony_ci		x = scale_width + 4
164462306a36Sopenharmony_ci		painter.drawText(QPointF(x, 10), self.Text())
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	def Unit(self):
164762306a36Sopenharmony_ci		return self.axis.ScaleUnit()
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	def Text(self):
165062306a36Sopenharmony_ci		return ""
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci# Switch graph scale graphics item
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ciclass SwitchScaleGraphicsItem(ScaleGraphicsItem):
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	def __init__(self, axis, parent=None):
165762306a36Sopenharmony_ci		super(SwitchScaleGraphicsItem, self).__init__(axis, parent)
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	def Text(self):
166062306a36Sopenharmony_ci		unit = self.Unit()
166162306a36Sopenharmony_ci		if unit >= 1000000000:
166262306a36Sopenharmony_ci			unit = int(unit / 1000000000)
166362306a36Sopenharmony_ci			us = "s"
166462306a36Sopenharmony_ci		elif unit >= 1000000:
166562306a36Sopenharmony_ci			unit = int(unit / 1000000)
166662306a36Sopenharmony_ci			us = "ms"
166762306a36Sopenharmony_ci		elif unit >= 1000:
166862306a36Sopenharmony_ci			unit = int(unit / 1000)
166962306a36Sopenharmony_ci			us = "us"
167062306a36Sopenharmony_ci		else:
167162306a36Sopenharmony_ci			unit = int(unit)
167262306a36Sopenharmony_ci			us = "ns"
167362306a36Sopenharmony_ci		return " = " + str(unit) + " " + us
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci# Switch graph graphics item contains graph title, scale, x/y-axis, and the graphed data
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ciclass SwitchGraphGraphicsItem(QGraphicsItem):
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	def __init__(self, collection, data, attrs, event_handler, first, parent=None):
168062306a36Sopenharmony_ci		super(SwitchGraphGraphicsItem, self).__init__(parent)
168162306a36Sopenharmony_ci		self.collection = collection
168262306a36Sopenharmony_ci		self.data = data
168362306a36Sopenharmony_ci		self.attrs = attrs
168462306a36Sopenharmony_ci		self.event_handler = event_handler
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci		margin = 20
168762306a36Sopenharmony_ci		title_width = 50
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		self.title_graphics = QGraphicsSimpleTextItem(data.title, self)
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci		self.title_graphics.setPos(margin, margin)
169262306a36Sopenharmony_ci		graph_width = attrs.XToPixel(attrs.subrange.x.hi) + 1
169362306a36Sopenharmony_ci		graph_height = attrs.YToPixel(attrs.subrange.y.hi) + 1
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci		self.graph_origin_x = margin + title_width + margin
169662306a36Sopenharmony_ci		self.graph_origin_y = graph_height + margin
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci		x_axis_size = 1
169962306a36Sopenharmony_ci		y_axis_size = 1
170062306a36Sopenharmony_ci		self.yline = QGraphicsLineItem(0, 0, 0, graph_height, self)
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci		self.x_axis = XAxisGraphicsItem(graph_width, self)
170362306a36Sopenharmony_ci		self.x_axis.setPos(self.graph_origin_x, self.graph_origin_y + 1)
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci		if first:
170662306a36Sopenharmony_ci			self.scale_item = SwitchScaleGraphicsItem(self.x_axis, self)
170762306a36Sopenharmony_ci			self.scale_item.setPos(self.graph_origin_x, self.graph_origin_y + 10)
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci		self.yline.setPos(self.graph_origin_x - y_axis_size, self.graph_origin_y - graph_height)
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		self.axis_point = QGraphicsLineItem(0, 0, 0, 0, self)
171262306a36Sopenharmony_ci		self.axis_point.setPos(self.graph_origin_x - 1, self.graph_origin_y +1)
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		self.width = self.graph_origin_x + graph_width + margin
171562306a36Sopenharmony_ci		self.height = self.graph_origin_y + margin
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		self.graph = SwitchGraphDataGraphicsItem(data, graph_width, graph_height, attrs, event_handler, self)
171862306a36Sopenharmony_ci		self.graph.setPos(self.graph_origin_x, self.graph_origin_y - graph_height)
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		if parent and 'EnableRubberBand' in dir(parent):
172162306a36Sopenharmony_ci			parent.EnableRubberBand(self.graph_origin_x, self.graph_origin_x + graph_width - 1, self)
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	def boundingRect(self):
172462306a36Sopenharmony_ci		return QRectF(0, 0, self.width, self.height)
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	def paint(self, painter, option, widget):
172762306a36Sopenharmony_ci		pass
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	def RBXToPixel(self, x):
173062306a36Sopenharmony_ci		return self.attrs.PixelToX(x - self.graph_origin_x)
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	def RBXRangeToPixel(self, x0, x1):
173362306a36Sopenharmony_ci		return (self.RBXToPixel(x0), self.RBXToPixel(x1 + 1))
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	def RBPixelToTime(self, x):
173662306a36Sopenharmony_ci		if x < self.data.points[0].x:
173762306a36Sopenharmony_ci			return self.data.XToData(0)
173862306a36Sopenharmony_ci		return self.data.XToData(x)
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	def RBEventTimes(self, x0, x1):
174162306a36Sopenharmony_ci		x0, x1 = self.RBXRangeToPixel(x0, x1)
174262306a36Sopenharmony_ci		time_from = self.RBPixelToTime(x0)
174362306a36Sopenharmony_ci		time_to = self.RBPixelToTime(x1)
174462306a36Sopenharmony_ci		return (time_from, time_to)
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	def RBEvent(self, x0, x1):
174762306a36Sopenharmony_ci		time_from, time_to = self.RBEventTimes(x0, x1)
174862306a36Sopenharmony_ci		self.event_handler.RangeEvent(time_from, time_to)
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	def RBMoveEvent(self, x0, x1):
175162306a36Sopenharmony_ci		if x1 < x0:
175262306a36Sopenharmony_ci			x0, x1 = x1, x0
175362306a36Sopenharmony_ci		self.RBEvent(x0, x1)
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	def RBReleaseEvent(self, x0, x1, selection_state):
175662306a36Sopenharmony_ci		if x1 < x0:
175762306a36Sopenharmony_ci			x0, x1 = x1, x0
175862306a36Sopenharmony_ci		x0, x1 = self.RBXRangeToPixel(x0, x1)
175962306a36Sopenharmony_ci		self.event_handler.SelectEvent(x0, x1, selection_state)
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci# Graphics item to draw a vertical bracket (used to highlight "forward" sub-range)
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ciclass VerticalBracketGraphicsItem(QGraphicsItem):
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	def __init__(self, parent=None):
176662306a36Sopenharmony_ci		super(VerticalBracketGraphicsItem, self).__init__(parent)
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci		self.width = 0
176962306a36Sopenharmony_ci		self.height = 0
177062306a36Sopenharmony_ci		self.hide()
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	def SetSize(self, width, height):
177362306a36Sopenharmony_ci		self.width = width + 1
177462306a36Sopenharmony_ci		self.height = height + 1
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	def boundingRect(self):
177762306a36Sopenharmony_ci		return QRectF(0, 0, self.width, self.height)
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	def paint(self, painter, option, widget):
178062306a36Sopenharmony_ci		colour = QColor(255, 255, 0, 32)
178162306a36Sopenharmony_ci		painter.fillRect(0, 0, self.width, self.height, colour)
178262306a36Sopenharmony_ci		x1 = self.width - 1
178362306a36Sopenharmony_ci		y1 = self.height - 1
178462306a36Sopenharmony_ci		painter.drawLine(0, 0, x1, 0)
178562306a36Sopenharmony_ci		painter.drawLine(0, 0, 0, 3)
178662306a36Sopenharmony_ci		painter.drawLine(x1, 0, x1, 3)
178762306a36Sopenharmony_ci		painter.drawLine(0, y1, x1, y1)
178862306a36Sopenharmony_ci		painter.drawLine(0, y1, 0, y1 - 3)
178962306a36Sopenharmony_ci		painter.drawLine(x1, y1, x1, y1 - 3)
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci# Graphics item to contain graphs arranged vertically
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ciclass VertcalGraphSetGraphicsItem(QGraphicsItem):
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	def __init__(self, collection, attrs, event_handler, child_class, parent=None):
179662306a36Sopenharmony_ci		super(VertcalGraphSetGraphicsItem, self).__init__(parent)
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci		self.collection = collection
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci		self.top = 10
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		self.width = 0
180362306a36Sopenharmony_ci		self.height = self.top
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci		self.rubber_band = None
180662306a36Sopenharmony_ci		self.rb_enabled = False
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		first = True
180962306a36Sopenharmony_ci		for data in collection.data:
181062306a36Sopenharmony_ci			child = child_class(collection, data, attrs, event_handler, first, self)
181162306a36Sopenharmony_ci			child.setPos(0, self.height + 1)
181262306a36Sopenharmony_ci			rect = child.boundingRect()
181362306a36Sopenharmony_ci			if rect.right() > self.width:
181462306a36Sopenharmony_ci				self.width = rect.right()
181562306a36Sopenharmony_ci			self.height = self.height + rect.bottom() + 1
181662306a36Sopenharmony_ci			first = False
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		self.bracket = VerticalBracketGraphicsItem(self)
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	def EnableRubberBand(self, xlo, xhi, rb_event_handler):
182162306a36Sopenharmony_ci		if self.rb_enabled:
182262306a36Sopenharmony_ci			return
182362306a36Sopenharmony_ci		self.rb_enabled = True
182462306a36Sopenharmony_ci		self.rb_in_view = False
182562306a36Sopenharmony_ci		self.setAcceptedMouseButtons(Qt.LeftButton)
182662306a36Sopenharmony_ci		self.rb_xlo = xlo
182762306a36Sopenharmony_ci		self.rb_xhi = xhi
182862306a36Sopenharmony_ci		self.rb_event_handler = rb_event_handler
182962306a36Sopenharmony_ci		self.mousePressEvent = self.MousePressEvent
183062306a36Sopenharmony_ci		self.mouseMoveEvent = self.MouseMoveEvent
183162306a36Sopenharmony_ci		self.mouseReleaseEvent = self.MouseReleaseEvent
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	def boundingRect(self):
183462306a36Sopenharmony_ci		return QRectF(0, 0, self.width, self.height)
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	def paint(self, painter, option, widget):
183762306a36Sopenharmony_ci		pass
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	def RubberBandParent(self):
184062306a36Sopenharmony_ci		scene = self.scene()
184162306a36Sopenharmony_ci		view = scene.views()[0]
184262306a36Sopenharmony_ci		viewport = view.viewport()
184362306a36Sopenharmony_ci		return viewport
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	def RubberBandSetGeometry(self, rect):
184662306a36Sopenharmony_ci		scene_rectf = self.mapRectToScene(QRectF(rect))
184762306a36Sopenharmony_ci		scene = self.scene()
184862306a36Sopenharmony_ci		view = scene.views()[0]
184962306a36Sopenharmony_ci		poly = view.mapFromScene(scene_rectf)
185062306a36Sopenharmony_ci		self.rubber_band.setGeometry(poly.boundingRect())
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	def SetSelection(self, selection_state):
185362306a36Sopenharmony_ci		if self.rubber_band:
185462306a36Sopenharmony_ci			if selection_state:
185562306a36Sopenharmony_ci				self.RubberBandSetGeometry(selection_state)
185662306a36Sopenharmony_ci				self.rubber_band.show()
185762306a36Sopenharmony_ci			else:
185862306a36Sopenharmony_ci				self.rubber_band.hide()
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	def SetBracket(self, rect):
186162306a36Sopenharmony_ci		if rect:
186262306a36Sopenharmony_ci			x, y, width, height = rect.x(), rect.y(), rect.width(), rect.height()
186362306a36Sopenharmony_ci			self.bracket.setPos(x, y)
186462306a36Sopenharmony_ci			self.bracket.SetSize(width, height)
186562306a36Sopenharmony_ci			self.bracket.show()
186662306a36Sopenharmony_ci		else:
186762306a36Sopenharmony_ci			self.bracket.hide()
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	def RubberBandX(self, event):
187062306a36Sopenharmony_ci		x = event.pos().toPoint().x()
187162306a36Sopenharmony_ci		if x < self.rb_xlo:
187262306a36Sopenharmony_ci			x = self.rb_xlo
187362306a36Sopenharmony_ci		elif x > self.rb_xhi:
187462306a36Sopenharmony_ci			x = self.rb_xhi
187562306a36Sopenharmony_ci		else:
187662306a36Sopenharmony_ci			self.rb_in_view = True
187762306a36Sopenharmony_ci		return x
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	def RubberBandRect(self, x):
188062306a36Sopenharmony_ci		if self.rb_origin.x() <= x:
188162306a36Sopenharmony_ci			width = x - self.rb_origin.x()
188262306a36Sopenharmony_ci			rect = QRect(self.rb_origin, QSize(width, self.height))
188362306a36Sopenharmony_ci		else:
188462306a36Sopenharmony_ci			width = self.rb_origin.x() - x
188562306a36Sopenharmony_ci			top_left = QPoint(self.rb_origin.x() - width, self.rb_origin.y())
188662306a36Sopenharmony_ci			rect = QRect(top_left, QSize(width, self.height))
188762306a36Sopenharmony_ci		return rect
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	def MousePressEvent(self, event):
189062306a36Sopenharmony_ci		self.rb_in_view = False
189162306a36Sopenharmony_ci		x = self.RubberBandX(event)
189262306a36Sopenharmony_ci		self.rb_origin = QPoint(x, self.top)
189362306a36Sopenharmony_ci		if self.rubber_band is None:
189462306a36Sopenharmony_ci			self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.RubberBandParent())
189562306a36Sopenharmony_ci		self.RubberBandSetGeometry(QRect(self.rb_origin, QSize(0, self.height)))
189662306a36Sopenharmony_ci		if self.rb_in_view:
189762306a36Sopenharmony_ci			self.rubber_band.show()
189862306a36Sopenharmony_ci			self.rb_event_handler.RBMoveEvent(x, x)
189962306a36Sopenharmony_ci		else:
190062306a36Sopenharmony_ci			self.rubber_band.hide()
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	def MouseMoveEvent(self, event):
190362306a36Sopenharmony_ci		x = self.RubberBandX(event)
190462306a36Sopenharmony_ci		rect = self.RubberBandRect(x)
190562306a36Sopenharmony_ci		self.RubberBandSetGeometry(rect)
190662306a36Sopenharmony_ci		if self.rb_in_view:
190762306a36Sopenharmony_ci			self.rubber_band.show()
190862306a36Sopenharmony_ci			self.rb_event_handler.RBMoveEvent(self.rb_origin.x(), x)
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	def MouseReleaseEvent(self, event):
191162306a36Sopenharmony_ci		x = self.RubberBandX(event)
191262306a36Sopenharmony_ci		if self.rb_in_view:
191362306a36Sopenharmony_ci			selection_state = self.RubberBandRect(x)
191462306a36Sopenharmony_ci		else:
191562306a36Sopenharmony_ci			selection_state = None
191662306a36Sopenharmony_ci		self.rb_event_handler.RBReleaseEvent(self.rb_origin.x(), x, selection_state)
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci# Switch graph legend data model
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ciclass SwitchGraphLegendModel(QAbstractTableModel):
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	def __init__(self, collection, region_attributes, parent=None):
192362306a36Sopenharmony_ci		super(SwitchGraphLegendModel, self).__init__(parent)
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci		self.region_attributes = region_attributes
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci		self.child_items = sorted(collection.hregions.values(), key=GraphDataRegionOrdinal)
192862306a36Sopenharmony_ci		self.child_count = len(self.child_items)
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci		self.highlight_set = set()
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		self.column_headers = ("pid", "tid", "comm")
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	def rowCount(self, parent):
193562306a36Sopenharmony_ci		return self.child_count
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	def headerData(self, section, orientation, role):
193862306a36Sopenharmony_ci		if role != Qt.DisplayRole:
193962306a36Sopenharmony_ci			return None
194062306a36Sopenharmony_ci		if orientation != Qt.Horizontal:
194162306a36Sopenharmony_ci			return None
194262306a36Sopenharmony_ci		return self.columnHeader(section)
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	def index(self, row, column, parent):
194562306a36Sopenharmony_ci		return self.createIndex(row, column, self.child_items[row])
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	def columnCount(self, parent=None):
194862306a36Sopenharmony_ci		return len(self.column_headers)
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	def columnHeader(self, column):
195162306a36Sopenharmony_ci		return self.column_headers[column]
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	def data(self, index, role):
195462306a36Sopenharmony_ci		if role == Qt.BackgroundRole:
195562306a36Sopenharmony_ci			child = self.child_items[index.row()]
195662306a36Sopenharmony_ci			if child in self.highlight_set:
195762306a36Sopenharmony_ci				return self.region_attributes[child.key].colour
195862306a36Sopenharmony_ci			return None
195962306a36Sopenharmony_ci		if role == Qt.ForegroundRole:
196062306a36Sopenharmony_ci			child = self.child_items[index.row()]
196162306a36Sopenharmony_ci			if child in self.highlight_set:
196262306a36Sopenharmony_ci				return QColor(255, 255, 255)
196362306a36Sopenharmony_ci			return self.region_attributes[child.key].colour
196462306a36Sopenharmony_ci		if role != Qt.DisplayRole:
196562306a36Sopenharmony_ci			return None
196662306a36Sopenharmony_ci		hregion = self.child_items[index.row()]
196762306a36Sopenharmony_ci		col = index.column()
196862306a36Sopenharmony_ci		if col == 0:
196962306a36Sopenharmony_ci			return hregion.pid
197062306a36Sopenharmony_ci		if col == 1:
197162306a36Sopenharmony_ci			return hregion.tid
197262306a36Sopenharmony_ci		if col == 2:
197362306a36Sopenharmony_ci			return hregion.comm
197462306a36Sopenharmony_ci		return None
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	def SetHighlight(self, row, set_highlight):
197762306a36Sopenharmony_ci		child = self.child_items[row]
197862306a36Sopenharmony_ci		top_left = self.createIndex(row, 0, child)
197962306a36Sopenharmony_ci		bottom_right = self.createIndex(row, len(self.column_headers) - 1, child)
198062306a36Sopenharmony_ci		self.dataChanged.emit(top_left, bottom_right)
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	def Highlight(self, highlight_set):
198362306a36Sopenharmony_ci		for row in xrange(self.child_count):
198462306a36Sopenharmony_ci			child = self.child_items[row]
198562306a36Sopenharmony_ci			if child in self.highlight_set:
198662306a36Sopenharmony_ci				if child not in highlight_set:
198762306a36Sopenharmony_ci					self.SetHighlight(row, False)
198862306a36Sopenharmony_ci			elif child in highlight_set:
198962306a36Sopenharmony_ci				self.SetHighlight(row, True)
199062306a36Sopenharmony_ci		self.highlight_set = highlight_set
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci# Switch graph legend is a table
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ciclass SwitchGraphLegend(QWidget):
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	def __init__(self, collection, region_attributes, parent=None):
199762306a36Sopenharmony_ci		super(SwitchGraphLegend, self).__init__(parent)
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci		self.data_model = SwitchGraphLegendModel(collection, region_attributes)
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci		self.model = QSortFilterProxyModel()
200262306a36Sopenharmony_ci		self.model.setSourceModel(self.data_model)
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci		self.view = QTableView()
200562306a36Sopenharmony_ci		self.view.setModel(self.model)
200662306a36Sopenharmony_ci		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
200762306a36Sopenharmony_ci		self.view.verticalHeader().setVisible(False)
200862306a36Sopenharmony_ci		self.view.sortByColumn(-1, Qt.AscendingOrder)
200962306a36Sopenharmony_ci		self.view.setSortingEnabled(True)
201062306a36Sopenharmony_ci		self.view.resizeColumnsToContents()
201162306a36Sopenharmony_ci		self.view.resizeRowsToContents()
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci		self.vbox = VBoxLayout(self.view)
201462306a36Sopenharmony_ci		self.setLayout(self.vbox)
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci		sz1 = self.view.columnWidth(0) + self.view.columnWidth(1) + self.view.columnWidth(2) + 2
201762306a36Sopenharmony_ci		sz1 = sz1 + self.view.verticalScrollBar().sizeHint().width()
201862306a36Sopenharmony_ci		self.saved_size = sz1
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	def resizeEvent(self, event):
202162306a36Sopenharmony_ci		self.saved_size = self.size().width()
202262306a36Sopenharmony_ci		super(SwitchGraphLegend, self).resizeEvent(event)
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	def Highlight(self, highlight_set):
202562306a36Sopenharmony_ci		self.data_model.Highlight(highlight_set)
202662306a36Sopenharmony_ci		self.update()
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	def changeEvent(self, event):
202962306a36Sopenharmony_ci		if event.type() == QEvent.FontChange:
203062306a36Sopenharmony_ci			self.view.resizeRowsToContents()
203162306a36Sopenharmony_ci			self.view.resizeColumnsToContents()
203262306a36Sopenharmony_ci			# Need to resize rows again after column resize
203362306a36Sopenharmony_ci			self.view.resizeRowsToContents()
203462306a36Sopenharmony_ci		super(SwitchGraphLegend, self).changeEvent(event)
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci# Random colour generation
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cidef RGBColourTooLight(r, g, b):
203962306a36Sopenharmony_ci	if g > 230:
204062306a36Sopenharmony_ci		return True
204162306a36Sopenharmony_ci	if g <= 160:
204262306a36Sopenharmony_ci		return False
204362306a36Sopenharmony_ci	if r <= 180 and g <= 180:
204462306a36Sopenharmony_ci		return False
204562306a36Sopenharmony_ci	if r < 60:
204662306a36Sopenharmony_ci		return False
204762306a36Sopenharmony_ci	return True
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_cidef GenerateColours(x):
205062306a36Sopenharmony_ci	cs = [0]
205162306a36Sopenharmony_ci	for i in xrange(1, x):
205262306a36Sopenharmony_ci		cs.append(int((255.0 / i) + 0.5))
205362306a36Sopenharmony_ci	colours = []
205462306a36Sopenharmony_ci	for r in cs:
205562306a36Sopenharmony_ci		for g in cs:
205662306a36Sopenharmony_ci			for b in cs:
205762306a36Sopenharmony_ci				# Exclude black and colours that look too light against a white background
205862306a36Sopenharmony_ci				if (r, g, b) == (0, 0, 0) or RGBColourTooLight(r, g, b):
205962306a36Sopenharmony_ci					continue
206062306a36Sopenharmony_ci				colours.append(QColor(r, g, b))
206162306a36Sopenharmony_ci	return colours
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_cidef GenerateNColours(n):
206462306a36Sopenharmony_ci	for x in xrange(2, n + 2):
206562306a36Sopenharmony_ci		colours = GenerateColours(x)
206662306a36Sopenharmony_ci		if len(colours) >= n:
206762306a36Sopenharmony_ci			return colours
206862306a36Sopenharmony_ci	return []
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_cidef GenerateNRandomColours(n, seed):
207162306a36Sopenharmony_ci	colours = GenerateNColours(n)
207262306a36Sopenharmony_ci	random.seed(seed)
207362306a36Sopenharmony_ci	random.shuffle(colours)
207462306a36Sopenharmony_ci	return colours
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci# Graph attributes, in particular the scale and subrange that change when zooming
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ciclass GraphAttributes():
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	def __init__(self, scale, subrange, region_attributes, dp):
208162306a36Sopenharmony_ci		self.scale = scale
208262306a36Sopenharmony_ci		self.subrange = subrange
208362306a36Sopenharmony_ci		self.region_attributes = region_attributes
208462306a36Sopenharmony_ci		# Rounding avoids errors due to finite floating point precision
208562306a36Sopenharmony_ci		self.dp = dp	# data decimal places
208662306a36Sopenharmony_ci		self.Update()
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	def XToPixel(self, x):
208962306a36Sopenharmony_ci		return int(round((x - self.subrange.x.lo) * self.scale.x, self.pdp.x))
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	def YToPixel(self, y):
209262306a36Sopenharmony_ci		return int(round((y - self.subrange.y.lo) * self.scale.y, self.pdp.y))
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	def PixelToXRounded(self, px):
209562306a36Sopenharmony_ci		return round((round(px, 0) / self.scale.x), self.dp.x) + self.subrange.x.lo
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	def PixelToYRounded(self, py):
209862306a36Sopenharmony_ci		return round((round(py, 0) / self.scale.y), self.dp.y) + self.subrange.y.lo
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	def PixelToX(self, px):
210162306a36Sopenharmony_ci		x = self.PixelToXRounded(px)
210262306a36Sopenharmony_ci		if self.pdp.x == 0:
210362306a36Sopenharmony_ci			rt = self.XToPixel(x)
210462306a36Sopenharmony_ci			if rt > px:
210562306a36Sopenharmony_ci				return x - 1
210662306a36Sopenharmony_ci		return x
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	def PixelToY(self, py):
210962306a36Sopenharmony_ci		y = self.PixelToYRounded(py)
211062306a36Sopenharmony_ci		if self.pdp.y == 0:
211162306a36Sopenharmony_ci			rt = self.YToPixel(y)
211262306a36Sopenharmony_ci			if rt > py:
211362306a36Sopenharmony_ci				return y - 1
211462306a36Sopenharmony_ci		return y
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	def ToPDP(self, dp, scale):
211762306a36Sopenharmony_ci		# Calculate pixel decimal places:
211862306a36Sopenharmony_ci		#    (10 ** dp) is the minimum delta in the data
211962306a36Sopenharmony_ci		#    scale it to get the minimum delta in pixels
212062306a36Sopenharmony_ci		#    log10 gives the number of decimals places negatively
212162306a36Sopenharmony_ci		#    subtrace 1 to divide by 10
212262306a36Sopenharmony_ci		#    round to the lower negative number
212362306a36Sopenharmony_ci		#    change the sign to get the number of decimals positively
212462306a36Sopenharmony_ci		x = math.log10((10 ** dp) * scale)
212562306a36Sopenharmony_ci		if x < 0:
212662306a36Sopenharmony_ci			x -= 1
212762306a36Sopenharmony_ci			x = -int(math.floor(x) - 0.1)
212862306a36Sopenharmony_ci		else:
212962306a36Sopenharmony_ci			x = 0
213062306a36Sopenharmony_ci		return x
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	def Update(self):
213362306a36Sopenharmony_ci		x = self.ToPDP(self.dp.x, self.scale.x)
213462306a36Sopenharmony_ci		y = self.ToPDP(self.dp.y, self.scale.y)
213562306a36Sopenharmony_ci		self.pdp = XY(x, y) # pixel decimal places
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci# Switch graph splitter which divides the CPU graphs from the legend
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ciclass SwitchGraphSplitter(QSplitter):
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	def __init__(self, parent=None):
214262306a36Sopenharmony_ci		super(SwitchGraphSplitter, self).__init__(parent)
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci		self.first_time = False
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	def resizeEvent(self, ev):
214762306a36Sopenharmony_ci		if self.first_time:
214862306a36Sopenharmony_ci			self.first_time = False
214962306a36Sopenharmony_ci			sz1 = self.widget(1).view.columnWidth(0) + self.widget(1).view.columnWidth(1) + self.widget(1).view.columnWidth(2) + 2
215062306a36Sopenharmony_ci			sz1 = sz1 + self.widget(1).view.verticalScrollBar().sizeHint().width()
215162306a36Sopenharmony_ci			sz0 = self.size().width() - self.handleWidth() - sz1
215262306a36Sopenharmony_ci			self.setSizes([sz0, sz1])
215362306a36Sopenharmony_ci		elif not(self.widget(1).saved_size is None):
215462306a36Sopenharmony_ci			sz1 = self.widget(1).saved_size
215562306a36Sopenharmony_ci			sz0 = self.size().width() - self.handleWidth() - sz1
215662306a36Sopenharmony_ci			self.setSizes([sz0, sz1])
215762306a36Sopenharmony_ci		super(SwitchGraphSplitter, self).resizeEvent(ev)
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci# Graph widget base class
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ciclass GraphWidget(QWidget):
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	graph_title_changed = Signal(object)
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	def __init__(self, parent=None):
216662306a36Sopenharmony_ci		super(GraphWidget, self).__init__(parent)
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	def GraphTitleChanged(self, title):
216962306a36Sopenharmony_ci		self.graph_title_changed.emit(title)
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	def Title(self):
217262306a36Sopenharmony_ci		return ""
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci# Display time in s, ms, us or ns
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_cidef ToTimeStr(val):
217762306a36Sopenharmony_ci	val = Decimal(val)
217862306a36Sopenharmony_ci	if val >= 1000000000:
217962306a36Sopenharmony_ci		return "{} s".format((val / 1000000000).quantize(Decimal("0.000000001")))
218062306a36Sopenharmony_ci	if val >= 1000000:
218162306a36Sopenharmony_ci		return "{} ms".format((val / 1000000).quantize(Decimal("0.000001")))
218262306a36Sopenharmony_ci	if val >= 1000:
218362306a36Sopenharmony_ci		return "{} us".format((val / 1000).quantize(Decimal("0.001")))
218462306a36Sopenharmony_ci	return "{} ns".format(val.quantize(Decimal("1")))
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci# Switch (i.e. context switch i.e. Time Chart by CPU) graph widget which contains the CPU graphs and the legend and control buttons
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ciclass SwitchGraphWidget(GraphWidget):
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	def __init__(self, glb, collection, parent=None):
219162306a36Sopenharmony_ci		super(SwitchGraphWidget, self).__init__(parent)
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci		self.glb = glb
219462306a36Sopenharmony_ci		self.collection = collection
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci		self.back_state = []
219762306a36Sopenharmony_ci		self.forward_state = []
219862306a36Sopenharmony_ci		self.selection_state = (None, None)
219962306a36Sopenharmony_ci		self.fwd_rect = None
220062306a36Sopenharmony_ci		self.start_time = self.glb.StartTime(collection.machine_id)
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci		i = 0
220362306a36Sopenharmony_ci		hregions = collection.hregions.values()
220462306a36Sopenharmony_ci		colours = GenerateNRandomColours(len(hregions), 1013)
220562306a36Sopenharmony_ci		region_attributes = {}
220662306a36Sopenharmony_ci		for hregion in hregions:
220762306a36Sopenharmony_ci			if hregion.pid == 0 and hregion.tid == 0:
220862306a36Sopenharmony_ci				region_attributes[hregion.key] = GraphRegionAttribute(QColor(0, 0, 0))
220962306a36Sopenharmony_ci			else:
221062306a36Sopenharmony_ci				region_attributes[hregion.key] = GraphRegionAttribute(colours[i])
221162306a36Sopenharmony_ci				i = i + 1
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci		# Default to entire range
221462306a36Sopenharmony_ci		xsubrange = Subrange(0.0, float(collection.xrangehi - collection.xrangelo) + 1.0)
221562306a36Sopenharmony_ci		ysubrange = Subrange(0.0, float(collection.yrangehi - collection.yrangelo) + 1.0)
221662306a36Sopenharmony_ci		subrange = XY(xsubrange, ysubrange)
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci		scale = self.GetScaleForRange(subrange)
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci		self.attrs = GraphAttributes(scale, subrange, region_attributes, collection.dp)
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		self.item = VertcalGraphSetGraphicsItem(collection, self.attrs, self, SwitchGraphGraphicsItem)
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci		self.scene = QGraphicsScene()
222562306a36Sopenharmony_ci		self.scene.addItem(self.item)
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci		self.view = QGraphicsView(self.scene)
222862306a36Sopenharmony_ci		self.view.centerOn(0, 0)
222962306a36Sopenharmony_ci		self.view.setAlignment(Qt.AlignLeft | Qt.AlignTop)
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci		self.legend = SwitchGraphLegend(collection, region_attributes)
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci		self.splitter = SwitchGraphSplitter()
223462306a36Sopenharmony_ci		self.splitter.addWidget(self.view)
223562306a36Sopenharmony_ci		self.splitter.addWidget(self.legend)
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci		self.point_label = QLabel("")
223862306a36Sopenharmony_ci		self.point_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci		self.back_button = QToolButton()
224162306a36Sopenharmony_ci		self.back_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowLeft))
224262306a36Sopenharmony_ci		self.back_button.setDisabled(True)
224362306a36Sopenharmony_ci		self.back_button.released.connect(lambda: self.Back())
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci		self.forward_button = QToolButton()
224662306a36Sopenharmony_ci		self.forward_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowRight))
224762306a36Sopenharmony_ci		self.forward_button.setDisabled(True)
224862306a36Sopenharmony_ci		self.forward_button.released.connect(lambda: self.Forward())
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci		self.zoom_button = QToolButton()
225162306a36Sopenharmony_ci		self.zoom_button.setText("Zoom")
225262306a36Sopenharmony_ci		self.zoom_button.setDisabled(True)
225362306a36Sopenharmony_ci		self.zoom_button.released.connect(lambda: self.Zoom())
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci		self.hbox = HBoxLayout(self.back_button, self.forward_button, self.zoom_button, self.point_label)
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		self.vbox = VBoxLayout(self.splitter, self.hbox)
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci		self.setLayout(self.vbox)
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	def GetScaleForRangeX(self, xsubrange):
226262306a36Sopenharmony_ci		# Default graph 1000 pixels wide
226362306a36Sopenharmony_ci		dflt = 1000.0
226462306a36Sopenharmony_ci		r = xsubrange.hi - xsubrange.lo
226562306a36Sopenharmony_ci		return dflt / r
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	def GetScaleForRangeY(self, ysubrange):
226862306a36Sopenharmony_ci		# Default graph 50 pixels high
226962306a36Sopenharmony_ci		dflt = 50.0
227062306a36Sopenharmony_ci		r = ysubrange.hi - ysubrange.lo
227162306a36Sopenharmony_ci		return dflt / r
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	def GetScaleForRange(self, subrange):
227462306a36Sopenharmony_ci		# Default graph 1000 pixels wide, 50 pixels high
227562306a36Sopenharmony_ci		xscale = self.GetScaleForRangeX(subrange.x)
227662306a36Sopenharmony_ci		yscale = self.GetScaleForRangeY(subrange.y)
227762306a36Sopenharmony_ci		return XY(xscale, yscale)
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	def PointEvent(self, cpu, time_from, time_to, hregions):
228062306a36Sopenharmony_ci		text = "CPU: " + str(cpu)
228162306a36Sopenharmony_ci		time_from = time_from.quantize(Decimal(1))
228262306a36Sopenharmony_ci		rel_time_from = time_from - self.glb.StartTime(self.collection.machine_id)
228362306a36Sopenharmony_ci		text = text + " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ")"
228462306a36Sopenharmony_ci		self.point_label.setText(text)
228562306a36Sopenharmony_ci		self.legend.Highlight(hregions)
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	def RightClickEvent(self, cpu, hregion_times, pos):
228862306a36Sopenharmony_ci		if not IsSelectable(self.glb.db, "calls", "WHERE parent_id >= 0"):
228962306a36Sopenharmony_ci			return
229062306a36Sopenharmony_ci		menu = QMenu(self.view)
229162306a36Sopenharmony_ci		for hregion, time in hregion_times:
229262306a36Sopenharmony_ci			thread_at_time = (hregion.exec_comm_id, hregion.thread_id, time)
229362306a36Sopenharmony_ci			menu_text = "Show Call Tree for {} {}:{} at {}".format(hregion.comm, hregion.pid, hregion.tid, time)
229462306a36Sopenharmony_ci			menu.addAction(CreateAction(menu_text, "Show Call Tree", lambda a=None, args=thread_at_time: self.RightClickSelect(args), self.view))
229562306a36Sopenharmony_ci		menu.exec_(pos)
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	def RightClickSelect(self, args):
229862306a36Sopenharmony_ci		CallTreeWindow(self.glb, self.glb.mainwindow, thread_at_time=args)
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	def NoPointEvent(self):
230162306a36Sopenharmony_ci		self.point_label.setText("")
230262306a36Sopenharmony_ci		self.legend.Highlight({})
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	def RangeEvent(self, time_from, time_to):
230562306a36Sopenharmony_ci		time_from = time_from.quantize(Decimal(1))
230662306a36Sopenharmony_ci		time_to = time_to.quantize(Decimal(1))
230762306a36Sopenharmony_ci		if time_to <= time_from:
230862306a36Sopenharmony_ci			self.point_label.setText("")
230962306a36Sopenharmony_ci			return
231062306a36Sopenharmony_ci		rel_time_from = time_from - self.start_time
231162306a36Sopenharmony_ci		rel_time_to = time_to - self.start_time
231262306a36Sopenharmony_ci		text = " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ") to: " + str(time_to) + " (+" + ToTimeStr(rel_time_to) + ")"
231362306a36Sopenharmony_ci		text = text + " duration: " + ToTimeStr(time_to - time_from)
231462306a36Sopenharmony_ci		self.point_label.setText(text)
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	def BackState(self):
231762306a36Sopenharmony_ci		return (self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect)
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	def PushBackState(self):
232062306a36Sopenharmony_ci		state = copy.deepcopy(self.BackState())
232162306a36Sopenharmony_ci		self.back_state.append(state)
232262306a36Sopenharmony_ci		self.back_button.setEnabled(True)
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	def PopBackState(self):
232562306a36Sopenharmony_ci		self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.back_state.pop()
232662306a36Sopenharmony_ci		self.attrs.Update()
232762306a36Sopenharmony_ci		if not self.back_state:
232862306a36Sopenharmony_ci			self.back_button.setDisabled(True)
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	def PushForwardState(self):
233162306a36Sopenharmony_ci		state = copy.deepcopy(self.BackState())
233262306a36Sopenharmony_ci		self.forward_state.append(state)
233362306a36Sopenharmony_ci		self.forward_button.setEnabled(True)
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	def PopForwardState(self):
233662306a36Sopenharmony_ci		self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.forward_state.pop()
233762306a36Sopenharmony_ci		self.attrs.Update()
233862306a36Sopenharmony_ci		if not self.forward_state:
233962306a36Sopenharmony_ci			self.forward_button.setDisabled(True)
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	def Title(self):
234262306a36Sopenharmony_ci		time_from = self.collection.xrangelo + Decimal(self.attrs.subrange.x.lo)
234362306a36Sopenharmony_ci		time_to = self.collection.xrangelo + Decimal(self.attrs.subrange.x.hi)
234462306a36Sopenharmony_ci		rel_time_from = time_from - self.start_time
234562306a36Sopenharmony_ci		rel_time_to = time_to - self.start_time
234662306a36Sopenharmony_ci		title = "+" + ToTimeStr(rel_time_from) + " to +" + ToTimeStr(rel_time_to)
234762306a36Sopenharmony_ci		title = title + " (" + ToTimeStr(time_to - time_from) + ")"
234862306a36Sopenharmony_ci		return title
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	def Update(self):
235162306a36Sopenharmony_ci		selected_subrange, selection_state = self.selection_state
235262306a36Sopenharmony_ci		self.item.SetSelection(selection_state)
235362306a36Sopenharmony_ci		self.item.SetBracket(self.fwd_rect)
235462306a36Sopenharmony_ci		self.zoom_button.setDisabled(selected_subrange is None)
235562306a36Sopenharmony_ci		self.GraphTitleChanged(self.Title())
235662306a36Sopenharmony_ci		self.item.update(self.item.boundingRect())
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	def Back(self):
235962306a36Sopenharmony_ci		if not self.back_state:
236062306a36Sopenharmony_ci			return
236162306a36Sopenharmony_ci		self.PushForwardState()
236262306a36Sopenharmony_ci		self.PopBackState()
236362306a36Sopenharmony_ci		self.Update()
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	def Forward(self):
236662306a36Sopenharmony_ci		if not self.forward_state:
236762306a36Sopenharmony_ci			return
236862306a36Sopenharmony_ci		self.PushBackState()
236962306a36Sopenharmony_ci		self.PopForwardState()
237062306a36Sopenharmony_ci		self.Update()
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	def SelectEvent(self, x0, x1, selection_state):
237362306a36Sopenharmony_ci		if selection_state is None:
237462306a36Sopenharmony_ci			selected_subrange = None
237562306a36Sopenharmony_ci		else:
237662306a36Sopenharmony_ci			if x1 - x0 < 1.0:
237762306a36Sopenharmony_ci				x1 += 1.0
237862306a36Sopenharmony_ci			selected_subrange = Subrange(x0, x1)
237962306a36Sopenharmony_ci		self.selection_state = (selected_subrange, selection_state)
238062306a36Sopenharmony_ci		self.zoom_button.setDisabled(selected_subrange is None)
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	def Zoom(self):
238362306a36Sopenharmony_ci		selected_subrange, selection_state = self.selection_state
238462306a36Sopenharmony_ci		if selected_subrange is None:
238562306a36Sopenharmony_ci			return
238662306a36Sopenharmony_ci		self.fwd_rect = selection_state
238762306a36Sopenharmony_ci		self.item.SetSelection(None)
238862306a36Sopenharmony_ci		self.PushBackState()
238962306a36Sopenharmony_ci		self.attrs.subrange.x = selected_subrange
239062306a36Sopenharmony_ci		self.forward_state = []
239162306a36Sopenharmony_ci		self.forward_button.setDisabled(True)
239262306a36Sopenharmony_ci		self.selection_state = (None, None)
239362306a36Sopenharmony_ci		self.fwd_rect = None
239462306a36Sopenharmony_ci		self.attrs.scale.x = self.GetScaleForRangeX(self.attrs.subrange.x)
239562306a36Sopenharmony_ci		self.attrs.Update()
239662306a36Sopenharmony_ci		self.Update()
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci# Slow initialization - perform non-GUI initialization in a separate thread and put up a modal message box while waiting
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ciclass SlowInitClass():
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	def __init__(self, glb, title, init_fn):
240362306a36Sopenharmony_ci		self.init_fn = init_fn
240462306a36Sopenharmony_ci		self.done = False
240562306a36Sopenharmony_ci		self.result = None
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci		self.msg_box = QMessageBox(glb.mainwindow)
240862306a36Sopenharmony_ci		self.msg_box.setText("Initializing " + title + ". Please wait.")
240962306a36Sopenharmony_ci		self.msg_box.setWindowTitle("Initializing " + title)
241062306a36Sopenharmony_ci		self.msg_box.setWindowIcon(glb.mainwindow.style().standardIcon(QStyle.SP_MessageBoxInformation))
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci		self.init_thread = Thread(self.ThreadFn, glb)
241362306a36Sopenharmony_ci		self.init_thread.done.connect(lambda: self.Done(), Qt.QueuedConnection)
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci		self.init_thread.start()
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	def Done(self):
241862306a36Sopenharmony_ci		self.msg_box.done(0)
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	def ThreadFn(self, glb):
242162306a36Sopenharmony_ci		conn_name = "SlowInitClass" + str(os.getpid())
242262306a36Sopenharmony_ci		db, dbname = glb.dbref.Open(conn_name)
242362306a36Sopenharmony_ci		self.result = self.init_fn(db)
242462306a36Sopenharmony_ci		self.done = True
242562306a36Sopenharmony_ci		return (True, 0)
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	def Result(self):
242862306a36Sopenharmony_ci		while not self.done:
242962306a36Sopenharmony_ci			self.msg_box.exec_()
243062306a36Sopenharmony_ci		self.init_thread.wait()
243162306a36Sopenharmony_ci		return self.result
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_cidef SlowInit(glb, title, init_fn):
243462306a36Sopenharmony_ci	init = SlowInitClass(glb, title, init_fn)
243562306a36Sopenharmony_ci	return init.Result()
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci# Time chart by CPU window
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ciclass TimeChartByCPUWindow(QMdiSubWindow):
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
244262306a36Sopenharmony_ci		super(TimeChartByCPUWindow, self).__init__(parent)
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci		self.glb = glb
244562306a36Sopenharmony_ci		self.machine_id = glb.HostMachineId()
244662306a36Sopenharmony_ci		self.collection_name = "SwitchGraphDataCollection " + str(self.machine_id)
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci		collection = LookupModel(self.collection_name)
244962306a36Sopenharmony_ci		if collection is None:
245062306a36Sopenharmony_ci			collection = SlowInit(glb, "Time Chart", self.Init)
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		self.widget = SwitchGraphWidget(glb, collection, self)
245362306a36Sopenharmony_ci		self.view = self.widget
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci		self.base_title = "Time Chart by CPU"
245662306a36Sopenharmony_ci		self.setWindowTitle(self.base_title + self.widget.Title())
245762306a36Sopenharmony_ci		self.widget.graph_title_changed.connect(self.GraphTitleChanged)
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci		self.setWidget(self.widget)
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, self.windowTitle())
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	def Init(self, db):
246462306a36Sopenharmony_ci		return LookupCreateModel(self.collection_name, lambda : SwitchGraphDataCollection(self.glb, db, self.machine_id))
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	def GraphTitleChanged(self, title):
246762306a36Sopenharmony_ci		self.setWindowTitle(self.base_title + " : " + title)
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci# Child data item  finder
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ciclass ChildDataItemFinder():
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	def __init__(self, root):
247462306a36Sopenharmony_ci		self.root = root
247562306a36Sopenharmony_ci		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5
247662306a36Sopenharmony_ci		self.rows = []
247762306a36Sopenharmony_ci		self.pos = 0
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	def FindSelect(self):
248062306a36Sopenharmony_ci		self.rows = []
248162306a36Sopenharmony_ci		if self.pattern:
248262306a36Sopenharmony_ci			pattern = re.compile(self.value)
248362306a36Sopenharmony_ci			for child in self.root.child_items:
248462306a36Sopenharmony_ci				for column_data in child.data:
248562306a36Sopenharmony_ci					if re.search(pattern, str(column_data)) is not None:
248662306a36Sopenharmony_ci						self.rows.append(child.row)
248762306a36Sopenharmony_ci						break
248862306a36Sopenharmony_ci		else:
248962306a36Sopenharmony_ci			for child in self.root.child_items:
249062306a36Sopenharmony_ci				for column_data in child.data:
249162306a36Sopenharmony_ci					if self.value in str(column_data):
249262306a36Sopenharmony_ci						self.rows.append(child.row)
249362306a36Sopenharmony_ci						break
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	def FindValue(self):
249662306a36Sopenharmony_ci		self.pos = 0
249762306a36Sopenharmony_ci		if self.last_value != self.value or self.pattern != self.last_pattern:
249862306a36Sopenharmony_ci			self.FindSelect()
249962306a36Sopenharmony_ci		if not len(self.rows):
250062306a36Sopenharmony_ci			return -1
250162306a36Sopenharmony_ci		return self.rows[self.pos]
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	def FindThread(self):
250462306a36Sopenharmony_ci		if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern:
250562306a36Sopenharmony_ci			row = self.FindValue()
250662306a36Sopenharmony_ci		elif len(self.rows):
250762306a36Sopenharmony_ci			if self.direction > 0:
250862306a36Sopenharmony_ci				self.pos += 1
250962306a36Sopenharmony_ci				if self.pos >= len(self.rows):
251062306a36Sopenharmony_ci					self.pos = 0
251162306a36Sopenharmony_ci			else:
251262306a36Sopenharmony_ci				self.pos -= 1
251362306a36Sopenharmony_ci				if self.pos < 0:
251462306a36Sopenharmony_ci					self.pos = len(self.rows) - 1
251562306a36Sopenharmony_ci			row = self.rows[self.pos]
251662306a36Sopenharmony_ci		else:
251762306a36Sopenharmony_ci			row = -1
251862306a36Sopenharmony_ci		return (True, row)
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context, callback):
252162306a36Sopenharmony_ci		self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern)
252262306a36Sopenharmony_ci		# Use a thread so the UI is not blocked
252362306a36Sopenharmony_ci		thread = Thread(self.FindThread)
252462306a36Sopenharmony_ci		thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection)
252562306a36Sopenharmony_ci		thread.start()
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	def FindDone(self, thread, callback, row):
252862306a36Sopenharmony_ci		callback(row)
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci# Number of database records to fetch in one go
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ciglb_chunk_sz = 10000
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci# Background process for SQL data fetcher
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ciclass SQLFetcherProcess():
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep):
253962306a36Sopenharmony_ci		# Need a unique connection name
254062306a36Sopenharmony_ci		conn_name = "SQLFetcher" + str(os.getpid())
254162306a36Sopenharmony_ci		self.db, dbname = dbref.Open(conn_name)
254262306a36Sopenharmony_ci		self.sql = sql
254362306a36Sopenharmony_ci		self.buffer = buffer
254462306a36Sopenharmony_ci		self.head = head
254562306a36Sopenharmony_ci		self.tail = tail
254662306a36Sopenharmony_ci		self.fetch_count = fetch_count
254762306a36Sopenharmony_ci		self.fetching_done = fetching_done
254862306a36Sopenharmony_ci		self.process_target = process_target
254962306a36Sopenharmony_ci		self.wait_event = wait_event
255062306a36Sopenharmony_ci		self.fetched_event = fetched_event
255162306a36Sopenharmony_ci		self.prep = prep
255262306a36Sopenharmony_ci		self.query = QSqlQuery(self.db)
255362306a36Sopenharmony_ci		self.query_limit = 0 if "$$last_id$$" in sql else 2
255462306a36Sopenharmony_ci		self.last_id = -1
255562306a36Sopenharmony_ci		self.fetched = 0
255662306a36Sopenharmony_ci		self.more = True
255762306a36Sopenharmony_ci		self.local_head = self.head.value
255862306a36Sopenharmony_ci		self.local_tail = self.tail.value
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	def Select(self):
256162306a36Sopenharmony_ci		if self.query_limit:
256262306a36Sopenharmony_ci			if self.query_limit == 1:
256362306a36Sopenharmony_ci				return
256462306a36Sopenharmony_ci			self.query_limit -= 1
256562306a36Sopenharmony_ci		stmt = self.sql.replace("$$last_id$$", str(self.last_id))
256662306a36Sopenharmony_ci		QueryExec(self.query, stmt)
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	def Next(self):
256962306a36Sopenharmony_ci		if not self.query.next():
257062306a36Sopenharmony_ci			self.Select()
257162306a36Sopenharmony_ci			if not self.query.next():
257262306a36Sopenharmony_ci				return None
257362306a36Sopenharmony_ci		self.last_id = self.query.value(0)
257462306a36Sopenharmony_ci		return self.prep(self.query)
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	def WaitForTarget(self):
257762306a36Sopenharmony_ci		while True:
257862306a36Sopenharmony_ci			self.wait_event.clear()
257962306a36Sopenharmony_ci			target = self.process_target.value
258062306a36Sopenharmony_ci			if target > self.fetched or target < 0:
258162306a36Sopenharmony_ci				break
258262306a36Sopenharmony_ci			self.wait_event.wait()
258362306a36Sopenharmony_ci		return target
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	def HasSpace(self, sz):
258662306a36Sopenharmony_ci		if self.local_tail <= self.local_head:
258762306a36Sopenharmony_ci			space = len(self.buffer) - self.local_head
258862306a36Sopenharmony_ci			if space > sz:
258962306a36Sopenharmony_ci				return True
259062306a36Sopenharmony_ci			if space >= glb_nsz:
259162306a36Sopenharmony_ci				# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
259262306a36Sopenharmony_ci				nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
259362306a36Sopenharmony_ci				self.buffer[self.local_head : self.local_head + len(nd)] = nd
259462306a36Sopenharmony_ci			self.local_head = 0
259562306a36Sopenharmony_ci		if self.local_tail - self.local_head > sz:
259662306a36Sopenharmony_ci			return True
259762306a36Sopenharmony_ci		return False
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	def WaitForSpace(self, sz):
260062306a36Sopenharmony_ci		if self.HasSpace(sz):
260162306a36Sopenharmony_ci			return
260262306a36Sopenharmony_ci		while True:
260362306a36Sopenharmony_ci			self.wait_event.clear()
260462306a36Sopenharmony_ci			self.local_tail = self.tail.value
260562306a36Sopenharmony_ci			if self.HasSpace(sz):
260662306a36Sopenharmony_ci				return
260762306a36Sopenharmony_ci			self.wait_event.wait()
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	def AddToBuffer(self, obj):
261062306a36Sopenharmony_ci		d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
261162306a36Sopenharmony_ci		n = len(d)
261262306a36Sopenharmony_ci		nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
261362306a36Sopenharmony_ci		sz = n + glb_nsz
261462306a36Sopenharmony_ci		self.WaitForSpace(sz)
261562306a36Sopenharmony_ci		pos = self.local_head
261662306a36Sopenharmony_ci		self.buffer[pos : pos + len(nd)] = nd
261762306a36Sopenharmony_ci		self.buffer[pos + glb_nsz : pos + sz] = d
261862306a36Sopenharmony_ci		self.local_head += sz
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	def FetchBatch(self, batch_size):
262162306a36Sopenharmony_ci		fetched = 0
262262306a36Sopenharmony_ci		while batch_size > fetched:
262362306a36Sopenharmony_ci			obj = self.Next()
262462306a36Sopenharmony_ci			if obj is None:
262562306a36Sopenharmony_ci				self.more = False
262662306a36Sopenharmony_ci				break
262762306a36Sopenharmony_ci			self.AddToBuffer(obj)
262862306a36Sopenharmony_ci			fetched += 1
262962306a36Sopenharmony_ci		if fetched:
263062306a36Sopenharmony_ci			self.fetched += fetched
263162306a36Sopenharmony_ci			with self.fetch_count.get_lock():
263262306a36Sopenharmony_ci				self.fetch_count.value += fetched
263362306a36Sopenharmony_ci			self.head.value = self.local_head
263462306a36Sopenharmony_ci			self.fetched_event.set()
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	def Run(self):
263762306a36Sopenharmony_ci		while self.more:
263862306a36Sopenharmony_ci			target = self.WaitForTarget()
263962306a36Sopenharmony_ci			if target < 0:
264062306a36Sopenharmony_ci				break
264162306a36Sopenharmony_ci			batch_size = min(glb_chunk_sz, target - self.fetched)
264262306a36Sopenharmony_ci			self.FetchBatch(batch_size)
264362306a36Sopenharmony_ci		self.fetching_done.value = True
264462306a36Sopenharmony_ci		self.fetched_event.set()
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_cidef SQLFetcherFn(*x):
264762306a36Sopenharmony_ci	process = SQLFetcherProcess(*x)
264862306a36Sopenharmony_ci	process.Run()
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci# SQL data fetcher
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ciclass SQLFetcher(QObject):
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	done = Signal(object)
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	def __init__(self, glb, sql, prep, process_data, parent=None):
265762306a36Sopenharmony_ci		super(SQLFetcher, self).__init__(parent)
265862306a36Sopenharmony_ci		self.process_data = process_data
265962306a36Sopenharmony_ci		self.more = True
266062306a36Sopenharmony_ci		self.target = 0
266162306a36Sopenharmony_ci		self.last_target = 0
266262306a36Sopenharmony_ci		self.fetched = 0
266362306a36Sopenharmony_ci		self.buffer_size = 16 * 1024 * 1024
266462306a36Sopenharmony_ci		self.buffer = Array(c_char, self.buffer_size, lock=False)
266562306a36Sopenharmony_ci		self.head = Value(c_longlong)
266662306a36Sopenharmony_ci		self.tail = Value(c_longlong)
266762306a36Sopenharmony_ci		self.local_tail = 0
266862306a36Sopenharmony_ci		self.fetch_count = Value(c_longlong)
266962306a36Sopenharmony_ci		self.fetching_done = Value(c_bool)
267062306a36Sopenharmony_ci		self.last_count = 0
267162306a36Sopenharmony_ci		self.process_target = Value(c_longlong)
267262306a36Sopenharmony_ci		self.wait_event = Event()
267362306a36Sopenharmony_ci		self.fetched_event = Event()
267462306a36Sopenharmony_ci		glb.AddInstanceToShutdownOnExit(self)
267562306a36Sopenharmony_ci		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))
267662306a36Sopenharmony_ci		self.process.start()
267762306a36Sopenharmony_ci		self.thread = Thread(self.Thread)
267862306a36Sopenharmony_ci		self.thread.done.connect(self.ProcessData, Qt.QueuedConnection)
267962306a36Sopenharmony_ci		self.thread.start()
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	def Shutdown(self):
268262306a36Sopenharmony_ci		# Tell the thread and process to exit
268362306a36Sopenharmony_ci		self.process_target.value = -1
268462306a36Sopenharmony_ci		self.wait_event.set()
268562306a36Sopenharmony_ci		self.more = False
268662306a36Sopenharmony_ci		self.fetching_done.value = True
268762306a36Sopenharmony_ci		self.fetched_event.set()
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	def Thread(self):
269062306a36Sopenharmony_ci		if not self.more:
269162306a36Sopenharmony_ci			return True, 0
269262306a36Sopenharmony_ci		while True:
269362306a36Sopenharmony_ci			self.fetched_event.clear()
269462306a36Sopenharmony_ci			fetch_count = self.fetch_count.value
269562306a36Sopenharmony_ci			if fetch_count != self.last_count:
269662306a36Sopenharmony_ci				break
269762306a36Sopenharmony_ci			if self.fetching_done.value:
269862306a36Sopenharmony_ci				self.more = False
269962306a36Sopenharmony_ci				return True, 0
270062306a36Sopenharmony_ci			self.fetched_event.wait()
270162306a36Sopenharmony_ci		count = fetch_count - self.last_count
270262306a36Sopenharmony_ci		self.last_count = fetch_count
270362306a36Sopenharmony_ci		self.fetched += count
270462306a36Sopenharmony_ci		return False, count
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	def Fetch(self, nr):
270762306a36Sopenharmony_ci		if not self.more:
270862306a36Sopenharmony_ci			# -1 inidcates there are no more
270962306a36Sopenharmony_ci			return -1
271062306a36Sopenharmony_ci		result = self.fetched
271162306a36Sopenharmony_ci		extra = result + nr - self.target
271262306a36Sopenharmony_ci		if extra > 0:
271362306a36Sopenharmony_ci			self.target += extra
271462306a36Sopenharmony_ci			# process_target < 0 indicates shutting down
271562306a36Sopenharmony_ci			if self.process_target.value >= 0:
271662306a36Sopenharmony_ci				self.process_target.value = self.target
271762306a36Sopenharmony_ci			self.wait_event.set()
271862306a36Sopenharmony_ci		return result
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	def RemoveFromBuffer(self):
272162306a36Sopenharmony_ci		pos = self.local_tail
272262306a36Sopenharmony_ci		if len(self.buffer) - pos < glb_nsz:
272362306a36Sopenharmony_ci			pos = 0
272462306a36Sopenharmony_ci		n = pickle.loads(self.buffer[pos : pos + glb_nsz])
272562306a36Sopenharmony_ci		if n == 0:
272662306a36Sopenharmony_ci			pos = 0
272762306a36Sopenharmony_ci			n = pickle.loads(self.buffer[0 : glb_nsz])
272862306a36Sopenharmony_ci		pos += glb_nsz
272962306a36Sopenharmony_ci		obj = pickle.loads(self.buffer[pos : pos + n])
273062306a36Sopenharmony_ci		self.local_tail = pos + n
273162306a36Sopenharmony_ci		return obj
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci	def ProcessData(self, count):
273462306a36Sopenharmony_ci		for i in xrange(count):
273562306a36Sopenharmony_ci			obj = self.RemoveFromBuffer()
273662306a36Sopenharmony_ci			self.process_data(obj)
273762306a36Sopenharmony_ci		self.tail.value = self.local_tail
273862306a36Sopenharmony_ci		self.wait_event.set()
273962306a36Sopenharmony_ci		self.done.emit(count)
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci# Fetch more records bar
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ciclass FetchMoreRecordsBar():
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	def __init__(self, model, parent):
274662306a36Sopenharmony_ci		self.model = model
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci		self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:")
274962306a36Sopenharmony_ci		self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci		self.fetch_count = QSpinBox()
275262306a36Sopenharmony_ci		self.fetch_count.setRange(1, 1000000)
275362306a36Sopenharmony_ci		self.fetch_count.setValue(10)
275462306a36Sopenharmony_ci		self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci		self.fetch = QPushButton("Go!")
275762306a36Sopenharmony_ci		self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
275862306a36Sopenharmony_ci		self.fetch.released.connect(self.FetchMoreRecords)
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci		self.progress = QProgressBar()
276162306a36Sopenharmony_ci		self.progress.setRange(0, 100)
276262306a36Sopenharmony_ci		self.progress.hide()
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci		self.done_label = QLabel("All records fetched")
276562306a36Sopenharmony_ci		self.done_label.hide()
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci		self.spacer = QLabel("")
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci		self.close_button = QToolButton()
277062306a36Sopenharmony_ci		self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
277162306a36Sopenharmony_ci		self.close_button.released.connect(self.Deactivate)
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci		self.hbox = QHBoxLayout()
277462306a36Sopenharmony_ci		self.hbox.setContentsMargins(0, 0, 0, 0)
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci		self.hbox.addWidget(self.label)
277762306a36Sopenharmony_ci		self.hbox.addWidget(self.fetch_count)
277862306a36Sopenharmony_ci		self.hbox.addWidget(self.fetch)
277962306a36Sopenharmony_ci		self.hbox.addWidget(self.spacer)
278062306a36Sopenharmony_ci		self.hbox.addWidget(self.progress)
278162306a36Sopenharmony_ci		self.hbox.addWidget(self.done_label)
278262306a36Sopenharmony_ci		self.hbox.addWidget(self.close_button)
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci		self.bar = QWidget()
278562306a36Sopenharmony_ci		self.bar.setLayout(self.hbox)
278662306a36Sopenharmony_ci		self.bar.show()
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci		self.in_progress = False
278962306a36Sopenharmony_ci		self.model.progress.connect(self.Progress)
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci		self.done = False
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci		if not model.HasMoreRecords():
279462306a36Sopenharmony_ci			self.Done()
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	def Widget(self):
279762306a36Sopenharmony_ci		return self.bar
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	def Activate(self):
280062306a36Sopenharmony_ci		self.bar.show()
280162306a36Sopenharmony_ci		self.fetch.setFocus()
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	def Deactivate(self):
280462306a36Sopenharmony_ci		self.bar.hide()
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	def Enable(self, enable):
280762306a36Sopenharmony_ci		self.fetch.setEnabled(enable)
280862306a36Sopenharmony_ci		self.fetch_count.setEnabled(enable)
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	def Busy(self):
281162306a36Sopenharmony_ci		self.Enable(False)
281262306a36Sopenharmony_ci		self.fetch.hide()
281362306a36Sopenharmony_ci		self.spacer.hide()
281462306a36Sopenharmony_ci		self.progress.show()
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	def Idle(self):
281762306a36Sopenharmony_ci		self.in_progress = False
281862306a36Sopenharmony_ci		self.Enable(True)
281962306a36Sopenharmony_ci		self.progress.hide()
282062306a36Sopenharmony_ci		self.fetch.show()
282162306a36Sopenharmony_ci		self.spacer.show()
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	def Target(self):
282462306a36Sopenharmony_ci		return self.fetch_count.value() * glb_chunk_sz
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	def Done(self):
282762306a36Sopenharmony_ci		self.done = True
282862306a36Sopenharmony_ci		self.Idle()
282962306a36Sopenharmony_ci		self.label.hide()
283062306a36Sopenharmony_ci		self.fetch_count.hide()
283162306a36Sopenharmony_ci		self.fetch.hide()
283262306a36Sopenharmony_ci		self.spacer.hide()
283362306a36Sopenharmony_ci		self.done_label.show()
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	def Progress(self, count):
283662306a36Sopenharmony_ci		if self.in_progress:
283762306a36Sopenharmony_ci			if count:
283862306a36Sopenharmony_ci				percent = ((count - self.start) * 100) / self.Target()
283962306a36Sopenharmony_ci				if percent >= 100:
284062306a36Sopenharmony_ci					self.Idle()
284162306a36Sopenharmony_ci				else:
284262306a36Sopenharmony_ci					self.progress.setValue(percent)
284362306a36Sopenharmony_ci		if not count:
284462306a36Sopenharmony_ci			# Count value of zero means no more records
284562306a36Sopenharmony_ci			self.Done()
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	def FetchMoreRecords(self):
284862306a36Sopenharmony_ci		if self.done:
284962306a36Sopenharmony_ci			return
285062306a36Sopenharmony_ci		self.progress.setValue(0)
285162306a36Sopenharmony_ci		self.Busy()
285262306a36Sopenharmony_ci		self.in_progress = True
285362306a36Sopenharmony_ci		self.start = self.model.FetchMoreRecords(self.Target())
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci# Brance data model level two item
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ciclass BranchLevelTwoItem():
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	def __init__(self, row, col, text, parent_item):
286062306a36Sopenharmony_ci		self.row = row
286162306a36Sopenharmony_ci		self.parent_item = parent_item
286262306a36Sopenharmony_ci		self.data = [""] * (col + 1)
286362306a36Sopenharmony_ci		self.data[col] = text
286462306a36Sopenharmony_ci		self.level = 2
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	def getParentItem(self):
286762306a36Sopenharmony_ci		return self.parent_item
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	def getRow(self):
287062306a36Sopenharmony_ci		return self.row
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	def childCount(self):
287362306a36Sopenharmony_ci		return 0
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	def hasChildren(self):
287662306a36Sopenharmony_ci		return False
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	def getData(self, column):
287962306a36Sopenharmony_ci		return self.data[column]
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci# Brance data model level one item
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ciclass BranchLevelOneItem():
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	def __init__(self, glb, row, data, parent_item):
288662306a36Sopenharmony_ci		self.glb = glb
288762306a36Sopenharmony_ci		self.row = row
288862306a36Sopenharmony_ci		self.parent_item = parent_item
288962306a36Sopenharmony_ci		self.child_count = 0
289062306a36Sopenharmony_ci		self.child_items = []
289162306a36Sopenharmony_ci		self.data = data[1:]
289262306a36Sopenharmony_ci		self.dbid = data[0]
289362306a36Sopenharmony_ci		self.level = 1
289462306a36Sopenharmony_ci		self.query_done = False
289562306a36Sopenharmony_ci		self.br_col = len(self.data) - 1
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	def getChildItem(self, row):
289862306a36Sopenharmony_ci		return self.child_items[row]
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	def getParentItem(self):
290162306a36Sopenharmony_ci		return self.parent_item
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	def getRow(self):
290462306a36Sopenharmony_ci		return self.row
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	def Select(self):
290762306a36Sopenharmony_ci		self.query_done = True
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci		if not self.glb.have_disassembler:
291062306a36Sopenharmony_ci			return
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci		QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip"
291562306a36Sopenharmony_ci				  " FROM samples"
291662306a36Sopenharmony_ci				  " INNER JOIN dsos ON samples.to_dso_id = dsos.id"
291762306a36Sopenharmony_ci				  " INNER JOIN symbols ON samples.to_symbol_id = symbols.id"
291862306a36Sopenharmony_ci				  " WHERE samples.id = " + str(self.dbid))
291962306a36Sopenharmony_ci		if not query.next():
292062306a36Sopenharmony_ci			return
292162306a36Sopenharmony_ci		cpu = query.value(0)
292262306a36Sopenharmony_ci		dso = query.value(1)
292362306a36Sopenharmony_ci		sym = query.value(2)
292462306a36Sopenharmony_ci		if dso == 0 or sym == 0:
292562306a36Sopenharmony_ci			return
292662306a36Sopenharmony_ci		off = query.value(3)
292762306a36Sopenharmony_ci		short_name = query.value(4)
292862306a36Sopenharmony_ci		long_name = query.value(5)
292962306a36Sopenharmony_ci		build_id = query.value(6)
293062306a36Sopenharmony_ci		sym_start = query.value(7)
293162306a36Sopenharmony_ci		ip = query.value(8)
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci		QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start"
293462306a36Sopenharmony_ci				  " FROM samples"
293562306a36Sopenharmony_ci				  " INNER JOIN symbols ON samples.symbol_id = symbols.id"
293662306a36Sopenharmony_ci				  " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) +
293762306a36Sopenharmony_ci				  " ORDER BY samples.id"
293862306a36Sopenharmony_ci				  " LIMIT 1")
293962306a36Sopenharmony_ci		if not query.next():
294062306a36Sopenharmony_ci			return
294162306a36Sopenharmony_ci		if query.value(0) != dso:
294262306a36Sopenharmony_ci			# Cannot disassemble from one dso to another
294362306a36Sopenharmony_ci			return
294462306a36Sopenharmony_ci		bsym = query.value(1)
294562306a36Sopenharmony_ci		boff = query.value(2)
294662306a36Sopenharmony_ci		bsym_start = query.value(3)
294762306a36Sopenharmony_ci		if bsym == 0:
294862306a36Sopenharmony_ci			return
294962306a36Sopenharmony_ci		tot = bsym_start + boff + 1 - sym_start - off
295062306a36Sopenharmony_ci		if tot <= 0 or tot > 16384:
295162306a36Sopenharmony_ci			return
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci		inst = self.glb.disassembler.Instruction()
295462306a36Sopenharmony_ci		f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id)
295562306a36Sopenharmony_ci		if not f:
295662306a36Sopenharmony_ci			return
295762306a36Sopenharmony_ci		mode = 0 if Is64Bit(f) else 1
295862306a36Sopenharmony_ci		self.glb.disassembler.SetMode(inst, mode)
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci		buf_sz = tot + 16
296162306a36Sopenharmony_ci		buf = create_string_buffer(tot + 16)
296262306a36Sopenharmony_ci		f.seek(sym_start + off)
296362306a36Sopenharmony_ci		buf.value = f.read(buf_sz)
296462306a36Sopenharmony_ci		buf_ptr = addressof(buf)
296562306a36Sopenharmony_ci		i = 0
296662306a36Sopenharmony_ci		while tot > 0:
296762306a36Sopenharmony_ci			cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip)
296862306a36Sopenharmony_ci			if cnt:
296962306a36Sopenharmony_ci				byte_str = tohex(ip).rjust(16)
297062306a36Sopenharmony_ci				for k in xrange(cnt):
297162306a36Sopenharmony_ci					byte_str += " %02x" % ord(buf[i])
297262306a36Sopenharmony_ci					i += 1
297362306a36Sopenharmony_ci				while k < 15:
297462306a36Sopenharmony_ci					byte_str += "   "
297562306a36Sopenharmony_ci					k += 1
297662306a36Sopenharmony_ci				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
297762306a36Sopenharmony_ci				self.child_count += 1
297862306a36Sopenharmony_ci			else:
297962306a36Sopenharmony_ci				return
298062306a36Sopenharmony_ci			buf_ptr += cnt
298162306a36Sopenharmony_ci			tot -= cnt
298262306a36Sopenharmony_ci			buf_sz -= cnt
298362306a36Sopenharmony_ci			ip += cnt
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	def childCount(self):
298662306a36Sopenharmony_ci		if not self.query_done:
298762306a36Sopenharmony_ci			self.Select()
298862306a36Sopenharmony_ci			if not self.child_count:
298962306a36Sopenharmony_ci				return -1
299062306a36Sopenharmony_ci		return self.child_count
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	def hasChildren(self):
299362306a36Sopenharmony_ci		if not self.query_done:
299462306a36Sopenharmony_ci			return True
299562306a36Sopenharmony_ci		return self.child_count > 0
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	def getData(self, column):
299862306a36Sopenharmony_ci		return self.data[column]
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci# Brance data model root item
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ciclass BranchRootItem():
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	def __init__(self):
300562306a36Sopenharmony_ci		self.child_count = 0
300662306a36Sopenharmony_ci		self.child_items = []
300762306a36Sopenharmony_ci		self.level = 0
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	def getChildItem(self, row):
301062306a36Sopenharmony_ci		return self.child_items[row]
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	def getParentItem(self):
301362306a36Sopenharmony_ci		return None
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	def getRow(self):
301662306a36Sopenharmony_ci		return 0
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	def childCount(self):
301962306a36Sopenharmony_ci		return self.child_count
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci	def hasChildren(self):
302262306a36Sopenharmony_ci		return self.child_count > 0
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	def getData(self, column):
302562306a36Sopenharmony_ci		return ""
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci# Calculate instructions per cycle
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_cidef CalcIPC(cyc_cnt, insn_cnt):
303062306a36Sopenharmony_ci	if cyc_cnt and insn_cnt:
303162306a36Sopenharmony_ci		ipc = Decimal(float(insn_cnt) / cyc_cnt)
303262306a36Sopenharmony_ci		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
303362306a36Sopenharmony_ci	else:
303462306a36Sopenharmony_ci		ipc = "0"
303562306a36Sopenharmony_ci	return ipc
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci# Branch data preparation
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_cidef BranchDataPrepBr(query, data):
304062306a36Sopenharmony_ci	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
304162306a36Sopenharmony_ci			" (" + dsoname(query.value(11)) + ")" + " -> " +
304262306a36Sopenharmony_ci			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
304362306a36Sopenharmony_ci			" (" + dsoname(query.value(15)) + ")")
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_cidef BranchDataPrepIPC(query, data):
304662306a36Sopenharmony_ci	insn_cnt = query.value(16)
304762306a36Sopenharmony_ci	cyc_cnt = query.value(17)
304862306a36Sopenharmony_ci	ipc = CalcIPC(cyc_cnt, insn_cnt)
304962306a36Sopenharmony_ci	data.append(insn_cnt)
305062306a36Sopenharmony_ci	data.append(cyc_cnt)
305162306a36Sopenharmony_ci	data.append(ipc)
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_cidef BranchDataPrep(query):
305462306a36Sopenharmony_ci	data = []
305562306a36Sopenharmony_ci	for i in xrange(0, 8):
305662306a36Sopenharmony_ci		data.append(query.value(i))
305762306a36Sopenharmony_ci	BranchDataPrepBr(query, data)
305862306a36Sopenharmony_ci	return data
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_cidef BranchDataPrepWA(query):
306162306a36Sopenharmony_ci	data = []
306262306a36Sopenharmony_ci	data.append(query.value(0))
306362306a36Sopenharmony_ci	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
306462306a36Sopenharmony_ci	data.append("{:>19}".format(query.value(1)))
306562306a36Sopenharmony_ci	for i in xrange(2, 8):
306662306a36Sopenharmony_ci		data.append(query.value(i))
306762306a36Sopenharmony_ci	BranchDataPrepBr(query, data)
306862306a36Sopenharmony_ci	return data
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_cidef BranchDataWithIPCPrep(query):
307162306a36Sopenharmony_ci	data = []
307262306a36Sopenharmony_ci	for i in xrange(0, 8):
307362306a36Sopenharmony_ci		data.append(query.value(i))
307462306a36Sopenharmony_ci	BranchDataPrepIPC(query, data)
307562306a36Sopenharmony_ci	BranchDataPrepBr(query, data)
307662306a36Sopenharmony_ci	return data
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_cidef BranchDataWithIPCPrepWA(query):
307962306a36Sopenharmony_ci	data = []
308062306a36Sopenharmony_ci	data.append(query.value(0))
308162306a36Sopenharmony_ci	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
308262306a36Sopenharmony_ci	data.append("{:>19}".format(query.value(1)))
308362306a36Sopenharmony_ci	for i in xrange(2, 8):
308462306a36Sopenharmony_ci		data.append(query.value(i))
308562306a36Sopenharmony_ci	BranchDataPrepIPC(query, data)
308662306a36Sopenharmony_ci	BranchDataPrepBr(query, data)
308762306a36Sopenharmony_ci	return data
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci# Branch data model
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ciclass BranchModel(TreeModel):
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	progress = Signal(object)
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	def __init__(self, glb, event_id, where_clause, parent=None):
309662306a36Sopenharmony_ci		super(BranchModel, self).__init__(glb, None, parent)
309762306a36Sopenharmony_ci		self.event_id = event_id
309862306a36Sopenharmony_ci		self.more = True
309962306a36Sopenharmony_ci		self.populated = 0
310062306a36Sopenharmony_ci		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
310162306a36Sopenharmony_ci		if self.have_ipc:
310262306a36Sopenharmony_ci			select_ipc = ", insn_count, cyc_count"
310362306a36Sopenharmony_ci			prep_fn = BranchDataWithIPCPrep
310462306a36Sopenharmony_ci			prep_wa_fn = BranchDataWithIPCPrepWA
310562306a36Sopenharmony_ci		else:
310662306a36Sopenharmony_ci			select_ipc = ""
310762306a36Sopenharmony_ci			prep_fn = BranchDataPrep
310862306a36Sopenharmony_ci			prep_wa_fn = BranchDataPrepWA
310962306a36Sopenharmony_ci		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
311062306a36Sopenharmony_ci			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
311162306a36Sopenharmony_ci			" ip, symbols.name, sym_offset, dsos.short_name,"
311262306a36Sopenharmony_ci			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
311362306a36Sopenharmony_ci			+ select_ipc +
311462306a36Sopenharmony_ci			" FROM samples"
311562306a36Sopenharmony_ci			" INNER JOIN comms ON comm_id = comms.id"
311662306a36Sopenharmony_ci			" INNER JOIN threads ON thread_id = threads.id"
311762306a36Sopenharmony_ci			" INNER JOIN branch_types ON branch_type = branch_types.id"
311862306a36Sopenharmony_ci			" INNER JOIN symbols ON symbol_id = symbols.id"
311962306a36Sopenharmony_ci			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"
312062306a36Sopenharmony_ci			" INNER JOIN dsos ON samples.dso_id = dsos.id"
312162306a36Sopenharmony_ci			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"
312262306a36Sopenharmony_ci			" WHERE samples.id > $$last_id$$" + where_clause +
312362306a36Sopenharmony_ci			" AND evsel_id = " + str(self.event_id) +
312462306a36Sopenharmony_ci			" ORDER BY samples.id"
312562306a36Sopenharmony_ci			" LIMIT " + str(glb_chunk_sz))
312662306a36Sopenharmony_ci		if pyside_version_1 and sys.version_info[0] == 3:
312762306a36Sopenharmony_ci			prep = prep_fn
312862306a36Sopenharmony_ci		else:
312962306a36Sopenharmony_ci			prep = prep_wa_fn
313062306a36Sopenharmony_ci		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
313162306a36Sopenharmony_ci		self.fetcher.done.connect(self.Update)
313262306a36Sopenharmony_ci		self.fetcher.Fetch(glb_chunk_sz)
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	def GetRoot(self):
313562306a36Sopenharmony_ci		return BranchRootItem()
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	def columnCount(self, parent=None):
313862306a36Sopenharmony_ci		if self.have_ipc:
313962306a36Sopenharmony_ci			return 11
314062306a36Sopenharmony_ci		else:
314162306a36Sopenharmony_ci			return 8
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	def columnHeader(self, column):
314462306a36Sopenharmony_ci		if self.have_ipc:
314562306a36Sopenharmony_ci			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
314662306a36Sopenharmony_ci		else:
314762306a36Sopenharmony_ci			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	def columnFont(self, column):
315062306a36Sopenharmony_ci		if self.have_ipc:
315162306a36Sopenharmony_ci			br_col = 10
315262306a36Sopenharmony_ci		else:
315362306a36Sopenharmony_ci			br_col = 7
315462306a36Sopenharmony_ci		if column != br_col:
315562306a36Sopenharmony_ci			return None
315662306a36Sopenharmony_ci		return QFont("Monospace")
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	def DisplayData(self, item, index):
315962306a36Sopenharmony_ci		if item.level == 1:
316062306a36Sopenharmony_ci			self.FetchIfNeeded(item.row)
316162306a36Sopenharmony_ci		return item.getData(index.column())
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	def AddSample(self, data):
316462306a36Sopenharmony_ci		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)
316562306a36Sopenharmony_ci		self.root.child_items.append(child)
316662306a36Sopenharmony_ci		self.populated += 1
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	def Update(self, fetched):
316962306a36Sopenharmony_ci		if not fetched:
317062306a36Sopenharmony_ci			self.more = False
317162306a36Sopenharmony_ci			self.progress.emit(0)
317262306a36Sopenharmony_ci		child_count = self.root.child_count
317362306a36Sopenharmony_ci		count = self.populated - child_count
317462306a36Sopenharmony_ci		if count > 0:
317562306a36Sopenharmony_ci			parent = QModelIndex()
317662306a36Sopenharmony_ci			self.beginInsertRows(parent, child_count, child_count + count - 1)
317762306a36Sopenharmony_ci			self.insertRows(child_count, count, parent)
317862306a36Sopenharmony_ci			self.root.child_count += count
317962306a36Sopenharmony_ci			self.endInsertRows()
318062306a36Sopenharmony_ci			self.progress.emit(self.root.child_count)
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	def FetchMoreRecords(self, count):
318362306a36Sopenharmony_ci		current = self.root.child_count
318462306a36Sopenharmony_ci		if self.more:
318562306a36Sopenharmony_ci			self.fetcher.Fetch(count)
318662306a36Sopenharmony_ci		else:
318762306a36Sopenharmony_ci			self.progress.emit(0)
318862306a36Sopenharmony_ci		return current
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	def HasMoreRecords(self):
319162306a36Sopenharmony_ci		return self.more
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci# Report Variables
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ciclass ReportVars():
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	def __init__(self, name = "", where_clause = "", limit = ""):
319862306a36Sopenharmony_ci		self.name = name
319962306a36Sopenharmony_ci		self.where_clause = where_clause
320062306a36Sopenharmony_ci		self.limit = limit
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	def UniqueId(self):
320362306a36Sopenharmony_ci		return str(self.where_clause + ";" + self.limit)
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci# Branch window
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ciclass BranchWindow(QMdiSubWindow):
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	def __init__(self, glb, event_id, report_vars, parent=None):
321062306a36Sopenharmony_ci		super(BranchWindow, self).__init__(parent)
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId()
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci		self.view = QTreeView()
321762306a36Sopenharmony_ci		self.view.setUniformRowHeights(True)
321862306a36Sopenharmony_ci		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
321962306a36Sopenharmony_ci		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard
322062306a36Sopenharmony_ci		self.view.setModel(self.model)
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci		self.ResizeColumnsToContents()
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci		self.context_menu = TreeContextMenu(self.view)
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci		self.find_bar = FindBar(self, self, True)
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci		self.finder = ChildDataItemFinder(self.model.root)
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci		self.fetch_bar = FetchMoreRecordsBar(self.model, self)
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci		self.setWidget(self.vbox.Widget())
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	def ResizeColumnToContents(self, column, n):
323962306a36Sopenharmony_ci		# Using the view's resizeColumnToContents() here is extrememly slow
324062306a36Sopenharmony_ci		# so implement a crude alternative
324162306a36Sopenharmony_ci		mm = "MM" if column else "MMMM"
324262306a36Sopenharmony_ci		font = self.view.font()
324362306a36Sopenharmony_ci		metrics = QFontMetrics(font)
324462306a36Sopenharmony_ci		max = 0
324562306a36Sopenharmony_ci		for row in xrange(n):
324662306a36Sopenharmony_ci			val = self.model.root.child_items[row].data[column]
324762306a36Sopenharmony_ci			len = metrics.width(str(val) + mm)
324862306a36Sopenharmony_ci			max = len if len > max else max
324962306a36Sopenharmony_ci		val = self.model.columnHeader(column)
325062306a36Sopenharmony_ci		len = metrics.width(str(val) + mm)
325162306a36Sopenharmony_ci		max = len if len > max else max
325262306a36Sopenharmony_ci		self.view.setColumnWidth(column, max)
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	def ResizeColumnsToContents(self):
325562306a36Sopenharmony_ci		n = min(self.model.root.child_count, 100)
325662306a36Sopenharmony_ci		if n < 1:
325762306a36Sopenharmony_ci			# No data yet, so connect a signal to notify when there is
325862306a36Sopenharmony_ci			self.model.rowsInserted.connect(self.UpdateColumnWidths)
325962306a36Sopenharmony_ci			return
326062306a36Sopenharmony_ci		columns = self.model.columnCount()
326162306a36Sopenharmony_ci		for i in xrange(columns):
326262306a36Sopenharmony_ci			self.ResizeColumnToContents(i, n)
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci	def UpdateColumnWidths(self, *x):
326562306a36Sopenharmony_ci		# This only needs to be done once, so disconnect the signal now
326662306a36Sopenharmony_ci		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)
326762306a36Sopenharmony_ci		self.ResizeColumnsToContents()
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context):
327062306a36Sopenharmony_ci		self.view.setFocus()
327162306a36Sopenharmony_ci		self.find_bar.Busy()
327262306a36Sopenharmony_ci		self.finder.Find(value, direction, pattern, context, self.FindDone)
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	def FindDone(self, row):
327562306a36Sopenharmony_ci		self.find_bar.Idle()
327662306a36Sopenharmony_ci		if row >= 0:
327762306a36Sopenharmony_ci			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
327862306a36Sopenharmony_ci		else:
327962306a36Sopenharmony_ci			self.find_bar.NotFound()
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci# Line edit data item
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ciclass LineEditDataItem(object):
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
328662306a36Sopenharmony_ci		self.glb = glb
328762306a36Sopenharmony_ci		self.label = label
328862306a36Sopenharmony_ci		self.placeholder_text = placeholder_text
328962306a36Sopenharmony_ci		self.parent = parent
329062306a36Sopenharmony_ci		self.id = id
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci		self.value = default
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci		self.widget = QLineEdit(default)
329562306a36Sopenharmony_ci		self.widget.editingFinished.connect(self.Validate)
329662306a36Sopenharmony_ci		self.widget.textChanged.connect(self.Invalidate)
329762306a36Sopenharmony_ci		self.red = False
329862306a36Sopenharmony_ci		self.error = ""
329962306a36Sopenharmony_ci		self.validated = True
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci		if placeholder_text:
330262306a36Sopenharmony_ci			self.widget.setPlaceholderText(placeholder_text)
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_ci	def TurnTextRed(self):
330562306a36Sopenharmony_ci		if not self.red:
330662306a36Sopenharmony_ci			palette = QPalette()
330762306a36Sopenharmony_ci			palette.setColor(QPalette.Text,Qt.red)
330862306a36Sopenharmony_ci			self.widget.setPalette(palette)
330962306a36Sopenharmony_ci			self.red = True
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	def TurnTextNormal(self):
331262306a36Sopenharmony_ci		if self.red:
331362306a36Sopenharmony_ci			palette = QPalette()
331462306a36Sopenharmony_ci			self.widget.setPalette(palette)
331562306a36Sopenharmony_ci			self.red = False
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	def InvalidValue(self, value):
331862306a36Sopenharmony_ci		self.value = ""
331962306a36Sopenharmony_ci		self.TurnTextRed()
332062306a36Sopenharmony_ci		self.error = self.label + " invalid value '" + value + "'"
332162306a36Sopenharmony_ci		self.parent.ShowMessage(self.error)
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	def Invalidate(self):
332462306a36Sopenharmony_ci		self.validated = False
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci	def DoValidate(self, input_string):
332762306a36Sopenharmony_ci		self.value = input_string.strip()
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	def Validate(self):
333062306a36Sopenharmony_ci		self.validated = True
333162306a36Sopenharmony_ci		self.error = ""
333262306a36Sopenharmony_ci		self.TurnTextNormal()
333362306a36Sopenharmony_ci		self.parent.ClearMessage()
333462306a36Sopenharmony_ci		input_string = self.widget.text()
333562306a36Sopenharmony_ci		if not len(input_string.strip()):
333662306a36Sopenharmony_ci			self.value = ""
333762306a36Sopenharmony_ci			return
333862306a36Sopenharmony_ci		self.DoValidate(input_string)
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	def IsValid(self):
334162306a36Sopenharmony_ci		if not self.validated:
334262306a36Sopenharmony_ci			self.Validate()
334362306a36Sopenharmony_ci		if len(self.error):
334462306a36Sopenharmony_ci			self.parent.ShowMessage(self.error)
334562306a36Sopenharmony_ci			return False
334662306a36Sopenharmony_ci		return True
334762306a36Sopenharmony_ci
334862306a36Sopenharmony_ci	def IsNumber(self, value):
334962306a36Sopenharmony_ci		try:
335062306a36Sopenharmony_ci			x = int(value)
335162306a36Sopenharmony_ci		except:
335262306a36Sopenharmony_ci			x = 0
335362306a36Sopenharmony_ci		return str(x) == value
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci# Non-negative integer ranges dialog data item
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ciclass NonNegativeIntegerRangesDataItem(LineEditDataItem):
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	def __init__(self, glb, label, placeholder_text, column_name, parent):
336062306a36Sopenharmony_ci		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci		self.column_name = column_name
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	def DoValidate(self, input_string):
336562306a36Sopenharmony_ci		singles = []
336662306a36Sopenharmony_ci		ranges = []
336762306a36Sopenharmony_ci		for value in [x.strip() for x in input_string.split(",")]:
336862306a36Sopenharmony_ci			if "-" in value:
336962306a36Sopenharmony_ci				vrange = value.split("-")
337062306a36Sopenharmony_ci				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
337162306a36Sopenharmony_ci					return self.InvalidValue(value)
337262306a36Sopenharmony_ci				ranges.append(vrange)
337362306a36Sopenharmony_ci			else:
337462306a36Sopenharmony_ci				if not self.IsNumber(value):
337562306a36Sopenharmony_ci					return self.InvalidValue(value)
337662306a36Sopenharmony_ci				singles.append(value)
337762306a36Sopenharmony_ci		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
337862306a36Sopenharmony_ci		if len(singles):
337962306a36Sopenharmony_ci			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
338062306a36Sopenharmony_ci		self.value = " OR ".join(ranges)
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci# Positive integer dialog data item
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ciclass PositiveIntegerDataItem(LineEditDataItem):
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
338762306a36Sopenharmony_ci		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	def DoValidate(self, input_string):
339062306a36Sopenharmony_ci		if not self.IsNumber(input_string.strip()):
339162306a36Sopenharmony_ci			return self.InvalidValue(input_string)
339262306a36Sopenharmony_ci		value = int(input_string.strip())
339362306a36Sopenharmony_ci		if value <= 0:
339462306a36Sopenharmony_ci			return self.InvalidValue(input_string)
339562306a36Sopenharmony_ci		self.value = str(value)
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci# Dialog data item converted and validated using a SQL table
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ciclass SQLTableDataItem(LineEditDataItem):
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
340262306a36Sopenharmony_ci		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci		self.table_name = table_name
340562306a36Sopenharmony_ci		self.match_column = match_column
340662306a36Sopenharmony_ci		self.column_name1 = column_name1
340762306a36Sopenharmony_ci		self.column_name2 = column_name2
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci	def ValueToIds(self, value):
341062306a36Sopenharmony_ci		ids = []
341162306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
341262306a36Sopenharmony_ci		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
341362306a36Sopenharmony_ci		ret = query.exec_(stmt)
341462306a36Sopenharmony_ci		if ret:
341562306a36Sopenharmony_ci			while query.next():
341662306a36Sopenharmony_ci				ids.append(str(query.value(0)))
341762306a36Sopenharmony_ci		return ids
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	def DoValidate(self, input_string):
342062306a36Sopenharmony_ci		all_ids = []
342162306a36Sopenharmony_ci		for value in [x.strip() for x in input_string.split(",")]:
342262306a36Sopenharmony_ci			ids = self.ValueToIds(value)
342362306a36Sopenharmony_ci			if len(ids):
342462306a36Sopenharmony_ci				all_ids.extend(ids)
342562306a36Sopenharmony_ci			else:
342662306a36Sopenharmony_ci				return self.InvalidValue(value)
342762306a36Sopenharmony_ci		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
342862306a36Sopenharmony_ci		if self.column_name2:
342962306a36Sopenharmony_ci			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci# Sample time ranges dialog data item converted and validated using 'samples' SQL table
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ciclass SampleTimeRangesDataItem(LineEditDataItem):
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	def __init__(self, glb, label, placeholder_text, column_name, parent):
343662306a36Sopenharmony_ci		self.column_name = column_name
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci		self.last_id = 0
343962306a36Sopenharmony_ci		self.first_time = 0
344062306a36Sopenharmony_ci		self.last_time = 2 ** 64
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci		query = QSqlQuery(glb.db)
344362306a36Sopenharmony_ci		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
344462306a36Sopenharmony_ci		if query.next():
344562306a36Sopenharmony_ci			self.last_id = int(query.value(0))
344662306a36Sopenharmony_ci		self.first_time = int(glb.HostStartTime())
344762306a36Sopenharmony_ci		self.last_time = int(glb.HostFinishTime())
344862306a36Sopenharmony_ci		if placeholder_text:
344962306a36Sopenharmony_ci			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci	def IdBetween(self, query, lower_id, higher_id, order):
345462306a36Sopenharmony_ci		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
345562306a36Sopenharmony_ci		if query.next():
345662306a36Sopenharmony_ci			return True, int(query.value(0))
345762306a36Sopenharmony_ci		else:
345862306a36Sopenharmony_ci			return False, 0
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
346162306a36Sopenharmony_ci		query = QSqlQuery(self.glb.db)
346262306a36Sopenharmony_ci		while True:
346362306a36Sopenharmony_ci			next_id = int((lower_id + higher_id) / 2)
346462306a36Sopenharmony_ci			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
346562306a36Sopenharmony_ci			if not query.next():
346662306a36Sopenharmony_ci				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
346762306a36Sopenharmony_ci				if not ok:
346862306a36Sopenharmony_ci					ok, dbid = self.IdBetween(query, next_id, higher_id, "")
346962306a36Sopenharmony_ci					if not ok:
347062306a36Sopenharmony_ci						return str(higher_id)
347162306a36Sopenharmony_ci				next_id = dbid
347262306a36Sopenharmony_ci				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
347362306a36Sopenharmony_ci			next_time = int(query.value(0))
347462306a36Sopenharmony_ci			if get_floor:
347562306a36Sopenharmony_ci				if target_time > next_time:
347662306a36Sopenharmony_ci					lower_id = next_id
347762306a36Sopenharmony_ci				else:
347862306a36Sopenharmony_ci					higher_id = next_id
347962306a36Sopenharmony_ci				if higher_id <= lower_id + 1:
348062306a36Sopenharmony_ci					return str(higher_id)
348162306a36Sopenharmony_ci			else:
348262306a36Sopenharmony_ci				if target_time >= next_time:
348362306a36Sopenharmony_ci					lower_id = next_id
348462306a36Sopenharmony_ci				else:
348562306a36Sopenharmony_ci					higher_id = next_id
348662306a36Sopenharmony_ci				if higher_id <= lower_id + 1:
348762306a36Sopenharmony_ci					return str(lower_id)
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	def ConvertRelativeTime(self, val):
349062306a36Sopenharmony_ci		mult = 1
349162306a36Sopenharmony_ci		suffix = val[-2:]
349262306a36Sopenharmony_ci		if suffix == "ms":
349362306a36Sopenharmony_ci			mult = 1000000
349462306a36Sopenharmony_ci		elif suffix == "us":
349562306a36Sopenharmony_ci			mult = 1000
349662306a36Sopenharmony_ci		elif suffix == "ns":
349762306a36Sopenharmony_ci			mult = 1
349862306a36Sopenharmony_ci		else:
349962306a36Sopenharmony_ci			return val
350062306a36Sopenharmony_ci		val = val[:-2].strip()
350162306a36Sopenharmony_ci		if not self.IsNumber(val):
350262306a36Sopenharmony_ci			return val
350362306a36Sopenharmony_ci		val = int(val) * mult
350462306a36Sopenharmony_ci		if val >= 0:
350562306a36Sopenharmony_ci			val += self.first_time
350662306a36Sopenharmony_ci		else:
350762306a36Sopenharmony_ci			val += self.last_time
350862306a36Sopenharmony_ci		return str(val)
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	def ConvertTimeRange(self, vrange):
351162306a36Sopenharmony_ci		if vrange[0] == "":
351262306a36Sopenharmony_ci			vrange[0] = str(self.first_time)
351362306a36Sopenharmony_ci		if vrange[1] == "":
351462306a36Sopenharmony_ci			vrange[1] = str(self.last_time)
351562306a36Sopenharmony_ci		vrange[0] = self.ConvertRelativeTime(vrange[0])
351662306a36Sopenharmony_ci		vrange[1] = self.ConvertRelativeTime(vrange[1])
351762306a36Sopenharmony_ci		if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
351862306a36Sopenharmony_ci			return False
351962306a36Sopenharmony_ci		beg_range = max(int(vrange[0]), self.first_time)
352062306a36Sopenharmony_ci		end_range = min(int(vrange[1]), self.last_time)
352162306a36Sopenharmony_ci		if beg_range > self.last_time or end_range < self.first_time:
352262306a36Sopenharmony_ci			return False
352362306a36Sopenharmony_ci		vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
352462306a36Sopenharmony_ci		vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
352562306a36Sopenharmony_ci		return True
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	def AddTimeRange(self, value, ranges):
352862306a36Sopenharmony_ci		n = value.count("-")
352962306a36Sopenharmony_ci		if n == 1:
353062306a36Sopenharmony_ci			pass
353162306a36Sopenharmony_ci		elif n == 2:
353262306a36Sopenharmony_ci			if value.split("-")[1].strip() == "":
353362306a36Sopenharmony_ci				n = 1
353462306a36Sopenharmony_ci		elif n == 3:
353562306a36Sopenharmony_ci			n = 2
353662306a36Sopenharmony_ci		else:
353762306a36Sopenharmony_ci			return False
353862306a36Sopenharmony_ci		pos = findnth(value, "-", n)
353962306a36Sopenharmony_ci		vrange = [value[:pos].strip() ,value[pos+1:].strip()]
354062306a36Sopenharmony_ci		if self.ConvertTimeRange(vrange):
354162306a36Sopenharmony_ci			ranges.append(vrange)
354262306a36Sopenharmony_ci			return True
354362306a36Sopenharmony_ci		return False
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	def DoValidate(self, input_string):
354662306a36Sopenharmony_ci		ranges = []
354762306a36Sopenharmony_ci		for value in [x.strip() for x in input_string.split(",")]:
354862306a36Sopenharmony_ci			if not self.AddTimeRange(value, ranges):
354962306a36Sopenharmony_ci				return self.InvalidValue(value)
355062306a36Sopenharmony_ci		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
355162306a36Sopenharmony_ci		self.value = " OR ".join(ranges)
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci# Report Dialog Base
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ciclass ReportDialogBase(QDialog):
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	def __init__(self, glb, title, items, partial, parent=None):
355862306a36Sopenharmony_ci		super(ReportDialogBase, self).__init__(parent)
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci		self.glb = glb
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci		self.report_vars = ReportVars()
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci		self.setWindowTitle(title)
356562306a36Sopenharmony_ci		self.setMinimumWidth(600)
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci		self.data_items = [x(glb, self) for x in items]
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci		self.partial = partial
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ci		self.grid = QGridLayout()
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci		for row in xrange(len(self.data_items)):
357462306a36Sopenharmony_ci			self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
357562306a36Sopenharmony_ci			self.grid.addWidget(self.data_items[row].widget, row, 1)
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci		self.status = QLabel()
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci		self.ok_button = QPushButton("Ok", self)
358062306a36Sopenharmony_ci		self.ok_button.setDefault(True)
358162306a36Sopenharmony_ci		self.ok_button.released.connect(self.Ok)
358262306a36Sopenharmony_ci		self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci		self.cancel_button = QPushButton("Cancel", self)
358562306a36Sopenharmony_ci		self.cancel_button.released.connect(self.reject)
358662306a36Sopenharmony_ci		self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci		self.hbox = QHBoxLayout()
358962306a36Sopenharmony_ci		#self.hbox.addStretch()
359062306a36Sopenharmony_ci		self.hbox.addWidget(self.status)
359162306a36Sopenharmony_ci		self.hbox.addWidget(self.ok_button)
359262306a36Sopenharmony_ci		self.hbox.addWidget(self.cancel_button)
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci		self.vbox = QVBoxLayout()
359562306a36Sopenharmony_ci		self.vbox.addLayout(self.grid)
359662306a36Sopenharmony_ci		self.vbox.addLayout(self.hbox)
359762306a36Sopenharmony_ci
359862306a36Sopenharmony_ci		self.setLayout(self.vbox)
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci	def Ok(self):
360162306a36Sopenharmony_ci		vars = self.report_vars
360262306a36Sopenharmony_ci		for d in self.data_items:
360362306a36Sopenharmony_ci			if d.id == "REPORTNAME":
360462306a36Sopenharmony_ci				vars.name = d.value
360562306a36Sopenharmony_ci		if not vars.name:
360662306a36Sopenharmony_ci			self.ShowMessage("Report name is required")
360762306a36Sopenharmony_ci			return
360862306a36Sopenharmony_ci		for d in self.data_items:
360962306a36Sopenharmony_ci			if not d.IsValid():
361062306a36Sopenharmony_ci				return
361162306a36Sopenharmony_ci		for d in self.data_items[1:]:
361262306a36Sopenharmony_ci			if d.id == "LIMIT":
361362306a36Sopenharmony_ci				vars.limit = d.value
361462306a36Sopenharmony_ci			elif len(d.value):
361562306a36Sopenharmony_ci				if len(vars.where_clause):
361662306a36Sopenharmony_ci					vars.where_clause += " AND "
361762306a36Sopenharmony_ci				vars.where_clause += d.value
361862306a36Sopenharmony_ci		if len(vars.where_clause):
361962306a36Sopenharmony_ci			if self.partial:
362062306a36Sopenharmony_ci				vars.where_clause = " AND ( " + vars.where_clause + " ) "
362162306a36Sopenharmony_ci			else:
362262306a36Sopenharmony_ci				vars.where_clause = " WHERE " + vars.where_clause + " "
362362306a36Sopenharmony_ci		self.accept()
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	def ShowMessage(self, msg):
362662306a36Sopenharmony_ci		self.status.setText("<font color=#FF0000>" + msg)
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	def ClearMessage(self):
362962306a36Sopenharmony_ci		self.status.setText("")
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci# Selected branch report creation dialog
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ciclass SelectedBranchDialog(ReportDialogBase):
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
363662306a36Sopenharmony_ci		title = "Selected Branches"
363762306a36Sopenharmony_ci		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
363862306a36Sopenharmony_ci			 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
363962306a36Sopenharmony_ci			 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
364062306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
364162306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
364262306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
364362306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p),
364462306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
364562306a36Sopenharmony_ci			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
364662306a36Sopenharmony_ci		super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci# Event list
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_cidef GetEventList(db):
365162306a36Sopenharmony_ci	events = []
365262306a36Sopenharmony_ci	query = QSqlQuery(db)
365362306a36Sopenharmony_ci	QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id")
365462306a36Sopenharmony_ci	while query.next():
365562306a36Sopenharmony_ci		events.append(query.value(0))
365662306a36Sopenharmony_ci	return events
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci# Is a table selectable
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_cidef IsSelectable(db, table, sql = "", columns = "*"):
366162306a36Sopenharmony_ci	query = QSqlQuery(db)
366262306a36Sopenharmony_ci	try:
366362306a36Sopenharmony_ci		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
366462306a36Sopenharmony_ci	except:
366562306a36Sopenharmony_ci		return False
366662306a36Sopenharmony_ci	return True
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci# SQL table data model item
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ciclass SQLTableItem():
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci	def __init__(self, row, data):
367362306a36Sopenharmony_ci		self.row = row
367462306a36Sopenharmony_ci		self.data = data
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_ci	def getData(self, column):
367762306a36Sopenharmony_ci		return self.data[column]
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci# SQL table data model
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ciclass SQLTableModel(TableModel):
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	progress = Signal(object)
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	def __init__(self, glb, sql, column_headers, parent=None):
368662306a36Sopenharmony_ci		super(SQLTableModel, self).__init__(parent)
368762306a36Sopenharmony_ci		self.glb = glb
368862306a36Sopenharmony_ci		self.more = True
368962306a36Sopenharmony_ci		self.populated = 0
369062306a36Sopenharmony_ci		self.column_headers = column_headers
369162306a36Sopenharmony_ci		self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample)
369262306a36Sopenharmony_ci		self.fetcher.done.connect(self.Update)
369362306a36Sopenharmony_ci		self.fetcher.Fetch(glb_chunk_sz)
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	def DisplayData(self, item, index):
369662306a36Sopenharmony_ci		self.FetchIfNeeded(item.row)
369762306a36Sopenharmony_ci		return item.getData(index.column())
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	def AddSample(self, data):
370062306a36Sopenharmony_ci		child = SQLTableItem(self.populated, data)
370162306a36Sopenharmony_ci		self.child_items.append(child)
370262306a36Sopenharmony_ci		self.populated += 1
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	def Update(self, fetched):
370562306a36Sopenharmony_ci		if not fetched:
370662306a36Sopenharmony_ci			self.more = False
370762306a36Sopenharmony_ci			self.progress.emit(0)
370862306a36Sopenharmony_ci		child_count = self.child_count
370962306a36Sopenharmony_ci		count = self.populated - child_count
371062306a36Sopenharmony_ci		if count > 0:
371162306a36Sopenharmony_ci			parent = QModelIndex()
371262306a36Sopenharmony_ci			self.beginInsertRows(parent, child_count, child_count + count - 1)
371362306a36Sopenharmony_ci			self.insertRows(child_count, count, parent)
371462306a36Sopenharmony_ci			self.child_count += count
371562306a36Sopenharmony_ci			self.endInsertRows()
371662306a36Sopenharmony_ci			self.progress.emit(self.child_count)
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	def FetchMoreRecords(self, count):
371962306a36Sopenharmony_ci		current = self.child_count
372062306a36Sopenharmony_ci		if self.more:
372162306a36Sopenharmony_ci			self.fetcher.Fetch(count)
372262306a36Sopenharmony_ci		else:
372362306a36Sopenharmony_ci			self.progress.emit(0)
372462306a36Sopenharmony_ci		return current
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	def HasMoreRecords(self):
372762306a36Sopenharmony_ci		return self.more
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	def columnCount(self, parent=None):
373062306a36Sopenharmony_ci		return len(self.column_headers)
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	def columnHeader(self, column):
373362306a36Sopenharmony_ci		return self.column_headers[column]
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	def SQLTableDataPrep(self, query, count):
373662306a36Sopenharmony_ci		data = []
373762306a36Sopenharmony_ci		for i in xrange(count):
373862306a36Sopenharmony_ci			data.append(query.value(i))
373962306a36Sopenharmony_ci		return data
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci# SQL automatic table data model
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ciclass SQLAutoTableModel(SQLTableModel):
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	def __init__(self, glb, table_name, parent=None):
374662306a36Sopenharmony_ci		sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz)
374762306a36Sopenharmony_ci		if table_name == "comm_threads_view":
374862306a36Sopenharmony_ci			# For now, comm_threads_view has no id column
374962306a36Sopenharmony_ci			sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
375062306a36Sopenharmony_ci		column_headers = []
375162306a36Sopenharmony_ci		query = QSqlQuery(glb.db)
375262306a36Sopenharmony_ci		if glb.dbref.is_sqlite3:
375362306a36Sopenharmony_ci			QueryExec(query, "PRAGMA table_info(" + table_name + ")")
375462306a36Sopenharmony_ci			while query.next():
375562306a36Sopenharmony_ci				column_headers.append(query.value(1))
375662306a36Sopenharmony_ci			if table_name == "sqlite_master":
375762306a36Sopenharmony_ci				sql = "SELECT * FROM " + table_name
375862306a36Sopenharmony_ci		else:
375962306a36Sopenharmony_ci			if table_name[:19] == "information_schema.":
376062306a36Sopenharmony_ci				sql = "SELECT * FROM " + table_name
376162306a36Sopenharmony_ci				select_table_name = table_name[19:]
376262306a36Sopenharmony_ci				schema = "information_schema"
376362306a36Sopenharmony_ci			else:
376462306a36Sopenharmony_ci				select_table_name = table_name
376562306a36Sopenharmony_ci				schema = "public"
376662306a36Sopenharmony_ci			QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
376762306a36Sopenharmony_ci			while query.next():
376862306a36Sopenharmony_ci				column_headers.append(query.value(0))
376962306a36Sopenharmony_ci		if pyside_version_1 and sys.version_info[0] == 3:
377062306a36Sopenharmony_ci			if table_name == "samples_view":
377162306a36Sopenharmony_ci				self.SQLTableDataPrep = self.samples_view_DataPrep
377262306a36Sopenharmony_ci			if table_name == "samples":
377362306a36Sopenharmony_ci				self.SQLTableDataPrep = self.samples_DataPrep
377462306a36Sopenharmony_ci		super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci	def samples_view_DataPrep(self, query, count):
377762306a36Sopenharmony_ci		data = []
377862306a36Sopenharmony_ci		data.append(query.value(0))
377962306a36Sopenharmony_ci		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
378062306a36Sopenharmony_ci		data.append("{:>19}".format(query.value(1)))
378162306a36Sopenharmony_ci		for i in xrange(2, count):
378262306a36Sopenharmony_ci			data.append(query.value(i))
378362306a36Sopenharmony_ci		return data
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci	def samples_DataPrep(self, query, count):
378662306a36Sopenharmony_ci		data = []
378762306a36Sopenharmony_ci		for i in xrange(9):
378862306a36Sopenharmony_ci			data.append(query.value(i))
378962306a36Sopenharmony_ci		# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
379062306a36Sopenharmony_ci		data.append("{:>19}".format(query.value(9)))
379162306a36Sopenharmony_ci		for i in xrange(10, count):
379262306a36Sopenharmony_ci			data.append(query.value(i))
379362306a36Sopenharmony_ci		return data
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_ci# Base class for custom ResizeColumnsToContents
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ciclass ResizeColumnsToContentsBase(QObject):
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci	def __init__(self, parent=None):
380062306a36Sopenharmony_ci		super(ResizeColumnsToContentsBase, self).__init__(parent)
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	def ResizeColumnToContents(self, column, n):
380362306a36Sopenharmony_ci		# Using the view's resizeColumnToContents() here is extrememly slow
380462306a36Sopenharmony_ci		# so implement a crude alternative
380562306a36Sopenharmony_ci		font = self.view.font()
380662306a36Sopenharmony_ci		metrics = QFontMetrics(font)
380762306a36Sopenharmony_ci		max = 0
380862306a36Sopenharmony_ci		for row in xrange(n):
380962306a36Sopenharmony_ci			val = self.data_model.child_items[row].data[column]
381062306a36Sopenharmony_ci			len = metrics.width(str(val) + "MM")
381162306a36Sopenharmony_ci			max = len if len > max else max
381262306a36Sopenharmony_ci		val = self.data_model.columnHeader(column)
381362306a36Sopenharmony_ci		len = metrics.width(str(val) + "MM")
381462306a36Sopenharmony_ci		max = len if len > max else max
381562306a36Sopenharmony_ci		self.view.setColumnWidth(column, max)
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	def ResizeColumnsToContents(self):
381862306a36Sopenharmony_ci		n = min(self.data_model.child_count, 100)
381962306a36Sopenharmony_ci		if n < 1:
382062306a36Sopenharmony_ci			# No data yet, so connect a signal to notify when there is
382162306a36Sopenharmony_ci			self.data_model.rowsInserted.connect(self.UpdateColumnWidths)
382262306a36Sopenharmony_ci			return
382362306a36Sopenharmony_ci		columns = self.data_model.columnCount()
382462306a36Sopenharmony_ci		for i in xrange(columns):
382562306a36Sopenharmony_ci			self.ResizeColumnToContents(i, n)
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	def UpdateColumnWidths(self, *x):
382862306a36Sopenharmony_ci		# This only needs to be done once, so disconnect the signal now
382962306a36Sopenharmony_ci		self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths)
383062306a36Sopenharmony_ci		self.ResizeColumnsToContents()
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci# Convert value to CSV
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_cidef ToCSValue(val):
383562306a36Sopenharmony_ci	if '"' in val:
383662306a36Sopenharmony_ci		val = val.replace('"', '""')
383762306a36Sopenharmony_ci	if "," in val or '"' in val:
383862306a36Sopenharmony_ci		val = '"' + val + '"'
383962306a36Sopenharmony_ci	return val
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci# Key to sort table model indexes by row / column, assuming fewer than 1000 columns
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ciglb_max_cols = 1000
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_cidef RowColumnKey(a):
384662306a36Sopenharmony_ci	return a.row() * glb_max_cols + a.column()
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci# Copy selected table cells to clipboard
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_cidef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False):
385162306a36Sopenharmony_ci	indexes = sorted(view.selectedIndexes(), key=RowColumnKey)
385262306a36Sopenharmony_ci	idx_cnt = len(indexes)
385362306a36Sopenharmony_ci	if not idx_cnt:
385462306a36Sopenharmony_ci		return
385562306a36Sopenharmony_ci	if idx_cnt == 1:
385662306a36Sopenharmony_ci		with_hdr=False
385762306a36Sopenharmony_ci	min_row = indexes[0].row()
385862306a36Sopenharmony_ci	max_row = indexes[0].row()
385962306a36Sopenharmony_ci	min_col = indexes[0].column()
386062306a36Sopenharmony_ci	max_col = indexes[0].column()
386162306a36Sopenharmony_ci	for i in indexes:
386262306a36Sopenharmony_ci		min_row = min(min_row, i.row())
386362306a36Sopenharmony_ci		max_row = max(max_row, i.row())
386462306a36Sopenharmony_ci		min_col = min(min_col, i.column())
386562306a36Sopenharmony_ci		max_col = max(max_col, i.column())
386662306a36Sopenharmony_ci	if max_col > glb_max_cols:
386762306a36Sopenharmony_ci		raise RuntimeError("glb_max_cols is too low")
386862306a36Sopenharmony_ci	max_width = [0] * (1 + max_col - min_col)
386962306a36Sopenharmony_ci	for i in indexes:
387062306a36Sopenharmony_ci		c = i.column() - min_col
387162306a36Sopenharmony_ci		max_width[c] = max(max_width[c], len(str(i.data())))
387262306a36Sopenharmony_ci	text = ""
387362306a36Sopenharmony_ci	pad = ""
387462306a36Sopenharmony_ci	sep = ""
387562306a36Sopenharmony_ci	if with_hdr:
387662306a36Sopenharmony_ci		model = indexes[0].model()
387762306a36Sopenharmony_ci		for col in range(min_col, max_col + 1):
387862306a36Sopenharmony_ci			val = model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
387962306a36Sopenharmony_ci			if as_csv:
388062306a36Sopenharmony_ci				text += sep + ToCSValue(val)
388162306a36Sopenharmony_ci				sep = ","
388262306a36Sopenharmony_ci			else:
388362306a36Sopenharmony_ci				c = col - min_col
388462306a36Sopenharmony_ci				max_width[c] = max(max_width[c], len(val))
388562306a36Sopenharmony_ci				width = max_width[c]
388662306a36Sopenharmony_ci				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)
388762306a36Sopenharmony_ci				if align & Qt.AlignRight:
388862306a36Sopenharmony_ci					val = val.rjust(width)
388962306a36Sopenharmony_ci				text += pad + sep + val
389062306a36Sopenharmony_ci				pad = " " * (width - len(val))
389162306a36Sopenharmony_ci				sep = "  "
389262306a36Sopenharmony_ci		text += "\n"
389362306a36Sopenharmony_ci		pad = ""
389462306a36Sopenharmony_ci		sep = ""
389562306a36Sopenharmony_ci	last_row = min_row
389662306a36Sopenharmony_ci	for i in indexes:
389762306a36Sopenharmony_ci		if i.row() > last_row:
389862306a36Sopenharmony_ci			last_row = i.row()
389962306a36Sopenharmony_ci			text += "\n"
390062306a36Sopenharmony_ci			pad = ""
390162306a36Sopenharmony_ci			sep = ""
390262306a36Sopenharmony_ci		if as_csv:
390362306a36Sopenharmony_ci			text += sep + ToCSValue(str(i.data()))
390462306a36Sopenharmony_ci			sep = ","
390562306a36Sopenharmony_ci		else:
390662306a36Sopenharmony_ci			width = max_width[i.column() - min_col]
390762306a36Sopenharmony_ci			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
390862306a36Sopenharmony_ci				val = str(i.data()).rjust(width)
390962306a36Sopenharmony_ci			else:
391062306a36Sopenharmony_ci				val = str(i.data())
391162306a36Sopenharmony_ci			text += pad + sep + val
391262306a36Sopenharmony_ci			pad = " " * (width - len(val))
391362306a36Sopenharmony_ci			sep = "  "
391462306a36Sopenharmony_ci	QApplication.clipboard().setText(text)
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_cidef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):
391762306a36Sopenharmony_ci	indexes = view.selectedIndexes()
391862306a36Sopenharmony_ci	if not len(indexes):
391962306a36Sopenharmony_ci		return
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	selection = view.selectionModel()
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	first = None
392462306a36Sopenharmony_ci	for i in indexes:
392562306a36Sopenharmony_ci		above = view.indexAbove(i)
392662306a36Sopenharmony_ci		if not selection.isSelected(above):
392762306a36Sopenharmony_ci			first = i
392862306a36Sopenharmony_ci			break
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci	if first is None:
393162306a36Sopenharmony_ci		raise RuntimeError("CopyTreeCellsToClipboard internal error")
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci	model = first.model()
393462306a36Sopenharmony_ci	row_cnt = 0
393562306a36Sopenharmony_ci	col_cnt = model.columnCount(first)
393662306a36Sopenharmony_ci	max_width = [0] * col_cnt
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci	indent_sz = 2
393962306a36Sopenharmony_ci	indent_str = " " * indent_sz
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	expanded_mark_sz = 2
394262306a36Sopenharmony_ci	if sys.version_info[0] == 3:
394362306a36Sopenharmony_ci		expanded_mark = "\u25BC "
394462306a36Sopenharmony_ci		not_expanded_mark = "\u25B6 "
394562306a36Sopenharmony_ci	else:
394662306a36Sopenharmony_ci		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")
394762306a36Sopenharmony_ci		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")
394862306a36Sopenharmony_ci	leaf_mark = "  "
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ci	if not as_csv:
395162306a36Sopenharmony_ci		pos = first
395262306a36Sopenharmony_ci		while True:
395362306a36Sopenharmony_ci			row_cnt += 1
395462306a36Sopenharmony_ci			row = pos.row()
395562306a36Sopenharmony_ci			for c in range(col_cnt):
395662306a36Sopenharmony_ci				i = pos.sibling(row, c)
395762306a36Sopenharmony_ci				if c:
395862306a36Sopenharmony_ci					n = len(str(i.data()))
395962306a36Sopenharmony_ci				else:
396062306a36Sopenharmony_ci					n = len(str(i.data()).strip())
396162306a36Sopenharmony_ci					n += (i.internalPointer().level - 1) * indent_sz
396262306a36Sopenharmony_ci					n += expanded_mark_sz
396362306a36Sopenharmony_ci				max_width[c] = max(max_width[c], n)
396462306a36Sopenharmony_ci			pos = view.indexBelow(pos)
396562306a36Sopenharmony_ci			if not selection.isSelected(pos):
396662306a36Sopenharmony_ci				break
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	text = ""
396962306a36Sopenharmony_ci	pad = ""
397062306a36Sopenharmony_ci	sep = ""
397162306a36Sopenharmony_ci	if with_hdr:
397262306a36Sopenharmony_ci		for c in range(col_cnt):
397362306a36Sopenharmony_ci			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()
397462306a36Sopenharmony_ci			if as_csv:
397562306a36Sopenharmony_ci				text += sep + ToCSValue(val)
397662306a36Sopenharmony_ci				sep = ","
397762306a36Sopenharmony_ci			else:
397862306a36Sopenharmony_ci				max_width[c] = max(max_width[c], len(val))
397962306a36Sopenharmony_ci				width = max_width[c]
398062306a36Sopenharmony_ci				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)
398162306a36Sopenharmony_ci				if align & Qt.AlignRight:
398262306a36Sopenharmony_ci					val = val.rjust(width)
398362306a36Sopenharmony_ci				text += pad + sep + val
398462306a36Sopenharmony_ci				pad = " " * (width - len(val))
398562306a36Sopenharmony_ci				sep = "   "
398662306a36Sopenharmony_ci		text += "\n"
398762306a36Sopenharmony_ci		pad = ""
398862306a36Sopenharmony_ci		sep = ""
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	pos = first
399162306a36Sopenharmony_ci	while True:
399262306a36Sopenharmony_ci		row = pos.row()
399362306a36Sopenharmony_ci		for c in range(col_cnt):
399462306a36Sopenharmony_ci			i = pos.sibling(row, c)
399562306a36Sopenharmony_ci			val = str(i.data())
399662306a36Sopenharmony_ci			if not c:
399762306a36Sopenharmony_ci				if model.hasChildren(i):
399862306a36Sopenharmony_ci					if view.isExpanded(i):
399962306a36Sopenharmony_ci						mark = expanded_mark
400062306a36Sopenharmony_ci					else:
400162306a36Sopenharmony_ci						mark = not_expanded_mark
400262306a36Sopenharmony_ci				else:
400362306a36Sopenharmony_ci					mark = leaf_mark
400462306a36Sopenharmony_ci				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()
400562306a36Sopenharmony_ci			if as_csv:
400662306a36Sopenharmony_ci				text += sep + ToCSValue(val)
400762306a36Sopenharmony_ci				sep = ","
400862306a36Sopenharmony_ci			else:
400962306a36Sopenharmony_ci				width = max_width[c]
401062306a36Sopenharmony_ci				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:
401162306a36Sopenharmony_ci					val = val.rjust(width)
401262306a36Sopenharmony_ci				text += pad + sep + val
401362306a36Sopenharmony_ci				pad = " " * (width - len(val))
401462306a36Sopenharmony_ci				sep = "   "
401562306a36Sopenharmony_ci		pos = view.indexBelow(pos)
401662306a36Sopenharmony_ci		if not selection.isSelected(pos):
401762306a36Sopenharmony_ci			break
401862306a36Sopenharmony_ci		text = text.rstrip() + "\n"
401962306a36Sopenharmony_ci		pad = ""
402062306a36Sopenharmony_ci		sep = ""
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci	QApplication.clipboard().setText(text)
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_cidef CopyCellsToClipboard(view, as_csv=False, with_hdr=False):
402562306a36Sopenharmony_ci	view.CopyCellsToClipboard(view, as_csv, with_hdr)
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_cidef CopyCellsToClipboardHdr(view):
402862306a36Sopenharmony_ci	CopyCellsToClipboard(view, False, True)
402962306a36Sopenharmony_ci
403062306a36Sopenharmony_cidef CopyCellsToClipboardCSV(view):
403162306a36Sopenharmony_ci	CopyCellsToClipboard(view, True, True)
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci# Context menu
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ciclass ContextMenu(object):
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	def __init__(self, view):
403862306a36Sopenharmony_ci		self.view = view
403962306a36Sopenharmony_ci		self.view.setContextMenuPolicy(Qt.CustomContextMenu)
404062306a36Sopenharmony_ci		self.view.customContextMenuRequested.connect(self.ShowContextMenu)
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	def ShowContextMenu(self, pos):
404362306a36Sopenharmony_ci		menu = QMenu(self.view)
404462306a36Sopenharmony_ci		self.AddActions(menu)
404562306a36Sopenharmony_ci		menu.exec_(self.view.mapToGlobal(pos))
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	def AddCopy(self, menu):
404862306a36Sopenharmony_ci		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))
404962306a36Sopenharmony_ci		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view))
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	def AddActions(self, menu):
405262306a36Sopenharmony_ci		self.AddCopy(menu)
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ciclass TreeContextMenu(ContextMenu):
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	def __init__(self, view):
405762306a36Sopenharmony_ci		super(TreeContextMenu, self).__init__(view)
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_ci	def AddActions(self, menu):
406062306a36Sopenharmony_ci		i = self.view.currentIndex()
406162306a36Sopenharmony_ci		text = str(i.data()).strip()
406262306a36Sopenharmony_ci		if len(text):
406362306a36Sopenharmony_ci			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))
406462306a36Sopenharmony_ci		self.AddCopy(menu)
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci# Table window
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_ciclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	def __init__(self, glb, table_name, parent=None):
407162306a36Sopenharmony_ci		super(TableWindow, self).__init__(parent)
407262306a36Sopenharmony_ci
407362306a36Sopenharmony_ci		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name))
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci		self.model = QSortFilterProxyModel()
407662306a36Sopenharmony_ci		self.model.setSourceModel(self.data_model)
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_ci		self.view = QTableView()
407962306a36Sopenharmony_ci		self.view.setModel(self.model)
408062306a36Sopenharmony_ci		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
408162306a36Sopenharmony_ci		self.view.verticalHeader().setVisible(False)
408262306a36Sopenharmony_ci		self.view.sortByColumn(-1, Qt.AscendingOrder)
408362306a36Sopenharmony_ci		self.view.setSortingEnabled(True)
408462306a36Sopenharmony_ci		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
408562306a36Sopenharmony_ci		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci		self.ResizeColumnsToContents()
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci		self.context_menu = ContextMenu(self.view)
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci		self.find_bar = FindBar(self, self, True)
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci		self.finder = ChildDataItemFinder(self.data_model)
409462306a36Sopenharmony_ci
409562306a36Sopenharmony_ci		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_ci		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci		self.setWidget(self.vbox.Widget())
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table")
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context):
410462306a36Sopenharmony_ci		self.view.setFocus()
410562306a36Sopenharmony_ci		self.find_bar.Busy()
410662306a36Sopenharmony_ci		self.finder.Find(value, direction, pattern, context, self.FindDone)
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci	def FindDone(self, row):
410962306a36Sopenharmony_ci		self.find_bar.Idle()
411062306a36Sopenharmony_ci		if row >= 0:
411162306a36Sopenharmony_ci			self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
411262306a36Sopenharmony_ci		else:
411362306a36Sopenharmony_ci			self.find_bar.NotFound()
411462306a36Sopenharmony_ci
411562306a36Sopenharmony_ci# Table list
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_cidef GetTableList(glb):
411862306a36Sopenharmony_ci	tables = []
411962306a36Sopenharmony_ci	query = QSqlQuery(glb.db)
412062306a36Sopenharmony_ci	if glb.dbref.is_sqlite3:
412162306a36Sopenharmony_ci		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")
412262306a36Sopenharmony_ci	else:
412362306a36Sopenharmony_ci		QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name")
412462306a36Sopenharmony_ci	while query.next():
412562306a36Sopenharmony_ci		tables.append(query.value(0))
412662306a36Sopenharmony_ci	if glb.dbref.is_sqlite3:
412762306a36Sopenharmony_ci		tables.append("sqlite_master")
412862306a36Sopenharmony_ci	else:
412962306a36Sopenharmony_ci		tables.append("information_schema.tables")
413062306a36Sopenharmony_ci		tables.append("information_schema.views")
413162306a36Sopenharmony_ci		tables.append("information_schema.columns")
413262306a36Sopenharmony_ci	return tables
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci# Top Calls data model
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_ciclass TopCallsModel(SQLTableModel):
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci	def __init__(self, glb, report_vars, parent=None):
413962306a36Sopenharmony_ci		text = ""
414062306a36Sopenharmony_ci		if not glb.dbref.is_sqlite3:
414162306a36Sopenharmony_ci			text = "::text"
414262306a36Sopenharmony_ci		limit = ""
414362306a36Sopenharmony_ci		if len(report_vars.limit):
414462306a36Sopenharmony_ci			limit = " LIMIT " + report_vars.limit
414562306a36Sopenharmony_ci		sql = ("SELECT comm, pid, tid, name,"
414662306a36Sopenharmony_ci			" CASE"
414762306a36Sopenharmony_ci			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
414862306a36Sopenharmony_ci			" ELSE short_name"
414962306a36Sopenharmony_ci			" END AS dso,"
415062306a36Sopenharmony_ci			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
415162306a36Sopenharmony_ci			" CASE"
415262306a36Sopenharmony_ci			" WHEN (calls.flags = 1) THEN 'no call'" + text +
415362306a36Sopenharmony_ci			" WHEN (calls.flags = 2) THEN 'no return'" + text +
415462306a36Sopenharmony_ci			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +
415562306a36Sopenharmony_ci			" ELSE ''" + text +
415662306a36Sopenharmony_ci			" END AS flags"
415762306a36Sopenharmony_ci			" FROM calls"
415862306a36Sopenharmony_ci			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
415962306a36Sopenharmony_ci			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
416062306a36Sopenharmony_ci			" INNER JOIN dsos ON symbols.dso_id = dsos.id"
416162306a36Sopenharmony_ci			" INNER JOIN comms ON calls.comm_id = comms.id"
416262306a36Sopenharmony_ci			" INNER JOIN threads ON calls.thread_id = threads.id" +
416362306a36Sopenharmony_ci			report_vars.where_clause +
416462306a36Sopenharmony_ci			" ORDER BY elapsed_time DESC" +
416562306a36Sopenharmony_ci			limit
416662306a36Sopenharmony_ci			)
416762306a36Sopenharmony_ci		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
416862306a36Sopenharmony_ci		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
416962306a36Sopenharmony_ci		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
417062306a36Sopenharmony_ci
417162306a36Sopenharmony_ci	def columnAlignment(self, column):
417262306a36Sopenharmony_ci		return self.alignment[column]
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci# Top Calls report creation dialog
417562306a36Sopenharmony_ci
417662306a36Sopenharmony_ciclass TopCallsDialog(ReportDialogBase):
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
417962306a36Sopenharmony_ci		title = "Top Calls by Elapsed Time"
418062306a36Sopenharmony_ci		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
418162306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
418262306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
418362306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
418462306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
418562306a36Sopenharmony_ci			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
418662306a36Sopenharmony_ci			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
418762306a36Sopenharmony_ci			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
418862306a36Sopenharmony_ci		super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci# Top Calls window
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ciclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	def __init__(self, glb, report_vars, parent=None):
419562306a36Sopenharmony_ci		super(TopCallsWindow, self).__init__(parent)
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_ci		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
419862306a36Sopenharmony_ci		self.model = self.data_model
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci		self.view = QTableView()
420162306a36Sopenharmony_ci		self.view.setModel(self.model)
420262306a36Sopenharmony_ci		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
420362306a36Sopenharmony_ci		self.view.verticalHeader().setVisible(False)
420462306a36Sopenharmony_ci		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)
420562306a36Sopenharmony_ci		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci		self.context_menu = ContextMenu(self.view)
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci		self.ResizeColumnsToContents()
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_ci		self.find_bar = FindBar(self, self, True)
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci		self.finder = ChildDataItemFinder(self.model)
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci		self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci		self.setWidget(self.vbox.Widget())
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci	def Find(self, value, direction, pattern, context):
422462306a36Sopenharmony_ci		self.view.setFocus()
422562306a36Sopenharmony_ci		self.find_bar.Busy()
422662306a36Sopenharmony_ci		self.finder.Find(value, direction, pattern, context, self.FindDone)
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci	def FindDone(self, row):
422962306a36Sopenharmony_ci		self.find_bar.Idle()
423062306a36Sopenharmony_ci		if row >= 0:
423162306a36Sopenharmony_ci			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
423262306a36Sopenharmony_ci		else:
423362306a36Sopenharmony_ci			self.find_bar.NotFound()
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_ci# Action Definition
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_cidef CreateAction(label, tip, callback, parent=None, shortcut=None):
423862306a36Sopenharmony_ci	action = QAction(label, parent)
423962306a36Sopenharmony_ci	if shortcut != None:
424062306a36Sopenharmony_ci		action.setShortcuts(shortcut)
424162306a36Sopenharmony_ci	action.setStatusTip(tip)
424262306a36Sopenharmony_ci	action.triggered.connect(callback)
424362306a36Sopenharmony_ci	return action
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci# Typical application actions
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_cidef CreateExitAction(app, parent=None):
424862306a36Sopenharmony_ci	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci# Typical MDI actions
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_cidef CreateCloseActiveWindowAction(mdi_area):
425362306a36Sopenharmony_ci	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
425462306a36Sopenharmony_ci
425562306a36Sopenharmony_cidef CreateCloseAllWindowsAction(mdi_area):
425662306a36Sopenharmony_ci	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_cidef CreateTileWindowsAction(mdi_area):
425962306a36Sopenharmony_ci	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
426062306a36Sopenharmony_ci
426162306a36Sopenharmony_cidef CreateCascadeWindowsAction(mdi_area):
426262306a36Sopenharmony_ci	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_cidef CreateNextWindowAction(mdi_area):
426562306a36Sopenharmony_ci	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_cidef CreatePreviousWindowAction(mdi_area):
426862306a36Sopenharmony_ci	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci# Typical MDI window menu
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ciclass WindowMenu():
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci	def __init__(self, mdi_area, menu):
427562306a36Sopenharmony_ci		self.mdi_area = mdi_area
427662306a36Sopenharmony_ci		self.window_menu = menu.addMenu("&Windows")
427762306a36Sopenharmony_ci		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
427862306a36Sopenharmony_ci		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
427962306a36Sopenharmony_ci		self.tile_windows = CreateTileWindowsAction(mdi_area)
428062306a36Sopenharmony_ci		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
428162306a36Sopenharmony_ci		self.next_window = CreateNextWindowAction(mdi_area)
428262306a36Sopenharmony_ci		self.previous_window = CreatePreviousWindowAction(mdi_area)
428362306a36Sopenharmony_ci		self.window_menu.aboutToShow.connect(self.Update)
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	def Update(self):
428662306a36Sopenharmony_ci		self.window_menu.clear()
428762306a36Sopenharmony_ci		sub_window_count = len(self.mdi_area.subWindowList())
428862306a36Sopenharmony_ci		have_sub_windows = sub_window_count != 0
428962306a36Sopenharmony_ci		self.close_active_window.setEnabled(have_sub_windows)
429062306a36Sopenharmony_ci		self.close_all_windows.setEnabled(have_sub_windows)
429162306a36Sopenharmony_ci		self.tile_windows.setEnabled(have_sub_windows)
429262306a36Sopenharmony_ci		self.cascade_windows.setEnabled(have_sub_windows)
429362306a36Sopenharmony_ci		self.next_window.setEnabled(have_sub_windows)
429462306a36Sopenharmony_ci		self.previous_window.setEnabled(have_sub_windows)
429562306a36Sopenharmony_ci		self.window_menu.addAction(self.close_active_window)
429662306a36Sopenharmony_ci		self.window_menu.addAction(self.close_all_windows)
429762306a36Sopenharmony_ci		self.window_menu.addSeparator()
429862306a36Sopenharmony_ci		self.window_menu.addAction(self.tile_windows)
429962306a36Sopenharmony_ci		self.window_menu.addAction(self.cascade_windows)
430062306a36Sopenharmony_ci		self.window_menu.addSeparator()
430162306a36Sopenharmony_ci		self.window_menu.addAction(self.next_window)
430262306a36Sopenharmony_ci		self.window_menu.addAction(self.previous_window)
430362306a36Sopenharmony_ci		if sub_window_count == 0:
430462306a36Sopenharmony_ci			return
430562306a36Sopenharmony_ci		self.window_menu.addSeparator()
430662306a36Sopenharmony_ci		nr = 1
430762306a36Sopenharmony_ci		for sub_window in self.mdi_area.subWindowList():
430862306a36Sopenharmony_ci			label = str(nr) + " " + sub_window.name
430962306a36Sopenharmony_ci			if nr < 10:
431062306a36Sopenharmony_ci				label = "&" + label
431162306a36Sopenharmony_ci			action = self.window_menu.addAction(label)
431262306a36Sopenharmony_ci			action.setCheckable(True)
431362306a36Sopenharmony_ci			action.setChecked(sub_window == self.mdi_area.activeSubWindow())
431462306a36Sopenharmony_ci			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
431562306a36Sopenharmony_ci			self.window_menu.addAction(action)
431662306a36Sopenharmony_ci			nr += 1
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	def setActiveSubWindow(self, nr):
431962306a36Sopenharmony_ci		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci# Help text
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ciglb_help_text = """
432462306a36Sopenharmony_ci<h1>Contents</h1>
432562306a36Sopenharmony_ci<style>
432662306a36Sopenharmony_cip.c1 {
432762306a36Sopenharmony_ci    text-indent: 40px;
432862306a36Sopenharmony_ci}
432962306a36Sopenharmony_cip.c2 {
433062306a36Sopenharmony_ci    text-indent: 80px;
433162306a36Sopenharmony_ci}
433262306a36Sopenharmony_ci}
433362306a36Sopenharmony_ci</style>
433462306a36Sopenharmony_ci<p class=c1><a href=#reports>1. Reports</a></p>
433562306a36Sopenharmony_ci<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
433662306a36Sopenharmony_ci<p class=c2><a href=#calltree>1.2 Call Tree</a></p>
433762306a36Sopenharmony_ci<p class=c2><a href=#allbranches>1.3 All branches</a></p>
433862306a36Sopenharmony_ci<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p>
433962306a36Sopenharmony_ci<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p>
434062306a36Sopenharmony_ci<p class=c1><a href=#charts>2. Charts</a></p>
434162306a36Sopenharmony_ci<p class=c2><a href=#timechartbycpu>2.1 Time chart by CPU</a></p>
434262306a36Sopenharmony_ci<p class=c1><a href=#tables>3. Tables</a></p>
434362306a36Sopenharmony_ci<h1 id=reports>1. Reports</h1>
434462306a36Sopenharmony_ci<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
434562306a36Sopenharmony_ciThe result is a GUI window with a tree representing a context-sensitive
434662306a36Sopenharmony_cicall-graph. Expanding a couple of levels of the tree and adjusting column
434762306a36Sopenharmony_ciwidths to suit will display something like:
434862306a36Sopenharmony_ci<pre>
434962306a36Sopenharmony_ci                                         Call Graph: pt_example
435062306a36Sopenharmony_ciCall Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
435162306a36Sopenharmony_civ- ls
435262306a36Sopenharmony_ci    v- 2638:2638
435362306a36Sopenharmony_ci        v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
435462306a36Sopenharmony_ci          |- unknown               unknown       1        13198     0.1              1              0.0
435562306a36Sopenharmony_ci          >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
435662306a36Sopenharmony_ci          >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
435762306a36Sopenharmony_ci          v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
435862306a36Sopenharmony_ci             >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
435962306a36Sopenharmony_ci             >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
436062306a36Sopenharmony_ci             >- __libc_csu_init    ls            1        10354     0.1             10              0.0
436162306a36Sopenharmony_ci             |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
436262306a36Sopenharmony_ci             v- main               ls            1      8182043    99.6         180254             99.9
436362306a36Sopenharmony_ci</pre>
436462306a36Sopenharmony_ci<h3>Points to note:</h3>
436562306a36Sopenharmony_ci<ul>
436662306a36Sopenharmony_ci<li>The top level is a command name (comm)</li>
436762306a36Sopenharmony_ci<li>The next level is a thread (pid:tid)</li>
436862306a36Sopenharmony_ci<li>Subsequent levels are functions</li>
436962306a36Sopenharmony_ci<li>'Count' is the number of calls</li>
437062306a36Sopenharmony_ci<li>'Time' is the elapsed time until the function returns</li>
437162306a36Sopenharmony_ci<li>Percentages are relative to the level above</li>
437262306a36Sopenharmony_ci<li>'Branch Count' is the total number of branches for that function and all functions that it calls
437362306a36Sopenharmony_ci</ul>
437462306a36Sopenharmony_ci<h3>Find</h3>
437562306a36Sopenharmony_ciCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
437662306a36Sopenharmony_ciThe pattern matching symbols are ? for any character and * for zero or more characters.
437762306a36Sopenharmony_ci<h2 id=calltree>1.2 Call Tree</h2>
437862306a36Sopenharmony_ciThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.
437962306a36Sopenharmony_ciAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'.
438062306a36Sopenharmony_ci<h2 id=allbranches>1.3 All branches</h2>
438162306a36Sopenharmony_ciThe All branches report displays all branches in chronological order.
438262306a36Sopenharmony_ciNot all data is fetched immediately. More records can be fetched using the Fetch bar provided.
438362306a36Sopenharmony_ci<h3>Disassembly</h3>
438462306a36Sopenharmony_ciOpen a branch to display disassembly. This only works if:
438562306a36Sopenharmony_ci<ol>
438662306a36Sopenharmony_ci<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
438762306a36Sopenharmony_ci<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
438862306a36Sopenharmony_ciThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
438962306a36Sopenharmony_ciOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
439062306a36Sopenharmony_cior alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
439162306a36Sopenharmony_ci</ol>
439262306a36Sopenharmony_ci<h4 id=xed>Intel XED Setup</h4>
439362306a36Sopenharmony_ciTo use Intel XED, libxed.so must be present.  To build and install libxed.so:
439462306a36Sopenharmony_ci<pre>
439562306a36Sopenharmony_cigit clone https://github.com/intelxed/mbuild.git mbuild
439662306a36Sopenharmony_cigit clone https://github.com/intelxed/xed
439762306a36Sopenharmony_cicd xed
439862306a36Sopenharmony_ci./mfile.py --share
439962306a36Sopenharmony_cisudo ./mfile.py --prefix=/usr/local install
440062306a36Sopenharmony_cisudo ldconfig
440162306a36Sopenharmony_ci</pre>
440262306a36Sopenharmony_ci<h3>Instructions per Cycle (IPC)</h3>
440362306a36Sopenharmony_ciIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
440462306a36Sopenharmony_ci<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
440562306a36Sopenharmony_ciDue to the granularity of timing information, the number of cycles for some code blocks will not be known.
440662306a36Sopenharmony_ciIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
440762306a36Sopenharmony_cisince the previous displayed 'IPC'.
440862306a36Sopenharmony_ci<h3>Find</h3>
440962306a36Sopenharmony_ciCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
441062306a36Sopenharmony_ciRefer to Python documentation for the regular expression syntax.
441162306a36Sopenharmony_ciAll columns are searched, but only currently fetched rows are searched.
441262306a36Sopenharmony_ci<h2 id=selectedbranches>1.4 Selected branches</h2>
441362306a36Sopenharmony_ciThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
441462306a36Sopenharmony_ciby various selection criteria. A dialog box displays available criteria which are AND'ed together.
441562306a36Sopenharmony_ci<h3>1.4.1 Time ranges</h3>
441662306a36Sopenharmony_ciThe time ranges hint text shows the total time range. Relative time ranges can also be entered in
441762306a36Sopenharmony_cims, us or ns. Also, negative values are relative to the end of trace.  Examples:
441862306a36Sopenharmony_ci<pre>
441962306a36Sopenharmony_ci	81073085947329-81073085958238	From 81073085947329 to 81073085958238
442062306a36Sopenharmony_ci	100us-200us		From 100us to 200us
442162306a36Sopenharmony_ci	10ms-			From 10ms to the end
442262306a36Sopenharmony_ci	-100ns			The first 100ns
442362306a36Sopenharmony_ci	-10ms-			The last 10ms
442462306a36Sopenharmony_ci</pre>
442562306a36Sopenharmony_ciN.B. Due to the granularity of timestamps, there could be no branches in any given time range.
442662306a36Sopenharmony_ci<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>
442762306a36Sopenharmony_ciThe Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
442862306a36Sopenharmony_ciThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
442962306a36Sopenharmony_ciIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
443062306a36Sopenharmony_ci<h1 id=charts>2. Charts</h1>
443162306a36Sopenharmony_ci<h2 id=timechartbycpu>2.1 Time chart by CPU</h2>
443262306a36Sopenharmony_ciThis chart displays context switch information when that data is available. Refer to context_switches_view on the Tables menu.
443362306a36Sopenharmony_ci<h3>Features</h3>
443462306a36Sopenharmony_ci<ol>
443562306a36Sopenharmony_ci<li>Mouse over to highight the task and show the time</li>
443662306a36Sopenharmony_ci<li>Drag the mouse to select a region and zoom by pushing the Zoom button</li>
443762306a36Sopenharmony_ci<li>Go back and forward by pressing the arrow buttons</li>
443862306a36Sopenharmony_ci<li>If call information is available, right-click to show a call tree opened to that task and time.
443962306a36Sopenharmony_ciNote, the call tree may take some time to appear, and there may not be call information for the task or time selected.
444062306a36Sopenharmony_ci</li>
444162306a36Sopenharmony_ci</ol>
444262306a36Sopenharmony_ci<h3>Important</h3>
444362306a36Sopenharmony_ciThe graph can be misleading in the following respects:
444462306a36Sopenharmony_ci<ol>
444562306a36Sopenharmony_ci<li>The graph shows the first task on each CPU as running from the beginning of the time range.
444662306a36Sopenharmony_ciBecause tracing might start on different CPUs at different times, that is not necessarily the case.
444762306a36Sopenharmony_ciRefer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li>
444862306a36Sopenharmony_ci<li>Similarly, the last task on each CPU can be showing running longer than it really was.
444962306a36Sopenharmony_ciAgain, refer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li>
445062306a36Sopenharmony_ci<li>When the mouse is over a task, the highlighted task might not be visible on the legend without scrolling if the legend does not fit fully in the window</li>
445162306a36Sopenharmony_ci</ol>
445262306a36Sopenharmony_ci<h1 id=tables>3. Tables</h1>
445362306a36Sopenharmony_ciThe Tables menu shows all tables and views in the database. Most tables have an associated view
445462306a36Sopenharmony_ciwhich displays the information in a more friendly way. Not all data for large tables is fetched
445562306a36Sopenharmony_ciimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
445662306a36Sopenharmony_cibut that can be slow for large tables.
445762306a36Sopenharmony_ci<p>There are also tables of database meta-information.
445862306a36Sopenharmony_ciFor SQLite3 databases, the sqlite_master table is included.
445962306a36Sopenharmony_ciFor PostgreSQL databases, information_schema.tables/views/columns are included.
446062306a36Sopenharmony_ci<h3>Find</h3>
446162306a36Sopenharmony_ciCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
446262306a36Sopenharmony_ciRefer to Python documentation for the regular expression syntax.
446362306a36Sopenharmony_ciAll columns are searched, but only currently fetched rows are searched.
446462306a36Sopenharmony_ci<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
446562306a36Sopenharmony_ciwill go to the next/previous result in id order, instead of display order.
446662306a36Sopenharmony_ci"""
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci# Help window
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ciclass HelpWindow(QMdiSubWindow):
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
447362306a36Sopenharmony_ci		super(HelpWindow, self).__init__(parent)
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci		self.text = QTextBrowser()
447662306a36Sopenharmony_ci		self.text.setHtml(glb_help_text)
447762306a36Sopenharmony_ci		self.text.setReadOnly(True)
447862306a36Sopenharmony_ci		self.text.setOpenExternalLinks(True)
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_ci		self.setWidget(self.text)
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci		AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci# Main window that only displays the help text
448562306a36Sopenharmony_ci
448662306a36Sopenharmony_ciclass HelpOnlyWindow(QMainWindow):
448762306a36Sopenharmony_ci
448862306a36Sopenharmony_ci	def __init__(self, parent=None):
448962306a36Sopenharmony_ci		super(HelpOnlyWindow, self).__init__(parent)
449062306a36Sopenharmony_ci
449162306a36Sopenharmony_ci		self.setMinimumSize(200, 100)
449262306a36Sopenharmony_ci		self.resize(800, 600)
449362306a36Sopenharmony_ci		self.setWindowTitle("Exported SQL Viewer Help")
449462306a36Sopenharmony_ci		self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
449562306a36Sopenharmony_ci
449662306a36Sopenharmony_ci		self.text = QTextBrowser()
449762306a36Sopenharmony_ci		self.text.setHtml(glb_help_text)
449862306a36Sopenharmony_ci		self.text.setReadOnly(True)
449962306a36Sopenharmony_ci		self.text.setOpenExternalLinks(True)
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci		self.setCentralWidget(self.text)
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_ci# PostqreSQL server version
450462306a36Sopenharmony_ci
450562306a36Sopenharmony_cidef PostqreSQLServerVersion(db):
450662306a36Sopenharmony_ci	query = QSqlQuery(db)
450762306a36Sopenharmony_ci	QueryExec(query, "SELECT VERSION()")
450862306a36Sopenharmony_ci	if query.next():
450962306a36Sopenharmony_ci		v_str = query.value(0)
451062306a36Sopenharmony_ci		v_list = v_str.strip().split(" ")
451162306a36Sopenharmony_ci		if v_list[0] == "PostgreSQL" and v_list[2] == "on":
451262306a36Sopenharmony_ci			return v_list[1]
451362306a36Sopenharmony_ci		return v_str
451462306a36Sopenharmony_ci	return "Unknown"
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_ci# SQLite version
451762306a36Sopenharmony_ci
451862306a36Sopenharmony_cidef SQLiteVersion(db):
451962306a36Sopenharmony_ci	query = QSqlQuery(db)
452062306a36Sopenharmony_ci	QueryExec(query, "SELECT sqlite_version()")
452162306a36Sopenharmony_ci	if query.next():
452262306a36Sopenharmony_ci		return query.value(0)
452362306a36Sopenharmony_ci	return "Unknown"
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci# About dialog
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_ciclass AboutDialog(QDialog):
452862306a36Sopenharmony_ci
452962306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
453062306a36Sopenharmony_ci		super(AboutDialog, self).__init__(parent)
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci		self.setWindowTitle("About Exported SQL Viewer")
453362306a36Sopenharmony_ci		self.setMinimumWidth(300)
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci		pyside_version = "1" if pyside_version_1 else "2"
453662306a36Sopenharmony_ci
453762306a36Sopenharmony_ci		text = "<pre>"
453862306a36Sopenharmony_ci		text += "Python version:     " + sys.version.split(" ")[0] + "\n"
453962306a36Sopenharmony_ci		text += "PySide version:     " + pyside_version + "\n"
454062306a36Sopenharmony_ci		text += "Qt version:         " + qVersion() + "\n"
454162306a36Sopenharmony_ci		if glb.dbref.is_sqlite3:
454262306a36Sopenharmony_ci			text += "SQLite version:     " + SQLiteVersion(glb.db) + "\n"
454362306a36Sopenharmony_ci		else:
454462306a36Sopenharmony_ci			text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n"
454562306a36Sopenharmony_ci		text += "</pre>"
454662306a36Sopenharmony_ci
454762306a36Sopenharmony_ci		self.text = QTextBrowser()
454862306a36Sopenharmony_ci		self.text.setHtml(text)
454962306a36Sopenharmony_ci		self.text.setReadOnly(True)
455062306a36Sopenharmony_ci		self.text.setOpenExternalLinks(True)
455162306a36Sopenharmony_ci
455262306a36Sopenharmony_ci		self.vbox = QVBoxLayout()
455362306a36Sopenharmony_ci		self.vbox.addWidget(self.text)
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci		self.setLayout(self.vbox)
455662306a36Sopenharmony_ci
455762306a36Sopenharmony_ci# Font resize
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_cidef ResizeFont(widget, diff):
456062306a36Sopenharmony_ci	font = widget.font()
456162306a36Sopenharmony_ci	sz = font.pointSize()
456262306a36Sopenharmony_ci	font.setPointSize(sz + diff)
456362306a36Sopenharmony_ci	widget.setFont(font)
456462306a36Sopenharmony_ci
456562306a36Sopenharmony_cidef ShrinkFont(widget):
456662306a36Sopenharmony_ci	ResizeFont(widget, -1)
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_cidef EnlargeFont(widget):
456962306a36Sopenharmony_ci	ResizeFont(widget, 1)
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci# Unique name for sub-windows
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_cidef NumberedWindowName(name, nr):
457462306a36Sopenharmony_ci	if nr > 1:
457562306a36Sopenharmony_ci		name += " <" + str(nr) + ">"
457662306a36Sopenharmony_ci	return name
457762306a36Sopenharmony_ci
457862306a36Sopenharmony_cidef UniqueSubWindowName(mdi_area, name):
457962306a36Sopenharmony_ci	nr = 1
458062306a36Sopenharmony_ci	while True:
458162306a36Sopenharmony_ci		unique_name = NumberedWindowName(name, nr)
458262306a36Sopenharmony_ci		ok = True
458362306a36Sopenharmony_ci		for sub_window in mdi_area.subWindowList():
458462306a36Sopenharmony_ci			if sub_window.name == unique_name:
458562306a36Sopenharmony_ci				ok = False
458662306a36Sopenharmony_ci				break
458762306a36Sopenharmony_ci		if ok:
458862306a36Sopenharmony_ci			return unique_name
458962306a36Sopenharmony_ci		nr += 1
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci# Add a sub-window
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_cidef AddSubWindow(mdi_area, sub_window, name):
459462306a36Sopenharmony_ci	unique_name = UniqueSubWindowName(mdi_area, name)
459562306a36Sopenharmony_ci	sub_window.setMinimumSize(200, 100)
459662306a36Sopenharmony_ci	sub_window.resize(800, 600)
459762306a36Sopenharmony_ci	sub_window.setWindowTitle(unique_name)
459862306a36Sopenharmony_ci	sub_window.setAttribute(Qt.WA_DeleteOnClose)
459962306a36Sopenharmony_ci	sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
460062306a36Sopenharmony_ci	sub_window.name = unique_name
460162306a36Sopenharmony_ci	mdi_area.addSubWindow(sub_window)
460262306a36Sopenharmony_ci	sub_window.show()
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci# Main window
460562306a36Sopenharmony_ci
460662306a36Sopenharmony_ciclass MainWindow(QMainWindow):
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	def __init__(self, glb, parent=None):
460962306a36Sopenharmony_ci		super(MainWindow, self).__init__(parent)
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_ci		self.glb = glb
461262306a36Sopenharmony_ci
461362306a36Sopenharmony_ci		self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
461462306a36Sopenharmony_ci		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
461562306a36Sopenharmony_ci		self.setMinimumSize(200, 100)
461662306a36Sopenharmony_ci
461762306a36Sopenharmony_ci		self.mdi_area = QMdiArea()
461862306a36Sopenharmony_ci		self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
461962306a36Sopenharmony_ci		self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci		self.setCentralWidget(self.mdi_area)
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci		menu = self.menuBar()
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_ci		file_menu = menu.addMenu("&File")
462662306a36Sopenharmony_ci		file_menu.addAction(CreateExitAction(glb.app, self))
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci		edit_menu = menu.addMenu("&Edit")
462962306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy))
463062306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self))
463162306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find))
463262306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)]))
463362306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")]))
463462306a36Sopenharmony_ci		edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_ci		reports_menu = menu.addMenu("&Reports")
463762306a36Sopenharmony_ci		if IsSelectable(glb.db, "calls"):
463862306a36Sopenharmony_ci			reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci		if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"):
464162306a36Sopenharmony_ci			reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self))
464262306a36Sopenharmony_ci
464362306a36Sopenharmony_ci		self.EventMenu(GetEventList(glb.db), reports_menu)
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci		if IsSelectable(glb.db, "calls"):
464662306a36Sopenharmony_ci			reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
464762306a36Sopenharmony_ci
464862306a36Sopenharmony_ci		if IsSelectable(glb.db, "context_switches"):
464962306a36Sopenharmony_ci			charts_menu = menu.addMenu("&Charts")
465062306a36Sopenharmony_ci			charts_menu.addAction(CreateAction("&Time chart by CPU", "Create a new window displaying time charts by CPU", self.TimeChartByCPU, self))
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci		self.TableMenu(GetTableList(glb), menu)
465362306a36Sopenharmony_ci
465462306a36Sopenharmony_ci		self.window_menu = WindowMenu(self.mdi_area, menu)
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_ci		help_menu = menu.addMenu("&Help")
465762306a36Sopenharmony_ci		help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
465862306a36Sopenharmony_ci		help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self))
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_ci	def Try(self, fn):
466162306a36Sopenharmony_ci		win = self.mdi_area.activeSubWindow()
466262306a36Sopenharmony_ci		if win:
466362306a36Sopenharmony_ci			try:
466462306a36Sopenharmony_ci				fn(win.view)
466562306a36Sopenharmony_ci			except:
466662306a36Sopenharmony_ci				pass
466762306a36Sopenharmony_ci
466862306a36Sopenharmony_ci	def CopyToClipboard(self):
466962306a36Sopenharmony_ci		self.Try(CopyCellsToClipboardHdr)
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	def CopyToClipboardCSV(self):
467262306a36Sopenharmony_ci		self.Try(CopyCellsToClipboardCSV)
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci	def Find(self):
467562306a36Sopenharmony_ci		win = self.mdi_area.activeSubWindow()
467662306a36Sopenharmony_ci		if win:
467762306a36Sopenharmony_ci			try:
467862306a36Sopenharmony_ci				win.find_bar.Activate()
467962306a36Sopenharmony_ci			except:
468062306a36Sopenharmony_ci				pass
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	def FetchMoreRecords(self):
468362306a36Sopenharmony_ci		win = self.mdi_area.activeSubWindow()
468462306a36Sopenharmony_ci		if win:
468562306a36Sopenharmony_ci			try:
468662306a36Sopenharmony_ci				win.fetch_bar.Activate()
468762306a36Sopenharmony_ci			except:
468862306a36Sopenharmony_ci				pass
468962306a36Sopenharmony_ci
469062306a36Sopenharmony_ci	def ShrinkFont(self):
469162306a36Sopenharmony_ci		self.Try(ShrinkFont)
469262306a36Sopenharmony_ci
469362306a36Sopenharmony_ci	def EnlargeFont(self):
469462306a36Sopenharmony_ci		self.Try(EnlargeFont)
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	def EventMenu(self, events, reports_menu):
469762306a36Sopenharmony_ci		branches_events = 0
469862306a36Sopenharmony_ci		for event in events:
469962306a36Sopenharmony_ci			event = event.split(":")[0]
470062306a36Sopenharmony_ci			if event == "branches":
470162306a36Sopenharmony_ci				branches_events += 1
470262306a36Sopenharmony_ci		dbid = 0
470362306a36Sopenharmony_ci		for event in events:
470462306a36Sopenharmony_ci			dbid += 1
470562306a36Sopenharmony_ci			event = event.split(":")[0]
470662306a36Sopenharmony_ci			if event == "branches":
470762306a36Sopenharmony_ci				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
470862306a36Sopenharmony_ci				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
470962306a36Sopenharmony_ci				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
471062306a36Sopenharmony_ci				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
471162306a36Sopenharmony_ci
471262306a36Sopenharmony_ci	def TimeChartByCPU(self):
471362306a36Sopenharmony_ci		TimeChartByCPUWindow(self.glb, self)
471462306a36Sopenharmony_ci
471562306a36Sopenharmony_ci	def TableMenu(self, tables, menu):
471662306a36Sopenharmony_ci		table_menu = menu.addMenu("&Tables")
471762306a36Sopenharmony_ci		for table in tables:
471862306a36Sopenharmony_ci			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
471962306a36Sopenharmony_ci
472062306a36Sopenharmony_ci	def NewCallGraph(self):
472162306a36Sopenharmony_ci		CallGraphWindow(self.glb, self)
472262306a36Sopenharmony_ci
472362306a36Sopenharmony_ci	def NewCallTree(self):
472462306a36Sopenharmony_ci		CallTreeWindow(self.glb, self)
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_ci	def NewTopCalls(self):
472762306a36Sopenharmony_ci		dialog = TopCallsDialog(self.glb, self)
472862306a36Sopenharmony_ci		ret = dialog.exec_()
472962306a36Sopenharmony_ci		if ret:
473062306a36Sopenharmony_ci			TopCallsWindow(self.glb, dialog.report_vars, self)
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	def NewBranchView(self, event_id):
473362306a36Sopenharmony_ci		BranchWindow(self.glb, event_id, ReportVars(), self)
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci	def NewSelectedBranchView(self, event_id):
473662306a36Sopenharmony_ci		dialog = SelectedBranchDialog(self.glb, self)
473762306a36Sopenharmony_ci		ret = dialog.exec_()
473862306a36Sopenharmony_ci		if ret:
473962306a36Sopenharmony_ci			BranchWindow(self.glb, event_id, dialog.report_vars, self)
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	def NewTableView(self, table_name):
474262306a36Sopenharmony_ci		TableWindow(self.glb, table_name, self)
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci	def Help(self):
474562306a36Sopenharmony_ci		HelpWindow(self.glb, self)
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	def About(self):
474862306a36Sopenharmony_ci		dialog = AboutDialog(self.glb, self)
474962306a36Sopenharmony_ci		dialog.exec_()
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_cidef TryOpen(file_name):
475262306a36Sopenharmony_ci	try:
475362306a36Sopenharmony_ci		return open(file_name, "rb")
475462306a36Sopenharmony_ci	except:
475562306a36Sopenharmony_ci		return None
475662306a36Sopenharmony_ci
475762306a36Sopenharmony_cidef Is64Bit(f):
475862306a36Sopenharmony_ci	result = sizeof(c_void_p)
475962306a36Sopenharmony_ci	# ELF support only
476062306a36Sopenharmony_ci	pos = f.tell()
476162306a36Sopenharmony_ci	f.seek(0)
476262306a36Sopenharmony_ci	header = f.read(7)
476362306a36Sopenharmony_ci	f.seek(pos)
476462306a36Sopenharmony_ci	magic = header[0:4]
476562306a36Sopenharmony_ci	if sys.version_info[0] == 2:
476662306a36Sopenharmony_ci		eclass = ord(header[4])
476762306a36Sopenharmony_ci		encoding = ord(header[5])
476862306a36Sopenharmony_ci		version = ord(header[6])
476962306a36Sopenharmony_ci	else:
477062306a36Sopenharmony_ci		eclass = header[4]
477162306a36Sopenharmony_ci		encoding = header[5]
477262306a36Sopenharmony_ci		version = header[6]
477362306a36Sopenharmony_ci	if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1:
477462306a36Sopenharmony_ci		result = True if eclass == 2 else False
477562306a36Sopenharmony_ci	return result
477662306a36Sopenharmony_ci
477762306a36Sopenharmony_ci# Global data
477862306a36Sopenharmony_ci
477962306a36Sopenharmony_ciclass Glb():
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci	def __init__(self, dbref, db, dbname):
478262306a36Sopenharmony_ci		self.dbref = dbref
478362306a36Sopenharmony_ci		self.db = db
478462306a36Sopenharmony_ci		self.dbname = dbname
478562306a36Sopenharmony_ci		self.home_dir = os.path.expanduser("~")
478662306a36Sopenharmony_ci		self.buildid_dir = os.getenv("PERF_BUILDID_DIR")
478762306a36Sopenharmony_ci		if self.buildid_dir:
478862306a36Sopenharmony_ci			self.buildid_dir += "/.build-id/"
478962306a36Sopenharmony_ci		else:
479062306a36Sopenharmony_ci			self.buildid_dir = self.home_dir + "/.debug/.build-id/"
479162306a36Sopenharmony_ci		self.app = None
479262306a36Sopenharmony_ci		self.mainwindow = None
479362306a36Sopenharmony_ci		self.instances_to_shutdown_on_exit = weakref.WeakSet()
479462306a36Sopenharmony_ci		try:
479562306a36Sopenharmony_ci			self.disassembler = LibXED()
479662306a36Sopenharmony_ci			self.have_disassembler = True
479762306a36Sopenharmony_ci		except:
479862306a36Sopenharmony_ci			self.have_disassembler = False
479962306a36Sopenharmony_ci		self.host_machine_id = 0
480062306a36Sopenharmony_ci		self.host_start_time = 0
480162306a36Sopenharmony_ci		self.host_finish_time = 0
480262306a36Sopenharmony_ci
480362306a36Sopenharmony_ci	def FileFromBuildId(self, build_id):
480462306a36Sopenharmony_ci		file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf"
480562306a36Sopenharmony_ci		return TryOpen(file_name)
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	def FileFromNamesAndBuildId(self, short_name, long_name, build_id):
480862306a36Sopenharmony_ci		# Assume current machine i.e. no support for virtualization
480962306a36Sopenharmony_ci		if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore":
481062306a36Sopenharmony_ci			file_name = os.getenv("PERF_KCORE")
481162306a36Sopenharmony_ci			f = TryOpen(file_name) if file_name else None
481262306a36Sopenharmony_ci			if f:
481362306a36Sopenharmony_ci				return f
481462306a36Sopenharmony_ci			# For now, no special handling if long_name is /proc/kcore
481562306a36Sopenharmony_ci			f = TryOpen(long_name)
481662306a36Sopenharmony_ci			if f:
481762306a36Sopenharmony_ci				return f
481862306a36Sopenharmony_ci		f = self.FileFromBuildId(build_id)
481962306a36Sopenharmony_ci		if f:
482062306a36Sopenharmony_ci			return f
482162306a36Sopenharmony_ci		return None
482262306a36Sopenharmony_ci
482362306a36Sopenharmony_ci	def AddInstanceToShutdownOnExit(self, instance):
482462306a36Sopenharmony_ci		self.instances_to_shutdown_on_exit.add(instance)
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	# Shutdown any background processes or threads
482762306a36Sopenharmony_ci	def ShutdownInstances(self):
482862306a36Sopenharmony_ci		for x in self.instances_to_shutdown_on_exit:
482962306a36Sopenharmony_ci			try:
483062306a36Sopenharmony_ci				x.Shutdown()
483162306a36Sopenharmony_ci			except:
483262306a36Sopenharmony_ci				pass
483362306a36Sopenharmony_ci
483462306a36Sopenharmony_ci	def GetHostMachineId(self):
483562306a36Sopenharmony_ci		query = QSqlQuery(self.db)
483662306a36Sopenharmony_ci		QueryExec(query, "SELECT id FROM machines WHERE pid = -1")
483762306a36Sopenharmony_ci		if query.next():
483862306a36Sopenharmony_ci			self.host_machine_id = query.value(0)
483962306a36Sopenharmony_ci		else:
484062306a36Sopenharmony_ci			self.host_machine_id = 0
484162306a36Sopenharmony_ci		return self.host_machine_id
484262306a36Sopenharmony_ci
484362306a36Sopenharmony_ci	def HostMachineId(self):
484462306a36Sopenharmony_ci		if self.host_machine_id:
484562306a36Sopenharmony_ci			return self.host_machine_id
484662306a36Sopenharmony_ci		return self.GetHostMachineId()
484762306a36Sopenharmony_ci
484862306a36Sopenharmony_ci	def SelectValue(self, sql):
484962306a36Sopenharmony_ci		query = QSqlQuery(self.db)
485062306a36Sopenharmony_ci		try:
485162306a36Sopenharmony_ci			QueryExec(query, sql)
485262306a36Sopenharmony_ci		except:
485362306a36Sopenharmony_ci			return None
485462306a36Sopenharmony_ci		if query.next():
485562306a36Sopenharmony_ci			return Decimal(query.value(0))
485662306a36Sopenharmony_ci		return None
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci	def SwitchesMinTime(self, machine_id):
485962306a36Sopenharmony_ci		return self.SelectValue("SELECT time"
486062306a36Sopenharmony_ci					" FROM context_switches"
486162306a36Sopenharmony_ci					" WHERE time != 0 AND machine_id = " + str(machine_id) +
486262306a36Sopenharmony_ci					" ORDER BY id LIMIT 1")
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ci	def SwitchesMaxTime(self, machine_id):
486562306a36Sopenharmony_ci		return self.SelectValue("SELECT time"
486662306a36Sopenharmony_ci					" FROM context_switches"
486762306a36Sopenharmony_ci					" WHERE time != 0 AND machine_id = " + str(machine_id) +
486862306a36Sopenharmony_ci					" ORDER BY id DESC LIMIT 1")
486962306a36Sopenharmony_ci
487062306a36Sopenharmony_ci	def SamplesMinTime(self, machine_id):
487162306a36Sopenharmony_ci		return self.SelectValue("SELECT time"
487262306a36Sopenharmony_ci					" FROM samples"
487362306a36Sopenharmony_ci					" WHERE time != 0 AND machine_id = " + str(machine_id) +
487462306a36Sopenharmony_ci					" ORDER BY id LIMIT 1")
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	def SamplesMaxTime(self, machine_id):
487762306a36Sopenharmony_ci		return self.SelectValue("SELECT time"
487862306a36Sopenharmony_ci					" FROM samples"
487962306a36Sopenharmony_ci					" WHERE time != 0 AND machine_id = " + str(machine_id) +
488062306a36Sopenharmony_ci					" ORDER BY id DESC LIMIT 1")
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci	def CallsMinTime(self, machine_id):
488362306a36Sopenharmony_ci		return self.SelectValue("SELECT calls.call_time"
488462306a36Sopenharmony_ci					" FROM calls"
488562306a36Sopenharmony_ci					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
488662306a36Sopenharmony_ci					" WHERE calls.call_time != 0 AND threads.machine_id = " + str(machine_id) +
488762306a36Sopenharmony_ci					" ORDER BY calls.id LIMIT 1")
488862306a36Sopenharmony_ci
488962306a36Sopenharmony_ci	def CallsMaxTime(self, machine_id):
489062306a36Sopenharmony_ci		return self.SelectValue("SELECT calls.return_time"
489162306a36Sopenharmony_ci					" FROM calls"
489262306a36Sopenharmony_ci					" INNER JOIN threads ON threads.thread_id = calls.thread_id"
489362306a36Sopenharmony_ci					" WHERE calls.return_time != 0 AND threads.machine_id = " + str(machine_id) +
489462306a36Sopenharmony_ci					" ORDER BY calls.return_time DESC LIMIT 1")
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	def GetStartTime(self, machine_id):
489762306a36Sopenharmony_ci		t0 = self.SwitchesMinTime(machine_id)
489862306a36Sopenharmony_ci		t1 = self.SamplesMinTime(machine_id)
489962306a36Sopenharmony_ci		t2 = self.CallsMinTime(machine_id)
490062306a36Sopenharmony_ci		if t0 is None or (not(t1 is None) and t1 < t0):
490162306a36Sopenharmony_ci			t0 = t1
490262306a36Sopenharmony_ci		if t0 is None or (not(t2 is None) and t2 < t0):
490362306a36Sopenharmony_ci			t0 = t2
490462306a36Sopenharmony_ci		return t0
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci	def GetFinishTime(self, machine_id):
490762306a36Sopenharmony_ci		t0 = self.SwitchesMaxTime(machine_id)
490862306a36Sopenharmony_ci		t1 = self.SamplesMaxTime(machine_id)
490962306a36Sopenharmony_ci		t2 = self.CallsMaxTime(machine_id)
491062306a36Sopenharmony_ci		if t0 is None or (not(t1 is None) and t1 > t0):
491162306a36Sopenharmony_ci			t0 = t1
491262306a36Sopenharmony_ci		if t0 is None or (not(t2 is None) and t2 > t0):
491362306a36Sopenharmony_ci			t0 = t2
491462306a36Sopenharmony_ci		return t0
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	def HostStartTime(self):
491762306a36Sopenharmony_ci		if self.host_start_time:
491862306a36Sopenharmony_ci			return self.host_start_time
491962306a36Sopenharmony_ci		self.host_start_time = self.GetStartTime(self.HostMachineId())
492062306a36Sopenharmony_ci		return self.host_start_time
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci	def HostFinishTime(self):
492362306a36Sopenharmony_ci		if self.host_finish_time:
492462306a36Sopenharmony_ci			return self.host_finish_time
492562306a36Sopenharmony_ci		self.host_finish_time = self.GetFinishTime(self.HostMachineId())
492662306a36Sopenharmony_ci		return self.host_finish_time
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci	def StartTime(self, machine_id):
492962306a36Sopenharmony_ci		if machine_id == self.HostMachineId():
493062306a36Sopenharmony_ci			return self.HostStartTime()
493162306a36Sopenharmony_ci		return self.GetStartTime(machine_id)
493262306a36Sopenharmony_ci
493362306a36Sopenharmony_ci	def FinishTime(self, machine_id):
493462306a36Sopenharmony_ci		if machine_id == self.HostMachineId():
493562306a36Sopenharmony_ci			return self.HostFinishTime()
493662306a36Sopenharmony_ci		return self.GetFinishTime(machine_id)
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_ci# Database reference
493962306a36Sopenharmony_ci
494062306a36Sopenharmony_ciclass DBRef():
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_ci	def __init__(self, is_sqlite3, dbname):
494362306a36Sopenharmony_ci		self.is_sqlite3 = is_sqlite3
494462306a36Sopenharmony_ci		self.dbname = dbname
494562306a36Sopenharmony_ci		self.TRUE = "TRUE"
494662306a36Sopenharmony_ci		self.FALSE = "FALSE"
494762306a36Sopenharmony_ci		# SQLite prior to version 3.23 does not support TRUE and FALSE
494862306a36Sopenharmony_ci		if self.is_sqlite3:
494962306a36Sopenharmony_ci			self.TRUE = "1"
495062306a36Sopenharmony_ci			self.FALSE = "0"
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci	def Open(self, connection_name):
495362306a36Sopenharmony_ci		dbname = self.dbname
495462306a36Sopenharmony_ci		if self.is_sqlite3:
495562306a36Sopenharmony_ci			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
495662306a36Sopenharmony_ci		else:
495762306a36Sopenharmony_ci			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
495862306a36Sopenharmony_ci			opts = dbname.split()
495962306a36Sopenharmony_ci			for opt in opts:
496062306a36Sopenharmony_ci				if "=" in opt:
496162306a36Sopenharmony_ci					opt = opt.split("=")
496262306a36Sopenharmony_ci					if opt[0] == "hostname":
496362306a36Sopenharmony_ci						db.setHostName(opt[1])
496462306a36Sopenharmony_ci					elif opt[0] == "port":
496562306a36Sopenharmony_ci						db.setPort(int(opt[1]))
496662306a36Sopenharmony_ci					elif opt[0] == "username":
496762306a36Sopenharmony_ci						db.setUserName(opt[1])
496862306a36Sopenharmony_ci					elif opt[0] == "password":
496962306a36Sopenharmony_ci						db.setPassword(opt[1])
497062306a36Sopenharmony_ci					elif opt[0] == "dbname":
497162306a36Sopenharmony_ci						dbname = opt[1]
497262306a36Sopenharmony_ci				else:
497362306a36Sopenharmony_ci					dbname = opt
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci		db.setDatabaseName(dbname)
497662306a36Sopenharmony_ci		if not db.open():
497762306a36Sopenharmony_ci			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
497862306a36Sopenharmony_ci		return db, dbname
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci# Main
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_cidef Main():
498362306a36Sopenharmony_ci	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
498462306a36Sopenharmony_ci			"   or: exported-sql-viewer.py --help-only"
498562306a36Sopenharmony_ci	ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
498662306a36Sopenharmony_ci	ap.add_argument("--pyside-version-1", action='store_true')
498762306a36Sopenharmony_ci	ap.add_argument("dbname", nargs="?")
498862306a36Sopenharmony_ci	ap.add_argument("--help-only", action='store_true')
498962306a36Sopenharmony_ci	args = ap.parse_args()
499062306a36Sopenharmony_ci
499162306a36Sopenharmony_ci	if args.help_only:
499262306a36Sopenharmony_ci		app = QApplication(sys.argv)
499362306a36Sopenharmony_ci		mainwindow = HelpOnlyWindow()
499462306a36Sopenharmony_ci		mainwindow.show()
499562306a36Sopenharmony_ci		err = app.exec_()
499662306a36Sopenharmony_ci		sys.exit(err)
499762306a36Sopenharmony_ci
499862306a36Sopenharmony_ci	dbname = args.dbname
499962306a36Sopenharmony_ci	if dbname is None:
500062306a36Sopenharmony_ci		ap.print_usage()
500162306a36Sopenharmony_ci		print("Too few arguments")
500262306a36Sopenharmony_ci		sys.exit(1)
500362306a36Sopenharmony_ci
500462306a36Sopenharmony_ci	is_sqlite3 = False
500562306a36Sopenharmony_ci	try:
500662306a36Sopenharmony_ci		f = open(dbname, "rb")
500762306a36Sopenharmony_ci		if f.read(15) == b'SQLite format 3':
500862306a36Sopenharmony_ci			is_sqlite3 = True
500962306a36Sopenharmony_ci		f.close()
501062306a36Sopenharmony_ci	except:
501162306a36Sopenharmony_ci		pass
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci	dbref = DBRef(is_sqlite3, dbname)
501462306a36Sopenharmony_ci	db, dbname = dbref.Open("main")
501562306a36Sopenharmony_ci	glb = Glb(dbref, db, dbname)
501662306a36Sopenharmony_ci	app = QApplication(sys.argv)
501762306a36Sopenharmony_ci	glb.app = app
501862306a36Sopenharmony_ci	mainwindow = MainWindow(glb)
501962306a36Sopenharmony_ci	glb.mainwindow = mainwindow
502062306a36Sopenharmony_ci	mainwindow.show()
502162306a36Sopenharmony_ci	err = app.exec_()
502262306a36Sopenharmony_ci	glb.ShutdownInstances()
502362306a36Sopenharmony_ci	db.close()
502462306a36Sopenharmony_ci	sys.exit(err)
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_ciif __name__ == "__main__":
502762306a36Sopenharmony_ci	Main()
5028