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