1c6aba1bfSAdrian Hunter#!/usr/bin/env python 2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0 3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database 4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation. 5031c2a00SAdrian Hunter 6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the 7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those 8031c2a00SAdrian Hunter# scripts for details. 9031c2a00SAdrian Hunter# 10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a 11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this: 12031c2a00SAdrian Hunter# 13031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py pt_example 14031c2a00SAdrian Hunter# 15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases 16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g. 17031c2a00SAdrian Hunter# 18031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" 19031c2a00SAdrian Hunter# 20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive 21031c2a00SAdrian Hunter# call-graph. Expanding a couple of levels of the tree and adjusting column 22031c2a00SAdrian Hunter# widths to suit will display something like: 23031c2a00SAdrian Hunter# 24031c2a00SAdrian Hunter# Call Graph: pt_example 25031c2a00SAdrian Hunter# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 26031c2a00SAdrian Hunter# v- ls 27031c2a00SAdrian Hunter# v- 2638:2638 28031c2a00SAdrian Hunter# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 29031c2a00SAdrian Hunter# |- unknown unknown 1 13198 0.1 1 0.0 30031c2a00SAdrian Hunter# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 31031c2a00SAdrian Hunter# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 32031c2a00SAdrian Hunter# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 33031c2a00SAdrian Hunter# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 34031c2a00SAdrian Hunter# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 35031c2a00SAdrian Hunter# >- __libc_csu_init ls 1 10354 0.1 10 0.0 36031c2a00SAdrian Hunter# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 37031c2a00SAdrian Hunter# v- main ls 1 8182043 99.6 180254 99.9 38031c2a00SAdrian Hunter# 39031c2a00SAdrian Hunter# Points to note: 40031c2a00SAdrian Hunter# The top level is a command name (comm) 41031c2a00SAdrian Hunter# The next level is a thread (pid:tid) 42031c2a00SAdrian Hunter# Subsequent levels are functions 43031c2a00SAdrian Hunter# 'Count' is the number of calls 44031c2a00SAdrian Hunter# 'Time' is the elapsed time until the function returns 45031c2a00SAdrian Hunter# Percentages are relative to the level above 46031c2a00SAdrian Hunter# 'Branch Count' is the total number of branches for that function and all 47031c2a00SAdrian Hunter# functions that it calls 48031c2a00SAdrian Hunter 4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and 5076099f98SAdrian Hunter# possibly disassembly. However, presently, the only supported disassembler is 5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID 5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install 5376099f98SAdrian Hunter# libxed.so: 5476099f98SAdrian Hunter# git clone https://github.com/intelxed/mbuild.git mbuild 5576099f98SAdrian Hunter# git clone https://github.com/intelxed/xed 5676099f98SAdrian Hunter# cd xed 5776099f98SAdrian Hunter# ./mfile.py --share 5876099f98SAdrian Hunter# sudo ./mfile.py --prefix=/usr/local install 5976099f98SAdrian Hunter# sudo ldconfig 6076099f98SAdrian Hunter# 6176099f98SAdrian Hunter# Example report: 6276099f98SAdrian Hunter# 6376099f98SAdrian Hunter# Time CPU Command PID TID Branch Type In Tx Branch 6476099f98SAdrian Hunter# 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) 6576099f98SAdrian Hunter# 7fab593ea260 48 89 e7 mov %rsp, %rdi 6676099f98SAdrian Hunter# 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 6776099f98SAdrian Hunter# 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) 6876099f98SAdrian Hunter# 7fab593ea260 48 89 e7 mov %rsp, %rdi 6976099f98SAdrian Hunter# 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930 7076099f98SAdrian Hunter# 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so) 7176099f98SAdrian Hunter# 7fab593ea930 55 pushq %rbp 7276099f98SAdrian Hunter# 7fab593ea931 48 89 e5 mov %rsp, %rbp 7376099f98SAdrian Hunter# 7fab593ea934 41 57 pushq %r15 7476099f98SAdrian Hunter# 7fab593ea936 41 56 pushq %r14 7576099f98SAdrian Hunter# 7fab593ea938 41 55 pushq %r13 7676099f98SAdrian Hunter# 7fab593ea93a 41 54 pushq %r12 7776099f98SAdrian Hunter# 7fab593ea93c 53 pushq %rbx 7876099f98SAdrian Hunter# 7fab593ea93d 48 89 fb mov %rdi, %rbx 7976099f98SAdrian Hunter# 7fab593ea940 48 83 ec 68 sub $0x68, %rsp 8076099f98SAdrian Hunter# 7fab593ea944 0f 31 rdtsc 8176099f98SAdrian Hunter# 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx 8276099f98SAdrian Hunter# 7fab593ea94a 89 c0 mov %eax, %eax 8376099f98SAdrian Hunter# 7fab593ea94c 48 09 c2 or %rax, %rdx 8476099f98SAdrian Hunter# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax 8576099f98SAdrian Hunter# 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 8676099f98SAdrian Hunter# 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so) 8776099f98SAdrian Hunter# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax 8876099f98SAdrian Hunter# 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) 8976099f98SAdrian Hunter# 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 9076099f98SAdrian Hunter 91beda0e72STony Jonesfrom __future__ import print_function 92beda0e72STony Jones 93031c2a00SAdrian Hunterimport sys 94*f56299a9SAdrian Hunter# Only change warnings if the python -W option was not used 95*f56299a9SAdrian Hunterif not sys.warnoptions: 96*f56299a9SAdrian Hunter import warnings 97*f56299a9SAdrian Hunter # PySide2 causes deprecation warnings, ignore them. 98*f56299a9SAdrian Hunter warnings.filterwarnings("ignore", category=DeprecationWarning) 991ed7f47fSAdrian Hunterimport argparse 1001beb5c7bSAdrian Hunterimport weakref 1011beb5c7bSAdrian Hunterimport threading 102ebd70c7dSAdrian Hunterimport string 103beda0e72STony Jonestry: 104beda0e72STony Jones # Python2 105beda0e72STony Jones import cPickle as pickle 106beda0e72STony Jones # size of pickled integer big enough for record size 107beda0e72STony Jones glb_nsz = 8 108beda0e72STony Jonesexcept ImportError: 109beda0e72STony Jones import pickle 110beda0e72STony Jones glb_nsz = 16 1118392b74bSAdrian Hunterimport re 1128392b74bSAdrian Hunterimport os 113b3700f21SAdrian Hunterimport random 114b3700f21SAdrian Hunterimport copy 115b3700f21SAdrian Hunterimport math 116df8ea22aSAdrian Hunter 117df8ea22aSAdrian Hunterpyside_version_1 = True 118df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv: 119df8ea22aSAdrian Hunter try: 120df8ea22aSAdrian Hunter from PySide2.QtCore import * 121df8ea22aSAdrian Hunter from PySide2.QtGui import * 122df8ea22aSAdrian Hunter from PySide2.QtSql import * 123df8ea22aSAdrian Hunter from PySide2.QtWidgets import * 124df8ea22aSAdrian Hunter pyside_version_1 = False 125df8ea22aSAdrian Hunter except: 126df8ea22aSAdrian Hunter pass 127df8ea22aSAdrian Hunter 128df8ea22aSAdrian Hunterif pyside_version_1: 129031c2a00SAdrian Hunter from PySide.QtCore import * 130031c2a00SAdrian Hunter from PySide.QtGui import * 131031c2a00SAdrian Hunter from PySide.QtSql import * 132df8ea22aSAdrian Hunter 133fd931b2eSAdrian Hunterfrom decimal import Decimal, ROUND_HALF_UP 134fd931b2eSAdrian Hunterfrom ctypes import CDLL, Structure, create_string_buffer, addressof, sizeof, \ 135fd931b2eSAdrian Hunter c_void_p, c_bool, c_byte, c_char, c_int, c_uint, c_longlong, c_ulonglong 1368392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event 137031c2a00SAdrian Hunter 138beda0e72STony Jones# xrange is range in Python3 139beda0e72STony Jonestry: 140beda0e72STony Jones xrange 141beda0e72STony Jonesexcept NameError: 142beda0e72STony Jones xrange = range 143beda0e72STony Jones 144beda0e72STony Jonesdef printerr(*args, **keyword_args): 145beda0e72STony Jones print(*args, file=sys.stderr, **keyword_args) 146beda0e72STony Jones 147031c2a00SAdrian Hunter# Data formatting helpers 148031c2a00SAdrian Hunter 14976099f98SAdrian Hunterdef tohex(ip): 15076099f98SAdrian Hunter if ip < 0: 15176099f98SAdrian Hunter ip += 1 << 64 15276099f98SAdrian Hunter return "%x" % ip 15376099f98SAdrian Hunter 15476099f98SAdrian Hunterdef offstr(offset): 15576099f98SAdrian Hunter if offset: 15676099f98SAdrian Hunter return "+0x%x" % offset 15776099f98SAdrian Hunter return "" 15876099f98SAdrian Hunter 159031c2a00SAdrian Hunterdef dsoname(name): 160031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 161031c2a00SAdrian Hunter return "[kernel]" 162031c2a00SAdrian Hunter return name 163031c2a00SAdrian Hunter 164210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0): 165210cf1f9SAdrian Hunter pos = s.find(sub) 166210cf1f9SAdrian Hunter if pos < 0: 167210cf1f9SAdrian Hunter return pos 168210cf1f9SAdrian Hunter if n <= 1: 169210cf1f9SAdrian Hunter return offs + pos 170210cf1f9SAdrian Hunter return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) 171210cf1f9SAdrian Hunter 172031c2a00SAdrian Hunter# Percent to one decimal place 173031c2a00SAdrian Hunter 174031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 175031c2a00SAdrian Hunter if not d: 176031c2a00SAdrian Hunter return "0.0" 177031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 178031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 179031c2a00SAdrian Hunter 180031c2a00SAdrian Hunter# Helper for queries that must not fail 181031c2a00SAdrian Hunter 182031c2a00SAdrian Hunterdef QueryExec(query, stmt): 183031c2a00SAdrian Hunter ret = query.exec_(stmt) 184031c2a00SAdrian Hunter if not ret: 185031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 186031c2a00SAdrian Hunter 187ebd70c7dSAdrian Hunter# Background thread 188ebd70c7dSAdrian Hunter 189ebd70c7dSAdrian Hunterclass Thread(QThread): 190ebd70c7dSAdrian Hunter 191ebd70c7dSAdrian Hunter done = Signal(object) 192ebd70c7dSAdrian Hunter 193ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 194ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 195ebd70c7dSAdrian Hunter self.task = task 196ebd70c7dSAdrian Hunter self.param = param 197ebd70c7dSAdrian Hunter 198ebd70c7dSAdrian Hunter def run(self): 199ebd70c7dSAdrian Hunter while True: 200ebd70c7dSAdrian Hunter if self.param is None: 201ebd70c7dSAdrian Hunter done, result = self.task() 202ebd70c7dSAdrian Hunter else: 203ebd70c7dSAdrian Hunter done, result = self.task(self.param) 204ebd70c7dSAdrian Hunter self.done.emit(result) 205ebd70c7dSAdrian Hunter if done: 206ebd70c7dSAdrian Hunter break 207ebd70c7dSAdrian Hunter 208031c2a00SAdrian Hunter# Tree data model 209031c2a00SAdrian Hunter 210031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 211031c2a00SAdrian Hunter 2124a0979d4SAdrian Hunter def __init__(self, glb, params, parent=None): 213031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 214a448ba23SAdrian Hunter self.glb = glb 2154a0979d4SAdrian Hunter self.params = params 216a448ba23SAdrian Hunter self.root = self.GetRoot() 217031c2a00SAdrian Hunter self.last_row_read = 0 218031c2a00SAdrian Hunter 219031c2a00SAdrian Hunter def Item(self, parent): 220031c2a00SAdrian Hunter if parent.isValid(): 221031c2a00SAdrian Hunter return parent.internalPointer() 222031c2a00SAdrian Hunter else: 223031c2a00SAdrian Hunter return self.root 224031c2a00SAdrian Hunter 225031c2a00SAdrian Hunter def rowCount(self, parent): 226031c2a00SAdrian Hunter result = self.Item(parent).childCount() 227031c2a00SAdrian Hunter if result < 0: 228031c2a00SAdrian Hunter result = 0 229031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 230031c2a00SAdrian Hunter return result 231031c2a00SAdrian Hunter 232031c2a00SAdrian Hunter def hasChildren(self, parent): 233031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 234031c2a00SAdrian Hunter 235031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 236031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 237031c2a00SAdrian Hunter return self.columnAlignment(section) 238031c2a00SAdrian Hunter if role != Qt.DisplayRole: 239031c2a00SAdrian Hunter return None 240031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 241031c2a00SAdrian Hunter return None 242031c2a00SAdrian Hunter return self.columnHeader(section) 243031c2a00SAdrian Hunter 244031c2a00SAdrian Hunter def parent(self, child): 245031c2a00SAdrian Hunter child_item = child.internalPointer() 246031c2a00SAdrian Hunter if child_item is self.root: 247031c2a00SAdrian Hunter return QModelIndex() 248031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 249031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 250031c2a00SAdrian Hunter 251031c2a00SAdrian Hunter def index(self, row, column, parent): 252031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 253031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 254031c2a00SAdrian Hunter 255031c2a00SAdrian Hunter def DisplayData(self, item, index): 256031c2a00SAdrian Hunter return item.getData(index.column()) 257031c2a00SAdrian Hunter 2588392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2598392b74bSAdrian Hunter if row > self.last_row_read: 2608392b74bSAdrian Hunter self.last_row_read = row 2618392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2628392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2638392b74bSAdrian Hunter 2648392b74bSAdrian Hunter def columnAlignment(self, column): 2658392b74bSAdrian Hunter return Qt.AlignLeft 2668392b74bSAdrian Hunter 2678392b74bSAdrian Hunter def columnFont(self, column): 2688392b74bSAdrian Hunter return None 2698392b74bSAdrian Hunter 2708392b74bSAdrian Hunter def data(self, index, role): 2718392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2728392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2738392b74bSAdrian Hunter if role == Qt.FontRole: 2748392b74bSAdrian Hunter return self.columnFont(index.column()) 2758392b74bSAdrian Hunter if role != Qt.DisplayRole: 2768392b74bSAdrian Hunter return None 2778392b74bSAdrian Hunter item = index.internalPointer() 2788392b74bSAdrian Hunter return self.DisplayData(item, index) 2798392b74bSAdrian Hunter 2808392b74bSAdrian Hunter# Table data model 2818392b74bSAdrian Hunter 2828392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2838392b74bSAdrian Hunter 2848392b74bSAdrian Hunter def __init__(self, parent=None): 2858392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2868392b74bSAdrian Hunter self.child_count = 0 2878392b74bSAdrian Hunter self.child_items = [] 2888392b74bSAdrian Hunter self.last_row_read = 0 2898392b74bSAdrian Hunter 2908392b74bSAdrian Hunter def Item(self, parent): 2918392b74bSAdrian Hunter if parent.isValid(): 2928392b74bSAdrian Hunter return parent.internalPointer() 2938392b74bSAdrian Hunter else: 2948392b74bSAdrian Hunter return self 2958392b74bSAdrian Hunter 2968392b74bSAdrian Hunter def rowCount(self, parent): 2978392b74bSAdrian Hunter return self.child_count 2988392b74bSAdrian Hunter 2998392b74bSAdrian Hunter def headerData(self, section, orientation, role): 3008392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 3018392b74bSAdrian Hunter return self.columnAlignment(section) 3028392b74bSAdrian Hunter if role != Qt.DisplayRole: 3038392b74bSAdrian Hunter return None 3048392b74bSAdrian Hunter if orientation != Qt.Horizontal: 3058392b74bSAdrian Hunter return None 3068392b74bSAdrian Hunter return self.columnHeader(section) 3078392b74bSAdrian Hunter 3088392b74bSAdrian Hunter def index(self, row, column, parent): 3098392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 3108392b74bSAdrian Hunter 3118392b74bSAdrian Hunter def DisplayData(self, item, index): 3128392b74bSAdrian Hunter return item.getData(index.column()) 3138392b74bSAdrian Hunter 3148392b74bSAdrian Hunter def FetchIfNeeded(self, row): 3158392b74bSAdrian Hunter if row > self.last_row_read: 3168392b74bSAdrian Hunter self.last_row_read = row 3178392b74bSAdrian Hunter if row + 10 >= self.child_count: 3188392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 3198392b74bSAdrian Hunter 320031c2a00SAdrian Hunter def columnAlignment(self, column): 321031c2a00SAdrian Hunter return Qt.AlignLeft 322031c2a00SAdrian Hunter 323031c2a00SAdrian Hunter def columnFont(self, column): 324031c2a00SAdrian Hunter return None 325031c2a00SAdrian Hunter 326031c2a00SAdrian Hunter def data(self, index, role): 327031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 328031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 329031c2a00SAdrian Hunter if role == Qt.FontRole: 330031c2a00SAdrian Hunter return self.columnFont(index.column()) 331031c2a00SAdrian Hunter if role != Qt.DisplayRole: 332031c2a00SAdrian Hunter return None 333031c2a00SAdrian Hunter item = index.internalPointer() 334031c2a00SAdrian Hunter return self.DisplayData(item, index) 335031c2a00SAdrian Hunter 3361beb5c7bSAdrian Hunter# Model cache 3371beb5c7bSAdrian Hunter 3381beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 3391beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 3401beb5c7bSAdrian Hunter 3411beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 3421beb5c7bSAdrian Hunter model_cache_lock.acquire() 3431beb5c7bSAdrian Hunter try: 3441beb5c7bSAdrian Hunter model = model_cache[model_name] 3451beb5c7bSAdrian Hunter except: 3461beb5c7bSAdrian Hunter model = None 3471beb5c7bSAdrian Hunter if model is None: 3481beb5c7bSAdrian Hunter model = create_fn() 3491beb5c7bSAdrian Hunter model_cache[model_name] = model 3501beb5c7bSAdrian Hunter model_cache_lock.release() 3511beb5c7bSAdrian Hunter return model 3521beb5c7bSAdrian Hunter 353181ea40aSAdrian Hunterdef LookupModel(model_name): 354181ea40aSAdrian Hunter model_cache_lock.acquire() 355181ea40aSAdrian Hunter try: 356181ea40aSAdrian Hunter model = model_cache[model_name] 357181ea40aSAdrian Hunter except: 358181ea40aSAdrian Hunter model = None 359181ea40aSAdrian Hunter model_cache_lock.release() 360181ea40aSAdrian Hunter return model 361181ea40aSAdrian Hunter 362ebd70c7dSAdrian Hunter# Find bar 363ebd70c7dSAdrian Hunter 364ebd70c7dSAdrian Hunterclass FindBar(): 365ebd70c7dSAdrian Hunter 366ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 367ebd70c7dSAdrian Hunter self.finder = finder 368ebd70c7dSAdrian Hunter self.context = [] 369ebd70c7dSAdrian Hunter self.last_value = None 370ebd70c7dSAdrian Hunter self.last_pattern = None 371ebd70c7dSAdrian Hunter 372ebd70c7dSAdrian Hunter label = QLabel("Find:") 373ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 374ebd70c7dSAdrian Hunter 375ebd70c7dSAdrian Hunter self.textbox = QComboBox() 376ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 377ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 378ebd70c7dSAdrian Hunter 379ebd70c7dSAdrian Hunter self.progress = QProgressBar() 380ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 381ebd70c7dSAdrian Hunter self.progress.hide() 382ebd70c7dSAdrian Hunter 383ebd70c7dSAdrian Hunter if is_reg_expr: 384ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 385ebd70c7dSAdrian Hunter else: 386ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 387ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 388ebd70c7dSAdrian Hunter 389ebd70c7dSAdrian Hunter self.next_button = QToolButton() 390ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 391ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 392ebd70c7dSAdrian Hunter 393ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 394ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 395ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 396ebd70c7dSAdrian Hunter 397ebd70c7dSAdrian Hunter self.close_button = QToolButton() 398ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 399ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 400ebd70c7dSAdrian Hunter 401ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 402ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 403ebd70c7dSAdrian Hunter 404ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 405ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 406ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 407ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 408ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 409ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 410ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 411ebd70c7dSAdrian Hunter 412ebd70c7dSAdrian Hunter self.bar = QWidget() 41326688729SAdrian Hunter self.bar.setLayout(self.hbox) 414ebd70c7dSAdrian Hunter self.bar.hide() 415ebd70c7dSAdrian Hunter 416ebd70c7dSAdrian Hunter def Widget(self): 417ebd70c7dSAdrian Hunter return self.bar 418ebd70c7dSAdrian Hunter 419ebd70c7dSAdrian Hunter def Activate(self): 420ebd70c7dSAdrian Hunter self.bar.show() 42180b3fb64SAdrian Hunter self.textbox.lineEdit().selectAll() 422ebd70c7dSAdrian Hunter self.textbox.setFocus() 423ebd70c7dSAdrian Hunter 424ebd70c7dSAdrian Hunter def Deactivate(self): 425ebd70c7dSAdrian Hunter self.bar.hide() 426ebd70c7dSAdrian Hunter 427ebd70c7dSAdrian Hunter def Busy(self): 428ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 429ebd70c7dSAdrian Hunter self.pattern.hide() 430ebd70c7dSAdrian Hunter self.next_button.hide() 431ebd70c7dSAdrian Hunter self.prev_button.hide() 432ebd70c7dSAdrian Hunter self.progress.show() 433ebd70c7dSAdrian Hunter 434ebd70c7dSAdrian Hunter def Idle(self): 435ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 436ebd70c7dSAdrian Hunter self.progress.hide() 437ebd70c7dSAdrian Hunter self.pattern.show() 438ebd70c7dSAdrian Hunter self.next_button.show() 439ebd70c7dSAdrian Hunter self.prev_button.show() 440ebd70c7dSAdrian Hunter 441ebd70c7dSAdrian Hunter def Find(self, direction): 442ebd70c7dSAdrian Hunter value = self.textbox.currentText() 443ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 444ebd70c7dSAdrian Hunter self.last_value = value 445ebd70c7dSAdrian Hunter self.last_pattern = pattern 446ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 447ebd70c7dSAdrian Hunter 448ebd70c7dSAdrian Hunter def ValueChanged(self): 449ebd70c7dSAdrian Hunter value = self.textbox.currentText() 450ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 451ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 452ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 453ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 454ebd70c7dSAdrian Hunter if data == None: 455ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 456ebd70c7dSAdrian Hunter else: 457ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 458ebd70c7dSAdrian Hunter self.Find(0) 459ebd70c7dSAdrian Hunter 460ebd70c7dSAdrian Hunter def NextPrev(self, direction): 461ebd70c7dSAdrian Hunter value = self.textbox.currentText() 462ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 463ebd70c7dSAdrian Hunter if value != self.last_value: 464ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 465ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 466ebd70c7dSAdrian Hunter if index < 0: 467ebd70c7dSAdrian Hunter index = self.textbox.count() 468ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 469ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 470ebd70c7dSAdrian Hunter return 471ebd70c7dSAdrian Hunter else: 472ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 473ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 474ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 475ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 476ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 477ebd70c7dSAdrian Hunter self.Find(direction) 478ebd70c7dSAdrian Hunter 479ebd70c7dSAdrian Hunter def NotFound(self): 480ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 481ebd70c7dSAdrian Hunter 482031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 483031c2a00SAdrian Hunter 484031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 485031c2a00SAdrian Hunter 4864a0979d4SAdrian Hunter def __init__(self, glb, params, row, parent_item): 487031c2a00SAdrian Hunter self.glb = glb 4884a0979d4SAdrian Hunter self.params = params 489031c2a00SAdrian Hunter self.row = row 490031c2a00SAdrian Hunter self.parent_item = parent_item 49126688729SAdrian Hunter self.query_done = False 492031c2a00SAdrian Hunter self.child_count = 0 493031c2a00SAdrian Hunter self.child_items = [] 4943ac641f4SAdrian Hunter if parent_item: 4953ac641f4SAdrian Hunter self.level = parent_item.level + 1 4963ac641f4SAdrian Hunter else: 4973ac641f4SAdrian Hunter self.level = 0 498031c2a00SAdrian Hunter 499031c2a00SAdrian Hunter def getChildItem(self, row): 500031c2a00SAdrian Hunter return self.child_items[row] 501031c2a00SAdrian Hunter 502031c2a00SAdrian Hunter def getParentItem(self): 503031c2a00SAdrian Hunter return self.parent_item 504031c2a00SAdrian Hunter 505031c2a00SAdrian Hunter def getRow(self): 506031c2a00SAdrian Hunter return self.row 507031c2a00SAdrian Hunter 508031c2a00SAdrian Hunter def childCount(self): 509031c2a00SAdrian Hunter if not self.query_done: 510031c2a00SAdrian Hunter self.Select() 511031c2a00SAdrian Hunter if not self.child_count: 512031c2a00SAdrian Hunter return -1 513031c2a00SAdrian Hunter return self.child_count 514031c2a00SAdrian Hunter 515031c2a00SAdrian Hunter def hasChildren(self): 516031c2a00SAdrian Hunter if not self.query_done: 517031c2a00SAdrian Hunter return True 518031c2a00SAdrian Hunter return self.child_count > 0 519031c2a00SAdrian Hunter 520031c2a00SAdrian Hunter def getData(self, column): 521031c2a00SAdrian Hunter return self.data[column] 522031c2a00SAdrian Hunter 523031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 524031c2a00SAdrian Hunter 525031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 526031c2a00SAdrian Hunter 52738a846d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): 5284a0979d4SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 529031c2a00SAdrian Hunter self.comm_id = comm_id 530031c2a00SAdrian Hunter self.thread_id = thread_id 531031c2a00SAdrian Hunter self.call_path_id = call_path_id 53238a846d4SAdrian Hunter self.insn_cnt = insn_cnt 53338a846d4SAdrian Hunter self.cyc_cnt = cyc_cnt 534031c2a00SAdrian Hunter self.branch_count = branch_count 535031c2a00SAdrian Hunter self.time = time 536031c2a00SAdrian Hunter 537031c2a00SAdrian Hunter def Select(self): 53826688729SAdrian Hunter self.query_done = True 539031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 54038a846d4SAdrian Hunter if self.params.have_ipc: 54138a846d4SAdrian Hunter ipc_str = ", SUM(insn_count), SUM(cyc_count)" 54238a846d4SAdrian Hunter else: 54338a846d4SAdrian Hunter ipc_str = "" 54438a846d4SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)" 545031c2a00SAdrian Hunter " FROM calls" 546031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 547031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 548031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 549031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 550031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 551031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 552031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 553031c2a00SAdrian Hunter " ORDER BY call_path_id") 554031c2a00SAdrian Hunter while query.next(): 55538a846d4SAdrian Hunter if self.params.have_ipc: 55638a846d4SAdrian Hunter insn_cnt = int(query.value(5)) 55738a846d4SAdrian Hunter cyc_cnt = int(query.value(6)) 55838a846d4SAdrian Hunter branch_count = int(query.value(7)) 55938a846d4SAdrian Hunter else: 56038a846d4SAdrian Hunter insn_cnt = 0 56138a846d4SAdrian Hunter cyc_cnt = 0 56238a846d4SAdrian Hunter branch_count = int(query.value(5)) 56338a846d4SAdrian Hunter child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self) 564031c2a00SAdrian Hunter self.child_items.append(child_item) 565031c2a00SAdrian Hunter self.child_count += 1 566031c2a00SAdrian Hunter 567031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 568031c2a00SAdrian Hunter 569031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 570031c2a00SAdrian Hunter 57138a846d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item): 57238a846d4SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) 573031c2a00SAdrian Hunter dso = dsoname(dso) 57438a846d4SAdrian Hunter if self.params.have_ipc: 57538a846d4SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 57638a846d4SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 57738a846d4SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 57838a846d4SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 57938a846d4SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ] 58038a846d4SAdrian Hunter else: 581031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 582031c2a00SAdrian Hunter self.dbid = call_path_id 583031c2a00SAdrian Hunter 584031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 585031c2a00SAdrian Hunter 586031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 587031c2a00SAdrian Hunter 5884a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 58938a846d4SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) 59038a846d4SAdrian Hunter if self.params.have_ipc: 59138a846d4SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 59238a846d4SAdrian Hunter else: 593031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 594031c2a00SAdrian Hunter self.dbid = thread_id 595031c2a00SAdrian Hunter 596031c2a00SAdrian Hunter def Select(self): 597031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 598031c2a00SAdrian Hunter for child_item in self.child_items: 599031c2a00SAdrian Hunter self.time += child_item.time 60038a846d4SAdrian Hunter self.insn_cnt += child_item.insn_cnt 60138a846d4SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 602031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 603031c2a00SAdrian Hunter for child_item in self.child_items: 604031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 60538a846d4SAdrian Hunter if self.params.have_ipc: 60638a846d4SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 60738a846d4SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 60838a846d4SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 60938a846d4SAdrian Hunter else: 610031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 611031c2a00SAdrian Hunter 612031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 613031c2a00SAdrian Hunter 614031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 615031c2a00SAdrian Hunter 6164a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 6174a0979d4SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) 61838a846d4SAdrian Hunter if self.params.have_ipc: 61938a846d4SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 62038a846d4SAdrian Hunter else: 621031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 622031c2a00SAdrian Hunter self.dbid = comm_id 623031c2a00SAdrian Hunter 624031c2a00SAdrian Hunter def Select(self): 62526688729SAdrian Hunter self.query_done = True 626031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 627031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 628031c2a00SAdrian Hunter " FROM comm_threads" 629031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 630031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 631031c2a00SAdrian Hunter while query.next(): 6324a0979d4SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 633031c2a00SAdrian Hunter self.child_items.append(child_item) 634031c2a00SAdrian Hunter self.child_count += 1 635031c2a00SAdrian Hunter 636031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 637031c2a00SAdrian Hunter 638031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 639031c2a00SAdrian Hunter 6404a0979d4SAdrian Hunter def __init__(self, glb, params): 6414a0979d4SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, params, 0, None) 642031c2a00SAdrian Hunter self.dbid = 0 64326688729SAdrian Hunter self.query_done = True 64426c11206SAdrian Hunter if_has_calls = "" 64526c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 646af833988SAdrian Hunter if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE 647031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 64826c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 649031c2a00SAdrian Hunter while query.next(): 650031c2a00SAdrian Hunter if not query.value(0): 651031c2a00SAdrian Hunter continue 6524a0979d4SAdrian Hunter child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 653031c2a00SAdrian Hunter self.child_items.append(child_item) 654031c2a00SAdrian Hunter self.child_count += 1 655031c2a00SAdrian Hunter 6564a0979d4SAdrian Hunter# Call graph model parameters 6574a0979d4SAdrian Hunter 6584a0979d4SAdrian Hunterclass CallGraphModelParams(): 6594a0979d4SAdrian Hunter 6604a0979d4SAdrian Hunter def __init__(self, glb, parent=None): 6614a0979d4SAdrian Hunter self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") 6624a0979d4SAdrian Hunter 663254c0d82SAdrian Hunter# Context-sensitive call graph data model base 664031c2a00SAdrian Hunter 665254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 666031c2a00SAdrian Hunter 667031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 6684a0979d4SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) 669031c2a00SAdrian Hunter 670ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 671ebd70c7dSAdrian Hunter if pattern: 672ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 673ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 674ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 675ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 676ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 677ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 678ebd70c7dSAdrian Hunter # Escape % and _ 679ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 680ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 681ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 682ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 683ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 684ebd70c7dSAdrian Hunter else: 685ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 686ebd70c7dSAdrian Hunter else: 687ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 688254c0d82SAdrian Hunter self.DoFindSelect(query, match) 689ebd70c7dSAdrian Hunter 690ebd70c7dSAdrian Hunter def Found(self, query, found): 691ebd70c7dSAdrian Hunter if found: 692ebd70c7dSAdrian Hunter return self.FindPath(query) 693ebd70c7dSAdrian Hunter return [] 694ebd70c7dSAdrian Hunter 695ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 696ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 697ebd70c7dSAdrian Hunter found = query.first() 698ebd70c7dSAdrian Hunter else: 699ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 700ebd70c7dSAdrian Hunter found = query.next() 701ebd70c7dSAdrian Hunter return self.Found(query, found) 702ebd70c7dSAdrian Hunter 703ebd70c7dSAdrian Hunter def FindNext(self, query): 704ebd70c7dSAdrian Hunter found = query.next() 705ebd70c7dSAdrian Hunter if not found: 706ebd70c7dSAdrian Hunter found = query.first() 707ebd70c7dSAdrian Hunter return self.Found(query, found) 708ebd70c7dSAdrian Hunter 709ebd70c7dSAdrian Hunter def FindPrev(self, query): 710ebd70c7dSAdrian Hunter found = query.previous() 711ebd70c7dSAdrian Hunter if not found: 712ebd70c7dSAdrian Hunter found = query.last() 713ebd70c7dSAdrian Hunter return self.Found(query, found) 714ebd70c7dSAdrian Hunter 715ebd70c7dSAdrian Hunter def FindThread(self, c): 716ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 717ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 718ebd70c7dSAdrian Hunter elif c.direction > 0: 719ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 720ebd70c7dSAdrian Hunter else: 721ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 722ebd70c7dSAdrian Hunter return (True, ids) 723ebd70c7dSAdrian Hunter 724ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 725ebd70c7dSAdrian Hunter class Context(): 726ebd70c7dSAdrian Hunter def __init__(self, *x): 727ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 728ebd70c7dSAdrian Hunter def Update(self, *x): 729ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 730ebd70c7dSAdrian Hunter if len(context): 731ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 732ebd70c7dSAdrian Hunter else: 733ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 734ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 735ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 736ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 737ebd70c7dSAdrian Hunter thread.start() 738ebd70c7dSAdrian Hunter 739ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 740ebd70c7dSAdrian Hunter callback(ids) 741ebd70c7dSAdrian Hunter 742254c0d82SAdrian Hunter# Context-sensitive call graph data model 743254c0d82SAdrian Hunter 744254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 745254c0d82SAdrian Hunter 746254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 747254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 748254c0d82SAdrian Hunter 749254c0d82SAdrian Hunter def GetRoot(self): 7504a0979d4SAdrian Hunter return CallGraphRootItem(self.glb, self.params) 751254c0d82SAdrian Hunter 752254c0d82SAdrian Hunter def columnCount(self, parent=None): 75338a846d4SAdrian Hunter if self.params.have_ipc: 75438a846d4SAdrian Hunter return 12 75538a846d4SAdrian Hunter else: 756254c0d82SAdrian Hunter return 7 757254c0d82SAdrian Hunter 758254c0d82SAdrian Hunter def columnHeader(self, column): 75938a846d4SAdrian Hunter if self.params.have_ipc: 76038a846d4SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 76138a846d4SAdrian Hunter else: 762254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 763254c0d82SAdrian Hunter return headers[column] 764254c0d82SAdrian Hunter 765254c0d82SAdrian Hunter def columnAlignment(self, column): 76638a846d4SAdrian Hunter if self.params.have_ipc: 76738a846d4SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 76838a846d4SAdrian Hunter else: 769254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 770254c0d82SAdrian Hunter return alignment[column] 771254c0d82SAdrian Hunter 772254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 773254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 774254c0d82SAdrian Hunter " FROM calls" 775254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 776254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 7777ff520b0SAdrian Hunter " WHERE calls.id <> 0" 7787ff520b0SAdrian Hunter " AND symbols.name" + match + 779254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 780254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 781254c0d82SAdrian Hunter 782254c0d82SAdrian Hunter def FindPath(self, query): 783254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 784254c0d82SAdrian Hunter # to open the tree at the right place. 785254c0d82SAdrian Hunter ids = [] 786254c0d82SAdrian Hunter parent_id = query.value(0) 787254c0d82SAdrian Hunter while parent_id: 788254c0d82SAdrian Hunter ids.insert(0, parent_id) 789254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 790254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 791254c0d82SAdrian Hunter " FROM call_paths" 792254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 793254c0d82SAdrian Hunter if not q2.next(): 794254c0d82SAdrian Hunter break 795254c0d82SAdrian Hunter parent_id = q2.value(0) 796254c0d82SAdrian Hunter # The call path root is not used 797254c0d82SAdrian Hunter if ids[0] == 1: 798254c0d82SAdrian Hunter del ids[0] 799254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 800254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 801254c0d82SAdrian Hunter return ids 802254c0d82SAdrian Hunter 803ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 804ae8b887cSAdrian Hunter 805ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 806ae8b887cSAdrian Hunter 807da4264f5SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item): 8084a0979d4SAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 809ae8b887cSAdrian Hunter self.comm_id = comm_id 810ae8b887cSAdrian Hunter self.thread_id = thread_id 811ae8b887cSAdrian Hunter self.calls_id = calls_id 812da4264f5SAdrian Hunter self.call_time = call_time 813da4264f5SAdrian Hunter self.time = time 814b3b66079SAdrian Hunter self.insn_cnt = insn_cnt 815b3b66079SAdrian Hunter self.cyc_cnt = cyc_cnt 816ae8b887cSAdrian Hunter self.branch_count = branch_count 817ae8b887cSAdrian Hunter 818ae8b887cSAdrian Hunter def Select(self): 81926688729SAdrian Hunter self.query_done = True 820ae8b887cSAdrian Hunter if self.calls_id == 0: 821ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 822ae8b887cSAdrian Hunter else: 823ae8b887cSAdrian Hunter comm_thread = "" 824b3b66079SAdrian Hunter if self.params.have_ipc: 825b3b66079SAdrian Hunter ipc_str = ", insn_count, cyc_count" 826b3b66079SAdrian Hunter else: 827b3b66079SAdrian Hunter ipc_str = "" 828ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 829b3b66079SAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count" 830ae8b887cSAdrian Hunter " FROM calls" 831ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 832ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 833ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 834ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 835ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 836ae8b887cSAdrian Hunter while query.next(): 837b3b66079SAdrian Hunter if self.params.have_ipc: 838b3b66079SAdrian Hunter insn_cnt = int(query.value(5)) 839b3b66079SAdrian Hunter cyc_cnt = int(query.value(6)) 840b3b66079SAdrian Hunter branch_count = int(query.value(7)) 841b3b66079SAdrian Hunter else: 842b3b66079SAdrian Hunter insn_cnt = 0 843b3b66079SAdrian Hunter cyc_cnt = 0 844b3b66079SAdrian Hunter branch_count = int(query.value(5)) 845b3b66079SAdrian Hunter child_item = CallTreeLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self) 846ae8b887cSAdrian Hunter self.child_items.append(child_item) 847ae8b887cSAdrian Hunter self.child_count += 1 848ae8b887cSAdrian Hunter 849ae8b887cSAdrian Hunter# Call tree data model level three item 850ae8b887cSAdrian Hunter 851ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 852ae8b887cSAdrian Hunter 853da4264f5SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item): 854da4264f5SAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item) 855ae8b887cSAdrian Hunter dso = dsoname(dso) 856b3b66079SAdrian Hunter if self.params.have_ipc: 857b3b66079SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 858b3b66079SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 859b3b66079SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 860b3b66079SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 861da4264f5SAdrian Hunter self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ] 862b3b66079SAdrian Hunter else: 863da4264f5SAdrian Hunter self.data = [ name, dso, str(call_time), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 864ae8b887cSAdrian Hunter self.dbid = calls_id 865ae8b887cSAdrian Hunter 866ae8b887cSAdrian Hunter# Call tree data model level two item 867ae8b887cSAdrian Hunter 868ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 869ae8b887cSAdrian Hunter 8704a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 871da4264f5SAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, 0, parent_item) 872b3b66079SAdrian Hunter if self.params.have_ipc: 873b3b66079SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 874b3b66079SAdrian Hunter else: 875ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 876ae8b887cSAdrian Hunter self.dbid = thread_id 877ae8b887cSAdrian Hunter 878ae8b887cSAdrian Hunter def Select(self): 879ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 880ae8b887cSAdrian Hunter for child_item in self.child_items: 881ae8b887cSAdrian Hunter self.time += child_item.time 882b3b66079SAdrian Hunter self.insn_cnt += child_item.insn_cnt 883b3b66079SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 884ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 885ae8b887cSAdrian Hunter for child_item in self.child_items: 886ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 887b3b66079SAdrian Hunter if self.params.have_ipc: 888b3b66079SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 889b3b66079SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 890b3b66079SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 891b3b66079SAdrian Hunter else: 892ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 893ae8b887cSAdrian Hunter 894ae8b887cSAdrian Hunter# Call tree data model level one item 895ae8b887cSAdrian Hunter 896ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 897ae8b887cSAdrian Hunter 8984a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 8994a0979d4SAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) 900b3b66079SAdrian Hunter if self.params.have_ipc: 901b3b66079SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 902b3b66079SAdrian Hunter else: 903ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 904ae8b887cSAdrian Hunter self.dbid = comm_id 905ae8b887cSAdrian Hunter 906ae8b887cSAdrian Hunter def Select(self): 90726688729SAdrian Hunter self.query_done = True 908ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 909ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 910ae8b887cSAdrian Hunter " FROM comm_threads" 911ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 912ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 913ae8b887cSAdrian Hunter while query.next(): 9144a0979d4SAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 915ae8b887cSAdrian Hunter self.child_items.append(child_item) 916ae8b887cSAdrian Hunter self.child_count += 1 917ae8b887cSAdrian Hunter 918ae8b887cSAdrian Hunter# Call tree data model root item 919ae8b887cSAdrian Hunter 920ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 921ae8b887cSAdrian Hunter 9224a0979d4SAdrian Hunter def __init__(self, glb, params): 9234a0979d4SAdrian Hunter super(CallTreeRootItem, self).__init__(glb, params, 0, None) 924ae8b887cSAdrian Hunter self.dbid = 0 92526688729SAdrian Hunter self.query_done = True 92626c11206SAdrian Hunter if_has_calls = "" 92726c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 928af833988SAdrian Hunter if_has_calls = " WHERE has_calls = " + glb.dbref.TRUE 929ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 93026c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 931ae8b887cSAdrian Hunter while query.next(): 932ae8b887cSAdrian Hunter if not query.value(0): 933ae8b887cSAdrian Hunter continue 9344a0979d4SAdrian Hunter child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 935ae8b887cSAdrian Hunter self.child_items.append(child_item) 936ae8b887cSAdrian Hunter self.child_count += 1 937ae8b887cSAdrian Hunter 938ae8b887cSAdrian Hunter# Call Tree data model 939ae8b887cSAdrian Hunter 940ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 941ae8b887cSAdrian Hunter 942ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 943ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 944ae8b887cSAdrian Hunter 945ae8b887cSAdrian Hunter def GetRoot(self): 9464a0979d4SAdrian Hunter return CallTreeRootItem(self.glb, self.params) 947ae8b887cSAdrian Hunter 948ae8b887cSAdrian Hunter def columnCount(self, parent=None): 949b3b66079SAdrian Hunter if self.params.have_ipc: 950b3b66079SAdrian Hunter return 12 951b3b66079SAdrian Hunter else: 952ae8b887cSAdrian Hunter return 7 953ae8b887cSAdrian Hunter 954ae8b887cSAdrian Hunter def columnHeader(self, column): 955b3b66079SAdrian Hunter if self.params.have_ipc: 956b3b66079SAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 957b3b66079SAdrian Hunter else: 958ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 959ae8b887cSAdrian Hunter return headers[column] 960ae8b887cSAdrian Hunter 961ae8b887cSAdrian Hunter def columnAlignment(self, column): 962b3b66079SAdrian Hunter if self.params.have_ipc: 963b3b66079SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 964b3b66079SAdrian Hunter else: 965ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 966ae8b887cSAdrian Hunter return alignment[column] 967ae8b887cSAdrian Hunter 968ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 969ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 970ae8b887cSAdrian Hunter " FROM calls" 971ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 972ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 973031c8d5eSAdrian Hunter " WHERE calls.id <> 0" 974031c8d5eSAdrian Hunter " AND symbols.name" + match + 975ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 976ae8b887cSAdrian Hunter 977ae8b887cSAdrian Hunter def FindPath(self, query): 978ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 979ae8b887cSAdrian Hunter # to open the tree at the right place. 980ae8b887cSAdrian Hunter ids = [] 981ae8b887cSAdrian Hunter parent_id = query.value(0) 982ae8b887cSAdrian Hunter while parent_id: 983ae8b887cSAdrian Hunter ids.insert(0, parent_id) 984ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 985ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 986ae8b887cSAdrian Hunter " FROM calls" 987ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 988ae8b887cSAdrian Hunter if not q2.next(): 989ae8b887cSAdrian Hunter break 990ae8b887cSAdrian Hunter parent_id = q2.value(0) 991ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 992ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 993ae8b887cSAdrian Hunter return ids 994ae8b887cSAdrian Hunter 99542c303ffSAdrian Hunter# Vertical layout 99642c303ffSAdrian Hunter 99742c303ffSAdrian Hunterclass HBoxLayout(QHBoxLayout): 99842c303ffSAdrian Hunter 99942c303ffSAdrian Hunter def __init__(self, *children): 100042c303ffSAdrian Hunter super(HBoxLayout, self).__init__() 100142c303ffSAdrian Hunter 100242c303ffSAdrian Hunter self.layout().setContentsMargins(0, 0, 0, 0) 100342c303ffSAdrian Hunter for child in children: 100442c303ffSAdrian Hunter if child.isWidgetType(): 100542c303ffSAdrian Hunter self.layout().addWidget(child) 100642c303ffSAdrian Hunter else: 100742c303ffSAdrian Hunter self.layout().addLayout(child) 100842c303ffSAdrian Hunter 100942c303ffSAdrian Hunter# Horizontal layout 101042c303ffSAdrian Hunter 101142c303ffSAdrian Hunterclass VBoxLayout(QVBoxLayout): 101242c303ffSAdrian Hunter 101342c303ffSAdrian Hunter def __init__(self, *children): 101442c303ffSAdrian Hunter super(VBoxLayout, self).__init__() 101542c303ffSAdrian Hunter 101642c303ffSAdrian Hunter self.layout().setContentsMargins(0, 0, 0, 0) 101742c303ffSAdrian Hunter for child in children: 101842c303ffSAdrian Hunter if child.isWidgetType(): 101942c303ffSAdrian Hunter self.layout().addWidget(child) 102042c303ffSAdrian Hunter else: 102142c303ffSAdrian Hunter self.layout().addLayout(child) 102242c303ffSAdrian Hunter 102342c303ffSAdrian Hunter# Vertical layout widget 1024ebd70c7dSAdrian Hunter 1025ebd70c7dSAdrian Hunterclass VBox(): 1026ebd70c7dSAdrian Hunter 102742c303ffSAdrian Hunter def __init__(self, *children): 1028ebd70c7dSAdrian Hunter self.vbox = QWidget() 102942c303ffSAdrian Hunter self.vbox.setLayout(VBoxLayout(*children)) 1030ebd70c7dSAdrian Hunter 1031ebd70c7dSAdrian Hunter def Widget(self): 1032ebd70c7dSAdrian Hunter return self.vbox 1033ebd70c7dSAdrian Hunter 1034a731cc4cSAdrian Hunter# Tree window base 10351beb5c7bSAdrian Hunter 1036a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 10371beb5c7bSAdrian Hunter 1038a731cc4cSAdrian Hunter def __init__(self, parent=None): 1039a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 10401beb5c7bSAdrian Hunter 1041a731cc4cSAdrian Hunter self.model = None 1042a731cc4cSAdrian Hunter self.find_bar = None 10431beb5c7bSAdrian Hunter 1044be6e7471SAdrian Hunter self.view = QTreeView() 104596c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 104696c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 1047be6e7471SAdrian Hunter 10489bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 10499bc4e4bfSAdrian Hunter 1050ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 1051ebd70c7dSAdrian Hunter if not len(ids): 1052ebd70c7dSAdrian Hunter return False 1053ebd70c7dSAdrian Hunter parent = QModelIndex() 1054ebd70c7dSAdrian Hunter for dbid in ids: 1055ebd70c7dSAdrian Hunter found = False 1056ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 1057ebd70c7dSAdrian Hunter for row in xrange(n): 1058ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 1059ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 1060ebd70c7dSAdrian Hunter found = True 10613a3cf7c5SAdrian Hunter self.view.setExpanded(parent, True) 1062ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 1063ebd70c7dSAdrian Hunter parent = child 1064ebd70c7dSAdrian Hunter break 1065ebd70c7dSAdrian Hunter if not found: 1066ebd70c7dSAdrian Hunter break 1067ebd70c7dSAdrian Hunter return found 1068ebd70c7dSAdrian Hunter 1069ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 1070ebd70c7dSAdrian Hunter self.view.setFocus() 1071ebd70c7dSAdrian Hunter self.find_bar.Busy() 1072ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 1073ebd70c7dSAdrian Hunter 1074ebd70c7dSAdrian Hunter def FindDone(self, ids): 1075ebd70c7dSAdrian Hunter found = True 1076ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 1077ebd70c7dSAdrian Hunter found = False 1078ebd70c7dSAdrian Hunter self.find_bar.Idle() 1079ebd70c7dSAdrian Hunter if not found: 1080ebd70c7dSAdrian Hunter self.find_bar.NotFound() 1081ebd70c7dSAdrian Hunter 1082a731cc4cSAdrian Hunter 1083a731cc4cSAdrian Hunter# Context-sensitive call graph window 1084a731cc4cSAdrian Hunter 1085a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 1086a731cc4cSAdrian Hunter 1087a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 1088a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 1089a731cc4cSAdrian Hunter 1090a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 1091a731cc4cSAdrian Hunter 1092a731cc4cSAdrian Hunter self.view.setModel(self.model) 1093a731cc4cSAdrian Hunter 1094a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 1095a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 1096a731cc4cSAdrian Hunter 1097a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 1098a731cc4cSAdrian Hunter 1099a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1100a731cc4cSAdrian Hunter 1101a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1102a731cc4cSAdrian Hunter 1103a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 1104a731cc4cSAdrian Hunter 1105ae8b887cSAdrian Hunter# Call tree window 1106ae8b887cSAdrian Hunter 1107ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 1108ae8b887cSAdrian Hunter 1109e69d5df7SAdrian Hunter def __init__(self, glb, parent=None, thread_at_time=None): 1110ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 1111ae8b887cSAdrian Hunter 1112ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 1113ae8b887cSAdrian Hunter 1114ae8b887cSAdrian Hunter self.view.setModel(self.model) 1115ae8b887cSAdrian Hunter 1116ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 1117ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 1118ae8b887cSAdrian Hunter 1119ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 1120ae8b887cSAdrian Hunter 1121ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1122ae8b887cSAdrian Hunter 1123ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1124ae8b887cSAdrian Hunter 1125ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 1126ae8b887cSAdrian Hunter 1127e69d5df7SAdrian Hunter if thread_at_time: 1128e69d5df7SAdrian Hunter self.DisplayThreadAtTime(*thread_at_time) 1129e69d5df7SAdrian Hunter 1130e69d5df7SAdrian Hunter def DisplayThreadAtTime(self, comm_id, thread_id, time): 1131e69d5df7SAdrian Hunter parent = QModelIndex() 1132e69d5df7SAdrian Hunter for dbid in (comm_id, thread_id): 1133e69d5df7SAdrian Hunter found = False 1134e69d5df7SAdrian Hunter n = self.model.rowCount(parent) 1135e69d5df7SAdrian Hunter for row in xrange(n): 1136e69d5df7SAdrian Hunter child = self.model.index(row, 0, parent) 1137e69d5df7SAdrian Hunter if child.internalPointer().dbid == dbid: 1138e69d5df7SAdrian Hunter found = True 1139f18d5cf8SAdrian Hunter self.view.setExpanded(parent, True) 1140e69d5df7SAdrian Hunter self.view.setCurrentIndex(child) 1141e69d5df7SAdrian Hunter parent = child 1142e69d5df7SAdrian Hunter break 1143e69d5df7SAdrian Hunter if not found: 1144e69d5df7SAdrian Hunter return 1145e69d5df7SAdrian Hunter found = False 1146e69d5df7SAdrian Hunter while True: 1147e69d5df7SAdrian Hunter n = self.model.rowCount(parent) 1148e69d5df7SAdrian Hunter if not n: 1149e69d5df7SAdrian Hunter return 1150e69d5df7SAdrian Hunter last_child = None 1151e69d5df7SAdrian Hunter for row in xrange(n): 1152f18d5cf8SAdrian Hunter self.view.setExpanded(parent, True) 1153e69d5df7SAdrian Hunter child = self.model.index(row, 0, parent) 1154e69d5df7SAdrian Hunter child_call_time = child.internalPointer().call_time 1155e69d5df7SAdrian Hunter if child_call_time < time: 1156e69d5df7SAdrian Hunter last_child = child 1157e69d5df7SAdrian Hunter elif child_call_time == time: 1158e69d5df7SAdrian Hunter self.view.setCurrentIndex(child) 1159e69d5df7SAdrian Hunter return 1160e69d5df7SAdrian Hunter elif child_call_time > time: 1161e69d5df7SAdrian Hunter break 1162e69d5df7SAdrian Hunter if not last_child: 1163e69d5df7SAdrian Hunter if not found: 1164e69d5df7SAdrian Hunter child = self.model.index(0, 0, parent) 1165f18d5cf8SAdrian Hunter self.view.setExpanded(parent, True) 1166e69d5df7SAdrian Hunter self.view.setCurrentIndex(child) 1167e69d5df7SAdrian Hunter return 1168e69d5df7SAdrian Hunter found = True 1169f18d5cf8SAdrian Hunter self.view.setExpanded(parent, True) 1170e69d5df7SAdrian Hunter self.view.setCurrentIndex(last_child) 1171e69d5df7SAdrian Hunter parent = last_child 1172e69d5df7SAdrian Hunter 1173b3700f21SAdrian Hunter# ExecComm() gets the comm_id of the command string that was set when the process exec'd i.e. the program name 1174b3700f21SAdrian Hunter 1175b3700f21SAdrian Hunterdef ExecComm(db, thread_id, time): 1176b3700f21SAdrian Hunter query = QSqlQuery(db) 1177b3700f21SAdrian Hunter QueryExec(query, "SELECT comm_threads.comm_id, comms.c_time, comms.exec_flag" 1178b3700f21SAdrian Hunter " FROM comm_threads" 1179b3700f21SAdrian Hunter " INNER JOIN comms ON comms.id = comm_threads.comm_id" 1180b3700f21SAdrian Hunter " WHERE comm_threads.thread_id = " + str(thread_id) + 1181b3700f21SAdrian Hunter " ORDER BY comms.c_time, comms.id") 1182b3700f21SAdrian Hunter first = None 1183b3700f21SAdrian Hunter last = None 1184b3700f21SAdrian Hunter while query.next(): 1185b3700f21SAdrian Hunter if first is None: 1186b3700f21SAdrian Hunter first = query.value(0) 1187b3700f21SAdrian Hunter if query.value(2) and Decimal(query.value(1)) <= Decimal(time): 1188b3700f21SAdrian Hunter last = query.value(0) 1189b3700f21SAdrian Hunter if not(last is None): 1190b3700f21SAdrian Hunter return last 1191b3700f21SAdrian Hunter return first 1192b3700f21SAdrian Hunter 1193b3700f21SAdrian Hunter# Container for (x, y) data 1194b3700f21SAdrian Hunter 1195b3700f21SAdrian Hunterclass XY(): 1196b3700f21SAdrian Hunter def __init__(self, x=0, y=0): 1197b3700f21SAdrian Hunter self.x = x 1198b3700f21SAdrian Hunter self.y = y 1199b3700f21SAdrian Hunter 1200b3700f21SAdrian Hunter def __str__(self): 1201b3700f21SAdrian Hunter return "XY({}, {})".format(str(self.x), str(self.y)) 1202b3700f21SAdrian Hunter 1203b3700f21SAdrian Hunter# Container for sub-range data 1204b3700f21SAdrian Hunter 1205b3700f21SAdrian Hunterclass Subrange(): 1206b3700f21SAdrian Hunter def __init__(self, lo=0, hi=0): 1207b3700f21SAdrian Hunter self.lo = lo 1208b3700f21SAdrian Hunter self.hi = hi 1209b3700f21SAdrian Hunter 1210b3700f21SAdrian Hunter def __str__(self): 1211b3700f21SAdrian Hunter return "Subrange({}, {})".format(str(self.lo), str(self.hi)) 1212b3700f21SAdrian Hunter 1213b3700f21SAdrian Hunter# Graph data region base class 1214b3700f21SAdrian Hunter 1215b3700f21SAdrian Hunterclass GraphDataRegion(object): 1216b3700f21SAdrian Hunter 1217b3700f21SAdrian Hunter def __init__(self, key, title = "", ordinal = ""): 1218b3700f21SAdrian Hunter self.key = key 1219b3700f21SAdrian Hunter self.title = title 1220b3700f21SAdrian Hunter self.ordinal = ordinal 1221b3700f21SAdrian Hunter 1222b3700f21SAdrian Hunter# Function to sort GraphDataRegion 1223b3700f21SAdrian Hunter 1224b3700f21SAdrian Hunterdef GraphDataRegionOrdinal(data_region): 1225b3700f21SAdrian Hunter return data_region.ordinal 1226b3700f21SAdrian Hunter 1227b3700f21SAdrian Hunter# Attributes for a graph region 1228b3700f21SAdrian Hunter 1229b3700f21SAdrian Hunterclass GraphRegionAttribute(): 1230b3700f21SAdrian Hunter 1231b3700f21SAdrian Hunter def __init__(self, colour): 1232b3700f21SAdrian Hunter self.colour = colour 1233b3700f21SAdrian Hunter 1234b3700f21SAdrian Hunter# Switch graph data region represents a task 1235b3700f21SAdrian Hunter 1236b3700f21SAdrian Hunterclass SwitchGraphDataRegion(GraphDataRegion): 1237b3700f21SAdrian Hunter 1238b3700f21SAdrian Hunter def __init__(self, key, exec_comm_id, pid, tid, comm, thread_id, comm_id): 1239b3700f21SAdrian Hunter super(SwitchGraphDataRegion, self).__init__(key) 1240b3700f21SAdrian Hunter 1241b3700f21SAdrian Hunter self.title = str(pid) + " / " + str(tid) + " " + comm 1242b3700f21SAdrian Hunter # Order graph legend within exec comm by pid / tid / time 1243b3700f21SAdrian Hunter self.ordinal = str(pid).rjust(16) + str(exec_comm_id).rjust(8) + str(tid).rjust(16) 1244b3700f21SAdrian Hunter self.exec_comm_id = exec_comm_id 1245b3700f21SAdrian Hunter self.pid = pid 1246b3700f21SAdrian Hunter self.tid = tid 1247b3700f21SAdrian Hunter self.comm = comm 1248b3700f21SAdrian Hunter self.thread_id = thread_id 1249b3700f21SAdrian Hunter self.comm_id = comm_id 1250b3700f21SAdrian Hunter 1251b3700f21SAdrian Hunter# Graph data point 1252b3700f21SAdrian Hunter 1253b3700f21SAdrian Hunterclass GraphDataPoint(): 1254b3700f21SAdrian Hunter 1255b3700f21SAdrian Hunter def __init__(self, data, index, x, y, altx=None, alty=None, hregion=None, vregion=None): 1256b3700f21SAdrian Hunter self.data = data 1257b3700f21SAdrian Hunter self.index = index 1258b3700f21SAdrian Hunter self.x = x 1259b3700f21SAdrian Hunter self.y = y 1260b3700f21SAdrian Hunter self.altx = altx 1261b3700f21SAdrian Hunter self.alty = alty 1262b3700f21SAdrian Hunter self.hregion = hregion 1263b3700f21SAdrian Hunter self.vregion = vregion 1264b3700f21SAdrian Hunter 1265b3700f21SAdrian Hunter# Graph data (single graph) base class 1266b3700f21SAdrian Hunter 1267b3700f21SAdrian Hunterclass GraphData(object): 1268b3700f21SAdrian Hunter 1269b3700f21SAdrian Hunter def __init__(self, collection, xbase=Decimal(0), ybase=Decimal(0)): 1270b3700f21SAdrian Hunter self.collection = collection 1271b3700f21SAdrian Hunter self.points = [] 1272b3700f21SAdrian Hunter self.xbase = xbase 1273b3700f21SAdrian Hunter self.ybase = ybase 1274b3700f21SAdrian Hunter self.title = "" 1275b3700f21SAdrian Hunter 1276b3700f21SAdrian Hunter def AddPoint(self, x, y, altx=None, alty=None, hregion=None, vregion=None): 1277b3700f21SAdrian Hunter index = len(self.points) 1278b3700f21SAdrian Hunter 1279b3700f21SAdrian Hunter x = float(Decimal(x) - self.xbase) 1280b3700f21SAdrian Hunter y = float(Decimal(y) - self.ybase) 1281b3700f21SAdrian Hunter 1282b3700f21SAdrian Hunter self.points.append(GraphDataPoint(self, index, x, y, altx, alty, hregion, vregion)) 1283b3700f21SAdrian Hunter 1284b3700f21SAdrian Hunter def XToData(self, x): 1285b3700f21SAdrian Hunter return Decimal(x) + self.xbase 1286b3700f21SAdrian Hunter 1287b3700f21SAdrian Hunter def YToData(self, y): 1288b3700f21SAdrian Hunter return Decimal(y) + self.ybase 1289b3700f21SAdrian Hunter 1290b3700f21SAdrian Hunter# Switch graph data (for one CPU) 1291b3700f21SAdrian Hunter 1292b3700f21SAdrian Hunterclass SwitchGraphData(GraphData): 1293b3700f21SAdrian Hunter 1294b3700f21SAdrian Hunter def __init__(self, db, collection, cpu, xbase): 1295b3700f21SAdrian Hunter super(SwitchGraphData, self).__init__(collection, xbase) 1296b3700f21SAdrian Hunter 1297b3700f21SAdrian Hunter self.cpu = cpu 1298b3700f21SAdrian Hunter self.title = "CPU " + str(cpu) 1299b3700f21SAdrian Hunter self.SelectSwitches(db) 1300b3700f21SAdrian Hunter 1301b3700f21SAdrian Hunter def SelectComms(self, db, thread_id, last_comm_id, start_time, end_time): 1302b3700f21SAdrian Hunter query = QSqlQuery(db) 1303b3700f21SAdrian Hunter QueryExec(query, "SELECT id, c_time" 1304b3700f21SAdrian Hunter " FROM comms" 1305b3700f21SAdrian Hunter " WHERE c_thread_id = " + str(thread_id) + 1306af833988SAdrian Hunter " AND exec_flag = " + self.collection.glb.dbref.TRUE + 1307b3700f21SAdrian Hunter " AND c_time >= " + str(start_time) + 1308b3700f21SAdrian Hunter " AND c_time <= " + str(end_time) + 1309b3700f21SAdrian Hunter " ORDER BY c_time, id") 1310b3700f21SAdrian Hunter while query.next(): 1311b3700f21SAdrian Hunter comm_id = query.value(0) 1312b3700f21SAdrian Hunter if comm_id == last_comm_id: 1313b3700f21SAdrian Hunter continue 1314b3700f21SAdrian Hunter time = query.value(1) 1315b3700f21SAdrian Hunter hregion = self.HRegion(db, thread_id, comm_id, time) 1316b3700f21SAdrian Hunter self.AddPoint(time, 1000, None, None, hregion) 1317b3700f21SAdrian Hunter 1318b3700f21SAdrian Hunter def SelectSwitches(self, db): 1319b3700f21SAdrian Hunter last_time = None 1320b3700f21SAdrian Hunter last_comm_id = None 1321b3700f21SAdrian Hunter last_thread_id = None 1322b3700f21SAdrian Hunter query = QSqlQuery(db) 1323b3700f21SAdrian Hunter QueryExec(query, "SELECT time, thread_out_id, thread_in_id, comm_out_id, comm_in_id, flags" 1324b3700f21SAdrian Hunter " FROM context_switches" 1325b3700f21SAdrian Hunter " WHERE machine_id = " + str(self.collection.machine_id) + 1326b3700f21SAdrian Hunter " AND cpu = " + str(self.cpu) + 1327b3700f21SAdrian Hunter " ORDER BY time, id") 1328b3700f21SAdrian Hunter while query.next(): 1329b3700f21SAdrian Hunter flags = int(query.value(5)) 1330b3700f21SAdrian Hunter if flags & 1: 1331b3700f21SAdrian Hunter # Schedule-out: detect and add exec's 1332b3700f21SAdrian Hunter if last_thread_id == query.value(1) and last_comm_id is not None and last_comm_id != query.value(3): 1333b3700f21SAdrian Hunter self.SelectComms(db, last_thread_id, last_comm_id, last_time, query.value(0)) 1334b3700f21SAdrian Hunter continue 1335b3700f21SAdrian Hunter # Schedule-in: add data point 1336b3700f21SAdrian Hunter if len(self.points) == 0: 1337b3700f21SAdrian Hunter start_time = self.collection.glb.StartTime(self.collection.machine_id) 1338b3700f21SAdrian Hunter hregion = self.HRegion(db, query.value(1), query.value(3), start_time) 1339b3700f21SAdrian Hunter self.AddPoint(start_time, 1000, None, None, hregion) 1340b3700f21SAdrian Hunter time = query.value(0) 1341b3700f21SAdrian Hunter comm_id = query.value(4) 1342b3700f21SAdrian Hunter thread_id = query.value(2) 1343b3700f21SAdrian Hunter hregion = self.HRegion(db, thread_id, comm_id, time) 1344b3700f21SAdrian Hunter self.AddPoint(time, 1000, None, None, hregion) 1345b3700f21SAdrian Hunter last_time = time 1346b3700f21SAdrian Hunter last_comm_id = comm_id 1347b3700f21SAdrian Hunter last_thread_id = thread_id 1348b3700f21SAdrian Hunter 1349b3700f21SAdrian Hunter def NewHRegion(self, db, key, thread_id, comm_id, time): 1350b3700f21SAdrian Hunter exec_comm_id = ExecComm(db, thread_id, time) 1351b3700f21SAdrian Hunter query = QSqlQuery(db) 1352b3700f21SAdrian Hunter QueryExec(query, "SELECT pid, tid FROM threads WHERE id = " + str(thread_id)) 1353b3700f21SAdrian Hunter if query.next(): 1354b3700f21SAdrian Hunter pid = query.value(0) 1355b3700f21SAdrian Hunter tid = query.value(1) 1356b3700f21SAdrian Hunter else: 1357b3700f21SAdrian Hunter pid = -1 1358b3700f21SAdrian Hunter tid = -1 1359b3700f21SAdrian Hunter query = QSqlQuery(db) 1360b3700f21SAdrian Hunter QueryExec(query, "SELECT comm FROM comms WHERE id = " + str(comm_id)) 1361b3700f21SAdrian Hunter if query.next(): 1362b3700f21SAdrian Hunter comm = query.value(0) 1363b3700f21SAdrian Hunter else: 1364b3700f21SAdrian Hunter comm = "" 1365b3700f21SAdrian Hunter return SwitchGraphDataRegion(key, exec_comm_id, pid, tid, comm, thread_id, comm_id) 1366b3700f21SAdrian Hunter 1367b3700f21SAdrian Hunter def HRegion(self, db, thread_id, comm_id, time): 1368b3700f21SAdrian Hunter key = str(thread_id) + ":" + str(comm_id) 1369b3700f21SAdrian Hunter hregion = self.collection.LookupHRegion(key) 1370b3700f21SAdrian Hunter if hregion is None: 1371b3700f21SAdrian Hunter hregion = self.NewHRegion(db, key, thread_id, comm_id, time) 1372b3700f21SAdrian Hunter self.collection.AddHRegion(key, hregion) 1373b3700f21SAdrian Hunter return hregion 1374b3700f21SAdrian Hunter 1375b3700f21SAdrian Hunter# Graph data collection (multiple related graphs) base class 1376b3700f21SAdrian Hunter 1377b3700f21SAdrian Hunterclass GraphDataCollection(object): 1378b3700f21SAdrian Hunter 1379b3700f21SAdrian Hunter def __init__(self, glb): 1380b3700f21SAdrian Hunter self.glb = glb 1381b3700f21SAdrian Hunter self.data = [] 1382b3700f21SAdrian Hunter self.hregions = {} 1383b3700f21SAdrian Hunter self.xrangelo = None 1384b3700f21SAdrian Hunter self.xrangehi = None 1385b3700f21SAdrian Hunter self.yrangelo = None 1386b3700f21SAdrian Hunter self.yrangehi = None 1387b3700f21SAdrian Hunter self.dp = XY(0, 0) 1388b3700f21SAdrian Hunter 1389b3700f21SAdrian Hunter def AddGraphData(self, data): 1390b3700f21SAdrian Hunter self.data.append(data) 1391b3700f21SAdrian Hunter 1392b3700f21SAdrian Hunter def LookupHRegion(self, key): 1393b3700f21SAdrian Hunter if key in self.hregions: 1394b3700f21SAdrian Hunter return self.hregions[key] 1395b3700f21SAdrian Hunter return None 1396b3700f21SAdrian Hunter 1397b3700f21SAdrian Hunter def AddHRegion(self, key, hregion): 1398b3700f21SAdrian Hunter self.hregions[key] = hregion 1399b3700f21SAdrian Hunter 1400b3700f21SAdrian Hunter# Switch graph data collection (SwitchGraphData for each CPU) 1401b3700f21SAdrian Hunter 1402b3700f21SAdrian Hunterclass SwitchGraphDataCollection(GraphDataCollection): 1403b3700f21SAdrian Hunter 1404b3700f21SAdrian Hunter def __init__(self, glb, db, machine_id): 1405b3700f21SAdrian Hunter super(SwitchGraphDataCollection, self).__init__(glb) 1406b3700f21SAdrian Hunter 1407b3700f21SAdrian Hunter self.machine_id = machine_id 1408b3700f21SAdrian Hunter self.cpus = self.SelectCPUs(db) 1409b3700f21SAdrian Hunter 1410b3700f21SAdrian Hunter self.xrangelo = glb.StartTime(machine_id) 1411b3700f21SAdrian Hunter self.xrangehi = glb.FinishTime(machine_id) 1412b3700f21SAdrian Hunter 1413b3700f21SAdrian Hunter self.yrangelo = Decimal(0) 1414b3700f21SAdrian Hunter self.yrangehi = Decimal(1000) 1415b3700f21SAdrian Hunter 1416b3700f21SAdrian Hunter for cpu in self.cpus: 1417b3700f21SAdrian Hunter self.AddGraphData(SwitchGraphData(db, self, cpu, self.xrangelo)) 1418b3700f21SAdrian Hunter 1419b3700f21SAdrian Hunter def SelectCPUs(self, db): 1420b3700f21SAdrian Hunter cpus = [] 1421b3700f21SAdrian Hunter query = QSqlQuery(db) 1422b3700f21SAdrian Hunter QueryExec(query, "SELECT DISTINCT cpu" 1423b3700f21SAdrian Hunter " FROM context_switches" 1424b3700f21SAdrian Hunter " WHERE machine_id = " + str(self.machine_id)) 1425b3700f21SAdrian Hunter while query.next(): 1426b3700f21SAdrian Hunter cpus.append(int(query.value(0))) 1427b3700f21SAdrian Hunter return sorted(cpus) 1428b3700f21SAdrian Hunter 1429b3700f21SAdrian Hunter# Switch graph data graphics item displays the graphed data 1430b3700f21SAdrian Hunter 1431b3700f21SAdrian Hunterclass SwitchGraphDataGraphicsItem(QGraphicsItem): 1432b3700f21SAdrian Hunter 1433b3700f21SAdrian Hunter def __init__(self, data, graph_width, graph_height, attrs, event_handler, parent=None): 1434b3700f21SAdrian Hunter super(SwitchGraphDataGraphicsItem, self).__init__(parent) 1435b3700f21SAdrian Hunter 1436b3700f21SAdrian Hunter self.data = data 1437b3700f21SAdrian Hunter self.graph_width = graph_width 1438b3700f21SAdrian Hunter self.graph_height = graph_height 1439b3700f21SAdrian Hunter self.attrs = attrs 1440b3700f21SAdrian Hunter self.event_handler = event_handler 1441b3700f21SAdrian Hunter self.setAcceptHoverEvents(True) 1442b3700f21SAdrian Hunter 1443b3700f21SAdrian Hunter def boundingRect(self): 1444b3700f21SAdrian Hunter return QRectF(0, 0, self.graph_width, self.graph_height) 1445b3700f21SAdrian Hunter 1446b3700f21SAdrian Hunter def PaintPoint(self, painter, last, x): 1447b3700f21SAdrian Hunter if not(last is None or last.hregion.pid == 0 or x < self.attrs.subrange.x.lo): 1448b3700f21SAdrian Hunter if last.x < self.attrs.subrange.x.lo: 1449b3700f21SAdrian Hunter x0 = self.attrs.subrange.x.lo 1450b3700f21SAdrian Hunter else: 1451b3700f21SAdrian Hunter x0 = last.x 1452b3700f21SAdrian Hunter if x > self.attrs.subrange.x.hi: 1453b3700f21SAdrian Hunter x1 = self.attrs.subrange.x.hi 1454b3700f21SAdrian Hunter else: 1455b3700f21SAdrian Hunter x1 = x - 1 1456b3700f21SAdrian Hunter x0 = self.attrs.XToPixel(x0) 1457b3700f21SAdrian Hunter x1 = self.attrs.XToPixel(x1) 1458b3700f21SAdrian Hunter 1459b3700f21SAdrian Hunter y0 = self.attrs.YToPixel(last.y) 1460b3700f21SAdrian Hunter 1461b3700f21SAdrian Hunter colour = self.attrs.region_attributes[last.hregion.key].colour 1462b3700f21SAdrian Hunter 1463b3700f21SAdrian Hunter width = x1 - x0 + 1 1464b3700f21SAdrian Hunter if width < 2: 1465b3700f21SAdrian Hunter painter.setPen(colour) 1466b3700f21SAdrian Hunter painter.drawLine(x0, self.graph_height - y0, x0, self.graph_height) 1467b3700f21SAdrian Hunter else: 1468b3700f21SAdrian Hunter painter.fillRect(x0, self.graph_height - y0, width, self.graph_height - 1, colour) 1469b3700f21SAdrian Hunter 1470b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1471b3700f21SAdrian Hunter last = None 1472b3700f21SAdrian Hunter for point in self.data.points: 1473b3700f21SAdrian Hunter self.PaintPoint(painter, last, point.x) 1474b3700f21SAdrian Hunter if point.x > self.attrs.subrange.x.hi: 1475b3700f21SAdrian Hunter break; 1476b3700f21SAdrian Hunter last = point 1477b3700f21SAdrian Hunter self.PaintPoint(painter, last, self.attrs.subrange.x.hi + 1) 1478b3700f21SAdrian Hunter 1479b3700f21SAdrian Hunter def BinarySearchPoint(self, target): 1480b3700f21SAdrian Hunter lower_pos = 0 1481b3700f21SAdrian Hunter higher_pos = len(self.data.points) 1482b3700f21SAdrian Hunter while True: 1483b3700f21SAdrian Hunter pos = int((lower_pos + higher_pos) / 2) 1484b3700f21SAdrian Hunter val = self.data.points[pos].x 1485b3700f21SAdrian Hunter if target >= val: 1486b3700f21SAdrian Hunter lower_pos = pos 1487b3700f21SAdrian Hunter else: 1488b3700f21SAdrian Hunter higher_pos = pos 1489b3700f21SAdrian Hunter if higher_pos <= lower_pos + 1: 1490b3700f21SAdrian Hunter return lower_pos 1491b3700f21SAdrian Hunter 1492b3700f21SAdrian Hunter def XPixelToData(self, x): 1493b3700f21SAdrian Hunter x = self.attrs.PixelToX(x) 1494b3700f21SAdrian Hunter if x < self.data.points[0].x: 1495b3700f21SAdrian Hunter x = 0 1496b3700f21SAdrian Hunter pos = 0 1497b3700f21SAdrian Hunter low = True 1498b3700f21SAdrian Hunter else: 1499b3700f21SAdrian Hunter pos = self.BinarySearchPoint(x) 1500b3700f21SAdrian Hunter low = False 1501b3700f21SAdrian Hunter return (low, pos, self.data.XToData(x)) 1502b3700f21SAdrian Hunter 1503b3700f21SAdrian Hunter def EventToData(self, event): 1504b3700f21SAdrian Hunter no_data = (None,) * 4 1505b3700f21SAdrian Hunter if len(self.data.points) < 1: 1506b3700f21SAdrian Hunter return no_data 1507b3700f21SAdrian Hunter x = event.pos().x() 1508b3700f21SAdrian Hunter if x < 0: 1509b3700f21SAdrian Hunter return no_data 1510b3700f21SAdrian Hunter low0, pos0, time_from = self.XPixelToData(x) 1511b3700f21SAdrian Hunter low1, pos1, time_to = self.XPixelToData(x + 1) 1512b3700f21SAdrian Hunter hregions = set() 1513b3700f21SAdrian Hunter hregion_times = [] 1514b3700f21SAdrian Hunter if not low1: 1515b3700f21SAdrian Hunter for i in xrange(pos0, pos1 + 1): 1516b3700f21SAdrian Hunter hregion = self.data.points[i].hregion 1517b3700f21SAdrian Hunter hregions.add(hregion) 1518b3700f21SAdrian Hunter if i == pos0: 1519b3700f21SAdrian Hunter time = time_from 1520b3700f21SAdrian Hunter else: 1521b3700f21SAdrian Hunter time = self.data.XToData(self.data.points[i].x) 1522b3700f21SAdrian Hunter hregion_times.append((hregion, time)) 1523b3700f21SAdrian Hunter return (time_from, time_to, hregions, hregion_times) 1524b3700f21SAdrian Hunter 1525b3700f21SAdrian Hunter def hoverMoveEvent(self, event): 1526b3700f21SAdrian Hunter time_from, time_to, hregions, hregion_times = self.EventToData(event) 1527b3700f21SAdrian Hunter if time_from is not None: 1528b3700f21SAdrian Hunter self.event_handler.PointEvent(self.data.cpu, time_from, time_to, hregions) 1529b3700f21SAdrian Hunter 1530b3700f21SAdrian Hunter def hoverLeaveEvent(self, event): 1531b3700f21SAdrian Hunter self.event_handler.NoPointEvent() 1532b3700f21SAdrian Hunter 1533b3700f21SAdrian Hunter def mousePressEvent(self, event): 1534b3700f21SAdrian Hunter if event.button() != Qt.RightButton: 1535b3700f21SAdrian Hunter super(SwitchGraphDataGraphicsItem, self).mousePressEvent(event) 1536b3700f21SAdrian Hunter return 1537b3700f21SAdrian Hunter time_from, time_to, hregions, hregion_times = self.EventToData(event) 1538b3700f21SAdrian Hunter if hregion_times: 1539b3700f21SAdrian Hunter self.event_handler.RightClickEvent(self.data.cpu, hregion_times, event.screenPos()) 1540b3700f21SAdrian Hunter 1541b3700f21SAdrian Hunter# X-axis graphics item 1542b3700f21SAdrian Hunter 1543b3700f21SAdrian Hunterclass XAxisGraphicsItem(QGraphicsItem): 1544b3700f21SAdrian Hunter 1545b3700f21SAdrian Hunter def __init__(self, width, parent=None): 1546b3700f21SAdrian Hunter super(XAxisGraphicsItem, self).__init__(parent) 1547b3700f21SAdrian Hunter 1548b3700f21SAdrian Hunter self.width = width 1549b3700f21SAdrian Hunter self.max_mark_sz = 4 1550b3700f21SAdrian Hunter self.height = self.max_mark_sz + 1 1551b3700f21SAdrian Hunter 1552b3700f21SAdrian Hunter def boundingRect(self): 1553b3700f21SAdrian Hunter return QRectF(0, 0, self.width, self.height) 1554b3700f21SAdrian Hunter 1555b3700f21SAdrian Hunter def Step(self): 1556b3700f21SAdrian Hunter attrs = self.parentItem().attrs 1557b3700f21SAdrian Hunter subrange = attrs.subrange.x 1558b3700f21SAdrian Hunter t = subrange.hi - subrange.lo 1559b3700f21SAdrian Hunter s = (3.0 * t) / self.width 1560b3700f21SAdrian Hunter n = 1.0 1561b3700f21SAdrian Hunter while s > n: 1562b3700f21SAdrian Hunter n = n * 10.0 1563b3700f21SAdrian Hunter return n 1564b3700f21SAdrian Hunter 1565b3700f21SAdrian Hunter def PaintMarks(self, painter, at_y, lo, hi, step, i): 1566b3700f21SAdrian Hunter attrs = self.parentItem().attrs 1567b3700f21SAdrian Hunter x = lo 1568b3700f21SAdrian Hunter while x <= hi: 1569b3700f21SAdrian Hunter xp = attrs.XToPixel(x) 1570b3700f21SAdrian Hunter if i % 10: 1571b3700f21SAdrian Hunter if i % 5: 1572b3700f21SAdrian Hunter sz = 1 1573b3700f21SAdrian Hunter else: 1574b3700f21SAdrian Hunter sz = 2 1575b3700f21SAdrian Hunter else: 1576b3700f21SAdrian Hunter sz = self.max_mark_sz 1577b3700f21SAdrian Hunter i = 0 1578b3700f21SAdrian Hunter painter.drawLine(xp, at_y, xp, at_y + sz) 1579b3700f21SAdrian Hunter x += step 1580b3700f21SAdrian Hunter i += 1 1581b3700f21SAdrian Hunter 1582b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1583b3700f21SAdrian Hunter # Using QPainter::drawLine(int x1, int y1, int x2, int y2) so x2 = width -1 1584b3700f21SAdrian Hunter painter.drawLine(0, 0, self.width - 1, 0) 1585b3700f21SAdrian Hunter n = self.Step() 1586b3700f21SAdrian Hunter attrs = self.parentItem().attrs 1587b3700f21SAdrian Hunter subrange = attrs.subrange.x 1588b3700f21SAdrian Hunter if subrange.lo: 1589b3700f21SAdrian Hunter x_offset = n - (subrange.lo % n) 1590b3700f21SAdrian Hunter else: 1591b3700f21SAdrian Hunter x_offset = 0.0 1592b3700f21SAdrian Hunter x = subrange.lo + x_offset 1593b3700f21SAdrian Hunter i = (x / n) % 10 1594b3700f21SAdrian Hunter self.PaintMarks(painter, 0, x, subrange.hi, n, i) 1595b3700f21SAdrian Hunter 1596b3700f21SAdrian Hunter def ScaleDimensions(self): 1597b3700f21SAdrian Hunter n = self.Step() 1598b3700f21SAdrian Hunter attrs = self.parentItem().attrs 1599b3700f21SAdrian Hunter lo = attrs.subrange.x.lo 1600b3700f21SAdrian Hunter hi = (n * 10.0) + lo 1601b3700f21SAdrian Hunter width = attrs.XToPixel(hi) 1602b3700f21SAdrian Hunter if width > 500: 1603b3700f21SAdrian Hunter width = 0 1604b3700f21SAdrian Hunter return (n, lo, hi, width) 1605b3700f21SAdrian Hunter 1606b3700f21SAdrian Hunter def PaintScale(self, painter, at_x, at_y): 1607b3700f21SAdrian Hunter n, lo, hi, width = self.ScaleDimensions() 1608b3700f21SAdrian Hunter if not width: 1609b3700f21SAdrian Hunter return 1610b3700f21SAdrian Hunter painter.drawLine(at_x, at_y, at_x + width, at_y) 1611b3700f21SAdrian Hunter self.PaintMarks(painter, at_y, lo, hi, n, 0) 1612b3700f21SAdrian Hunter 1613b3700f21SAdrian Hunter def ScaleWidth(self): 1614b3700f21SAdrian Hunter n, lo, hi, width = self.ScaleDimensions() 1615b3700f21SAdrian Hunter return width 1616b3700f21SAdrian Hunter 1617b3700f21SAdrian Hunter def ScaleHeight(self): 1618b3700f21SAdrian Hunter return self.height 1619b3700f21SAdrian Hunter 1620b3700f21SAdrian Hunter def ScaleUnit(self): 1621b3700f21SAdrian Hunter return self.Step() * 10 1622b3700f21SAdrian Hunter 1623b3700f21SAdrian Hunter# Scale graphics item base class 1624b3700f21SAdrian Hunter 1625b3700f21SAdrian Hunterclass ScaleGraphicsItem(QGraphicsItem): 1626b3700f21SAdrian Hunter 1627b3700f21SAdrian Hunter def __init__(self, axis, parent=None): 1628b3700f21SAdrian Hunter super(ScaleGraphicsItem, self).__init__(parent) 1629b3700f21SAdrian Hunter self.axis = axis 1630b3700f21SAdrian Hunter 1631b3700f21SAdrian Hunter def boundingRect(self): 1632b3700f21SAdrian Hunter scale_width = self.axis.ScaleWidth() 1633b3700f21SAdrian Hunter if not scale_width: 1634b3700f21SAdrian Hunter return QRectF() 1635b3700f21SAdrian Hunter return QRectF(0, 0, self.axis.ScaleWidth() + 100, self.axis.ScaleHeight()) 1636b3700f21SAdrian Hunter 1637b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1638b3700f21SAdrian Hunter scale_width = self.axis.ScaleWidth() 1639b3700f21SAdrian Hunter if not scale_width: 1640b3700f21SAdrian Hunter return 1641b3700f21SAdrian Hunter self.axis.PaintScale(painter, 0, 5) 1642b3700f21SAdrian Hunter x = scale_width + 4 1643b3700f21SAdrian Hunter painter.drawText(QPointF(x, 10), self.Text()) 1644b3700f21SAdrian Hunter 1645b3700f21SAdrian Hunter def Unit(self): 1646b3700f21SAdrian Hunter return self.axis.ScaleUnit() 1647b3700f21SAdrian Hunter 1648b3700f21SAdrian Hunter def Text(self): 1649b3700f21SAdrian Hunter return "" 1650b3700f21SAdrian Hunter 1651b3700f21SAdrian Hunter# Switch graph scale graphics item 1652b3700f21SAdrian Hunter 1653b3700f21SAdrian Hunterclass SwitchScaleGraphicsItem(ScaleGraphicsItem): 1654b3700f21SAdrian Hunter 1655b3700f21SAdrian Hunter def __init__(self, axis, parent=None): 1656b3700f21SAdrian Hunter super(SwitchScaleGraphicsItem, self).__init__(axis, parent) 1657b3700f21SAdrian Hunter 1658b3700f21SAdrian Hunter def Text(self): 1659b3700f21SAdrian Hunter unit = self.Unit() 1660b3700f21SAdrian Hunter if unit >= 1000000000: 1661b3700f21SAdrian Hunter unit = int(unit / 1000000000) 1662b3700f21SAdrian Hunter us = "s" 1663b3700f21SAdrian Hunter elif unit >= 1000000: 1664b3700f21SAdrian Hunter unit = int(unit / 1000000) 1665b3700f21SAdrian Hunter us = "ms" 1666b3700f21SAdrian Hunter elif unit >= 1000: 1667b3700f21SAdrian Hunter unit = int(unit / 1000) 1668b3700f21SAdrian Hunter us = "us" 1669b3700f21SAdrian Hunter else: 1670b3700f21SAdrian Hunter unit = int(unit) 1671b3700f21SAdrian Hunter us = "ns" 1672b3700f21SAdrian Hunter return " = " + str(unit) + " " + us 1673b3700f21SAdrian Hunter 1674b3700f21SAdrian Hunter# Switch graph graphics item contains graph title, scale, x/y-axis, and the graphed data 1675b3700f21SAdrian Hunter 1676b3700f21SAdrian Hunterclass SwitchGraphGraphicsItem(QGraphicsItem): 1677b3700f21SAdrian Hunter 1678b3700f21SAdrian Hunter def __init__(self, collection, data, attrs, event_handler, first, parent=None): 1679b3700f21SAdrian Hunter super(SwitchGraphGraphicsItem, self).__init__(parent) 1680b3700f21SAdrian Hunter self.collection = collection 1681b3700f21SAdrian Hunter self.data = data 1682b3700f21SAdrian Hunter self.attrs = attrs 1683b3700f21SAdrian Hunter self.event_handler = event_handler 1684b3700f21SAdrian Hunter 1685b3700f21SAdrian Hunter margin = 20 1686b3700f21SAdrian Hunter title_width = 50 1687b3700f21SAdrian Hunter 1688b3700f21SAdrian Hunter self.title_graphics = QGraphicsSimpleTextItem(data.title, self) 1689b3700f21SAdrian Hunter 1690b3700f21SAdrian Hunter self.title_graphics.setPos(margin, margin) 1691b3700f21SAdrian Hunter graph_width = attrs.XToPixel(attrs.subrange.x.hi) + 1 1692b3700f21SAdrian Hunter graph_height = attrs.YToPixel(attrs.subrange.y.hi) + 1 1693b3700f21SAdrian Hunter 1694b3700f21SAdrian Hunter self.graph_origin_x = margin + title_width + margin 1695b3700f21SAdrian Hunter self.graph_origin_y = graph_height + margin 1696b3700f21SAdrian Hunter 1697b3700f21SAdrian Hunter x_axis_size = 1 1698b3700f21SAdrian Hunter y_axis_size = 1 1699b3700f21SAdrian Hunter self.yline = QGraphicsLineItem(0, 0, 0, graph_height, self) 1700b3700f21SAdrian Hunter 1701b3700f21SAdrian Hunter self.x_axis = XAxisGraphicsItem(graph_width, self) 1702b3700f21SAdrian Hunter self.x_axis.setPos(self.graph_origin_x, self.graph_origin_y + 1) 1703b3700f21SAdrian Hunter 1704b3700f21SAdrian Hunter if first: 1705b3700f21SAdrian Hunter self.scale_item = SwitchScaleGraphicsItem(self.x_axis, self) 1706b3700f21SAdrian Hunter self.scale_item.setPos(self.graph_origin_x, self.graph_origin_y + 10) 1707b3700f21SAdrian Hunter 1708b3700f21SAdrian Hunter self.yline.setPos(self.graph_origin_x - y_axis_size, self.graph_origin_y - graph_height) 1709b3700f21SAdrian Hunter 1710b3700f21SAdrian Hunter self.axis_point = QGraphicsLineItem(0, 0, 0, 0, self) 1711b3700f21SAdrian Hunter self.axis_point.setPos(self.graph_origin_x - 1, self.graph_origin_y +1) 1712b3700f21SAdrian Hunter 1713b3700f21SAdrian Hunter self.width = self.graph_origin_x + graph_width + margin 1714b3700f21SAdrian Hunter self.height = self.graph_origin_y + margin 1715b3700f21SAdrian Hunter 1716b3700f21SAdrian Hunter self.graph = SwitchGraphDataGraphicsItem(data, graph_width, graph_height, attrs, event_handler, self) 1717b3700f21SAdrian Hunter self.graph.setPos(self.graph_origin_x, self.graph_origin_y - graph_height) 1718b3700f21SAdrian Hunter 1719b3700f21SAdrian Hunter if parent and 'EnableRubberBand' in dir(parent): 1720b3700f21SAdrian Hunter parent.EnableRubberBand(self.graph_origin_x, self.graph_origin_x + graph_width - 1, self) 1721b3700f21SAdrian Hunter 1722b3700f21SAdrian Hunter def boundingRect(self): 1723b3700f21SAdrian Hunter return QRectF(0, 0, self.width, self.height) 1724b3700f21SAdrian Hunter 1725b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1726b3700f21SAdrian Hunter pass 1727b3700f21SAdrian Hunter 1728b3700f21SAdrian Hunter def RBXToPixel(self, x): 1729b3700f21SAdrian Hunter return self.attrs.PixelToX(x - self.graph_origin_x) 1730b3700f21SAdrian Hunter 1731b3700f21SAdrian Hunter def RBXRangeToPixel(self, x0, x1): 1732b3700f21SAdrian Hunter return (self.RBXToPixel(x0), self.RBXToPixel(x1 + 1)) 1733b3700f21SAdrian Hunter 1734b3700f21SAdrian Hunter def RBPixelToTime(self, x): 1735b3700f21SAdrian Hunter if x < self.data.points[0].x: 1736b3700f21SAdrian Hunter return self.data.XToData(0) 1737b3700f21SAdrian Hunter return self.data.XToData(x) 1738b3700f21SAdrian Hunter 1739b3700f21SAdrian Hunter def RBEventTimes(self, x0, x1): 1740b3700f21SAdrian Hunter x0, x1 = self.RBXRangeToPixel(x0, x1) 1741b3700f21SAdrian Hunter time_from = self.RBPixelToTime(x0) 1742b3700f21SAdrian Hunter time_to = self.RBPixelToTime(x1) 1743b3700f21SAdrian Hunter return (time_from, time_to) 1744b3700f21SAdrian Hunter 1745b3700f21SAdrian Hunter def RBEvent(self, x0, x1): 1746b3700f21SAdrian Hunter time_from, time_to = self.RBEventTimes(x0, x1) 1747b3700f21SAdrian Hunter self.event_handler.RangeEvent(time_from, time_to) 1748b3700f21SAdrian Hunter 1749b3700f21SAdrian Hunter def RBMoveEvent(self, x0, x1): 1750b3700f21SAdrian Hunter if x1 < x0: 1751b3700f21SAdrian Hunter x0, x1 = x1, x0 1752b3700f21SAdrian Hunter self.RBEvent(x0, x1) 1753b3700f21SAdrian Hunter 1754b3700f21SAdrian Hunter def RBReleaseEvent(self, x0, x1, selection_state): 1755b3700f21SAdrian Hunter if x1 < x0: 1756b3700f21SAdrian Hunter x0, x1 = x1, x0 1757b3700f21SAdrian Hunter x0, x1 = self.RBXRangeToPixel(x0, x1) 1758b3700f21SAdrian Hunter self.event_handler.SelectEvent(x0, x1, selection_state) 1759b3700f21SAdrian Hunter 1760b3700f21SAdrian Hunter# Graphics item to draw a vertical bracket (used to highlight "forward" sub-range) 1761b3700f21SAdrian Hunter 1762b3700f21SAdrian Hunterclass VerticalBracketGraphicsItem(QGraphicsItem): 1763b3700f21SAdrian Hunter 1764b3700f21SAdrian Hunter def __init__(self, parent=None): 1765b3700f21SAdrian Hunter super(VerticalBracketGraphicsItem, self).__init__(parent) 1766b3700f21SAdrian Hunter 1767b3700f21SAdrian Hunter self.width = 0 1768b3700f21SAdrian Hunter self.height = 0 1769b3700f21SAdrian Hunter self.hide() 1770b3700f21SAdrian Hunter 1771b3700f21SAdrian Hunter def SetSize(self, width, height): 1772b3700f21SAdrian Hunter self.width = width + 1 1773b3700f21SAdrian Hunter self.height = height + 1 1774b3700f21SAdrian Hunter 1775b3700f21SAdrian Hunter def boundingRect(self): 1776b3700f21SAdrian Hunter return QRectF(0, 0, self.width, self.height) 1777b3700f21SAdrian Hunter 1778b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1779b3700f21SAdrian Hunter colour = QColor(255, 255, 0, 32) 1780b3700f21SAdrian Hunter painter.fillRect(0, 0, self.width, self.height, colour) 1781b3700f21SAdrian Hunter x1 = self.width - 1 1782b3700f21SAdrian Hunter y1 = self.height - 1 1783b3700f21SAdrian Hunter painter.drawLine(0, 0, x1, 0) 1784b3700f21SAdrian Hunter painter.drawLine(0, 0, 0, 3) 1785b3700f21SAdrian Hunter painter.drawLine(x1, 0, x1, 3) 1786b3700f21SAdrian Hunter painter.drawLine(0, y1, x1, y1) 1787b3700f21SAdrian Hunter painter.drawLine(0, y1, 0, y1 - 3) 1788b3700f21SAdrian Hunter painter.drawLine(x1, y1, x1, y1 - 3) 1789b3700f21SAdrian Hunter 1790b3700f21SAdrian Hunter# Graphics item to contain graphs arranged vertically 1791b3700f21SAdrian Hunter 1792b3700f21SAdrian Hunterclass VertcalGraphSetGraphicsItem(QGraphicsItem): 1793b3700f21SAdrian Hunter 1794b3700f21SAdrian Hunter def __init__(self, collection, attrs, event_handler, child_class, parent=None): 1795b3700f21SAdrian Hunter super(VertcalGraphSetGraphicsItem, self).__init__(parent) 1796b3700f21SAdrian Hunter 1797b3700f21SAdrian Hunter self.collection = collection 1798b3700f21SAdrian Hunter 1799b3700f21SAdrian Hunter self.top = 10 1800b3700f21SAdrian Hunter 1801b3700f21SAdrian Hunter self.width = 0 1802b3700f21SAdrian Hunter self.height = self.top 1803b3700f21SAdrian Hunter 1804b3700f21SAdrian Hunter self.rubber_band = None 1805b3700f21SAdrian Hunter self.rb_enabled = False 1806b3700f21SAdrian Hunter 1807b3700f21SAdrian Hunter first = True 1808b3700f21SAdrian Hunter for data in collection.data: 1809b3700f21SAdrian Hunter child = child_class(collection, data, attrs, event_handler, first, self) 1810b3700f21SAdrian Hunter child.setPos(0, self.height + 1) 1811b3700f21SAdrian Hunter rect = child.boundingRect() 1812b3700f21SAdrian Hunter if rect.right() > self.width: 1813b3700f21SAdrian Hunter self.width = rect.right() 1814b3700f21SAdrian Hunter self.height = self.height + rect.bottom() + 1 1815b3700f21SAdrian Hunter first = False 1816b3700f21SAdrian Hunter 1817b3700f21SAdrian Hunter self.bracket = VerticalBracketGraphicsItem(self) 1818b3700f21SAdrian Hunter 1819b3700f21SAdrian Hunter def EnableRubberBand(self, xlo, xhi, rb_event_handler): 1820b3700f21SAdrian Hunter if self.rb_enabled: 1821b3700f21SAdrian Hunter return 1822b3700f21SAdrian Hunter self.rb_enabled = True 1823b3700f21SAdrian Hunter self.rb_in_view = False 1824b3700f21SAdrian Hunter self.setAcceptedMouseButtons(Qt.LeftButton) 1825b3700f21SAdrian Hunter self.rb_xlo = xlo 1826b3700f21SAdrian Hunter self.rb_xhi = xhi 1827b3700f21SAdrian Hunter self.rb_event_handler = rb_event_handler 1828b3700f21SAdrian Hunter self.mousePressEvent = self.MousePressEvent 1829b3700f21SAdrian Hunter self.mouseMoveEvent = self.MouseMoveEvent 1830b3700f21SAdrian Hunter self.mouseReleaseEvent = self.MouseReleaseEvent 1831b3700f21SAdrian Hunter 1832b3700f21SAdrian Hunter def boundingRect(self): 1833b3700f21SAdrian Hunter return QRectF(0, 0, self.width, self.height) 1834b3700f21SAdrian Hunter 1835b3700f21SAdrian Hunter def paint(self, painter, option, widget): 1836b3700f21SAdrian Hunter pass 1837b3700f21SAdrian Hunter 1838b3700f21SAdrian Hunter def RubberBandParent(self): 1839b3700f21SAdrian Hunter scene = self.scene() 1840b3700f21SAdrian Hunter view = scene.views()[0] 1841b3700f21SAdrian Hunter viewport = view.viewport() 1842b3700f21SAdrian Hunter return viewport 1843b3700f21SAdrian Hunter 1844b3700f21SAdrian Hunter def RubberBandSetGeometry(self, rect): 1845b3700f21SAdrian Hunter scene_rectf = self.mapRectToScene(QRectF(rect)) 1846b3700f21SAdrian Hunter scene = self.scene() 1847b3700f21SAdrian Hunter view = scene.views()[0] 1848b3700f21SAdrian Hunter poly = view.mapFromScene(scene_rectf) 1849b3700f21SAdrian Hunter self.rubber_band.setGeometry(poly.boundingRect()) 1850b3700f21SAdrian Hunter 1851b3700f21SAdrian Hunter def SetSelection(self, selection_state): 1852b3700f21SAdrian Hunter if self.rubber_band: 1853b3700f21SAdrian Hunter if selection_state: 1854b3700f21SAdrian Hunter self.RubberBandSetGeometry(selection_state) 1855b3700f21SAdrian Hunter self.rubber_band.show() 1856b3700f21SAdrian Hunter else: 1857b3700f21SAdrian Hunter self.rubber_band.hide() 1858b3700f21SAdrian Hunter 1859b3700f21SAdrian Hunter def SetBracket(self, rect): 1860b3700f21SAdrian Hunter if rect: 1861b3700f21SAdrian Hunter x, y, width, height = rect.x(), rect.y(), rect.width(), rect.height() 1862b3700f21SAdrian Hunter self.bracket.setPos(x, y) 1863b3700f21SAdrian Hunter self.bracket.SetSize(width, height) 1864b3700f21SAdrian Hunter self.bracket.show() 1865b3700f21SAdrian Hunter else: 1866b3700f21SAdrian Hunter self.bracket.hide() 1867b3700f21SAdrian Hunter 1868b3700f21SAdrian Hunter def RubberBandX(self, event): 1869b3700f21SAdrian Hunter x = event.pos().toPoint().x() 1870b3700f21SAdrian Hunter if x < self.rb_xlo: 1871b3700f21SAdrian Hunter x = self.rb_xlo 1872b3700f21SAdrian Hunter elif x > self.rb_xhi: 1873b3700f21SAdrian Hunter x = self.rb_xhi 1874b3700f21SAdrian Hunter else: 1875b3700f21SAdrian Hunter self.rb_in_view = True 1876b3700f21SAdrian Hunter return x 1877b3700f21SAdrian Hunter 1878b3700f21SAdrian Hunter def RubberBandRect(self, x): 1879b3700f21SAdrian Hunter if self.rb_origin.x() <= x: 1880b3700f21SAdrian Hunter width = x - self.rb_origin.x() 1881b3700f21SAdrian Hunter rect = QRect(self.rb_origin, QSize(width, self.height)) 1882b3700f21SAdrian Hunter else: 1883b3700f21SAdrian Hunter width = self.rb_origin.x() - x 1884b3700f21SAdrian Hunter top_left = QPoint(self.rb_origin.x() - width, self.rb_origin.y()) 1885b3700f21SAdrian Hunter rect = QRect(top_left, QSize(width, self.height)) 1886b3700f21SAdrian Hunter return rect 1887b3700f21SAdrian Hunter 1888b3700f21SAdrian Hunter def MousePressEvent(self, event): 1889b3700f21SAdrian Hunter self.rb_in_view = False 1890b3700f21SAdrian Hunter x = self.RubberBandX(event) 1891b3700f21SAdrian Hunter self.rb_origin = QPoint(x, self.top) 1892b3700f21SAdrian Hunter if self.rubber_band is None: 1893b3700f21SAdrian Hunter self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.RubberBandParent()) 1894b3700f21SAdrian Hunter self.RubberBandSetGeometry(QRect(self.rb_origin, QSize(0, self.height))) 1895b3700f21SAdrian Hunter if self.rb_in_view: 1896b3700f21SAdrian Hunter self.rubber_band.show() 1897b3700f21SAdrian Hunter self.rb_event_handler.RBMoveEvent(x, x) 1898b3700f21SAdrian Hunter else: 1899b3700f21SAdrian Hunter self.rubber_band.hide() 1900b3700f21SAdrian Hunter 1901b3700f21SAdrian Hunter def MouseMoveEvent(self, event): 1902b3700f21SAdrian Hunter x = self.RubberBandX(event) 1903b3700f21SAdrian Hunter rect = self.RubberBandRect(x) 1904b3700f21SAdrian Hunter self.RubberBandSetGeometry(rect) 1905b3700f21SAdrian Hunter if self.rb_in_view: 1906b3700f21SAdrian Hunter self.rubber_band.show() 1907b3700f21SAdrian Hunter self.rb_event_handler.RBMoveEvent(self.rb_origin.x(), x) 1908b3700f21SAdrian Hunter 1909b3700f21SAdrian Hunter def MouseReleaseEvent(self, event): 1910b3700f21SAdrian Hunter x = self.RubberBandX(event) 1911b3700f21SAdrian Hunter if self.rb_in_view: 1912b3700f21SAdrian Hunter selection_state = self.RubberBandRect(x) 1913b3700f21SAdrian Hunter else: 1914b3700f21SAdrian Hunter selection_state = None 1915b3700f21SAdrian Hunter self.rb_event_handler.RBReleaseEvent(self.rb_origin.x(), x, selection_state) 1916b3700f21SAdrian Hunter 1917b3700f21SAdrian Hunter# Switch graph legend data model 1918b3700f21SAdrian Hunter 1919b3700f21SAdrian Hunterclass SwitchGraphLegendModel(QAbstractTableModel): 1920b3700f21SAdrian Hunter 1921b3700f21SAdrian Hunter def __init__(self, collection, region_attributes, parent=None): 1922b3700f21SAdrian Hunter super(SwitchGraphLegendModel, self).__init__(parent) 1923b3700f21SAdrian Hunter 1924b3700f21SAdrian Hunter self.region_attributes = region_attributes 1925b3700f21SAdrian Hunter 1926b3700f21SAdrian Hunter self.child_items = sorted(collection.hregions.values(), key=GraphDataRegionOrdinal) 1927b3700f21SAdrian Hunter self.child_count = len(self.child_items) 1928b3700f21SAdrian Hunter 1929b3700f21SAdrian Hunter self.highlight_set = set() 1930b3700f21SAdrian Hunter 1931b3700f21SAdrian Hunter self.column_headers = ("pid", "tid", "comm") 1932b3700f21SAdrian Hunter 1933b3700f21SAdrian Hunter def rowCount(self, parent): 1934b3700f21SAdrian Hunter return self.child_count 1935b3700f21SAdrian Hunter 1936b3700f21SAdrian Hunter def headerData(self, section, orientation, role): 1937b3700f21SAdrian Hunter if role != Qt.DisplayRole: 1938b3700f21SAdrian Hunter return None 1939b3700f21SAdrian Hunter if orientation != Qt.Horizontal: 1940b3700f21SAdrian Hunter return None 1941b3700f21SAdrian Hunter return self.columnHeader(section) 1942b3700f21SAdrian Hunter 1943b3700f21SAdrian Hunter def index(self, row, column, parent): 1944b3700f21SAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 1945b3700f21SAdrian Hunter 1946b3700f21SAdrian Hunter def columnCount(self, parent=None): 1947b3700f21SAdrian Hunter return len(self.column_headers) 1948b3700f21SAdrian Hunter 1949b3700f21SAdrian Hunter def columnHeader(self, column): 1950b3700f21SAdrian Hunter return self.column_headers[column] 1951b3700f21SAdrian Hunter 1952b3700f21SAdrian Hunter def data(self, index, role): 1953b3700f21SAdrian Hunter if role == Qt.BackgroundRole: 1954b3700f21SAdrian Hunter child = self.child_items[index.row()] 1955b3700f21SAdrian Hunter if child in self.highlight_set: 1956b3700f21SAdrian Hunter return self.region_attributes[child.key].colour 1957b3700f21SAdrian Hunter return None 1958b3700f21SAdrian Hunter if role == Qt.ForegroundRole: 1959b3700f21SAdrian Hunter child = self.child_items[index.row()] 1960b3700f21SAdrian Hunter if child in self.highlight_set: 1961b3700f21SAdrian Hunter return QColor(255, 255, 255) 1962b3700f21SAdrian Hunter return self.region_attributes[child.key].colour 1963b3700f21SAdrian Hunter if role != Qt.DisplayRole: 1964b3700f21SAdrian Hunter return None 1965b3700f21SAdrian Hunter hregion = self.child_items[index.row()] 1966b3700f21SAdrian Hunter col = index.column() 1967b3700f21SAdrian Hunter if col == 0: 1968b3700f21SAdrian Hunter return hregion.pid 1969b3700f21SAdrian Hunter if col == 1: 1970b3700f21SAdrian Hunter return hregion.tid 1971b3700f21SAdrian Hunter if col == 2: 1972b3700f21SAdrian Hunter return hregion.comm 1973b3700f21SAdrian Hunter return None 1974b3700f21SAdrian Hunter 1975b3700f21SAdrian Hunter def SetHighlight(self, row, set_highlight): 1976b3700f21SAdrian Hunter child = self.child_items[row] 1977b3700f21SAdrian Hunter top_left = self.createIndex(row, 0, child) 1978b3700f21SAdrian Hunter bottom_right = self.createIndex(row, len(self.column_headers) - 1, child) 1979b3700f21SAdrian Hunter self.dataChanged.emit(top_left, bottom_right) 1980b3700f21SAdrian Hunter 1981b3700f21SAdrian Hunter def Highlight(self, highlight_set): 1982b3700f21SAdrian Hunter for row in xrange(self.child_count): 1983b3700f21SAdrian Hunter child = self.child_items[row] 1984b3700f21SAdrian Hunter if child in self.highlight_set: 1985b3700f21SAdrian Hunter if child not in highlight_set: 1986b3700f21SAdrian Hunter self.SetHighlight(row, False) 1987b3700f21SAdrian Hunter elif child in highlight_set: 1988b3700f21SAdrian Hunter self.SetHighlight(row, True) 1989b3700f21SAdrian Hunter self.highlight_set = highlight_set 1990b3700f21SAdrian Hunter 1991b3700f21SAdrian Hunter# Switch graph legend is a table 1992b3700f21SAdrian Hunter 1993b3700f21SAdrian Hunterclass SwitchGraphLegend(QWidget): 1994b3700f21SAdrian Hunter 1995b3700f21SAdrian Hunter def __init__(self, collection, region_attributes, parent=None): 1996b3700f21SAdrian Hunter super(SwitchGraphLegend, self).__init__(parent) 1997b3700f21SAdrian Hunter 1998b3700f21SAdrian Hunter self.data_model = SwitchGraphLegendModel(collection, region_attributes) 1999b3700f21SAdrian Hunter 2000b3700f21SAdrian Hunter self.model = QSortFilterProxyModel() 2001b3700f21SAdrian Hunter self.model.setSourceModel(self.data_model) 2002b3700f21SAdrian Hunter 2003b3700f21SAdrian Hunter self.view = QTableView() 2004b3700f21SAdrian Hunter self.view.setModel(self.model) 2005b3700f21SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2006b3700f21SAdrian Hunter self.view.verticalHeader().setVisible(False) 2007b3700f21SAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 2008b3700f21SAdrian Hunter self.view.setSortingEnabled(True) 2009b3700f21SAdrian Hunter self.view.resizeColumnsToContents() 2010b3700f21SAdrian Hunter self.view.resizeRowsToContents() 2011b3700f21SAdrian Hunter 2012b3700f21SAdrian Hunter self.vbox = VBoxLayout(self.view) 2013b3700f21SAdrian Hunter self.setLayout(self.vbox) 2014b3700f21SAdrian Hunter 2015b3700f21SAdrian Hunter sz1 = self.view.columnWidth(0) + self.view.columnWidth(1) + self.view.columnWidth(2) + 2 2016b3700f21SAdrian Hunter sz1 = sz1 + self.view.verticalScrollBar().sizeHint().width() 2017b3700f21SAdrian Hunter self.saved_size = sz1 2018b3700f21SAdrian Hunter 2019b3700f21SAdrian Hunter def resizeEvent(self, event): 2020b3700f21SAdrian Hunter self.saved_size = self.size().width() 2021b3700f21SAdrian Hunter super(SwitchGraphLegend, self).resizeEvent(event) 2022b3700f21SAdrian Hunter 2023b3700f21SAdrian Hunter def Highlight(self, highlight_set): 2024b3700f21SAdrian Hunter self.data_model.Highlight(highlight_set) 2025b3700f21SAdrian Hunter self.update() 2026b3700f21SAdrian Hunter 2027b3700f21SAdrian Hunter def changeEvent(self, event): 2028b3700f21SAdrian Hunter if event.type() == QEvent.FontChange: 2029b3700f21SAdrian Hunter self.view.resizeRowsToContents() 2030b3700f21SAdrian Hunter self.view.resizeColumnsToContents() 2031b3700f21SAdrian Hunter # Need to resize rows again after column resize 2032b3700f21SAdrian Hunter self.view.resizeRowsToContents() 2033b3700f21SAdrian Hunter super(SwitchGraphLegend, self).changeEvent(event) 2034b3700f21SAdrian Hunter 2035b3700f21SAdrian Hunter# Random colour generation 2036b3700f21SAdrian Hunter 2037b3700f21SAdrian Hunterdef RGBColourTooLight(r, g, b): 2038b3700f21SAdrian Hunter if g > 230: 2039b3700f21SAdrian Hunter return True 2040b3700f21SAdrian Hunter if g <= 160: 2041b3700f21SAdrian Hunter return False 2042b3700f21SAdrian Hunter if r <= 180 and g <= 180: 2043b3700f21SAdrian Hunter return False 2044b3700f21SAdrian Hunter if r < 60: 2045b3700f21SAdrian Hunter return False 2046b3700f21SAdrian Hunter return True 2047b3700f21SAdrian Hunter 2048b3700f21SAdrian Hunterdef GenerateColours(x): 2049b3700f21SAdrian Hunter cs = [0] 2050b3700f21SAdrian Hunter for i in xrange(1, x): 2051b3700f21SAdrian Hunter cs.append(int((255.0 / i) + 0.5)) 2052b3700f21SAdrian Hunter colours = [] 2053b3700f21SAdrian Hunter for r in cs: 2054b3700f21SAdrian Hunter for g in cs: 2055b3700f21SAdrian Hunter for b in cs: 2056b3700f21SAdrian Hunter # Exclude black and colours that look too light against a white background 2057b3700f21SAdrian Hunter if (r, g, b) == (0, 0, 0) or RGBColourTooLight(r, g, b): 2058b3700f21SAdrian Hunter continue 2059b3700f21SAdrian Hunter colours.append(QColor(r, g, b)) 2060b3700f21SAdrian Hunter return colours 2061b3700f21SAdrian Hunter 2062b3700f21SAdrian Hunterdef GenerateNColours(n): 2063b3700f21SAdrian Hunter for x in xrange(2, n + 2): 2064b3700f21SAdrian Hunter colours = GenerateColours(x) 2065b3700f21SAdrian Hunter if len(colours) >= n: 2066b3700f21SAdrian Hunter return colours 2067b3700f21SAdrian Hunter return [] 2068b3700f21SAdrian Hunter 2069b3700f21SAdrian Hunterdef GenerateNRandomColours(n, seed): 2070b3700f21SAdrian Hunter colours = GenerateNColours(n) 2071b3700f21SAdrian Hunter random.seed(seed) 2072b3700f21SAdrian Hunter random.shuffle(colours) 2073b3700f21SAdrian Hunter return colours 2074b3700f21SAdrian Hunter 2075b3700f21SAdrian Hunter# Graph attributes, in particular the scale and subrange that change when zooming 2076b3700f21SAdrian Hunter 2077b3700f21SAdrian Hunterclass GraphAttributes(): 2078b3700f21SAdrian Hunter 2079b3700f21SAdrian Hunter def __init__(self, scale, subrange, region_attributes, dp): 2080b3700f21SAdrian Hunter self.scale = scale 2081b3700f21SAdrian Hunter self.subrange = subrange 2082b3700f21SAdrian Hunter self.region_attributes = region_attributes 2083b3700f21SAdrian Hunter # Rounding avoids errors due to finite floating point precision 2084b3700f21SAdrian Hunter self.dp = dp # data decimal places 2085b3700f21SAdrian Hunter self.Update() 2086b3700f21SAdrian Hunter 2087b3700f21SAdrian Hunter def XToPixel(self, x): 2088b3700f21SAdrian Hunter return int(round((x - self.subrange.x.lo) * self.scale.x, self.pdp.x)) 2089b3700f21SAdrian Hunter 2090b3700f21SAdrian Hunter def YToPixel(self, y): 2091b3700f21SAdrian Hunter return int(round((y - self.subrange.y.lo) * self.scale.y, self.pdp.y)) 2092b3700f21SAdrian Hunter 2093b3700f21SAdrian Hunter def PixelToXRounded(self, px): 2094b3700f21SAdrian Hunter return round((round(px, 0) / self.scale.x), self.dp.x) + self.subrange.x.lo 2095b3700f21SAdrian Hunter 2096b3700f21SAdrian Hunter def PixelToYRounded(self, py): 2097b3700f21SAdrian Hunter return round((round(py, 0) / self.scale.y), self.dp.y) + self.subrange.y.lo 2098b3700f21SAdrian Hunter 2099b3700f21SAdrian Hunter def PixelToX(self, px): 2100b3700f21SAdrian Hunter x = self.PixelToXRounded(px) 2101b3700f21SAdrian Hunter if self.pdp.x == 0: 2102b3700f21SAdrian Hunter rt = self.XToPixel(x) 2103b3700f21SAdrian Hunter if rt > px: 2104b3700f21SAdrian Hunter return x - 1 2105b3700f21SAdrian Hunter return x 2106b3700f21SAdrian Hunter 2107b3700f21SAdrian Hunter def PixelToY(self, py): 2108b3700f21SAdrian Hunter y = self.PixelToYRounded(py) 2109b3700f21SAdrian Hunter if self.pdp.y == 0: 2110b3700f21SAdrian Hunter rt = self.YToPixel(y) 2111b3700f21SAdrian Hunter if rt > py: 2112b3700f21SAdrian Hunter return y - 1 2113b3700f21SAdrian Hunter return y 2114b3700f21SAdrian Hunter 2115b3700f21SAdrian Hunter def ToPDP(self, dp, scale): 2116b3700f21SAdrian Hunter # Calculate pixel decimal places: 2117b3700f21SAdrian Hunter # (10 ** dp) is the minimum delta in the data 2118b3700f21SAdrian Hunter # scale it to get the minimum delta in pixels 2119b3700f21SAdrian Hunter # log10 gives the number of decimals places negatively 2120b3700f21SAdrian Hunter # subtrace 1 to divide by 10 2121b3700f21SAdrian Hunter # round to the lower negative number 2122b3700f21SAdrian Hunter # change the sign to get the number of decimals positively 2123b3700f21SAdrian Hunter x = math.log10((10 ** dp) * scale) 2124b3700f21SAdrian Hunter if x < 0: 2125b3700f21SAdrian Hunter x -= 1 2126b3700f21SAdrian Hunter x = -int(math.floor(x) - 0.1) 2127b3700f21SAdrian Hunter else: 2128b3700f21SAdrian Hunter x = 0 2129b3700f21SAdrian Hunter return x 2130b3700f21SAdrian Hunter 2131b3700f21SAdrian Hunter def Update(self): 2132b3700f21SAdrian Hunter x = self.ToPDP(self.dp.x, self.scale.x) 2133b3700f21SAdrian Hunter y = self.ToPDP(self.dp.y, self.scale.y) 2134b3700f21SAdrian Hunter self.pdp = XY(x, y) # pixel decimal places 2135b3700f21SAdrian Hunter 2136b3700f21SAdrian Hunter# Switch graph splitter which divides the CPU graphs from the legend 2137b3700f21SAdrian Hunter 2138b3700f21SAdrian Hunterclass SwitchGraphSplitter(QSplitter): 2139b3700f21SAdrian Hunter 2140b3700f21SAdrian Hunter def __init__(self, parent=None): 2141b3700f21SAdrian Hunter super(SwitchGraphSplitter, self).__init__(parent) 2142b3700f21SAdrian Hunter 2143b3700f21SAdrian Hunter self.first_time = False 2144b3700f21SAdrian Hunter 2145b3700f21SAdrian Hunter def resizeEvent(self, ev): 2146b3700f21SAdrian Hunter if self.first_time: 2147b3700f21SAdrian Hunter self.first_time = False 2148b3700f21SAdrian Hunter sz1 = self.widget(1).view.columnWidth(0) + self.widget(1).view.columnWidth(1) + self.widget(1).view.columnWidth(2) + 2 2149b3700f21SAdrian Hunter sz1 = sz1 + self.widget(1).view.verticalScrollBar().sizeHint().width() 2150b3700f21SAdrian Hunter sz0 = self.size().width() - self.handleWidth() - sz1 2151b3700f21SAdrian Hunter self.setSizes([sz0, sz1]) 2152b3700f21SAdrian Hunter elif not(self.widget(1).saved_size is None): 2153b3700f21SAdrian Hunter sz1 = self.widget(1).saved_size 2154b3700f21SAdrian Hunter sz0 = self.size().width() - self.handleWidth() - sz1 2155b3700f21SAdrian Hunter self.setSizes([sz0, sz1]) 2156b3700f21SAdrian Hunter super(SwitchGraphSplitter, self).resizeEvent(ev) 2157b3700f21SAdrian Hunter 2158b3700f21SAdrian Hunter# Graph widget base class 2159b3700f21SAdrian Hunter 2160b3700f21SAdrian Hunterclass GraphWidget(QWidget): 2161b3700f21SAdrian Hunter 2162b3700f21SAdrian Hunter graph_title_changed = Signal(object) 2163b3700f21SAdrian Hunter 2164b3700f21SAdrian Hunter def __init__(self, parent=None): 2165b3700f21SAdrian Hunter super(GraphWidget, self).__init__(parent) 2166b3700f21SAdrian Hunter 2167b3700f21SAdrian Hunter def GraphTitleChanged(self, title): 2168b3700f21SAdrian Hunter self.graph_title_changed.emit(title) 2169b3700f21SAdrian Hunter 2170b3700f21SAdrian Hunter def Title(self): 2171b3700f21SAdrian Hunter return "" 2172b3700f21SAdrian Hunter 2173b3700f21SAdrian Hunter# Display time in s, ms, us or ns 2174b3700f21SAdrian Hunter 2175b3700f21SAdrian Hunterdef ToTimeStr(val): 2176b3700f21SAdrian Hunter val = Decimal(val) 2177b3700f21SAdrian Hunter if val >= 1000000000: 2178b3700f21SAdrian Hunter return "{} s".format((val / 1000000000).quantize(Decimal("0.000000001"))) 2179b3700f21SAdrian Hunter if val >= 1000000: 2180b3700f21SAdrian Hunter return "{} ms".format((val / 1000000).quantize(Decimal("0.000001"))) 2181b3700f21SAdrian Hunter if val >= 1000: 2182b3700f21SAdrian Hunter return "{} us".format((val / 1000).quantize(Decimal("0.001"))) 2183b3700f21SAdrian Hunter return "{} ns".format(val.quantize(Decimal("1"))) 2184b3700f21SAdrian Hunter 2185b3700f21SAdrian Hunter# Switch (i.e. context switch i.e. Time Chart by CPU) graph widget which contains the CPU graphs and the legend and control buttons 2186b3700f21SAdrian Hunter 2187b3700f21SAdrian Hunterclass SwitchGraphWidget(GraphWidget): 2188b3700f21SAdrian Hunter 2189b3700f21SAdrian Hunter def __init__(self, glb, collection, parent=None): 2190b3700f21SAdrian Hunter super(SwitchGraphWidget, self).__init__(parent) 2191b3700f21SAdrian Hunter 2192b3700f21SAdrian Hunter self.glb = glb 2193b3700f21SAdrian Hunter self.collection = collection 2194b3700f21SAdrian Hunter 2195b3700f21SAdrian Hunter self.back_state = [] 2196b3700f21SAdrian Hunter self.forward_state = [] 2197b3700f21SAdrian Hunter self.selection_state = (None, None) 2198b3700f21SAdrian Hunter self.fwd_rect = None 2199b3700f21SAdrian Hunter self.start_time = self.glb.StartTime(collection.machine_id) 2200b3700f21SAdrian Hunter 2201b3700f21SAdrian Hunter i = 0 2202b3700f21SAdrian Hunter hregions = collection.hregions.values() 2203b3700f21SAdrian Hunter colours = GenerateNRandomColours(len(hregions), 1013) 2204b3700f21SAdrian Hunter region_attributes = {} 2205b3700f21SAdrian Hunter for hregion in hregions: 2206b3700f21SAdrian Hunter if hregion.pid == 0 and hregion.tid == 0: 2207b3700f21SAdrian Hunter region_attributes[hregion.key] = GraphRegionAttribute(QColor(0, 0, 0)) 2208b3700f21SAdrian Hunter else: 2209b3700f21SAdrian Hunter region_attributes[hregion.key] = GraphRegionAttribute(colours[i]) 2210b3700f21SAdrian Hunter i = i + 1 2211b3700f21SAdrian Hunter 2212b3700f21SAdrian Hunter # Default to entire range 2213b3700f21SAdrian Hunter xsubrange = Subrange(0.0, float(collection.xrangehi - collection.xrangelo) + 1.0) 2214b3700f21SAdrian Hunter ysubrange = Subrange(0.0, float(collection.yrangehi - collection.yrangelo) + 1.0) 2215b3700f21SAdrian Hunter subrange = XY(xsubrange, ysubrange) 2216b3700f21SAdrian Hunter 2217b3700f21SAdrian Hunter scale = self.GetScaleForRange(subrange) 2218b3700f21SAdrian Hunter 2219b3700f21SAdrian Hunter self.attrs = GraphAttributes(scale, subrange, region_attributes, collection.dp) 2220b3700f21SAdrian Hunter 2221b3700f21SAdrian Hunter self.item = VertcalGraphSetGraphicsItem(collection, self.attrs, self, SwitchGraphGraphicsItem) 2222b3700f21SAdrian Hunter 2223b3700f21SAdrian Hunter self.scene = QGraphicsScene() 2224b3700f21SAdrian Hunter self.scene.addItem(self.item) 2225b3700f21SAdrian Hunter 2226b3700f21SAdrian Hunter self.view = QGraphicsView(self.scene) 2227b3700f21SAdrian Hunter self.view.centerOn(0, 0) 2228b3700f21SAdrian Hunter self.view.setAlignment(Qt.AlignLeft | Qt.AlignTop) 2229b3700f21SAdrian Hunter 2230b3700f21SAdrian Hunter self.legend = SwitchGraphLegend(collection, region_attributes) 2231b3700f21SAdrian Hunter 2232b3700f21SAdrian Hunter self.splitter = SwitchGraphSplitter() 2233b3700f21SAdrian Hunter self.splitter.addWidget(self.view) 2234b3700f21SAdrian Hunter self.splitter.addWidget(self.legend) 2235b3700f21SAdrian Hunter 2236b3700f21SAdrian Hunter self.point_label = QLabel("") 2237b3700f21SAdrian Hunter self.point_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) 2238b3700f21SAdrian Hunter 2239b3700f21SAdrian Hunter self.back_button = QToolButton() 2240b3700f21SAdrian Hunter self.back_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowLeft)) 2241b3700f21SAdrian Hunter self.back_button.setDisabled(True) 2242b3700f21SAdrian Hunter self.back_button.released.connect(lambda: self.Back()) 2243b3700f21SAdrian Hunter 2244b3700f21SAdrian Hunter self.forward_button = QToolButton() 2245b3700f21SAdrian Hunter self.forward_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowRight)) 2246b3700f21SAdrian Hunter self.forward_button.setDisabled(True) 2247b3700f21SAdrian Hunter self.forward_button.released.connect(lambda: self.Forward()) 2248b3700f21SAdrian Hunter 2249b3700f21SAdrian Hunter self.zoom_button = QToolButton() 2250b3700f21SAdrian Hunter self.zoom_button.setText("Zoom") 2251b3700f21SAdrian Hunter self.zoom_button.setDisabled(True) 2252b3700f21SAdrian Hunter self.zoom_button.released.connect(lambda: self.Zoom()) 2253b3700f21SAdrian Hunter 2254b3700f21SAdrian Hunter self.hbox = HBoxLayout(self.back_button, self.forward_button, self.zoom_button, self.point_label) 2255b3700f21SAdrian Hunter 2256b3700f21SAdrian Hunter self.vbox = VBoxLayout(self.splitter, self.hbox) 2257b3700f21SAdrian Hunter 2258b3700f21SAdrian Hunter self.setLayout(self.vbox) 2259b3700f21SAdrian Hunter 2260b3700f21SAdrian Hunter def GetScaleForRangeX(self, xsubrange): 2261b3700f21SAdrian Hunter # Default graph 1000 pixels wide 2262b3700f21SAdrian Hunter dflt = 1000.0 2263b3700f21SAdrian Hunter r = xsubrange.hi - xsubrange.lo 2264b3700f21SAdrian Hunter return dflt / r 2265b3700f21SAdrian Hunter 2266b3700f21SAdrian Hunter def GetScaleForRangeY(self, ysubrange): 2267b3700f21SAdrian Hunter # Default graph 50 pixels high 2268b3700f21SAdrian Hunter dflt = 50.0 2269b3700f21SAdrian Hunter r = ysubrange.hi - ysubrange.lo 2270b3700f21SAdrian Hunter return dflt / r 2271b3700f21SAdrian Hunter 2272b3700f21SAdrian Hunter def GetScaleForRange(self, subrange): 2273b3700f21SAdrian Hunter # Default graph 1000 pixels wide, 50 pixels high 2274b3700f21SAdrian Hunter xscale = self.GetScaleForRangeX(subrange.x) 2275b3700f21SAdrian Hunter yscale = self.GetScaleForRangeY(subrange.y) 2276b3700f21SAdrian Hunter return XY(xscale, yscale) 2277b3700f21SAdrian Hunter 2278b3700f21SAdrian Hunter def PointEvent(self, cpu, time_from, time_to, hregions): 2279b3700f21SAdrian Hunter text = "CPU: " + str(cpu) 2280b3700f21SAdrian Hunter time_from = time_from.quantize(Decimal(1)) 2281b3700f21SAdrian Hunter rel_time_from = time_from - self.glb.StartTime(self.collection.machine_id) 2282b3700f21SAdrian Hunter text = text + " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ")" 2283b3700f21SAdrian Hunter self.point_label.setText(text) 2284b3700f21SAdrian Hunter self.legend.Highlight(hregions) 2285b3700f21SAdrian Hunter 2286b3700f21SAdrian Hunter def RightClickEvent(self, cpu, hregion_times, pos): 2287b3700f21SAdrian Hunter if not IsSelectable(self.glb.db, "calls", "WHERE parent_id >= 0"): 2288b3700f21SAdrian Hunter return 2289b3700f21SAdrian Hunter menu = QMenu(self.view) 2290b3700f21SAdrian Hunter for hregion, time in hregion_times: 2291b3700f21SAdrian Hunter thread_at_time = (hregion.exec_comm_id, hregion.thread_id, time) 2292b3700f21SAdrian Hunter menu_text = "Show Call Tree for {} {}:{} at {}".format(hregion.comm, hregion.pid, hregion.tid, time) 2293b3700f21SAdrian Hunter menu.addAction(CreateAction(menu_text, "Show Call Tree", lambda a=None, args=thread_at_time: self.RightClickSelect(args), self.view)) 2294b3700f21SAdrian Hunter menu.exec_(pos) 2295b3700f21SAdrian Hunter 2296b3700f21SAdrian Hunter def RightClickSelect(self, args): 2297b3700f21SAdrian Hunter CallTreeWindow(self.glb, self.glb.mainwindow, thread_at_time=args) 2298b3700f21SAdrian Hunter 2299b3700f21SAdrian Hunter def NoPointEvent(self): 2300b3700f21SAdrian Hunter self.point_label.setText("") 2301b3700f21SAdrian Hunter self.legend.Highlight({}) 2302b3700f21SAdrian Hunter 2303b3700f21SAdrian Hunter def RangeEvent(self, time_from, time_to): 2304b3700f21SAdrian Hunter time_from = time_from.quantize(Decimal(1)) 2305b3700f21SAdrian Hunter time_to = time_to.quantize(Decimal(1)) 2306b3700f21SAdrian Hunter if time_to <= time_from: 2307b3700f21SAdrian Hunter self.point_label.setText("") 2308b3700f21SAdrian Hunter return 2309b3700f21SAdrian Hunter rel_time_from = time_from - self.start_time 2310b3700f21SAdrian Hunter rel_time_to = time_to - self.start_time 2311b3700f21SAdrian Hunter text = " Time: " + str(time_from) + " (+" + ToTimeStr(rel_time_from) + ") to: " + str(time_to) + " (+" + ToTimeStr(rel_time_to) + ")" 2312b3700f21SAdrian Hunter text = text + " duration: " + ToTimeStr(time_to - time_from) 2313b3700f21SAdrian Hunter self.point_label.setText(text) 2314b3700f21SAdrian Hunter 2315b3700f21SAdrian Hunter def BackState(self): 2316b3700f21SAdrian Hunter return (self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect) 2317b3700f21SAdrian Hunter 2318b3700f21SAdrian Hunter def PushBackState(self): 2319b3700f21SAdrian Hunter state = copy.deepcopy(self.BackState()) 2320b3700f21SAdrian Hunter self.back_state.append(state) 2321b3700f21SAdrian Hunter self.back_button.setEnabled(True) 2322b3700f21SAdrian Hunter 2323b3700f21SAdrian Hunter def PopBackState(self): 2324b3700f21SAdrian Hunter self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.back_state.pop() 2325b3700f21SAdrian Hunter self.attrs.Update() 2326b3700f21SAdrian Hunter if not self.back_state: 2327b3700f21SAdrian Hunter self.back_button.setDisabled(True) 2328b3700f21SAdrian Hunter 2329b3700f21SAdrian Hunter def PushForwardState(self): 2330b3700f21SAdrian Hunter state = copy.deepcopy(self.BackState()) 2331b3700f21SAdrian Hunter self.forward_state.append(state) 2332b3700f21SAdrian Hunter self.forward_button.setEnabled(True) 2333b3700f21SAdrian Hunter 2334b3700f21SAdrian Hunter def PopForwardState(self): 2335b3700f21SAdrian Hunter self.attrs.subrange, self.attrs.scale, self.selection_state, self.fwd_rect = self.forward_state.pop() 2336b3700f21SAdrian Hunter self.attrs.Update() 2337b3700f21SAdrian Hunter if not self.forward_state: 2338b3700f21SAdrian Hunter self.forward_button.setDisabled(True) 2339b3700f21SAdrian Hunter 2340b3700f21SAdrian Hunter def Title(self): 2341b3700f21SAdrian Hunter time_from = self.collection.xrangelo + Decimal(self.attrs.subrange.x.lo) 2342b3700f21SAdrian Hunter time_to = self.collection.xrangelo + Decimal(self.attrs.subrange.x.hi) 2343b3700f21SAdrian Hunter rel_time_from = time_from - self.start_time 2344b3700f21SAdrian Hunter rel_time_to = time_to - self.start_time 2345b3700f21SAdrian Hunter title = "+" + ToTimeStr(rel_time_from) + " to +" + ToTimeStr(rel_time_to) 2346b3700f21SAdrian Hunter title = title + " (" + ToTimeStr(time_to - time_from) + ")" 2347b3700f21SAdrian Hunter return title 2348b3700f21SAdrian Hunter 2349b3700f21SAdrian Hunter def Update(self): 2350b3700f21SAdrian Hunter selected_subrange, selection_state = self.selection_state 2351b3700f21SAdrian Hunter self.item.SetSelection(selection_state) 2352b3700f21SAdrian Hunter self.item.SetBracket(self.fwd_rect) 2353b3700f21SAdrian Hunter self.zoom_button.setDisabled(selected_subrange is None) 2354b3700f21SAdrian Hunter self.GraphTitleChanged(self.Title()) 2355b3700f21SAdrian Hunter self.item.update(self.item.boundingRect()) 2356b3700f21SAdrian Hunter 2357b3700f21SAdrian Hunter def Back(self): 2358b3700f21SAdrian Hunter if not self.back_state: 2359b3700f21SAdrian Hunter return 2360b3700f21SAdrian Hunter self.PushForwardState() 2361b3700f21SAdrian Hunter self.PopBackState() 2362b3700f21SAdrian Hunter self.Update() 2363b3700f21SAdrian Hunter 2364b3700f21SAdrian Hunter def Forward(self): 2365b3700f21SAdrian Hunter if not self.forward_state: 2366b3700f21SAdrian Hunter return 2367b3700f21SAdrian Hunter self.PushBackState() 2368b3700f21SAdrian Hunter self.PopForwardState() 2369b3700f21SAdrian Hunter self.Update() 2370b3700f21SAdrian Hunter 2371b3700f21SAdrian Hunter def SelectEvent(self, x0, x1, selection_state): 2372b3700f21SAdrian Hunter if selection_state is None: 2373b3700f21SAdrian Hunter selected_subrange = None 2374b3700f21SAdrian Hunter else: 2375b3700f21SAdrian Hunter if x1 - x0 < 1.0: 2376b3700f21SAdrian Hunter x1 += 1.0 2377b3700f21SAdrian Hunter selected_subrange = Subrange(x0, x1) 2378b3700f21SAdrian Hunter self.selection_state = (selected_subrange, selection_state) 2379b3700f21SAdrian Hunter self.zoom_button.setDisabled(selected_subrange is None) 2380b3700f21SAdrian Hunter 2381b3700f21SAdrian Hunter def Zoom(self): 2382b3700f21SAdrian Hunter selected_subrange, selection_state = self.selection_state 2383b3700f21SAdrian Hunter if selected_subrange is None: 2384b3700f21SAdrian Hunter return 2385b3700f21SAdrian Hunter self.fwd_rect = selection_state 2386b3700f21SAdrian Hunter self.item.SetSelection(None) 2387b3700f21SAdrian Hunter self.PushBackState() 2388b3700f21SAdrian Hunter self.attrs.subrange.x = selected_subrange 2389b3700f21SAdrian Hunter self.forward_state = [] 2390b3700f21SAdrian Hunter self.forward_button.setDisabled(True) 2391b3700f21SAdrian Hunter self.selection_state = (None, None) 2392b3700f21SAdrian Hunter self.fwd_rect = None 2393b3700f21SAdrian Hunter self.attrs.scale.x = self.GetScaleForRangeX(self.attrs.subrange.x) 2394b3700f21SAdrian Hunter self.attrs.Update() 2395b3700f21SAdrian Hunter self.Update() 2396b3700f21SAdrian Hunter 2397b3700f21SAdrian Hunter# Slow initialization - perform non-GUI initialization in a separate thread and put up a modal message box while waiting 2398b3700f21SAdrian Hunter 2399b3700f21SAdrian Hunterclass SlowInitClass(): 2400b3700f21SAdrian Hunter 2401b3700f21SAdrian Hunter def __init__(self, glb, title, init_fn): 2402b3700f21SAdrian Hunter self.init_fn = init_fn 2403b3700f21SAdrian Hunter self.done = False 2404b3700f21SAdrian Hunter self.result = None 2405b3700f21SAdrian Hunter 2406b3700f21SAdrian Hunter self.msg_box = QMessageBox(glb.mainwindow) 2407b3700f21SAdrian Hunter self.msg_box.setText("Initializing " + title + ". Please wait.") 2408b3700f21SAdrian Hunter self.msg_box.setWindowTitle("Initializing " + title) 2409b3700f21SAdrian Hunter self.msg_box.setWindowIcon(glb.mainwindow.style().standardIcon(QStyle.SP_MessageBoxInformation)) 2410b3700f21SAdrian Hunter 2411b3700f21SAdrian Hunter self.init_thread = Thread(self.ThreadFn, glb) 2412b3700f21SAdrian Hunter self.init_thread.done.connect(lambda: self.Done(), Qt.QueuedConnection) 2413b3700f21SAdrian Hunter 2414b3700f21SAdrian Hunter self.init_thread.start() 2415b3700f21SAdrian Hunter 2416b3700f21SAdrian Hunter def Done(self): 2417b3700f21SAdrian Hunter self.msg_box.done(0) 2418b3700f21SAdrian Hunter 2419b3700f21SAdrian Hunter def ThreadFn(self, glb): 2420b3700f21SAdrian Hunter conn_name = "SlowInitClass" + str(os.getpid()) 2421b3700f21SAdrian Hunter db, dbname = glb.dbref.Open(conn_name) 2422b3700f21SAdrian Hunter self.result = self.init_fn(db) 2423b3700f21SAdrian Hunter self.done = True 2424b3700f21SAdrian Hunter return (True, 0) 2425b3700f21SAdrian Hunter 2426b3700f21SAdrian Hunter def Result(self): 2427b3700f21SAdrian Hunter while not self.done: 2428b3700f21SAdrian Hunter self.msg_box.exec_() 2429b3700f21SAdrian Hunter self.init_thread.wait() 2430b3700f21SAdrian Hunter return self.result 2431b3700f21SAdrian Hunter 2432b3700f21SAdrian Hunterdef SlowInit(glb, title, init_fn): 2433b3700f21SAdrian Hunter init = SlowInitClass(glb, title, init_fn) 2434b3700f21SAdrian Hunter return init.Result() 2435b3700f21SAdrian Hunter 2436b3700f21SAdrian Hunter# Time chart by CPU window 2437b3700f21SAdrian Hunter 2438b3700f21SAdrian Hunterclass TimeChartByCPUWindow(QMdiSubWindow): 2439b3700f21SAdrian Hunter 2440b3700f21SAdrian Hunter def __init__(self, glb, parent=None): 2441b3700f21SAdrian Hunter super(TimeChartByCPUWindow, self).__init__(parent) 2442b3700f21SAdrian Hunter 2443b3700f21SAdrian Hunter self.glb = glb 2444b3700f21SAdrian Hunter self.machine_id = glb.HostMachineId() 2445b3700f21SAdrian Hunter self.collection_name = "SwitchGraphDataCollection " + str(self.machine_id) 2446b3700f21SAdrian Hunter 2447b3700f21SAdrian Hunter collection = LookupModel(self.collection_name) 2448b3700f21SAdrian Hunter if collection is None: 2449b3700f21SAdrian Hunter collection = SlowInit(glb, "Time Chart", self.Init) 2450b3700f21SAdrian Hunter 2451b3700f21SAdrian Hunter self.widget = SwitchGraphWidget(glb, collection, self) 2452b3700f21SAdrian Hunter self.view = self.widget 2453b3700f21SAdrian Hunter 2454b3700f21SAdrian Hunter self.base_title = "Time Chart by CPU" 2455b3700f21SAdrian Hunter self.setWindowTitle(self.base_title + self.widget.Title()) 2456b3700f21SAdrian Hunter self.widget.graph_title_changed.connect(self.GraphTitleChanged) 2457b3700f21SAdrian Hunter 2458b3700f21SAdrian Hunter self.setWidget(self.widget) 2459b3700f21SAdrian Hunter 2460b3700f21SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, self.windowTitle()) 2461b3700f21SAdrian Hunter 2462b3700f21SAdrian Hunter def Init(self, db): 2463b3700f21SAdrian Hunter return LookupCreateModel(self.collection_name, lambda : SwitchGraphDataCollection(self.glb, db, self.machine_id)) 2464b3700f21SAdrian Hunter 2465b3700f21SAdrian Hunter def GraphTitleChanged(self, title): 2466b3700f21SAdrian Hunter self.setWindowTitle(self.base_title + " : " + title) 2467b3700f21SAdrian Hunter 24688392b74bSAdrian Hunter# Child data item finder 24698392b74bSAdrian Hunter 24708392b74bSAdrian Hunterclass ChildDataItemFinder(): 24718392b74bSAdrian Hunter 24728392b74bSAdrian Hunter def __init__(self, root): 24738392b74bSAdrian Hunter self.root = root 24748392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 24758392b74bSAdrian Hunter self.rows = [] 24768392b74bSAdrian Hunter self.pos = 0 24778392b74bSAdrian Hunter 24788392b74bSAdrian Hunter def FindSelect(self): 24798392b74bSAdrian Hunter self.rows = [] 24808392b74bSAdrian Hunter if self.pattern: 24818392b74bSAdrian Hunter pattern = re.compile(self.value) 24828392b74bSAdrian Hunter for child in self.root.child_items: 24838392b74bSAdrian Hunter for column_data in child.data: 24848392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 24858392b74bSAdrian Hunter self.rows.append(child.row) 24868392b74bSAdrian Hunter break 24878392b74bSAdrian Hunter else: 24888392b74bSAdrian Hunter for child in self.root.child_items: 24898392b74bSAdrian Hunter for column_data in child.data: 24908392b74bSAdrian Hunter if self.value in str(column_data): 24918392b74bSAdrian Hunter self.rows.append(child.row) 24928392b74bSAdrian Hunter break 24938392b74bSAdrian Hunter 24948392b74bSAdrian Hunter def FindValue(self): 24958392b74bSAdrian Hunter self.pos = 0 24968392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 24978392b74bSAdrian Hunter self.FindSelect() 24988392b74bSAdrian Hunter if not len(self.rows): 24998392b74bSAdrian Hunter return -1 25008392b74bSAdrian Hunter return self.rows[self.pos] 25018392b74bSAdrian Hunter 25028392b74bSAdrian Hunter def FindThread(self): 25038392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 25048392b74bSAdrian Hunter row = self.FindValue() 25058392b74bSAdrian Hunter elif len(self.rows): 25068392b74bSAdrian Hunter if self.direction > 0: 25078392b74bSAdrian Hunter self.pos += 1 25088392b74bSAdrian Hunter if self.pos >= len(self.rows): 25098392b74bSAdrian Hunter self.pos = 0 25108392b74bSAdrian Hunter else: 25118392b74bSAdrian Hunter self.pos -= 1 25128392b74bSAdrian Hunter if self.pos < 0: 25138392b74bSAdrian Hunter self.pos = len(self.rows) - 1 25148392b74bSAdrian Hunter row = self.rows[self.pos] 25158392b74bSAdrian Hunter else: 25168392b74bSAdrian Hunter row = -1 25178392b74bSAdrian Hunter return (True, row) 25188392b74bSAdrian Hunter 25198392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 25208392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 25218392b74bSAdrian Hunter # Use a thread so the UI is not blocked 25228392b74bSAdrian Hunter thread = Thread(self.FindThread) 25238392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 25248392b74bSAdrian Hunter thread.start() 25258392b74bSAdrian Hunter 25268392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 25278392b74bSAdrian Hunter callback(row) 25288392b74bSAdrian Hunter 25298392b74bSAdrian Hunter# Number of database records to fetch in one go 25308392b74bSAdrian Hunter 25318392b74bSAdrian Hunterglb_chunk_sz = 10000 25328392b74bSAdrian Hunter 25338392b74bSAdrian Hunter# Background process for SQL data fetcher 25348392b74bSAdrian Hunter 25358392b74bSAdrian Hunterclass SQLFetcherProcess(): 25368392b74bSAdrian Hunter 25378392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 25388392b74bSAdrian Hunter # Need a unique connection name 25398392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 25408392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 25418392b74bSAdrian Hunter self.sql = sql 25428392b74bSAdrian Hunter self.buffer = buffer 25438392b74bSAdrian Hunter self.head = head 25448392b74bSAdrian Hunter self.tail = tail 25458392b74bSAdrian Hunter self.fetch_count = fetch_count 25468392b74bSAdrian Hunter self.fetching_done = fetching_done 25478392b74bSAdrian Hunter self.process_target = process_target 25488392b74bSAdrian Hunter self.wait_event = wait_event 25498392b74bSAdrian Hunter self.fetched_event = fetched_event 25508392b74bSAdrian Hunter self.prep = prep 25518392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 25528392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 25538392b74bSAdrian Hunter self.last_id = -1 25548392b74bSAdrian Hunter self.fetched = 0 25558392b74bSAdrian Hunter self.more = True 25568392b74bSAdrian Hunter self.local_head = self.head.value 25578392b74bSAdrian Hunter self.local_tail = self.tail.value 25588392b74bSAdrian Hunter 25598392b74bSAdrian Hunter def Select(self): 25608392b74bSAdrian Hunter if self.query_limit: 25618392b74bSAdrian Hunter if self.query_limit == 1: 25628392b74bSAdrian Hunter return 25638392b74bSAdrian Hunter self.query_limit -= 1 25648392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 25658392b74bSAdrian Hunter QueryExec(self.query, stmt) 25668392b74bSAdrian Hunter 25678392b74bSAdrian Hunter def Next(self): 25688392b74bSAdrian Hunter if not self.query.next(): 25698392b74bSAdrian Hunter self.Select() 25708392b74bSAdrian Hunter if not self.query.next(): 25718392b74bSAdrian Hunter return None 25728392b74bSAdrian Hunter self.last_id = self.query.value(0) 25738392b74bSAdrian Hunter return self.prep(self.query) 25748392b74bSAdrian Hunter 25758392b74bSAdrian Hunter def WaitForTarget(self): 25768392b74bSAdrian Hunter while True: 25778392b74bSAdrian Hunter self.wait_event.clear() 25788392b74bSAdrian Hunter target = self.process_target.value 25798392b74bSAdrian Hunter if target > self.fetched or target < 0: 25808392b74bSAdrian Hunter break 25818392b74bSAdrian Hunter self.wait_event.wait() 25828392b74bSAdrian Hunter return target 25838392b74bSAdrian Hunter 25848392b74bSAdrian Hunter def HasSpace(self, sz): 25858392b74bSAdrian Hunter if self.local_tail <= self.local_head: 25868392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 25878392b74bSAdrian Hunter if space > sz: 25888392b74bSAdrian Hunter return True 25898392b74bSAdrian Hunter if space >= glb_nsz: 25908392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 2591beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 25928392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 25938392b74bSAdrian Hunter self.local_head = 0 25948392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 25958392b74bSAdrian Hunter return True 25968392b74bSAdrian Hunter return False 25978392b74bSAdrian Hunter 25988392b74bSAdrian Hunter def WaitForSpace(self, sz): 25998392b74bSAdrian Hunter if self.HasSpace(sz): 26008392b74bSAdrian Hunter return 26018392b74bSAdrian Hunter while True: 26028392b74bSAdrian Hunter self.wait_event.clear() 26038392b74bSAdrian Hunter self.local_tail = self.tail.value 26048392b74bSAdrian Hunter if self.HasSpace(sz): 26058392b74bSAdrian Hunter return 26068392b74bSAdrian Hunter self.wait_event.wait() 26078392b74bSAdrian Hunter 26088392b74bSAdrian Hunter def AddToBuffer(self, obj): 2609beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 26108392b74bSAdrian Hunter n = len(d) 2611beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 26128392b74bSAdrian Hunter sz = n + glb_nsz 26138392b74bSAdrian Hunter self.WaitForSpace(sz) 26148392b74bSAdrian Hunter pos = self.local_head 26158392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 26168392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 26178392b74bSAdrian Hunter self.local_head += sz 26188392b74bSAdrian Hunter 26198392b74bSAdrian Hunter def FetchBatch(self, batch_size): 26208392b74bSAdrian Hunter fetched = 0 26218392b74bSAdrian Hunter while batch_size > fetched: 26228392b74bSAdrian Hunter obj = self.Next() 26238392b74bSAdrian Hunter if obj is None: 26248392b74bSAdrian Hunter self.more = False 26258392b74bSAdrian Hunter break 26268392b74bSAdrian Hunter self.AddToBuffer(obj) 26278392b74bSAdrian Hunter fetched += 1 26288392b74bSAdrian Hunter if fetched: 26298392b74bSAdrian Hunter self.fetched += fetched 26308392b74bSAdrian Hunter with self.fetch_count.get_lock(): 26318392b74bSAdrian Hunter self.fetch_count.value += fetched 26328392b74bSAdrian Hunter self.head.value = self.local_head 26338392b74bSAdrian Hunter self.fetched_event.set() 26348392b74bSAdrian Hunter 26358392b74bSAdrian Hunter def Run(self): 26368392b74bSAdrian Hunter while self.more: 26378392b74bSAdrian Hunter target = self.WaitForTarget() 26388392b74bSAdrian Hunter if target < 0: 26398392b74bSAdrian Hunter break 26408392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 26418392b74bSAdrian Hunter self.FetchBatch(batch_size) 26428392b74bSAdrian Hunter self.fetching_done.value = True 26438392b74bSAdrian Hunter self.fetched_event.set() 26448392b74bSAdrian Hunter 26458392b74bSAdrian Hunterdef SQLFetcherFn(*x): 26468392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 26478392b74bSAdrian Hunter process.Run() 26488392b74bSAdrian Hunter 26498392b74bSAdrian Hunter# SQL data fetcher 26508392b74bSAdrian Hunter 26518392b74bSAdrian Hunterclass SQLFetcher(QObject): 26528392b74bSAdrian Hunter 26538392b74bSAdrian Hunter done = Signal(object) 26548392b74bSAdrian Hunter 26558392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 26568392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 26578392b74bSAdrian Hunter self.process_data = process_data 26588392b74bSAdrian Hunter self.more = True 26598392b74bSAdrian Hunter self.target = 0 26608392b74bSAdrian Hunter self.last_target = 0 26618392b74bSAdrian Hunter self.fetched = 0 26628392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 26638392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 26648392b74bSAdrian Hunter self.head = Value(c_longlong) 26658392b74bSAdrian Hunter self.tail = Value(c_longlong) 26668392b74bSAdrian Hunter self.local_tail = 0 26678392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 26688392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 26698392b74bSAdrian Hunter self.last_count = 0 26708392b74bSAdrian Hunter self.process_target = Value(c_longlong) 26718392b74bSAdrian Hunter self.wait_event = Event() 26728392b74bSAdrian Hunter self.fetched_event = Event() 26738392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 26748392b74bSAdrian Hunter self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep)) 26758392b74bSAdrian Hunter self.process.start() 26768392b74bSAdrian Hunter self.thread = Thread(self.Thread) 26778392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 26788392b74bSAdrian Hunter self.thread.start() 26798392b74bSAdrian Hunter 26808392b74bSAdrian Hunter def Shutdown(self): 26818392b74bSAdrian Hunter # Tell the thread and process to exit 26828392b74bSAdrian Hunter self.process_target.value = -1 26838392b74bSAdrian Hunter self.wait_event.set() 26848392b74bSAdrian Hunter self.more = False 26858392b74bSAdrian Hunter self.fetching_done.value = True 26868392b74bSAdrian Hunter self.fetched_event.set() 26878392b74bSAdrian Hunter 26888392b74bSAdrian Hunter def Thread(self): 26898392b74bSAdrian Hunter if not self.more: 26908392b74bSAdrian Hunter return True, 0 26918392b74bSAdrian Hunter while True: 26928392b74bSAdrian Hunter self.fetched_event.clear() 26938392b74bSAdrian Hunter fetch_count = self.fetch_count.value 26948392b74bSAdrian Hunter if fetch_count != self.last_count: 26958392b74bSAdrian Hunter break 26968392b74bSAdrian Hunter if self.fetching_done.value: 26978392b74bSAdrian Hunter self.more = False 26988392b74bSAdrian Hunter return True, 0 26998392b74bSAdrian Hunter self.fetched_event.wait() 27008392b74bSAdrian Hunter count = fetch_count - self.last_count 27018392b74bSAdrian Hunter self.last_count = fetch_count 27028392b74bSAdrian Hunter self.fetched += count 27038392b74bSAdrian Hunter return False, count 27048392b74bSAdrian Hunter 27058392b74bSAdrian Hunter def Fetch(self, nr): 27068392b74bSAdrian Hunter if not self.more: 27078392b74bSAdrian Hunter # -1 inidcates there are no more 27088392b74bSAdrian Hunter return -1 27098392b74bSAdrian Hunter result = self.fetched 27108392b74bSAdrian Hunter extra = result + nr - self.target 27118392b74bSAdrian Hunter if extra > 0: 27128392b74bSAdrian Hunter self.target += extra 27138392b74bSAdrian Hunter # process_target < 0 indicates shutting down 27148392b74bSAdrian Hunter if self.process_target.value >= 0: 27158392b74bSAdrian Hunter self.process_target.value = self.target 27168392b74bSAdrian Hunter self.wait_event.set() 27178392b74bSAdrian Hunter return result 27188392b74bSAdrian Hunter 27198392b74bSAdrian Hunter def RemoveFromBuffer(self): 27208392b74bSAdrian Hunter pos = self.local_tail 27218392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 27228392b74bSAdrian Hunter pos = 0 2723beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 27248392b74bSAdrian Hunter if n == 0: 27258392b74bSAdrian Hunter pos = 0 2726beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 27278392b74bSAdrian Hunter pos += glb_nsz 2728beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 27298392b74bSAdrian Hunter self.local_tail = pos + n 27308392b74bSAdrian Hunter return obj 27318392b74bSAdrian Hunter 27328392b74bSAdrian Hunter def ProcessData(self, count): 27338392b74bSAdrian Hunter for i in xrange(count): 27348392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 27358392b74bSAdrian Hunter self.process_data(obj) 27368392b74bSAdrian Hunter self.tail.value = self.local_tail 27378392b74bSAdrian Hunter self.wait_event.set() 27388392b74bSAdrian Hunter self.done.emit(count) 27398392b74bSAdrian Hunter 27408392b74bSAdrian Hunter# Fetch more records bar 27418392b74bSAdrian Hunter 27428392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 27438392b74bSAdrian Hunter 27448392b74bSAdrian Hunter def __init__(self, model, parent): 27458392b74bSAdrian Hunter self.model = model 27468392b74bSAdrian Hunter 27478392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 27488392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 27498392b74bSAdrian Hunter 27508392b74bSAdrian Hunter self.fetch_count = QSpinBox() 27518392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 27528392b74bSAdrian Hunter self.fetch_count.setValue(10) 27538392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 27548392b74bSAdrian Hunter 27558392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 27568392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 27578392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 27588392b74bSAdrian Hunter 27598392b74bSAdrian Hunter self.progress = QProgressBar() 27608392b74bSAdrian Hunter self.progress.setRange(0, 100) 27618392b74bSAdrian Hunter self.progress.hide() 27628392b74bSAdrian Hunter 27638392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 27648392b74bSAdrian Hunter self.done_label.hide() 27658392b74bSAdrian Hunter 27668392b74bSAdrian Hunter self.spacer = QLabel("") 27678392b74bSAdrian Hunter 27688392b74bSAdrian Hunter self.close_button = QToolButton() 27698392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 27708392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 27718392b74bSAdrian Hunter 27728392b74bSAdrian Hunter self.hbox = QHBoxLayout() 27738392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 27748392b74bSAdrian Hunter 27758392b74bSAdrian Hunter self.hbox.addWidget(self.label) 27768392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 27778392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 27788392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 27798392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 27808392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 27818392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 27828392b74bSAdrian Hunter 27838392b74bSAdrian Hunter self.bar = QWidget() 278426688729SAdrian Hunter self.bar.setLayout(self.hbox) 27858392b74bSAdrian Hunter self.bar.show() 27868392b74bSAdrian Hunter 27878392b74bSAdrian Hunter self.in_progress = False 27888392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 27898392b74bSAdrian Hunter 27908392b74bSAdrian Hunter self.done = False 27918392b74bSAdrian Hunter 27928392b74bSAdrian Hunter if not model.HasMoreRecords(): 27938392b74bSAdrian Hunter self.Done() 27948392b74bSAdrian Hunter 27958392b74bSAdrian Hunter def Widget(self): 27968392b74bSAdrian Hunter return self.bar 27978392b74bSAdrian Hunter 27988392b74bSAdrian Hunter def Activate(self): 27998392b74bSAdrian Hunter self.bar.show() 28008392b74bSAdrian Hunter self.fetch.setFocus() 28018392b74bSAdrian Hunter 28028392b74bSAdrian Hunter def Deactivate(self): 28038392b74bSAdrian Hunter self.bar.hide() 28048392b74bSAdrian Hunter 28058392b74bSAdrian Hunter def Enable(self, enable): 28068392b74bSAdrian Hunter self.fetch.setEnabled(enable) 28078392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 28088392b74bSAdrian Hunter 28098392b74bSAdrian Hunter def Busy(self): 28108392b74bSAdrian Hunter self.Enable(False) 28118392b74bSAdrian Hunter self.fetch.hide() 28128392b74bSAdrian Hunter self.spacer.hide() 28138392b74bSAdrian Hunter self.progress.show() 28148392b74bSAdrian Hunter 28158392b74bSAdrian Hunter def Idle(self): 28168392b74bSAdrian Hunter self.in_progress = False 28178392b74bSAdrian Hunter self.Enable(True) 28188392b74bSAdrian Hunter self.progress.hide() 28198392b74bSAdrian Hunter self.fetch.show() 28208392b74bSAdrian Hunter self.spacer.show() 28218392b74bSAdrian Hunter 28228392b74bSAdrian Hunter def Target(self): 28238392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 28248392b74bSAdrian Hunter 28258392b74bSAdrian Hunter def Done(self): 28268392b74bSAdrian Hunter self.done = True 28278392b74bSAdrian Hunter self.Idle() 28288392b74bSAdrian Hunter self.label.hide() 28298392b74bSAdrian Hunter self.fetch_count.hide() 28308392b74bSAdrian Hunter self.fetch.hide() 28318392b74bSAdrian Hunter self.spacer.hide() 28328392b74bSAdrian Hunter self.done_label.show() 28338392b74bSAdrian Hunter 28348392b74bSAdrian Hunter def Progress(self, count): 28358392b74bSAdrian Hunter if self.in_progress: 28368392b74bSAdrian Hunter if count: 28378392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 28388392b74bSAdrian Hunter if percent >= 100: 28398392b74bSAdrian Hunter self.Idle() 28408392b74bSAdrian Hunter else: 28418392b74bSAdrian Hunter self.progress.setValue(percent) 28428392b74bSAdrian Hunter if not count: 28438392b74bSAdrian Hunter # Count value of zero means no more records 28448392b74bSAdrian Hunter self.Done() 28458392b74bSAdrian Hunter 28468392b74bSAdrian Hunter def FetchMoreRecords(self): 28478392b74bSAdrian Hunter if self.done: 28488392b74bSAdrian Hunter return 28498392b74bSAdrian Hunter self.progress.setValue(0) 28508392b74bSAdrian Hunter self.Busy() 28518392b74bSAdrian Hunter self.in_progress = True 28528392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 28538392b74bSAdrian Hunter 285476099f98SAdrian Hunter# Brance data model level two item 285576099f98SAdrian Hunter 285676099f98SAdrian Hunterclass BranchLevelTwoItem(): 285776099f98SAdrian Hunter 2858530e22fdSAdrian Hunter def __init__(self, row, col, text, parent_item): 285976099f98SAdrian Hunter self.row = row 286076099f98SAdrian Hunter self.parent_item = parent_item 2861530e22fdSAdrian Hunter self.data = [""] * (col + 1) 2862530e22fdSAdrian Hunter self.data[col] = text 286376099f98SAdrian Hunter self.level = 2 286476099f98SAdrian Hunter 286576099f98SAdrian Hunter def getParentItem(self): 286676099f98SAdrian Hunter return self.parent_item 286776099f98SAdrian Hunter 286876099f98SAdrian Hunter def getRow(self): 286976099f98SAdrian Hunter return self.row 287076099f98SAdrian Hunter 287176099f98SAdrian Hunter def childCount(self): 287276099f98SAdrian Hunter return 0 287376099f98SAdrian Hunter 287476099f98SAdrian Hunter def hasChildren(self): 287576099f98SAdrian Hunter return False 287676099f98SAdrian Hunter 287776099f98SAdrian Hunter def getData(self, column): 287876099f98SAdrian Hunter return self.data[column] 287976099f98SAdrian Hunter 288076099f98SAdrian Hunter# Brance data model level one item 288176099f98SAdrian Hunter 288276099f98SAdrian Hunterclass BranchLevelOneItem(): 288376099f98SAdrian Hunter 288476099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 288576099f98SAdrian Hunter self.glb = glb 288676099f98SAdrian Hunter self.row = row 288776099f98SAdrian Hunter self.parent_item = parent_item 288876099f98SAdrian Hunter self.child_count = 0 288976099f98SAdrian Hunter self.child_items = [] 289076099f98SAdrian Hunter self.data = data[1:] 289176099f98SAdrian Hunter self.dbid = data[0] 289276099f98SAdrian Hunter self.level = 1 289376099f98SAdrian Hunter self.query_done = False 2894530e22fdSAdrian Hunter self.br_col = len(self.data) - 1 289576099f98SAdrian Hunter 289676099f98SAdrian Hunter def getChildItem(self, row): 289776099f98SAdrian Hunter return self.child_items[row] 289876099f98SAdrian Hunter 289976099f98SAdrian Hunter def getParentItem(self): 290076099f98SAdrian Hunter return self.parent_item 290176099f98SAdrian Hunter 290276099f98SAdrian Hunter def getRow(self): 290376099f98SAdrian Hunter return self.row 290476099f98SAdrian Hunter 290576099f98SAdrian Hunter def Select(self): 290676099f98SAdrian Hunter self.query_done = True 290776099f98SAdrian Hunter 290876099f98SAdrian Hunter if not self.glb.have_disassembler: 290976099f98SAdrian Hunter return 291076099f98SAdrian Hunter 291176099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 291276099f98SAdrian Hunter 291376099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 291476099f98SAdrian Hunter " FROM samples" 291576099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 291676099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 291776099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 291876099f98SAdrian Hunter if not query.next(): 291976099f98SAdrian Hunter return 292076099f98SAdrian Hunter cpu = query.value(0) 292176099f98SAdrian Hunter dso = query.value(1) 292276099f98SAdrian Hunter sym = query.value(2) 292376099f98SAdrian Hunter if dso == 0 or sym == 0: 292476099f98SAdrian Hunter return 292576099f98SAdrian Hunter off = query.value(3) 292676099f98SAdrian Hunter short_name = query.value(4) 292776099f98SAdrian Hunter long_name = query.value(5) 292876099f98SAdrian Hunter build_id = query.value(6) 292976099f98SAdrian Hunter sym_start = query.value(7) 293076099f98SAdrian Hunter ip = query.value(8) 293176099f98SAdrian Hunter 293276099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 293376099f98SAdrian Hunter " FROM samples" 293476099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 293576099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 293676099f98SAdrian Hunter " ORDER BY samples.id" 293776099f98SAdrian Hunter " LIMIT 1") 293876099f98SAdrian Hunter if not query.next(): 293976099f98SAdrian Hunter return 294076099f98SAdrian Hunter if query.value(0) != dso: 294176099f98SAdrian Hunter # Cannot disassemble from one dso to another 294276099f98SAdrian Hunter return 294376099f98SAdrian Hunter bsym = query.value(1) 294476099f98SAdrian Hunter boff = query.value(2) 294576099f98SAdrian Hunter bsym_start = query.value(3) 294676099f98SAdrian Hunter if bsym == 0: 294776099f98SAdrian Hunter return 294876099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 294976099f98SAdrian Hunter if tot <= 0 or tot > 16384: 295076099f98SAdrian Hunter return 295176099f98SAdrian Hunter 295276099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 295376099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 295476099f98SAdrian Hunter if not f: 295576099f98SAdrian Hunter return 295676099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 295776099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 295876099f98SAdrian Hunter 295976099f98SAdrian Hunter buf_sz = tot + 16 296076099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 296176099f98SAdrian Hunter f.seek(sym_start + off) 296276099f98SAdrian Hunter buf.value = f.read(buf_sz) 296376099f98SAdrian Hunter buf_ptr = addressof(buf) 296476099f98SAdrian Hunter i = 0 296576099f98SAdrian Hunter while tot > 0: 296676099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 296776099f98SAdrian Hunter if cnt: 296876099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 296976099f98SAdrian Hunter for k in xrange(cnt): 297076099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 297176099f98SAdrian Hunter i += 1 297276099f98SAdrian Hunter while k < 15: 297376099f98SAdrian Hunter byte_str += " " 297476099f98SAdrian Hunter k += 1 2975530e22fdSAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) 297676099f98SAdrian Hunter self.child_count += 1 297776099f98SAdrian Hunter else: 297876099f98SAdrian Hunter return 297976099f98SAdrian Hunter buf_ptr += cnt 298076099f98SAdrian Hunter tot -= cnt 298176099f98SAdrian Hunter buf_sz -= cnt 298276099f98SAdrian Hunter ip += cnt 298376099f98SAdrian Hunter 298476099f98SAdrian Hunter def childCount(self): 298576099f98SAdrian Hunter if not self.query_done: 298676099f98SAdrian Hunter self.Select() 298776099f98SAdrian Hunter if not self.child_count: 298876099f98SAdrian Hunter return -1 298976099f98SAdrian Hunter return self.child_count 299076099f98SAdrian Hunter 299176099f98SAdrian Hunter def hasChildren(self): 299276099f98SAdrian Hunter if not self.query_done: 299376099f98SAdrian Hunter return True 299476099f98SAdrian Hunter return self.child_count > 0 299576099f98SAdrian Hunter 299676099f98SAdrian Hunter def getData(self, column): 299776099f98SAdrian Hunter return self.data[column] 299876099f98SAdrian Hunter 299976099f98SAdrian Hunter# Brance data model root item 300076099f98SAdrian Hunter 300176099f98SAdrian Hunterclass BranchRootItem(): 300276099f98SAdrian Hunter 300376099f98SAdrian Hunter def __init__(self): 300476099f98SAdrian Hunter self.child_count = 0 300576099f98SAdrian Hunter self.child_items = [] 300676099f98SAdrian Hunter self.level = 0 300776099f98SAdrian Hunter 300876099f98SAdrian Hunter def getChildItem(self, row): 300976099f98SAdrian Hunter return self.child_items[row] 301076099f98SAdrian Hunter 301176099f98SAdrian Hunter def getParentItem(self): 301276099f98SAdrian Hunter return None 301376099f98SAdrian Hunter 301476099f98SAdrian Hunter def getRow(self): 301576099f98SAdrian Hunter return 0 301676099f98SAdrian Hunter 301776099f98SAdrian Hunter def childCount(self): 301876099f98SAdrian Hunter return self.child_count 301976099f98SAdrian Hunter 302076099f98SAdrian Hunter def hasChildren(self): 302176099f98SAdrian Hunter return self.child_count > 0 302276099f98SAdrian Hunter 302376099f98SAdrian Hunter def getData(self, column): 302476099f98SAdrian Hunter return "" 302576099f98SAdrian Hunter 3026530e22fdSAdrian Hunter# Calculate instructions per cycle 3027530e22fdSAdrian Hunter 3028530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt): 3029530e22fdSAdrian Hunter if cyc_cnt and insn_cnt: 3030530e22fdSAdrian Hunter ipc = Decimal(float(insn_cnt) / cyc_cnt) 3031530e22fdSAdrian Hunter ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) 3032530e22fdSAdrian Hunter else: 3033530e22fdSAdrian Hunter ipc = "0" 3034530e22fdSAdrian Hunter return ipc 3035530e22fdSAdrian Hunter 303676099f98SAdrian Hunter# Branch data preparation 303776099f98SAdrian Hunter 3038530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data): 3039530e22fdSAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 3040530e22fdSAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 3041530e22fdSAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 3042530e22fdSAdrian Hunter " (" + dsoname(query.value(15)) + ")") 3043530e22fdSAdrian Hunter 3044530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data): 3045530e22fdSAdrian Hunter insn_cnt = query.value(16) 3046530e22fdSAdrian Hunter cyc_cnt = query.value(17) 3047530e22fdSAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 3048530e22fdSAdrian Hunter data.append(insn_cnt) 3049530e22fdSAdrian Hunter data.append(cyc_cnt) 3050530e22fdSAdrian Hunter data.append(ipc) 3051530e22fdSAdrian Hunter 305276099f98SAdrian Hunterdef BranchDataPrep(query): 305376099f98SAdrian Hunter data = [] 305476099f98SAdrian Hunter for i in xrange(0, 8): 305576099f98SAdrian Hunter data.append(query.value(i)) 3056530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 305776099f98SAdrian Hunter return data 305876099f98SAdrian Hunter 30598453c936SAdrian Hunterdef BranchDataPrepWA(query): 30608453c936SAdrian Hunter data = [] 30618453c936SAdrian Hunter data.append(query.value(0)) 30628453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 30638453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 30648453c936SAdrian Hunter for i in xrange(2, 8): 30658453c936SAdrian Hunter data.append(query.value(i)) 3066530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 3067530e22fdSAdrian Hunter return data 3068530e22fdSAdrian Hunter 3069530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query): 3070530e22fdSAdrian Hunter data = [] 3071530e22fdSAdrian Hunter for i in xrange(0, 8): 3072530e22fdSAdrian Hunter data.append(query.value(i)) 3073530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 3074530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 3075530e22fdSAdrian Hunter return data 3076530e22fdSAdrian Hunter 3077530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query): 3078530e22fdSAdrian Hunter data = [] 3079530e22fdSAdrian Hunter data.append(query.value(0)) 3080530e22fdSAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 3081530e22fdSAdrian Hunter data.append("{:>19}".format(query.value(1))) 3082530e22fdSAdrian Hunter for i in xrange(2, 8): 3083530e22fdSAdrian Hunter data.append(query.value(i)) 3084530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 3085530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 30868453c936SAdrian Hunter return data 30878453c936SAdrian Hunter 308876099f98SAdrian Hunter# Branch data model 308976099f98SAdrian Hunter 309076099f98SAdrian Hunterclass BranchModel(TreeModel): 309176099f98SAdrian Hunter 309276099f98SAdrian Hunter progress = Signal(object) 309376099f98SAdrian Hunter 309476099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 30954a0979d4SAdrian Hunter super(BranchModel, self).__init__(glb, None, parent) 309676099f98SAdrian Hunter self.event_id = event_id 309776099f98SAdrian Hunter self.more = True 309876099f98SAdrian Hunter self.populated = 0 3099530e22fdSAdrian Hunter self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") 3100530e22fdSAdrian Hunter if self.have_ipc: 3101530e22fdSAdrian Hunter select_ipc = ", insn_count, cyc_count" 3102530e22fdSAdrian Hunter prep_fn = BranchDataWithIPCPrep 3103530e22fdSAdrian Hunter prep_wa_fn = BranchDataWithIPCPrepWA 3104530e22fdSAdrian Hunter else: 3105530e22fdSAdrian Hunter select_ipc = "" 3106530e22fdSAdrian Hunter prep_fn = BranchDataPrep 3107530e22fdSAdrian Hunter prep_wa_fn = BranchDataPrepWA 310876099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 310976099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 311076099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 311176099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 3112530e22fdSAdrian Hunter + select_ipc + 311376099f98SAdrian Hunter " FROM samples" 311476099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 311576099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 311676099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 311776099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 311876099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 311976099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 312076099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 312176099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 312276099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 312376099f98SAdrian Hunter " ORDER BY samples.id" 312476099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 31258453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 3126530e22fdSAdrian Hunter prep = prep_fn 31278453c936SAdrian Hunter else: 3128530e22fdSAdrian Hunter prep = prep_wa_fn 31298453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 313076099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 313176099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 313276099f98SAdrian Hunter 3133a448ba23SAdrian Hunter def GetRoot(self): 3134a448ba23SAdrian Hunter return BranchRootItem() 3135a448ba23SAdrian Hunter 313676099f98SAdrian Hunter def columnCount(self, parent=None): 3137530e22fdSAdrian Hunter if self.have_ipc: 3138530e22fdSAdrian Hunter return 11 3139530e22fdSAdrian Hunter else: 314076099f98SAdrian Hunter return 8 314176099f98SAdrian Hunter 314276099f98SAdrian Hunter def columnHeader(self, column): 3143530e22fdSAdrian Hunter if self.have_ipc: 3144530e22fdSAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] 3145530e22fdSAdrian Hunter else: 314676099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 314776099f98SAdrian Hunter 314876099f98SAdrian Hunter def columnFont(self, column): 3149530e22fdSAdrian Hunter if self.have_ipc: 3150530e22fdSAdrian Hunter br_col = 10 3151530e22fdSAdrian Hunter else: 3152530e22fdSAdrian Hunter br_col = 7 3153530e22fdSAdrian Hunter if column != br_col: 315476099f98SAdrian Hunter return None 315576099f98SAdrian Hunter return QFont("Monospace") 315676099f98SAdrian Hunter 315776099f98SAdrian Hunter def DisplayData(self, item, index): 315876099f98SAdrian Hunter if item.level == 1: 315976099f98SAdrian Hunter self.FetchIfNeeded(item.row) 316076099f98SAdrian Hunter return item.getData(index.column()) 316176099f98SAdrian Hunter 316276099f98SAdrian Hunter def AddSample(self, data): 316376099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 316476099f98SAdrian Hunter self.root.child_items.append(child) 316576099f98SAdrian Hunter self.populated += 1 316676099f98SAdrian Hunter 316776099f98SAdrian Hunter def Update(self, fetched): 316876099f98SAdrian Hunter if not fetched: 316976099f98SAdrian Hunter self.more = False 317076099f98SAdrian Hunter self.progress.emit(0) 317176099f98SAdrian Hunter child_count = self.root.child_count 317276099f98SAdrian Hunter count = self.populated - child_count 317376099f98SAdrian Hunter if count > 0: 317476099f98SAdrian Hunter parent = QModelIndex() 317576099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 317676099f98SAdrian Hunter self.insertRows(child_count, count, parent) 317776099f98SAdrian Hunter self.root.child_count += count 317876099f98SAdrian Hunter self.endInsertRows() 317976099f98SAdrian Hunter self.progress.emit(self.root.child_count) 318076099f98SAdrian Hunter 318176099f98SAdrian Hunter def FetchMoreRecords(self, count): 318276099f98SAdrian Hunter current = self.root.child_count 318376099f98SAdrian Hunter if self.more: 318476099f98SAdrian Hunter self.fetcher.Fetch(count) 318576099f98SAdrian Hunter else: 318676099f98SAdrian Hunter self.progress.emit(0) 318776099f98SAdrian Hunter return current 318876099f98SAdrian Hunter 318976099f98SAdrian Hunter def HasMoreRecords(self): 319076099f98SAdrian Hunter return self.more 319176099f98SAdrian Hunter 31920bf0947aSAdrian Hunter# Report Variables 31930bf0947aSAdrian Hunter 31940bf0947aSAdrian Hunterclass ReportVars(): 31950bf0947aSAdrian Hunter 3196cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 3197947cc38dSAdrian Hunter self.name = name 31980bf0947aSAdrian Hunter self.where_clause = where_clause 3199cd358012SAdrian Hunter self.limit = limit 32000bf0947aSAdrian Hunter 32010bf0947aSAdrian Hunter def UniqueId(self): 3202cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 32030bf0947aSAdrian Hunter 320476099f98SAdrian Hunter# Branch window 320576099f98SAdrian Hunter 320676099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 320776099f98SAdrian Hunter 3208947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 320976099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 321076099f98SAdrian Hunter 32110bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 321276099f98SAdrian Hunter 32130bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 321476099f98SAdrian Hunter 321576099f98SAdrian Hunter self.view = QTreeView() 321676099f98SAdrian Hunter self.view.setUniformRowHeights(True) 321796c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 321896c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 321976099f98SAdrian Hunter self.view.setModel(self.model) 322076099f98SAdrian Hunter 322176099f98SAdrian Hunter self.ResizeColumnsToContents() 322276099f98SAdrian Hunter 32239bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 32249bc4e4bfSAdrian Hunter 322576099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 322676099f98SAdrian Hunter 322776099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 322876099f98SAdrian Hunter 322976099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 323076099f98SAdrian Hunter 323176099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 323276099f98SAdrian Hunter 323376099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 323476099f98SAdrian Hunter 3235947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 323676099f98SAdrian Hunter 323776099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 323876099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 323976099f98SAdrian Hunter # so implement a crude alternative 324076099f98SAdrian Hunter mm = "MM" if column else "MMMM" 324176099f98SAdrian Hunter font = self.view.font() 324276099f98SAdrian Hunter metrics = QFontMetrics(font) 324376099f98SAdrian Hunter max = 0 324476099f98SAdrian Hunter for row in xrange(n): 324576099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 324676099f98SAdrian Hunter len = metrics.width(str(val) + mm) 324776099f98SAdrian Hunter max = len if len > max else max 324876099f98SAdrian Hunter val = self.model.columnHeader(column) 324976099f98SAdrian Hunter len = metrics.width(str(val) + mm) 325076099f98SAdrian Hunter max = len if len > max else max 325176099f98SAdrian Hunter self.view.setColumnWidth(column, max) 325276099f98SAdrian Hunter 325376099f98SAdrian Hunter def ResizeColumnsToContents(self): 325476099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 325576099f98SAdrian Hunter if n < 1: 325676099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 325776099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 325876099f98SAdrian Hunter return 325976099f98SAdrian Hunter columns = self.model.columnCount() 326076099f98SAdrian Hunter for i in xrange(columns): 326176099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 326276099f98SAdrian Hunter 326376099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 326476099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 326576099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 326676099f98SAdrian Hunter self.ResizeColumnsToContents() 326776099f98SAdrian Hunter 326876099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 326976099f98SAdrian Hunter self.view.setFocus() 327076099f98SAdrian Hunter self.find_bar.Busy() 327176099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 327276099f98SAdrian Hunter 327376099f98SAdrian Hunter def FindDone(self, row): 327476099f98SAdrian Hunter self.find_bar.Idle() 327576099f98SAdrian Hunter if row >= 0: 327676099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 327776099f98SAdrian Hunter else: 327876099f98SAdrian Hunter self.find_bar.NotFound() 327976099f98SAdrian Hunter 32801c3ca1b3SAdrian Hunter# Line edit data item 32811c3ca1b3SAdrian Hunter 32821c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 32831c3ca1b3SAdrian Hunter 3284cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 32851c3ca1b3SAdrian Hunter self.glb = glb 32861c3ca1b3SAdrian Hunter self.label = label 32871c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 32881c3ca1b3SAdrian Hunter self.parent = parent 32891c3ca1b3SAdrian Hunter self.id = id 32901c3ca1b3SAdrian Hunter 3291cd358012SAdrian Hunter self.value = default 32921c3ca1b3SAdrian Hunter 3293cd358012SAdrian Hunter self.widget = QLineEdit(default) 32941c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 32951c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 32961c3ca1b3SAdrian Hunter self.red = False 32971c3ca1b3SAdrian Hunter self.error = "" 32981c3ca1b3SAdrian Hunter self.validated = True 32991c3ca1b3SAdrian Hunter 33001c3ca1b3SAdrian Hunter if placeholder_text: 33011c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 33021c3ca1b3SAdrian Hunter 33031c3ca1b3SAdrian Hunter def TurnTextRed(self): 33041c3ca1b3SAdrian Hunter if not self.red: 33051c3ca1b3SAdrian Hunter palette = QPalette() 33061c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 33071c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 33081c3ca1b3SAdrian Hunter self.red = True 33091c3ca1b3SAdrian Hunter 33101c3ca1b3SAdrian Hunter def TurnTextNormal(self): 33111c3ca1b3SAdrian Hunter if self.red: 33121c3ca1b3SAdrian Hunter palette = QPalette() 33131c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 33141c3ca1b3SAdrian Hunter self.red = False 33151c3ca1b3SAdrian Hunter 33161c3ca1b3SAdrian Hunter def InvalidValue(self, value): 33171c3ca1b3SAdrian Hunter self.value = "" 33181c3ca1b3SAdrian Hunter self.TurnTextRed() 33191c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 33201c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 33211c3ca1b3SAdrian Hunter 33221c3ca1b3SAdrian Hunter def Invalidate(self): 33231c3ca1b3SAdrian Hunter self.validated = False 33241c3ca1b3SAdrian Hunter 33251c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 33261c3ca1b3SAdrian Hunter self.value = input_string.strip() 33271c3ca1b3SAdrian Hunter 33281c3ca1b3SAdrian Hunter def Validate(self): 33291c3ca1b3SAdrian Hunter self.validated = True 33301c3ca1b3SAdrian Hunter self.error = "" 33311c3ca1b3SAdrian Hunter self.TurnTextNormal() 33321c3ca1b3SAdrian Hunter self.parent.ClearMessage() 33331c3ca1b3SAdrian Hunter input_string = self.widget.text() 33341c3ca1b3SAdrian Hunter if not len(input_string.strip()): 33351c3ca1b3SAdrian Hunter self.value = "" 33361c3ca1b3SAdrian Hunter return 33371c3ca1b3SAdrian Hunter self.DoValidate(input_string) 33381c3ca1b3SAdrian Hunter 33391c3ca1b3SAdrian Hunter def IsValid(self): 33401c3ca1b3SAdrian Hunter if not self.validated: 33411c3ca1b3SAdrian Hunter self.Validate() 33421c3ca1b3SAdrian Hunter if len(self.error): 33431c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 33441c3ca1b3SAdrian Hunter return False 33451c3ca1b3SAdrian Hunter return True 33461c3ca1b3SAdrian Hunter 33471c3ca1b3SAdrian Hunter def IsNumber(self, value): 33481c3ca1b3SAdrian Hunter try: 33491c3ca1b3SAdrian Hunter x = int(value) 33501c3ca1b3SAdrian Hunter except: 33511c3ca1b3SAdrian Hunter x = 0 33521c3ca1b3SAdrian Hunter return str(x) == value 33531c3ca1b3SAdrian Hunter 33541c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 33551c3ca1b3SAdrian Hunter 33561c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 33571c3ca1b3SAdrian Hunter 33581c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 33591c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 33601c3ca1b3SAdrian Hunter 33611c3ca1b3SAdrian Hunter self.column_name = column_name 33621c3ca1b3SAdrian Hunter 33631c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 33641c3ca1b3SAdrian Hunter singles = [] 33651c3ca1b3SAdrian Hunter ranges = [] 33661c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 33671c3ca1b3SAdrian Hunter if "-" in value: 33681c3ca1b3SAdrian Hunter vrange = value.split("-") 33691c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 33701c3ca1b3SAdrian Hunter return self.InvalidValue(value) 33711c3ca1b3SAdrian Hunter ranges.append(vrange) 33721c3ca1b3SAdrian Hunter else: 33731c3ca1b3SAdrian Hunter if not self.IsNumber(value): 33741c3ca1b3SAdrian Hunter return self.InvalidValue(value) 33751c3ca1b3SAdrian Hunter singles.append(value) 33761c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 33771c3ca1b3SAdrian Hunter if len(singles): 33781c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 33791c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 33801c3ca1b3SAdrian Hunter 3381cd358012SAdrian Hunter# Positive integer dialog data item 3382cd358012SAdrian Hunter 3383cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 3384cd358012SAdrian Hunter 3385cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 3386cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 3387cd358012SAdrian Hunter 3388cd358012SAdrian Hunter def DoValidate(self, input_string): 3389cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 3390cd358012SAdrian Hunter return self.InvalidValue(input_string) 3391cd358012SAdrian Hunter value = int(input_string.strip()) 3392cd358012SAdrian Hunter if value <= 0: 3393cd358012SAdrian Hunter return self.InvalidValue(input_string) 3394cd358012SAdrian Hunter self.value = str(value) 3395cd358012SAdrian Hunter 33961c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 33971c3ca1b3SAdrian Hunter 33981c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 33991c3ca1b3SAdrian Hunter 34001c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 34011c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 34021c3ca1b3SAdrian Hunter 34031c3ca1b3SAdrian Hunter self.table_name = table_name 34041c3ca1b3SAdrian Hunter self.match_column = match_column 34051c3ca1b3SAdrian Hunter self.column_name1 = column_name1 34061c3ca1b3SAdrian Hunter self.column_name2 = column_name2 34071c3ca1b3SAdrian Hunter 34081c3ca1b3SAdrian Hunter def ValueToIds(self, value): 34091c3ca1b3SAdrian Hunter ids = [] 34101c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 34111c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 34121c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 34131c3ca1b3SAdrian Hunter if ret: 34141c3ca1b3SAdrian Hunter while query.next(): 34151c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 34161c3ca1b3SAdrian Hunter return ids 34171c3ca1b3SAdrian Hunter 34181c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 34191c3ca1b3SAdrian Hunter all_ids = [] 34201c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 34211c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 34221c3ca1b3SAdrian Hunter if len(ids): 34231c3ca1b3SAdrian Hunter all_ids.extend(ids) 34241c3ca1b3SAdrian Hunter else: 34251c3ca1b3SAdrian Hunter return self.InvalidValue(value) 34261c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 34271c3ca1b3SAdrian Hunter if self.column_name2: 34281c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 34291c3ca1b3SAdrian Hunter 34301c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 34311c3ca1b3SAdrian Hunter 34321c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 34331c3ca1b3SAdrian Hunter 34341c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 34351c3ca1b3SAdrian Hunter self.column_name = column_name 34361c3ca1b3SAdrian Hunter 34371c3ca1b3SAdrian Hunter self.last_id = 0 34381c3ca1b3SAdrian Hunter self.first_time = 0 34391c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 34401c3ca1b3SAdrian Hunter 34411c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 34421c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 34431c3ca1b3SAdrian Hunter if query.next(): 34441c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 34459a9dae36SAdrian Hunter self.first_time = int(glb.HostStartTime()) 34469a9dae36SAdrian Hunter self.last_time = int(glb.HostFinishTime()) 34471c3ca1b3SAdrian Hunter if placeholder_text: 34481c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 34491c3ca1b3SAdrian Hunter 34501c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 34511c3ca1b3SAdrian Hunter 34521c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 34531c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 34541c3ca1b3SAdrian Hunter if query.next(): 34551c3ca1b3SAdrian Hunter return True, int(query.value(0)) 34561c3ca1b3SAdrian Hunter else: 34571c3ca1b3SAdrian Hunter return False, 0 34581c3ca1b3SAdrian Hunter 34591c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 34601c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 34611c3ca1b3SAdrian Hunter while True: 34621c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 34631c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 34641c3ca1b3SAdrian Hunter if not query.next(): 34651c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 34661c3ca1b3SAdrian Hunter if not ok: 34671c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 34681c3ca1b3SAdrian Hunter if not ok: 34691c3ca1b3SAdrian Hunter return str(higher_id) 34701c3ca1b3SAdrian Hunter next_id = dbid 34711c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 34721c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 34731c3ca1b3SAdrian Hunter if get_floor: 34741c3ca1b3SAdrian Hunter if target_time > next_time: 34751c3ca1b3SAdrian Hunter lower_id = next_id 34761c3ca1b3SAdrian Hunter else: 34771c3ca1b3SAdrian Hunter higher_id = next_id 34781c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 34791c3ca1b3SAdrian Hunter return str(higher_id) 34801c3ca1b3SAdrian Hunter else: 34811c3ca1b3SAdrian Hunter if target_time >= next_time: 34821c3ca1b3SAdrian Hunter lower_id = next_id 34831c3ca1b3SAdrian Hunter else: 34841c3ca1b3SAdrian Hunter higher_id = next_id 34851c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 34861c3ca1b3SAdrian Hunter return str(lower_id) 34871c3ca1b3SAdrian Hunter 34881c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 34891c3ca1b3SAdrian Hunter mult = 1 34901c3ca1b3SAdrian Hunter suffix = val[-2:] 34911c3ca1b3SAdrian Hunter if suffix == "ms": 34921c3ca1b3SAdrian Hunter mult = 1000000 34931c3ca1b3SAdrian Hunter elif suffix == "us": 34941c3ca1b3SAdrian Hunter mult = 1000 34951c3ca1b3SAdrian Hunter elif suffix == "ns": 34961c3ca1b3SAdrian Hunter mult = 1 34971c3ca1b3SAdrian Hunter else: 34981c3ca1b3SAdrian Hunter return val 34991c3ca1b3SAdrian Hunter val = val[:-2].strip() 35001c3ca1b3SAdrian Hunter if not self.IsNumber(val): 35011c3ca1b3SAdrian Hunter return val 35021c3ca1b3SAdrian Hunter val = int(val) * mult 35031c3ca1b3SAdrian Hunter if val >= 0: 35041c3ca1b3SAdrian Hunter val += self.first_time 35051c3ca1b3SAdrian Hunter else: 35061c3ca1b3SAdrian Hunter val += self.last_time 35071c3ca1b3SAdrian Hunter return str(val) 35081c3ca1b3SAdrian Hunter 35091c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 35101c3ca1b3SAdrian Hunter if vrange[0] == "": 35111c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 35121c3ca1b3SAdrian Hunter if vrange[1] == "": 35131c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 35141c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 35151c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 35161c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 35171c3ca1b3SAdrian Hunter return False 35181c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 35191c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 35201c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 35211c3ca1b3SAdrian Hunter return False 35221c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 35231c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 35241c3ca1b3SAdrian Hunter return True 35251c3ca1b3SAdrian Hunter 35261c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 35271c3ca1b3SAdrian Hunter n = value.count("-") 35281c3ca1b3SAdrian Hunter if n == 1: 35291c3ca1b3SAdrian Hunter pass 35301c3ca1b3SAdrian Hunter elif n == 2: 35311c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 35321c3ca1b3SAdrian Hunter n = 1 35331c3ca1b3SAdrian Hunter elif n == 3: 35341c3ca1b3SAdrian Hunter n = 2 35351c3ca1b3SAdrian Hunter else: 35361c3ca1b3SAdrian Hunter return False 35371c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 35381c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 35391c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 35401c3ca1b3SAdrian Hunter ranges.append(vrange) 35411c3ca1b3SAdrian Hunter return True 35421c3ca1b3SAdrian Hunter return False 35431c3ca1b3SAdrian Hunter 35441c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 35451c3ca1b3SAdrian Hunter ranges = [] 35461c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 35471c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 35481c3ca1b3SAdrian Hunter return self.InvalidValue(value) 35491c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 35501c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 35511c3ca1b3SAdrian Hunter 35520924cd68SAdrian Hunter# Report Dialog Base 3553210cf1f9SAdrian Hunter 35540924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 3555210cf1f9SAdrian Hunter 35560924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 35570924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 3558210cf1f9SAdrian Hunter 3559210cf1f9SAdrian Hunter self.glb = glb 3560210cf1f9SAdrian Hunter 35610bf0947aSAdrian Hunter self.report_vars = ReportVars() 3562210cf1f9SAdrian Hunter 35630924cd68SAdrian Hunter self.setWindowTitle(title) 3564210cf1f9SAdrian Hunter self.setMinimumWidth(600) 3565210cf1f9SAdrian Hunter 35661c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 3567210cf1f9SAdrian Hunter 35680924cd68SAdrian Hunter self.partial = partial 35690924cd68SAdrian Hunter 3570210cf1f9SAdrian Hunter self.grid = QGridLayout() 3571210cf1f9SAdrian Hunter 3572210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 3573210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 3574210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 3575210cf1f9SAdrian Hunter 3576210cf1f9SAdrian Hunter self.status = QLabel() 3577210cf1f9SAdrian Hunter 3578210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 3579210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 3580210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 3581210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 3582210cf1f9SAdrian Hunter 3583210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 3584210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 3585210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 3586210cf1f9SAdrian Hunter 3587210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 3588210cf1f9SAdrian Hunter #self.hbox.addStretch() 3589210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 3590210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 3591210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 3592210cf1f9SAdrian Hunter 3593210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 3594210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 3595210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 3596210cf1f9SAdrian Hunter 359726688729SAdrian Hunter self.setLayout(self.vbox) 3598210cf1f9SAdrian Hunter 3599210cf1f9SAdrian Hunter def Ok(self): 36000bf0947aSAdrian Hunter vars = self.report_vars 36011c3ca1b3SAdrian Hunter for d in self.data_items: 36021c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 36031c3ca1b3SAdrian Hunter vars.name = d.value 3604947cc38dSAdrian Hunter if not vars.name: 3605210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 3606210cf1f9SAdrian Hunter return 3607210cf1f9SAdrian Hunter for d in self.data_items: 3608210cf1f9SAdrian Hunter if not d.IsValid(): 3609210cf1f9SAdrian Hunter return 3610210cf1f9SAdrian Hunter for d in self.data_items[1:]: 3611cd358012SAdrian Hunter if d.id == "LIMIT": 3612cd358012SAdrian Hunter vars.limit = d.value 3613cd358012SAdrian Hunter elif len(d.value): 36140bf0947aSAdrian Hunter if len(vars.where_clause): 36150bf0947aSAdrian Hunter vars.where_clause += " AND " 36160bf0947aSAdrian Hunter vars.where_clause += d.value 36170bf0947aSAdrian Hunter if len(vars.where_clause): 36180924cd68SAdrian Hunter if self.partial: 36190bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 3620210cf1f9SAdrian Hunter else: 36210bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 3622210cf1f9SAdrian Hunter self.accept() 3623210cf1f9SAdrian Hunter 3624210cf1f9SAdrian Hunter def ShowMessage(self, msg): 3625210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 3626210cf1f9SAdrian Hunter 3627210cf1f9SAdrian Hunter def ClearMessage(self): 3628210cf1f9SAdrian Hunter self.status.setText("") 3629210cf1f9SAdrian Hunter 36300924cd68SAdrian Hunter# Selected branch report creation dialog 36310924cd68SAdrian Hunter 36320924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 36330924cd68SAdrian Hunter 36340924cd68SAdrian Hunter def __init__(self, glb, parent=None): 36350924cd68SAdrian Hunter title = "Selected Branches" 36361c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 36371c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 36381c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 36391c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 36401c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 36411c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 36421c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p), 36431c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 36441c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 36450924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 36460924cd68SAdrian Hunter 364776099f98SAdrian Hunter# Event list 364876099f98SAdrian Hunter 364976099f98SAdrian Hunterdef GetEventList(db): 365076099f98SAdrian Hunter events = [] 365176099f98SAdrian Hunter query = QSqlQuery(db) 365276099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 365376099f98SAdrian Hunter while query.next(): 365476099f98SAdrian Hunter events.append(query.value(0)) 365576099f98SAdrian Hunter return events 365676099f98SAdrian Hunter 3657655cb952SAdrian Hunter# Is a table selectable 3658655cb952SAdrian Hunter 3659530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"): 3660655cb952SAdrian Hunter query = QSqlQuery(db) 3661655cb952SAdrian Hunter try: 3662530e22fdSAdrian Hunter QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") 3663655cb952SAdrian Hunter except: 3664655cb952SAdrian Hunter return False 3665655cb952SAdrian Hunter return True 3666655cb952SAdrian Hunter 36678392b74bSAdrian Hunter# SQL table data model item 36688392b74bSAdrian Hunter 36698392b74bSAdrian Hunterclass SQLTableItem(): 36708392b74bSAdrian Hunter 36718392b74bSAdrian Hunter def __init__(self, row, data): 36728392b74bSAdrian Hunter self.row = row 36738392b74bSAdrian Hunter self.data = data 36748392b74bSAdrian Hunter 36758392b74bSAdrian Hunter def getData(self, column): 36768392b74bSAdrian Hunter return self.data[column] 36778392b74bSAdrian Hunter 36788392b74bSAdrian Hunter# SQL table data model 36798392b74bSAdrian Hunter 36808392b74bSAdrian Hunterclass SQLTableModel(TableModel): 36818392b74bSAdrian Hunter 36828392b74bSAdrian Hunter progress = Signal(object) 36838392b74bSAdrian Hunter 36848c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 36858392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 36868392b74bSAdrian Hunter self.glb = glb 36878392b74bSAdrian Hunter self.more = True 36888392b74bSAdrian Hunter self.populated = 0 36898c90fef9SAdrian Hunter self.column_headers = column_headers 36908453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 36918392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 36928392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 36938392b74bSAdrian Hunter 36948392b74bSAdrian Hunter def DisplayData(self, item, index): 36958392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 36968392b74bSAdrian Hunter return item.getData(index.column()) 36978392b74bSAdrian Hunter 36988392b74bSAdrian Hunter def AddSample(self, data): 36998392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 37008392b74bSAdrian Hunter self.child_items.append(child) 37018392b74bSAdrian Hunter self.populated += 1 37028392b74bSAdrian Hunter 37038392b74bSAdrian Hunter def Update(self, fetched): 37048392b74bSAdrian Hunter if not fetched: 37058392b74bSAdrian Hunter self.more = False 37068392b74bSAdrian Hunter self.progress.emit(0) 37078392b74bSAdrian Hunter child_count = self.child_count 37088392b74bSAdrian Hunter count = self.populated - child_count 37098392b74bSAdrian Hunter if count > 0: 37108392b74bSAdrian Hunter parent = QModelIndex() 37118392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 37128392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 37138392b74bSAdrian Hunter self.child_count += count 37148392b74bSAdrian Hunter self.endInsertRows() 37158392b74bSAdrian Hunter self.progress.emit(self.child_count) 37168392b74bSAdrian Hunter 37178392b74bSAdrian Hunter def FetchMoreRecords(self, count): 37188392b74bSAdrian Hunter current = self.child_count 37198392b74bSAdrian Hunter if self.more: 37208392b74bSAdrian Hunter self.fetcher.Fetch(count) 37218392b74bSAdrian Hunter else: 37228392b74bSAdrian Hunter self.progress.emit(0) 37238392b74bSAdrian Hunter return current 37248392b74bSAdrian Hunter 37258392b74bSAdrian Hunter def HasMoreRecords(self): 37268392b74bSAdrian Hunter return self.more 37278392b74bSAdrian Hunter 37288c90fef9SAdrian Hunter def columnCount(self, parent=None): 37298c90fef9SAdrian Hunter return len(self.column_headers) 37308c90fef9SAdrian Hunter 37318c90fef9SAdrian Hunter def columnHeader(self, column): 37328c90fef9SAdrian Hunter return self.column_headers[column] 37338c90fef9SAdrian Hunter 37348453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 37358453c936SAdrian Hunter data = [] 37368453c936SAdrian Hunter for i in xrange(count): 37378453c936SAdrian Hunter data.append(query.value(i)) 37388453c936SAdrian Hunter return data 37398453c936SAdrian Hunter 37408392b74bSAdrian Hunter# SQL automatic table data model 37418392b74bSAdrian Hunter 37428392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 37438392b74bSAdrian Hunter 37448392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 37458392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 37468392b74bSAdrian Hunter if table_name == "comm_threads_view": 37478392b74bSAdrian Hunter # For now, comm_threads_view has no id column 37488392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 37498c90fef9SAdrian Hunter column_headers = [] 37508392b74bSAdrian Hunter query = QSqlQuery(glb.db) 37518392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 37528392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 37538392b74bSAdrian Hunter while query.next(): 37548c90fef9SAdrian Hunter column_headers.append(query.value(1)) 37558392b74bSAdrian Hunter if table_name == "sqlite_master": 37568392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 37578392b74bSAdrian Hunter else: 37588392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 37598392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 37608392b74bSAdrian Hunter select_table_name = table_name[19:] 37618392b74bSAdrian Hunter schema = "information_schema" 37628392b74bSAdrian Hunter else: 37638392b74bSAdrian Hunter select_table_name = table_name 37648392b74bSAdrian Hunter schema = "public" 37658392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 37668392b74bSAdrian Hunter while query.next(): 37678c90fef9SAdrian Hunter column_headers.append(query.value(0)) 37688453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 37698453c936SAdrian Hunter if table_name == "samples_view": 37708453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 37718453c936SAdrian Hunter if table_name == "samples": 37728453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 37738c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 37748392b74bSAdrian Hunter 37758453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 37768453c936SAdrian Hunter data = [] 37778453c936SAdrian Hunter data.append(query.value(0)) 37788453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 37798453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 37808453c936SAdrian Hunter for i in xrange(2, count): 37818453c936SAdrian Hunter data.append(query.value(i)) 37828453c936SAdrian Hunter return data 37838453c936SAdrian Hunter 37848453c936SAdrian Hunter def samples_DataPrep(self, query, count): 37858453c936SAdrian Hunter data = [] 37868453c936SAdrian Hunter for i in xrange(9): 37878453c936SAdrian Hunter data.append(query.value(i)) 37888453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 37898453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 37908453c936SAdrian Hunter for i in xrange(10, count): 37918453c936SAdrian Hunter data.append(query.value(i)) 37928453c936SAdrian Hunter return data 37938453c936SAdrian Hunter 37948392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 37958392b74bSAdrian Hunter 37968392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 37978392b74bSAdrian Hunter 37988392b74bSAdrian Hunter def __init__(self, parent=None): 37998392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 38008392b74bSAdrian Hunter 38018392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 38028392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 38038392b74bSAdrian Hunter # so implement a crude alternative 38048392b74bSAdrian Hunter font = self.view.font() 38058392b74bSAdrian Hunter metrics = QFontMetrics(font) 38068392b74bSAdrian Hunter max = 0 38078392b74bSAdrian Hunter for row in xrange(n): 38088392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 38098392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 38108392b74bSAdrian Hunter max = len if len > max else max 38118392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 38128392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 38138392b74bSAdrian Hunter max = len if len > max else max 38148392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 38158392b74bSAdrian Hunter 38168392b74bSAdrian Hunter def ResizeColumnsToContents(self): 38178392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 38188392b74bSAdrian Hunter if n < 1: 38198392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 38208392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 38218392b74bSAdrian Hunter return 38228392b74bSAdrian Hunter columns = self.data_model.columnCount() 38238392b74bSAdrian Hunter for i in xrange(columns): 38248392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 38258392b74bSAdrian Hunter 38268392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 38278392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 38288392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 38298392b74bSAdrian Hunter self.ResizeColumnsToContents() 38308392b74bSAdrian Hunter 383196c43b9aSAdrian Hunter# Convert value to CSV 383296c43b9aSAdrian Hunter 383396c43b9aSAdrian Hunterdef ToCSValue(val): 383496c43b9aSAdrian Hunter if '"' in val: 383596c43b9aSAdrian Hunter val = val.replace('"', '""') 383696c43b9aSAdrian Hunter if "," in val or '"' in val: 383796c43b9aSAdrian Hunter val = '"' + val + '"' 383896c43b9aSAdrian Hunter return val 383996c43b9aSAdrian Hunter 384096c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 384196c43b9aSAdrian Hunter 384296c43b9aSAdrian Hunterglb_max_cols = 1000 384396c43b9aSAdrian Hunter 384496c43b9aSAdrian Hunterdef RowColumnKey(a): 384596c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 384696c43b9aSAdrian Hunter 384796c43b9aSAdrian Hunter# Copy selected table cells to clipboard 384896c43b9aSAdrian Hunter 384996c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 385096c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 385196c43b9aSAdrian Hunter idx_cnt = len(indexes) 385296c43b9aSAdrian Hunter if not idx_cnt: 385396c43b9aSAdrian Hunter return 385496c43b9aSAdrian Hunter if idx_cnt == 1: 385596c43b9aSAdrian Hunter with_hdr=False 385696c43b9aSAdrian Hunter min_row = indexes[0].row() 385796c43b9aSAdrian Hunter max_row = indexes[0].row() 385896c43b9aSAdrian Hunter min_col = indexes[0].column() 385996c43b9aSAdrian Hunter max_col = indexes[0].column() 386096c43b9aSAdrian Hunter for i in indexes: 386196c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 386296c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 386396c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 386496c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 386596c43b9aSAdrian Hunter if max_col > glb_max_cols: 386696c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 386796c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 386896c43b9aSAdrian Hunter for i in indexes: 386996c43b9aSAdrian Hunter c = i.column() - min_col 387096c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 387196c43b9aSAdrian Hunter text = "" 387296c43b9aSAdrian Hunter pad = "" 387396c43b9aSAdrian Hunter sep = "" 387496c43b9aSAdrian Hunter if with_hdr: 387596c43b9aSAdrian Hunter model = indexes[0].model() 387696c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 3877a6172059SAdrian Hunter val = model.headerData(col, Qt.Horizontal, Qt.DisplayRole) 387896c43b9aSAdrian Hunter if as_csv: 387996c43b9aSAdrian Hunter text += sep + ToCSValue(val) 388096c43b9aSAdrian Hunter sep = "," 388196c43b9aSAdrian Hunter else: 388296c43b9aSAdrian Hunter c = col - min_col 388396c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 388496c43b9aSAdrian Hunter width = max_width[c] 388596c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 388696c43b9aSAdrian Hunter if align & Qt.AlignRight: 388796c43b9aSAdrian Hunter val = val.rjust(width) 388896c43b9aSAdrian Hunter text += pad + sep + val 388996c43b9aSAdrian Hunter pad = " " * (width - len(val)) 389096c43b9aSAdrian Hunter sep = " " 389196c43b9aSAdrian Hunter text += "\n" 389296c43b9aSAdrian Hunter pad = "" 389396c43b9aSAdrian Hunter sep = "" 389496c43b9aSAdrian Hunter last_row = min_row 389596c43b9aSAdrian Hunter for i in indexes: 389696c43b9aSAdrian Hunter if i.row() > last_row: 389796c43b9aSAdrian Hunter last_row = i.row() 389896c43b9aSAdrian Hunter text += "\n" 389996c43b9aSAdrian Hunter pad = "" 390096c43b9aSAdrian Hunter sep = "" 390196c43b9aSAdrian Hunter if as_csv: 390296c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 390396c43b9aSAdrian Hunter sep = "," 390496c43b9aSAdrian Hunter else: 390596c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 390696c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 390796c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 390896c43b9aSAdrian Hunter else: 390996c43b9aSAdrian Hunter val = str(i.data()) 391096c43b9aSAdrian Hunter text += pad + sep + val 391196c43b9aSAdrian Hunter pad = " " * (width - len(val)) 391296c43b9aSAdrian Hunter sep = " " 391396c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 391496c43b9aSAdrian Hunter 391596c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 391696c43b9aSAdrian Hunter indexes = view.selectedIndexes() 391796c43b9aSAdrian Hunter if not len(indexes): 391896c43b9aSAdrian Hunter return 391996c43b9aSAdrian Hunter 392096c43b9aSAdrian Hunter selection = view.selectionModel() 392196c43b9aSAdrian Hunter 392296c43b9aSAdrian Hunter first = None 392396c43b9aSAdrian Hunter for i in indexes: 392496c43b9aSAdrian Hunter above = view.indexAbove(i) 392596c43b9aSAdrian Hunter if not selection.isSelected(above): 392696c43b9aSAdrian Hunter first = i 392796c43b9aSAdrian Hunter break 392896c43b9aSAdrian Hunter 392996c43b9aSAdrian Hunter if first is None: 393096c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 393196c43b9aSAdrian Hunter 393296c43b9aSAdrian Hunter model = first.model() 393396c43b9aSAdrian Hunter row_cnt = 0 393496c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 393596c43b9aSAdrian Hunter max_width = [0] * col_cnt 393696c43b9aSAdrian Hunter 393796c43b9aSAdrian Hunter indent_sz = 2 393896c43b9aSAdrian Hunter indent_str = " " * indent_sz 393996c43b9aSAdrian Hunter 394096c43b9aSAdrian Hunter expanded_mark_sz = 2 394196c43b9aSAdrian Hunter if sys.version_info[0] == 3: 394296c43b9aSAdrian Hunter expanded_mark = "\u25BC " 394396c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 394496c43b9aSAdrian Hunter else: 394596c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 394696c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 394796c43b9aSAdrian Hunter leaf_mark = " " 394896c43b9aSAdrian Hunter 394996c43b9aSAdrian Hunter if not as_csv: 395096c43b9aSAdrian Hunter pos = first 395196c43b9aSAdrian Hunter while True: 395296c43b9aSAdrian Hunter row_cnt += 1 395396c43b9aSAdrian Hunter row = pos.row() 395496c43b9aSAdrian Hunter for c in range(col_cnt): 395596c43b9aSAdrian Hunter i = pos.sibling(row, c) 395696c43b9aSAdrian Hunter if c: 395796c43b9aSAdrian Hunter n = len(str(i.data())) 395896c43b9aSAdrian Hunter else: 395996c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 396096c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 396196c43b9aSAdrian Hunter n += expanded_mark_sz 396296c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 396396c43b9aSAdrian Hunter pos = view.indexBelow(pos) 396496c43b9aSAdrian Hunter if not selection.isSelected(pos): 396596c43b9aSAdrian Hunter break 396696c43b9aSAdrian Hunter 396796c43b9aSAdrian Hunter text = "" 396896c43b9aSAdrian Hunter pad = "" 396996c43b9aSAdrian Hunter sep = "" 397096c43b9aSAdrian Hunter if with_hdr: 397196c43b9aSAdrian Hunter for c in range(col_cnt): 397296c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 397396c43b9aSAdrian Hunter if as_csv: 397496c43b9aSAdrian Hunter text += sep + ToCSValue(val) 397596c43b9aSAdrian Hunter sep = "," 397696c43b9aSAdrian Hunter else: 397796c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 397896c43b9aSAdrian Hunter width = max_width[c] 397996c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 398096c43b9aSAdrian Hunter if align & Qt.AlignRight: 398196c43b9aSAdrian Hunter val = val.rjust(width) 398296c43b9aSAdrian Hunter text += pad + sep + val 398396c43b9aSAdrian Hunter pad = " " * (width - len(val)) 398496c43b9aSAdrian Hunter sep = " " 398596c43b9aSAdrian Hunter text += "\n" 398696c43b9aSAdrian Hunter pad = "" 398796c43b9aSAdrian Hunter sep = "" 398896c43b9aSAdrian Hunter 398996c43b9aSAdrian Hunter pos = first 399096c43b9aSAdrian Hunter while True: 399196c43b9aSAdrian Hunter row = pos.row() 399296c43b9aSAdrian Hunter for c in range(col_cnt): 399396c43b9aSAdrian Hunter i = pos.sibling(row, c) 399496c43b9aSAdrian Hunter val = str(i.data()) 399596c43b9aSAdrian Hunter if not c: 399696c43b9aSAdrian Hunter if model.hasChildren(i): 399796c43b9aSAdrian Hunter if view.isExpanded(i): 399896c43b9aSAdrian Hunter mark = expanded_mark 399996c43b9aSAdrian Hunter else: 400096c43b9aSAdrian Hunter mark = not_expanded_mark 400196c43b9aSAdrian Hunter else: 400296c43b9aSAdrian Hunter mark = leaf_mark 400396c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 400496c43b9aSAdrian Hunter if as_csv: 400596c43b9aSAdrian Hunter text += sep + ToCSValue(val) 400696c43b9aSAdrian Hunter sep = "," 400796c43b9aSAdrian Hunter else: 400896c43b9aSAdrian Hunter width = max_width[c] 400996c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 401096c43b9aSAdrian Hunter val = val.rjust(width) 401196c43b9aSAdrian Hunter text += pad + sep + val 401296c43b9aSAdrian Hunter pad = " " * (width - len(val)) 401396c43b9aSAdrian Hunter sep = " " 401496c43b9aSAdrian Hunter pos = view.indexBelow(pos) 401596c43b9aSAdrian Hunter if not selection.isSelected(pos): 401696c43b9aSAdrian Hunter break 401796c43b9aSAdrian Hunter text = text.rstrip() + "\n" 401896c43b9aSAdrian Hunter pad = "" 401996c43b9aSAdrian Hunter sep = "" 402096c43b9aSAdrian Hunter 402196c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 402296c43b9aSAdrian Hunter 402396c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 402496c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 402596c43b9aSAdrian Hunter 402696c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 402796c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 402896c43b9aSAdrian Hunter 402996c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 403096c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 403196c43b9aSAdrian Hunter 40329bc4e4bfSAdrian Hunter# Context menu 40339bc4e4bfSAdrian Hunter 40349bc4e4bfSAdrian Hunterclass ContextMenu(object): 40359bc4e4bfSAdrian Hunter 40369bc4e4bfSAdrian Hunter def __init__(self, view): 40379bc4e4bfSAdrian Hunter self.view = view 40389bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 40399bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 40409bc4e4bfSAdrian Hunter 40419bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 40429bc4e4bfSAdrian Hunter menu = QMenu(self.view) 40439bc4e4bfSAdrian Hunter self.AddActions(menu) 40449bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 40459bc4e4bfSAdrian Hunter 40469bc4e4bfSAdrian Hunter def AddCopy(self, menu): 40479bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 40489bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 40499bc4e4bfSAdrian Hunter 40509bc4e4bfSAdrian Hunter def AddActions(self, menu): 40519bc4e4bfSAdrian Hunter self.AddCopy(menu) 40529bc4e4bfSAdrian Hunter 40539bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 40549bc4e4bfSAdrian Hunter 40559bc4e4bfSAdrian Hunter def __init__(self, view): 40569bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 40579bc4e4bfSAdrian Hunter 40589bc4e4bfSAdrian Hunter def AddActions(self, menu): 40599bc4e4bfSAdrian Hunter i = self.view.currentIndex() 40609bc4e4bfSAdrian Hunter text = str(i.data()).strip() 40619bc4e4bfSAdrian Hunter if len(text): 40629bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 40639bc4e4bfSAdrian Hunter self.AddCopy(menu) 40649bc4e4bfSAdrian Hunter 40658392b74bSAdrian Hunter# Table window 40668392b74bSAdrian Hunter 40678392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 40688392b74bSAdrian Hunter 40698392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 40708392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 40718392b74bSAdrian Hunter 40728392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 40738392b74bSAdrian Hunter 40748392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 40758392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 40768392b74bSAdrian Hunter 40778392b74bSAdrian Hunter self.view = QTableView() 40788392b74bSAdrian Hunter self.view.setModel(self.model) 40798392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 40808392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 40818392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 40828392b74bSAdrian Hunter self.view.setSortingEnabled(True) 408396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 408496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 40858392b74bSAdrian Hunter 40868392b74bSAdrian Hunter self.ResizeColumnsToContents() 40878392b74bSAdrian Hunter 40889bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 40899bc4e4bfSAdrian Hunter 40908392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 40918392b74bSAdrian Hunter 40928392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 40938392b74bSAdrian Hunter 40948392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 40958392b74bSAdrian Hunter 40968392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 40978392b74bSAdrian Hunter 40988392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 40998392b74bSAdrian Hunter 41008392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 41018392b74bSAdrian Hunter 41028392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 41038392b74bSAdrian Hunter self.view.setFocus() 41048392b74bSAdrian Hunter self.find_bar.Busy() 41058392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 41068392b74bSAdrian Hunter 41078392b74bSAdrian Hunter def FindDone(self, row): 41088392b74bSAdrian Hunter self.find_bar.Idle() 41098392b74bSAdrian Hunter if row >= 0: 411035fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 41118392b74bSAdrian Hunter else: 41128392b74bSAdrian Hunter self.find_bar.NotFound() 41138392b74bSAdrian Hunter 41148392b74bSAdrian Hunter# Table list 41158392b74bSAdrian Hunter 41168392b74bSAdrian Hunterdef GetTableList(glb): 41178392b74bSAdrian Hunter tables = [] 41188392b74bSAdrian Hunter query = QSqlQuery(glb.db) 41198392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 41208392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 41218392b74bSAdrian Hunter else: 41228392b74bSAdrian Hunter QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name") 41238392b74bSAdrian Hunter while query.next(): 41248392b74bSAdrian Hunter tables.append(query.value(0)) 41258392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 41268392b74bSAdrian Hunter tables.append("sqlite_master") 41278392b74bSAdrian Hunter else: 41288392b74bSAdrian Hunter tables.append("information_schema.tables") 41298392b74bSAdrian Hunter tables.append("information_schema.views") 41308392b74bSAdrian Hunter tables.append("information_schema.columns") 41318392b74bSAdrian Hunter return tables 41328392b74bSAdrian Hunter 4133cd358012SAdrian Hunter# Top Calls data model 4134cd358012SAdrian Hunter 4135cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 4136cd358012SAdrian Hunter 4137cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 4138cd358012SAdrian Hunter text = "" 4139cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 4140cd358012SAdrian Hunter text = "::text" 4141cd358012SAdrian Hunter limit = "" 4142cd358012SAdrian Hunter if len(report_vars.limit): 4143cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 4144cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 4145cd358012SAdrian Hunter " CASE" 4146cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 4147cd358012SAdrian Hunter " ELSE short_name" 4148cd358012SAdrian Hunter " END AS dso," 4149cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 4150cd358012SAdrian Hunter " CASE" 4151cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 4152cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 4153cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 4154cd358012SAdrian Hunter " ELSE ''" + text + 4155cd358012SAdrian Hunter " END AS flags" 4156cd358012SAdrian Hunter " FROM calls" 4157cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 4158cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 4159cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 4160cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 4161cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 4162cd358012SAdrian Hunter report_vars.where_clause + 4163cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 4164cd358012SAdrian Hunter limit 4165cd358012SAdrian Hunter ) 4166cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 4167cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 4168cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 4169cd358012SAdrian Hunter 4170cd358012SAdrian Hunter def columnAlignment(self, column): 4171cd358012SAdrian Hunter return self.alignment[column] 4172cd358012SAdrian Hunter 4173cd358012SAdrian Hunter# Top Calls report creation dialog 4174cd358012SAdrian Hunter 4175cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 4176cd358012SAdrian Hunter 4177cd358012SAdrian Hunter def __init__(self, glb, parent=None): 4178cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 4179cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 4180cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 4181cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 4182cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 4183cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 4184cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 4185cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 4186cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 4187cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 4188cd358012SAdrian Hunter 4189cd358012SAdrian Hunter# Top Calls window 4190cd358012SAdrian Hunter 4191cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 4192cd358012SAdrian Hunter 4193cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 4194cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 4195cd358012SAdrian Hunter 4196cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 4197cd358012SAdrian Hunter self.model = self.data_model 4198cd358012SAdrian Hunter 4199cd358012SAdrian Hunter self.view = QTableView() 4200cd358012SAdrian Hunter self.view.setModel(self.model) 4201cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 4202cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 420396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 420496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 4205cd358012SAdrian Hunter 42069bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 42079bc4e4bfSAdrian Hunter 4208cd358012SAdrian Hunter self.ResizeColumnsToContents() 4209cd358012SAdrian Hunter 4210cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 4211cd358012SAdrian Hunter 4212cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 4213cd358012SAdrian Hunter 4214cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 4215cd358012SAdrian Hunter 4216cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 4217cd358012SAdrian Hunter 4218cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 4219cd358012SAdrian Hunter 4220cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 4221cd358012SAdrian Hunter 4222cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 4223cd358012SAdrian Hunter self.view.setFocus() 4224cd358012SAdrian Hunter self.find_bar.Busy() 4225cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 4226cd358012SAdrian Hunter 4227cd358012SAdrian Hunter def FindDone(self, row): 4228cd358012SAdrian Hunter self.find_bar.Idle() 4229cd358012SAdrian Hunter if row >= 0: 4230cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 4231cd358012SAdrian Hunter else: 4232cd358012SAdrian Hunter self.find_bar.NotFound() 4233cd358012SAdrian Hunter 42341beb5c7bSAdrian Hunter# Action Definition 42351beb5c7bSAdrian Hunter 42361beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 42371beb5c7bSAdrian Hunter action = QAction(label, parent) 42381beb5c7bSAdrian Hunter if shortcut != None: 42391beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 42401beb5c7bSAdrian Hunter action.setStatusTip(tip) 42411beb5c7bSAdrian Hunter action.triggered.connect(callback) 42421beb5c7bSAdrian Hunter return action 42431beb5c7bSAdrian Hunter 42441beb5c7bSAdrian Hunter# Typical application actions 42451beb5c7bSAdrian Hunter 42461beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 42471beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 42481beb5c7bSAdrian Hunter 42491beb5c7bSAdrian Hunter# Typical MDI actions 42501beb5c7bSAdrian Hunter 42511beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 42521beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 42531beb5c7bSAdrian Hunter 42541beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 42551beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 42561beb5c7bSAdrian Hunter 42571beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 42581beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 42591beb5c7bSAdrian Hunter 42601beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 42611beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 42621beb5c7bSAdrian Hunter 42631beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 42641beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 42651beb5c7bSAdrian Hunter 42661beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 42671beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 42681beb5c7bSAdrian Hunter 42691beb5c7bSAdrian Hunter# Typical MDI window menu 42701beb5c7bSAdrian Hunter 42711beb5c7bSAdrian Hunterclass WindowMenu(): 42721beb5c7bSAdrian Hunter 42731beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 42741beb5c7bSAdrian Hunter self.mdi_area = mdi_area 42751beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 42761beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 42771beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 42781beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 42791beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 42801beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 42811beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 42821beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 42831beb5c7bSAdrian Hunter 42841beb5c7bSAdrian Hunter def Update(self): 42851beb5c7bSAdrian Hunter self.window_menu.clear() 42861beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 42871beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 42881beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 42891beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 42901beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 42911beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 42921beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 42931beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 42941beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 42951beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 42961beb5c7bSAdrian Hunter self.window_menu.addSeparator() 42971beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 42981beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 42991beb5c7bSAdrian Hunter self.window_menu.addSeparator() 43001beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 43011beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 43021beb5c7bSAdrian Hunter if sub_window_count == 0: 43031beb5c7bSAdrian Hunter return 43041beb5c7bSAdrian Hunter self.window_menu.addSeparator() 43051beb5c7bSAdrian Hunter nr = 1 43061beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 43071beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 43081beb5c7bSAdrian Hunter if nr < 10: 43091beb5c7bSAdrian Hunter label = "&" + label 43101beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 43111beb5c7bSAdrian Hunter action.setCheckable(True) 43121beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 4313df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 43141beb5c7bSAdrian Hunter self.window_menu.addAction(action) 43151beb5c7bSAdrian Hunter nr += 1 43161beb5c7bSAdrian Hunter 43171beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 43181beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 43191beb5c7bSAdrian Hunter 432065b24292SAdrian Hunter# Help text 432165b24292SAdrian Hunter 432265b24292SAdrian Hunterglb_help_text = """ 432365b24292SAdrian Hunter<h1>Contents</h1> 432465b24292SAdrian Hunter<style> 432565b24292SAdrian Hunterp.c1 { 432665b24292SAdrian Hunter text-indent: 40px; 432765b24292SAdrian Hunter} 432865b24292SAdrian Hunterp.c2 { 432965b24292SAdrian Hunter text-indent: 80px; 433065b24292SAdrian Hunter} 433165b24292SAdrian Hunter} 433265b24292SAdrian Hunter</style> 433365b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 433465b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 4335ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 4336ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 4337ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 4338ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 4339b3700f21SAdrian Hunter<p class=c1><a href=#charts>2. Charts</a></p> 4340b3700f21SAdrian Hunter<p class=c2><a href=#timechartbycpu>2.1 Time chart by CPU</a></p> 4341b3700f21SAdrian Hunter<p class=c1><a href=#tables>3. Tables</a></p> 434265b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 434365b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 434465b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 434565b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 434665b24292SAdrian Hunterwidths to suit will display something like: 434765b24292SAdrian Hunter<pre> 434865b24292SAdrian Hunter Call Graph: pt_example 434965b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 435065b24292SAdrian Hunterv- ls 435165b24292SAdrian Hunter v- 2638:2638 435265b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 435365b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 435465b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 435565b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 435665b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 435765b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 435865b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 435965b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 436065b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 436165b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 436265b24292SAdrian Hunter</pre> 436365b24292SAdrian Hunter<h3>Points to note:</h3> 436465b24292SAdrian Hunter<ul> 436565b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 436665b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 436765b24292SAdrian Hunter<li>Subsequent levels are functions</li> 436865b24292SAdrian Hunter<li>'Count' is the number of calls</li> 436965b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 437065b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 437165b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 437265b24292SAdrian Hunter</ul> 437365b24292SAdrian Hunter<h3>Find</h3> 437465b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 437565b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 4376ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 4377ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 4378ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 4379ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 438065b24292SAdrian HunterThe All branches report displays all branches in chronological order. 438165b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 438265b24292SAdrian Hunter<h3>Disassembly</h3> 438365b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 438465b24292SAdrian Hunter<ol> 438565b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 438665b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 438765b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 438865b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 438965b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 439065b24292SAdrian Hunter</ol> 439165b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 439265b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 439365b24292SAdrian Hunter<pre> 439465b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 439565b24292SAdrian Huntergit clone https://github.com/intelxed/xed 439665b24292SAdrian Huntercd xed 439765b24292SAdrian Hunter./mfile.py --share 439865b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 439965b24292SAdrian Huntersudo ldconfig 440065b24292SAdrian Hunter</pre> 4401530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3> 4402530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. 4403530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. 4404530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known. 4405530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period 4406530e22fdSAdrian Huntersince the previous displayed 'IPC'. 440765b24292SAdrian Hunter<h3>Find</h3> 440865b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 440965b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 441065b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 4411ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 441265b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 441365b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 4414ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 441565b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 441665b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 441765b24292SAdrian Hunter<pre> 441865b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 441965b24292SAdrian Hunter 100us-200us From 100us to 200us 442065b24292SAdrian Hunter 10ms- From 10ms to the end 442165b24292SAdrian Hunter -100ns The first 100ns 442265b24292SAdrian Hunter -10ms- The last 10ms 442365b24292SAdrian Hunter</pre> 442465b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 4425ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 4426cd358012SAdrian HunterThe Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned. 4427cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 4428cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 4429b3700f21SAdrian Hunter<h1 id=charts>2. Charts</h1> 4430b3700f21SAdrian Hunter<h2 id=timechartbycpu>2.1 Time chart by CPU</h2> 4431b3700f21SAdrian HunterThis chart displays context switch information when that data is available. Refer to context_switches_view on the Tables menu. 4432b3700f21SAdrian Hunter<h3>Features</h3> 4433b3700f21SAdrian Hunter<ol> 4434b3700f21SAdrian Hunter<li>Mouse over to highight the task and show the time</li> 4435b3700f21SAdrian Hunter<li>Drag the mouse to select a region and zoom by pushing the Zoom button</li> 4436b3700f21SAdrian Hunter<li>Go back and forward by pressing the arrow buttons</li> 4437b3700f21SAdrian Hunter<li>If call information is available, right-click to show a call tree opened to that task and time. 4438b3700f21SAdrian HunterNote, the call tree may take some time to appear, and there may not be call information for the task or time selected. 4439b3700f21SAdrian Hunter</li> 4440b3700f21SAdrian Hunter</ol> 4441b3700f21SAdrian Hunter<h3>Important</h3> 4442b3700f21SAdrian HunterThe graph can be misleading in the following respects: 4443b3700f21SAdrian Hunter<ol> 4444b3700f21SAdrian Hunter<li>The graph shows the first task on each CPU as running from the beginning of the time range. 4445b3700f21SAdrian HunterBecause tracing might start on different CPUs at different times, that is not necessarily the case. 4446b3700f21SAdrian HunterRefer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li> 4447b3700f21SAdrian Hunter<li>Similarly, the last task on each CPU can be showing running longer than it really was. 4448b3700f21SAdrian HunterAgain, refer to context_switches_view on the Tables menu to understand what data the graph is based upon.</li> 4449b3700f21SAdrian Hunter<li>When the mouse is over a task, the highlighted task might not be visible on the legend without scrolling if the legend does not fit fully in the window</li> 4450b3700f21SAdrian Hunter</ol> 4451b3700f21SAdrian Hunter<h1 id=tables>3. Tables</h1> 445265b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 445365b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 445465b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 445565b24292SAdrian Hunterbut that can be slow for large tables. 445665b24292SAdrian Hunter<p>There are also tables of database meta-information. 445765b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 445865b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 445965b24292SAdrian Hunter<h3>Find</h3> 446065b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 446165b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 446265b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 446335fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 446435fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 446565b24292SAdrian Hunter""" 446665b24292SAdrian Hunter 446765b24292SAdrian Hunter# Help window 446865b24292SAdrian Hunter 446965b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 447065b24292SAdrian Hunter 447165b24292SAdrian Hunter def __init__(self, glb, parent=None): 447265b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 447365b24292SAdrian Hunter 447465b24292SAdrian Hunter self.text = QTextBrowser() 447565b24292SAdrian Hunter self.text.setHtml(glb_help_text) 447665b24292SAdrian Hunter self.text.setReadOnly(True) 447765b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 447865b24292SAdrian Hunter 447965b24292SAdrian Hunter self.setWidget(self.text) 448065b24292SAdrian Hunter 448165b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 448265b24292SAdrian Hunter 448365b24292SAdrian Hunter# Main window that only displays the help text 448465b24292SAdrian Hunter 448565b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 448665b24292SAdrian Hunter 448765b24292SAdrian Hunter def __init__(self, parent=None): 448865b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 448965b24292SAdrian Hunter 449065b24292SAdrian Hunter self.setMinimumSize(200, 100) 449165b24292SAdrian Hunter self.resize(800, 600) 449265b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 449365b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 449465b24292SAdrian Hunter 449565b24292SAdrian Hunter self.text = QTextBrowser() 449665b24292SAdrian Hunter self.text.setHtml(glb_help_text) 449765b24292SAdrian Hunter self.text.setReadOnly(True) 449865b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 449965b24292SAdrian Hunter 450065b24292SAdrian Hunter self.setCentralWidget(self.text) 450165b24292SAdrian Hunter 4502b62d18abSAdrian Hunter# PostqreSQL server version 4503b62d18abSAdrian Hunter 4504b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 4505b62d18abSAdrian Hunter query = QSqlQuery(db) 4506b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 4507b62d18abSAdrian Hunter if query.next(): 4508b62d18abSAdrian Hunter v_str = query.value(0) 4509b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 4510b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 4511b62d18abSAdrian Hunter return v_list[1] 4512b62d18abSAdrian Hunter return v_str 4513b62d18abSAdrian Hunter return "Unknown" 4514b62d18abSAdrian Hunter 4515b62d18abSAdrian Hunter# SQLite version 4516b62d18abSAdrian Hunter 4517b62d18abSAdrian Hunterdef SQLiteVersion(db): 4518b62d18abSAdrian Hunter query = QSqlQuery(db) 4519b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 4520b62d18abSAdrian Hunter if query.next(): 4521b62d18abSAdrian Hunter return query.value(0) 4522b62d18abSAdrian Hunter return "Unknown" 4523b62d18abSAdrian Hunter 4524b62d18abSAdrian Hunter# About dialog 4525b62d18abSAdrian Hunter 4526b62d18abSAdrian Hunterclass AboutDialog(QDialog): 4527b62d18abSAdrian Hunter 4528b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 4529b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 4530b62d18abSAdrian Hunter 4531b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 4532b62d18abSAdrian Hunter self.setMinimumWidth(300) 4533b62d18abSAdrian Hunter 4534b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 4535b62d18abSAdrian Hunter 4536b62d18abSAdrian Hunter text = "<pre>" 4537b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 4538b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 4539b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 4540b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 4541b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 4542b62d18abSAdrian Hunter else: 4543b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 4544b62d18abSAdrian Hunter text += "</pre>" 4545b62d18abSAdrian Hunter 4546b62d18abSAdrian Hunter self.text = QTextBrowser() 4547b62d18abSAdrian Hunter self.text.setHtml(text) 4548b62d18abSAdrian Hunter self.text.setReadOnly(True) 4549b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 4550b62d18abSAdrian Hunter 4551b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 4552b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 4553b62d18abSAdrian Hunter 455426688729SAdrian Hunter self.setLayout(self.vbox) 4555b62d18abSAdrian Hunter 455682f68e28SAdrian Hunter# Font resize 455782f68e28SAdrian Hunter 455882f68e28SAdrian Hunterdef ResizeFont(widget, diff): 455982f68e28SAdrian Hunter font = widget.font() 456082f68e28SAdrian Hunter sz = font.pointSize() 456182f68e28SAdrian Hunter font.setPointSize(sz + diff) 456282f68e28SAdrian Hunter widget.setFont(font) 456382f68e28SAdrian Hunter 456482f68e28SAdrian Hunterdef ShrinkFont(widget): 456582f68e28SAdrian Hunter ResizeFont(widget, -1) 456682f68e28SAdrian Hunter 456782f68e28SAdrian Hunterdef EnlargeFont(widget): 456882f68e28SAdrian Hunter ResizeFont(widget, 1) 456982f68e28SAdrian Hunter 45701beb5c7bSAdrian Hunter# Unique name for sub-windows 45711beb5c7bSAdrian Hunter 45721beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 45731beb5c7bSAdrian Hunter if nr > 1: 45741beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 45751beb5c7bSAdrian Hunter return name 45761beb5c7bSAdrian Hunter 45771beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 45781beb5c7bSAdrian Hunter nr = 1 45791beb5c7bSAdrian Hunter while True: 45801beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 45811beb5c7bSAdrian Hunter ok = True 45821beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 45831beb5c7bSAdrian Hunter if sub_window.name == unique_name: 45841beb5c7bSAdrian Hunter ok = False 45851beb5c7bSAdrian Hunter break 45861beb5c7bSAdrian Hunter if ok: 45871beb5c7bSAdrian Hunter return unique_name 45881beb5c7bSAdrian Hunter nr += 1 45891beb5c7bSAdrian Hunter 45901beb5c7bSAdrian Hunter# Add a sub-window 45911beb5c7bSAdrian Hunter 45921beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 45931beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 45941beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 45951beb5c7bSAdrian Hunter sub_window.resize(800, 600) 45961beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 45971beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 45981beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 45991beb5c7bSAdrian Hunter sub_window.name = unique_name 46001beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 46011beb5c7bSAdrian Hunter sub_window.show() 46021beb5c7bSAdrian Hunter 4603031c2a00SAdrian Hunter# Main window 4604031c2a00SAdrian Hunter 4605031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 4606031c2a00SAdrian Hunter 4607031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 4608031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 4609031c2a00SAdrian Hunter 4610031c2a00SAdrian Hunter self.glb = glb 4611031c2a00SAdrian Hunter 46121beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 4613031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 4614031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 4615031c2a00SAdrian Hunter 46161beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 46171beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 46181beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 4619031c2a00SAdrian Hunter 46201beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 4621031c2a00SAdrian Hunter 46221beb5c7bSAdrian Hunter menu = self.menuBar() 4623031c2a00SAdrian Hunter 46241beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 46251beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 46261beb5c7bSAdrian Hunter 4627ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 462896c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 462996c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 4630ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 46318392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 463282f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 463382f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 4634ebd70c7dSAdrian Hunter 46351beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 4636655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 46371beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 46381beb5c7bSAdrian Hunter 4639ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 4640ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 4641ae8b887cSAdrian Hunter 464276099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 464376099f98SAdrian Hunter 4644cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 4645cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 4646cd358012SAdrian Hunter 4647b3700f21SAdrian Hunter if IsSelectable(glb.db, "context_switches"): 4648b3700f21SAdrian Hunter charts_menu = menu.addMenu("&Charts") 4649b3700f21SAdrian Hunter charts_menu.addAction(CreateAction("&Time chart by CPU", "Create a new window displaying time charts by CPU", self.TimeChartByCPU, self)) 4650b3700f21SAdrian Hunter 46518392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 46528392b74bSAdrian Hunter 46531beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 46541beb5c7bSAdrian Hunter 465565b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 465665b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 4657b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 465865b24292SAdrian Hunter 46594b208453SAdrian Hunter def Try(self, fn): 46604b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 46614b208453SAdrian Hunter if win: 46624b208453SAdrian Hunter try: 46634b208453SAdrian Hunter fn(win.view) 46644b208453SAdrian Hunter except: 46654b208453SAdrian Hunter pass 46664b208453SAdrian Hunter 466796c43b9aSAdrian Hunter def CopyToClipboard(self): 466896c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 466996c43b9aSAdrian Hunter 467096c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 467196c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 467296c43b9aSAdrian Hunter 4673ebd70c7dSAdrian Hunter def Find(self): 4674ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 4675ebd70c7dSAdrian Hunter if win: 4676ebd70c7dSAdrian Hunter try: 4677ebd70c7dSAdrian Hunter win.find_bar.Activate() 4678ebd70c7dSAdrian Hunter except: 4679ebd70c7dSAdrian Hunter pass 4680ebd70c7dSAdrian Hunter 46818392b74bSAdrian Hunter def FetchMoreRecords(self): 46828392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 46838392b74bSAdrian Hunter if win: 46848392b74bSAdrian Hunter try: 46858392b74bSAdrian Hunter win.fetch_bar.Activate() 46868392b74bSAdrian Hunter except: 46878392b74bSAdrian Hunter pass 46888392b74bSAdrian Hunter 468982f68e28SAdrian Hunter def ShrinkFont(self): 46904b208453SAdrian Hunter self.Try(ShrinkFont) 469182f68e28SAdrian Hunter 469282f68e28SAdrian Hunter def EnlargeFont(self): 46934b208453SAdrian Hunter self.Try(EnlargeFont) 469482f68e28SAdrian Hunter 469576099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 469676099f98SAdrian Hunter branches_events = 0 469776099f98SAdrian Hunter for event in events: 469876099f98SAdrian Hunter event = event.split(":")[0] 469976099f98SAdrian Hunter if event == "branches": 470076099f98SAdrian Hunter branches_events += 1 470176099f98SAdrian Hunter dbid = 0 470276099f98SAdrian Hunter for event in events: 470376099f98SAdrian Hunter dbid += 1 470476099f98SAdrian Hunter event = event.split(":")[0] 470576099f98SAdrian Hunter if event == "branches": 470676099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 4707df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 4708210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 4709df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 471076099f98SAdrian Hunter 4711b3700f21SAdrian Hunter def TimeChartByCPU(self): 4712b3700f21SAdrian Hunter TimeChartByCPUWindow(self.glb, self) 4713b3700f21SAdrian Hunter 47148392b74bSAdrian Hunter def TableMenu(self, tables, menu): 47158392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 47168392b74bSAdrian Hunter for table in tables: 4717df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 47188392b74bSAdrian Hunter 47191beb5c7bSAdrian Hunter def NewCallGraph(self): 47201beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 4721031c2a00SAdrian Hunter 4722ae8b887cSAdrian Hunter def NewCallTree(self): 4723ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 4724ae8b887cSAdrian Hunter 4725cd358012SAdrian Hunter def NewTopCalls(self): 4726cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 4727cd358012SAdrian Hunter ret = dialog.exec_() 4728cd358012SAdrian Hunter if ret: 4729cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 4730cd358012SAdrian Hunter 473176099f98SAdrian Hunter def NewBranchView(self, event_id): 4732947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 473376099f98SAdrian Hunter 4734210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 4735210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 4736210cf1f9SAdrian Hunter ret = dialog.exec_() 4737210cf1f9SAdrian Hunter if ret: 4738947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 4739210cf1f9SAdrian Hunter 47408392b74bSAdrian Hunter def NewTableView(self, table_name): 47418392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 47428392b74bSAdrian Hunter 474365b24292SAdrian Hunter def Help(self): 474465b24292SAdrian Hunter HelpWindow(self.glb, self) 474565b24292SAdrian Hunter 4746b62d18abSAdrian Hunter def About(self): 4747b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 4748b62d18abSAdrian Hunter dialog.exec_() 4749b62d18abSAdrian Hunter 475076099f98SAdrian Hunter# XED Disassembler 475176099f98SAdrian Hunter 475276099f98SAdrian Hunterclass xed_state_t(Structure): 475376099f98SAdrian Hunter 475476099f98SAdrian Hunter _fields_ = [ 475576099f98SAdrian Hunter ("mode", c_int), 475676099f98SAdrian Hunter ("width", c_int) 475776099f98SAdrian Hunter ] 475876099f98SAdrian Hunter 475976099f98SAdrian Hunterclass XEDInstruction(): 476076099f98SAdrian Hunter 476176099f98SAdrian Hunter def __init__(self, libxed): 476276099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 476376099f98SAdrian Hunter xedd_t = c_byte * 512 476476099f98SAdrian Hunter self.xedd = xedd_t() 476576099f98SAdrian Hunter self.xedp = addressof(self.xedd) 476676099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 476776099f98SAdrian Hunter self.state = xed_state_t() 476876099f98SAdrian Hunter self.statep = addressof(self.state) 476976099f98SAdrian Hunter # Buffer for disassembled instruction text 477076099f98SAdrian Hunter self.buffer = create_string_buffer(256) 477176099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 477276099f98SAdrian Hunter 477376099f98SAdrian Hunterclass LibXED(): 477476099f98SAdrian Hunter 477576099f98SAdrian Hunter def __init__(self): 47765ed4419dSAdrian Hunter try: 477776099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 47785ed4419dSAdrian Hunter except: 47795ed4419dSAdrian Hunter self.libxed = None 47805ed4419dSAdrian Hunter if not self.libxed: 47815ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 478276099f98SAdrian Hunter 478376099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 478476099f98SAdrian Hunter self.xed_tables_init.restype = None 478576099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 478676099f98SAdrian Hunter 478776099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 478876099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 478976099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 479076099f98SAdrian Hunter 479176099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 479276099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 479376099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 479476099f98SAdrian Hunter 479576099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 479676099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 479776099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 479876099f98SAdrian Hunter 479976099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 480076099f98SAdrian Hunter self.xed_decode.restype = c_int 480176099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 480276099f98SAdrian Hunter 480376099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 480476099f98SAdrian Hunter self.xed_format_context.restype = c_uint 480576099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 480676099f98SAdrian Hunter 480776099f98SAdrian Hunter self.xed_tables_init() 480876099f98SAdrian Hunter 480976099f98SAdrian Hunter def Instruction(self): 481076099f98SAdrian Hunter return XEDInstruction(self) 481176099f98SAdrian Hunter 481276099f98SAdrian Hunter def SetMode(self, inst, mode): 481376099f98SAdrian Hunter if mode: 481476099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 481576099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 481676099f98SAdrian Hunter else: 481776099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 481876099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 481976099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 482076099f98SAdrian Hunter 482176099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 482276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 482376099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 482476099f98SAdrian Hunter if err: 482576099f98SAdrian Hunter return 0, "" 482676099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 482776099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 482876099f98SAdrian Hunter if not ok: 482976099f98SAdrian Hunter return 0, "" 4830606bd60aSAdrian Hunter if sys.version_info[0] == 2: 4831606bd60aSAdrian Hunter result = inst.buffer.value 4832606bd60aSAdrian Hunter else: 4833606bd60aSAdrian Hunter result = inst.buffer.value.decode() 483476099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 483576099f98SAdrian Hunter # For now, assume the length is in byte 166 4836606bd60aSAdrian Hunter return inst.xedd[166], result 483776099f98SAdrian Hunter 483876099f98SAdrian Hunterdef TryOpen(file_name): 483976099f98SAdrian Hunter try: 484076099f98SAdrian Hunter return open(file_name, "rb") 484176099f98SAdrian Hunter except: 484276099f98SAdrian Hunter return None 484376099f98SAdrian Hunter 484476099f98SAdrian Hunterdef Is64Bit(f): 484576099f98SAdrian Hunter result = sizeof(c_void_p) 484676099f98SAdrian Hunter # ELF support only 484776099f98SAdrian Hunter pos = f.tell() 484876099f98SAdrian Hunter f.seek(0) 484976099f98SAdrian Hunter header = f.read(7) 485076099f98SAdrian Hunter f.seek(pos) 485176099f98SAdrian Hunter magic = header[0:4] 4852606bd60aSAdrian Hunter if sys.version_info[0] == 2: 485376099f98SAdrian Hunter eclass = ord(header[4]) 485476099f98SAdrian Hunter encoding = ord(header[5]) 485576099f98SAdrian Hunter version = ord(header[6]) 4856606bd60aSAdrian Hunter else: 4857606bd60aSAdrian Hunter eclass = header[4] 4858606bd60aSAdrian Hunter encoding = header[5] 4859606bd60aSAdrian Hunter version = header[6] 486076099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 486176099f98SAdrian Hunter result = True if eclass == 2 else False 486276099f98SAdrian Hunter return result 486376099f98SAdrian Hunter 4864031c2a00SAdrian Hunter# Global data 4865031c2a00SAdrian Hunter 4866031c2a00SAdrian Hunterclass Glb(): 4867031c2a00SAdrian Hunter 4868031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 4869031c2a00SAdrian Hunter self.dbref = dbref 4870031c2a00SAdrian Hunter self.db = db 4871031c2a00SAdrian Hunter self.dbname = dbname 487276099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 487376099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 487476099f98SAdrian Hunter if self.buildid_dir: 487576099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 487676099f98SAdrian Hunter else: 487776099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 4878031c2a00SAdrian Hunter self.app = None 4879031c2a00SAdrian Hunter self.mainwindow = None 48808392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 488176099f98SAdrian Hunter try: 488276099f98SAdrian Hunter self.disassembler = LibXED() 488376099f98SAdrian Hunter self.have_disassembler = True 488476099f98SAdrian Hunter except: 488576099f98SAdrian Hunter self.have_disassembler = False 48869a9dae36SAdrian Hunter self.host_machine_id = 0 48879a9dae36SAdrian Hunter self.host_start_time = 0 48889a9dae36SAdrian Hunter self.host_finish_time = 0 488976099f98SAdrian Hunter 489076099f98SAdrian Hunter def FileFromBuildId(self, build_id): 489176099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 489276099f98SAdrian Hunter return TryOpen(file_name) 489376099f98SAdrian Hunter 489476099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 489576099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 489676099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 489776099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 489876099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 489976099f98SAdrian Hunter if f: 490076099f98SAdrian Hunter return f 490176099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 490276099f98SAdrian Hunter f = TryOpen(long_name) 490376099f98SAdrian Hunter if f: 490476099f98SAdrian Hunter return f 490576099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 490676099f98SAdrian Hunter if f: 490776099f98SAdrian Hunter return f 490876099f98SAdrian Hunter return None 49098392b74bSAdrian Hunter 49108392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 49118392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 49128392b74bSAdrian Hunter 49138392b74bSAdrian Hunter # Shutdown any background processes or threads 49148392b74bSAdrian Hunter def ShutdownInstances(self): 49158392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 49168392b74bSAdrian Hunter try: 49178392b74bSAdrian Hunter x.Shutdown() 49188392b74bSAdrian Hunter except: 49198392b74bSAdrian Hunter pass 4920031c2a00SAdrian Hunter 49219a9dae36SAdrian Hunter def GetHostMachineId(self): 49229a9dae36SAdrian Hunter query = QSqlQuery(self.db) 49239a9dae36SAdrian Hunter QueryExec(query, "SELECT id FROM machines WHERE pid = -1") 49249a9dae36SAdrian Hunter if query.next(): 49259a9dae36SAdrian Hunter self.host_machine_id = query.value(0) 49269a9dae36SAdrian Hunter else: 49279a9dae36SAdrian Hunter self.host_machine_id = 0 49289a9dae36SAdrian Hunter return self.host_machine_id 49299a9dae36SAdrian Hunter 49309a9dae36SAdrian Hunter def HostMachineId(self): 49319a9dae36SAdrian Hunter if self.host_machine_id: 49329a9dae36SAdrian Hunter return self.host_machine_id 49339a9dae36SAdrian Hunter return self.GetHostMachineId() 49349a9dae36SAdrian Hunter 49359a9dae36SAdrian Hunter def SelectValue(self, sql): 49369a9dae36SAdrian Hunter query = QSqlQuery(self.db) 49379a9dae36SAdrian Hunter try: 49389a9dae36SAdrian Hunter QueryExec(query, sql) 49399a9dae36SAdrian Hunter except: 49409a9dae36SAdrian Hunter return None 49419a9dae36SAdrian Hunter if query.next(): 49429a9dae36SAdrian Hunter return Decimal(query.value(0)) 49439a9dae36SAdrian Hunter return None 49449a9dae36SAdrian Hunter 49459a9dae36SAdrian Hunter def SwitchesMinTime(self, machine_id): 49469a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 49479a9dae36SAdrian Hunter " FROM context_switches" 49489a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 49499a9dae36SAdrian Hunter " ORDER BY id LIMIT 1") 49509a9dae36SAdrian Hunter 49519a9dae36SAdrian Hunter def SwitchesMaxTime(self, machine_id): 49529a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 49539a9dae36SAdrian Hunter " FROM context_switches" 49549a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 49559a9dae36SAdrian Hunter " ORDER BY id DESC LIMIT 1") 49569a9dae36SAdrian Hunter 49579a9dae36SAdrian Hunter def SamplesMinTime(self, machine_id): 49589a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 49599a9dae36SAdrian Hunter " FROM samples" 49609a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 49619a9dae36SAdrian Hunter " ORDER BY id LIMIT 1") 49629a9dae36SAdrian Hunter 49639a9dae36SAdrian Hunter def SamplesMaxTime(self, machine_id): 49649a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 49659a9dae36SAdrian Hunter " FROM samples" 49669a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 49679a9dae36SAdrian Hunter " ORDER BY id DESC LIMIT 1") 49689a9dae36SAdrian Hunter 49699a9dae36SAdrian Hunter def CallsMinTime(self, machine_id): 49709a9dae36SAdrian Hunter return self.SelectValue("SELECT calls.call_time" 49719a9dae36SAdrian Hunter " FROM calls" 49729a9dae36SAdrian Hunter " INNER JOIN threads ON threads.thread_id = calls.thread_id" 49739a9dae36SAdrian Hunter " WHERE calls.call_time != 0 AND threads.machine_id = " + str(machine_id) + 49749a9dae36SAdrian Hunter " ORDER BY calls.id LIMIT 1") 49759a9dae36SAdrian Hunter 49769a9dae36SAdrian Hunter def CallsMaxTime(self, machine_id): 49779a9dae36SAdrian Hunter return self.SelectValue("SELECT calls.return_time" 49789a9dae36SAdrian Hunter " FROM calls" 49799a9dae36SAdrian Hunter " INNER JOIN threads ON threads.thread_id = calls.thread_id" 49809a9dae36SAdrian Hunter " WHERE calls.return_time != 0 AND threads.machine_id = " + str(machine_id) + 49819a9dae36SAdrian Hunter " ORDER BY calls.return_time DESC LIMIT 1") 49829a9dae36SAdrian Hunter 49839a9dae36SAdrian Hunter def GetStartTime(self, machine_id): 49849a9dae36SAdrian Hunter t0 = self.SwitchesMinTime(machine_id) 49859a9dae36SAdrian Hunter t1 = self.SamplesMinTime(machine_id) 49869a9dae36SAdrian Hunter t2 = self.CallsMinTime(machine_id) 49879a9dae36SAdrian Hunter if t0 is None or (not(t1 is None) and t1 < t0): 49889a9dae36SAdrian Hunter t0 = t1 49899a9dae36SAdrian Hunter if t0 is None or (not(t2 is None) and t2 < t0): 49909a9dae36SAdrian Hunter t0 = t2 49919a9dae36SAdrian Hunter return t0 49929a9dae36SAdrian Hunter 49939a9dae36SAdrian Hunter def GetFinishTime(self, machine_id): 49949a9dae36SAdrian Hunter t0 = self.SwitchesMaxTime(machine_id) 49959a9dae36SAdrian Hunter t1 = self.SamplesMaxTime(machine_id) 49969a9dae36SAdrian Hunter t2 = self.CallsMaxTime(machine_id) 49979a9dae36SAdrian Hunter if t0 is None or (not(t1 is None) and t1 > t0): 49989a9dae36SAdrian Hunter t0 = t1 49999a9dae36SAdrian Hunter if t0 is None or (not(t2 is None) and t2 > t0): 50009a9dae36SAdrian Hunter t0 = t2 50019a9dae36SAdrian Hunter return t0 50029a9dae36SAdrian Hunter 50039a9dae36SAdrian Hunter def HostStartTime(self): 50049a9dae36SAdrian Hunter if self.host_start_time: 50059a9dae36SAdrian Hunter return self.host_start_time 50069a9dae36SAdrian Hunter self.host_start_time = self.GetStartTime(self.HostMachineId()) 50079a9dae36SAdrian Hunter return self.host_start_time 50089a9dae36SAdrian Hunter 50099a9dae36SAdrian Hunter def HostFinishTime(self): 50109a9dae36SAdrian Hunter if self.host_finish_time: 50119a9dae36SAdrian Hunter return self.host_finish_time 50129a9dae36SAdrian Hunter self.host_finish_time = self.GetFinishTime(self.HostMachineId()) 50139a9dae36SAdrian Hunter return self.host_finish_time 50149a9dae36SAdrian Hunter 50159a9dae36SAdrian Hunter def StartTime(self, machine_id): 50169a9dae36SAdrian Hunter if machine_id == self.HostMachineId(): 50179a9dae36SAdrian Hunter return self.HostStartTime() 50189a9dae36SAdrian Hunter return self.GetStartTime(machine_id) 50199a9dae36SAdrian Hunter 50209a9dae36SAdrian Hunter def FinishTime(self, machine_id): 50219a9dae36SAdrian Hunter if machine_id == self.HostMachineId(): 50229a9dae36SAdrian Hunter return self.HostFinishTime() 50239a9dae36SAdrian Hunter return self.GetFinishTime(machine_id) 50249a9dae36SAdrian Hunter 5025031c2a00SAdrian Hunter# Database reference 5026031c2a00SAdrian Hunter 5027031c2a00SAdrian Hunterclass DBRef(): 5028031c2a00SAdrian Hunter 5029031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 5030031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 5031031c2a00SAdrian Hunter self.dbname = dbname 5032af833988SAdrian Hunter self.TRUE = "TRUE" 5033af833988SAdrian Hunter self.FALSE = "FALSE" 5034af833988SAdrian Hunter # SQLite prior to version 3.23 does not support TRUE and FALSE 5035af833988SAdrian Hunter if self.is_sqlite3: 5036af833988SAdrian Hunter self.TRUE = "1" 5037af833988SAdrian Hunter self.FALSE = "0" 5038031c2a00SAdrian Hunter 5039031c2a00SAdrian Hunter def Open(self, connection_name): 5040031c2a00SAdrian Hunter dbname = self.dbname 5041031c2a00SAdrian Hunter if self.is_sqlite3: 5042031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 5043031c2a00SAdrian Hunter else: 5044031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 5045031c2a00SAdrian Hunter opts = dbname.split() 5046031c2a00SAdrian Hunter for opt in opts: 5047031c2a00SAdrian Hunter if "=" in opt: 5048031c2a00SAdrian Hunter opt = opt.split("=") 5049031c2a00SAdrian Hunter if opt[0] == "hostname": 5050031c2a00SAdrian Hunter db.setHostName(opt[1]) 5051031c2a00SAdrian Hunter elif opt[0] == "port": 5052031c2a00SAdrian Hunter db.setPort(int(opt[1])) 5053031c2a00SAdrian Hunter elif opt[0] == "username": 5054031c2a00SAdrian Hunter db.setUserName(opt[1]) 5055031c2a00SAdrian Hunter elif opt[0] == "password": 5056031c2a00SAdrian Hunter db.setPassword(opt[1]) 5057031c2a00SAdrian Hunter elif opt[0] == "dbname": 5058031c2a00SAdrian Hunter dbname = opt[1] 5059031c2a00SAdrian Hunter else: 5060031c2a00SAdrian Hunter dbname = opt 5061031c2a00SAdrian Hunter 5062031c2a00SAdrian Hunter db.setDatabaseName(dbname) 5063031c2a00SAdrian Hunter if not db.open(): 5064031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 5065031c2a00SAdrian Hunter return db, dbname 5066031c2a00SAdrian Hunter 5067031c2a00SAdrian Hunter# Main 5068031c2a00SAdrian Hunter 5069031c2a00SAdrian Hunterdef Main(): 50701ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 50711ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 50721ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 5073df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 50741ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 50751ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 50761ed7f47fSAdrian Hunter args = ap.parse_args() 5077031c2a00SAdrian Hunter 50781ed7f47fSAdrian Hunter if args.help_only: 507965b24292SAdrian Hunter app = QApplication(sys.argv) 508065b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 508165b24292SAdrian Hunter mainwindow.show() 508265b24292SAdrian Hunter err = app.exec_() 508365b24292SAdrian Hunter sys.exit(err) 5084031c2a00SAdrian Hunter 50851ed7f47fSAdrian Hunter dbname = args.dbname 50861ed7f47fSAdrian Hunter if dbname is None: 50871ed7f47fSAdrian Hunter ap.print_usage() 50881ed7f47fSAdrian Hunter print("Too few arguments") 50891ed7f47fSAdrian Hunter sys.exit(1) 50901ed7f47fSAdrian Hunter 5091031c2a00SAdrian Hunter is_sqlite3 = False 5092031c2a00SAdrian Hunter try: 5093beda0e72STony Jones f = open(dbname, "rb") 5094beda0e72STony Jones if f.read(15) == b'SQLite format 3': 5095031c2a00SAdrian Hunter is_sqlite3 = True 5096031c2a00SAdrian Hunter f.close() 5097031c2a00SAdrian Hunter except: 5098031c2a00SAdrian Hunter pass 5099031c2a00SAdrian Hunter 5100031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 5101031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 5102031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 5103031c2a00SAdrian Hunter app = QApplication(sys.argv) 5104031c2a00SAdrian Hunter glb.app = app 5105031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 5106031c2a00SAdrian Hunter glb.mainwindow = mainwindow 5107031c2a00SAdrian Hunter mainwindow.show() 5108031c2a00SAdrian Hunter err = app.exec_() 51098392b74bSAdrian Hunter glb.ShutdownInstances() 5110031c2a00SAdrian Hunter db.close() 5111031c2a00SAdrian Hunter sys.exit(err) 5112031c2a00SAdrian Hunter 5113031c2a00SAdrian Hunterif __name__ == "__main__": 5114031c2a00SAdrian Hunter Main() 5115