1c6aba1bfSAdrian Hunter#!/usr/bin/env python 2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0 3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database 4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation. 5031c2a00SAdrian Hunter 6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the 7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those 8031c2a00SAdrian Hunter# scripts for details. 9031c2a00SAdrian Hunter# 10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a 11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this: 12031c2a00SAdrian Hunter# 13031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py pt_example 14031c2a00SAdrian Hunter# 15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases 16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g. 17031c2a00SAdrian Hunter# 18031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" 19031c2a00SAdrian Hunter# 20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive 21031c2a00SAdrian Hunter# call-graph. Expanding a couple of levels of the tree and adjusting column 22031c2a00SAdrian Hunter# widths to suit will display something like: 23031c2a00SAdrian Hunter# 24031c2a00SAdrian Hunter# Call Graph: pt_example 25031c2a00SAdrian Hunter# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 26031c2a00SAdrian Hunter# v- ls 27031c2a00SAdrian Hunter# v- 2638:2638 28031c2a00SAdrian Hunter# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 29031c2a00SAdrian Hunter# |- unknown unknown 1 13198 0.1 1 0.0 30031c2a00SAdrian Hunter# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 31031c2a00SAdrian Hunter# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 32031c2a00SAdrian Hunter# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 33031c2a00SAdrian Hunter# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 34031c2a00SAdrian Hunter# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 35031c2a00SAdrian Hunter# >- __libc_csu_init ls 1 10354 0.1 10 0.0 36031c2a00SAdrian Hunter# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 37031c2a00SAdrian Hunter# v- main ls 1 8182043 99.6 180254 99.9 38031c2a00SAdrian Hunter# 39031c2a00SAdrian Hunter# Points to note: 40031c2a00SAdrian Hunter# The top level is a command name (comm) 41031c2a00SAdrian Hunter# The next level is a thread (pid:tid) 42031c2a00SAdrian Hunter# Subsequent levels are functions 43031c2a00SAdrian Hunter# 'Count' is the number of calls 44031c2a00SAdrian Hunter# 'Time' is the elapsed time until the function returns 45031c2a00SAdrian Hunter# Percentages are relative to the level above 46031c2a00SAdrian Hunter# 'Branch Count' is the total number of branches for that function and all 47031c2a00SAdrian Hunter# functions that it calls 48031c2a00SAdrian Hunter 4976099f98SAdrian Hunter# There is also a "All branches" report, which displays branches and 5076099f98SAdrian Hunter# possibly disassembly. However, presently, the only supported disassembler is 5176099f98SAdrian Hunter# Intel XED, and additionally the object code must be present in perf build ID 5276099f98SAdrian Hunter# cache. To use Intel XED, libxed.so must be present. To build and install 5376099f98SAdrian Hunter# libxed.so: 5476099f98SAdrian Hunter# git clone https://github.com/intelxed/mbuild.git mbuild 5576099f98SAdrian Hunter# git clone https://github.com/intelxed/xed 5676099f98SAdrian Hunter# cd xed 5776099f98SAdrian Hunter# ./mfile.py --share 5876099f98SAdrian Hunter# sudo ./mfile.py --prefix=/usr/local install 5976099f98SAdrian Hunter# sudo ldconfig 6076099f98SAdrian Hunter# 6176099f98SAdrian Hunter# Example report: 6276099f98SAdrian Hunter# 6376099f98SAdrian Hunter# Time CPU Command PID TID Branch Type In Tx Branch 6476099f98SAdrian Hunter# 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) 6576099f98SAdrian Hunter# 7fab593ea260 48 89 e7 mov %rsp, %rdi 6676099f98SAdrian Hunter# 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 6776099f98SAdrian Hunter# 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) 6876099f98SAdrian Hunter# 7fab593ea260 48 89 e7 mov %rsp, %rdi 6976099f98SAdrian Hunter# 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930 7076099f98SAdrian Hunter# 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so) 7176099f98SAdrian Hunter# 7fab593ea930 55 pushq %rbp 7276099f98SAdrian Hunter# 7fab593ea931 48 89 e5 mov %rsp, %rbp 7376099f98SAdrian Hunter# 7fab593ea934 41 57 pushq %r15 7476099f98SAdrian Hunter# 7fab593ea936 41 56 pushq %r14 7576099f98SAdrian Hunter# 7fab593ea938 41 55 pushq %r13 7676099f98SAdrian Hunter# 7fab593ea93a 41 54 pushq %r12 7776099f98SAdrian Hunter# 7fab593ea93c 53 pushq %rbx 7876099f98SAdrian Hunter# 7fab593ea93d 48 89 fb mov %rdi, %rbx 7976099f98SAdrian Hunter# 7fab593ea940 48 83 ec 68 sub $0x68, %rsp 8076099f98SAdrian Hunter# 7fab593ea944 0f 31 rdtsc 8176099f98SAdrian Hunter# 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx 8276099f98SAdrian Hunter# 7fab593ea94a 89 c0 mov %eax, %eax 8376099f98SAdrian Hunter# 7fab593ea94c 48 09 c2 or %rax, %rdx 8476099f98SAdrian Hunter# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax 8576099f98SAdrian Hunter# 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 8676099f98SAdrian Hunter# 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so) 8776099f98SAdrian Hunter# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax 8876099f98SAdrian Hunter# 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) 8976099f98SAdrian Hunter# 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) 9076099f98SAdrian Hunter 91beda0e72STony Jonesfrom __future__ import print_function 92beda0e72STony Jones 93031c2a00SAdrian Hunterimport sys 941ed7f47fSAdrian Hunterimport argparse 951beb5c7bSAdrian Hunterimport weakref 961beb5c7bSAdrian Hunterimport threading 97ebd70c7dSAdrian Hunterimport string 98beda0e72STony Jonestry: 99beda0e72STony Jones # Python2 100beda0e72STony Jones import cPickle as pickle 101beda0e72STony Jones # size of pickled integer big enough for record size 102beda0e72STony Jones glb_nsz = 8 103beda0e72STony Jonesexcept ImportError: 104beda0e72STony Jones import pickle 105beda0e72STony Jones glb_nsz = 16 1068392b74bSAdrian Hunterimport re 1078392b74bSAdrian Hunterimport os 108df8ea22aSAdrian Hunter 109df8ea22aSAdrian Hunterpyside_version_1 = True 110df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv: 111df8ea22aSAdrian Hunter try: 112df8ea22aSAdrian Hunter from PySide2.QtCore import * 113df8ea22aSAdrian Hunter from PySide2.QtGui import * 114df8ea22aSAdrian Hunter from PySide2.QtSql import * 115df8ea22aSAdrian Hunter from PySide2.QtWidgets import * 116df8ea22aSAdrian Hunter pyside_version_1 = False 117df8ea22aSAdrian Hunter except: 118df8ea22aSAdrian Hunter pass 119df8ea22aSAdrian Hunter 120df8ea22aSAdrian Hunterif pyside_version_1: 121031c2a00SAdrian Hunter from PySide.QtCore import * 122031c2a00SAdrian Hunter from PySide.QtGui import * 123031c2a00SAdrian Hunter from PySide.QtSql import * 124df8ea22aSAdrian Hunter 125031c2a00SAdrian Hunterfrom decimal import * 1268392b74bSAdrian Hunterfrom ctypes import * 1278392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event 128031c2a00SAdrian Hunter 129beda0e72STony Jones# xrange is range in Python3 130beda0e72STony Jonestry: 131beda0e72STony Jones xrange 132beda0e72STony Jonesexcept NameError: 133beda0e72STony Jones xrange = range 134beda0e72STony Jones 135beda0e72STony Jonesdef printerr(*args, **keyword_args): 136beda0e72STony Jones print(*args, file=sys.stderr, **keyword_args) 137beda0e72STony Jones 138031c2a00SAdrian Hunter# Data formatting helpers 139031c2a00SAdrian Hunter 14076099f98SAdrian Hunterdef tohex(ip): 14176099f98SAdrian Hunter if ip < 0: 14276099f98SAdrian Hunter ip += 1 << 64 14376099f98SAdrian Hunter return "%x" % ip 14476099f98SAdrian Hunter 14576099f98SAdrian Hunterdef offstr(offset): 14676099f98SAdrian Hunter if offset: 14776099f98SAdrian Hunter return "+0x%x" % offset 14876099f98SAdrian Hunter return "" 14976099f98SAdrian Hunter 150031c2a00SAdrian Hunterdef dsoname(name): 151031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 152031c2a00SAdrian Hunter return "[kernel]" 153031c2a00SAdrian Hunter return name 154031c2a00SAdrian Hunter 155210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0): 156210cf1f9SAdrian Hunter pos = s.find(sub) 157210cf1f9SAdrian Hunter if pos < 0: 158210cf1f9SAdrian Hunter return pos 159210cf1f9SAdrian Hunter if n <= 1: 160210cf1f9SAdrian Hunter return offs + pos 161210cf1f9SAdrian Hunter return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) 162210cf1f9SAdrian Hunter 163031c2a00SAdrian Hunter# Percent to one decimal place 164031c2a00SAdrian Hunter 165031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 166031c2a00SAdrian Hunter if not d: 167031c2a00SAdrian Hunter return "0.0" 168031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 169031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 170031c2a00SAdrian Hunter 171031c2a00SAdrian Hunter# Helper for queries that must not fail 172031c2a00SAdrian Hunter 173031c2a00SAdrian Hunterdef QueryExec(query, stmt): 174031c2a00SAdrian Hunter ret = query.exec_(stmt) 175031c2a00SAdrian Hunter if not ret: 176031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 177031c2a00SAdrian Hunter 178ebd70c7dSAdrian Hunter# Background thread 179ebd70c7dSAdrian Hunter 180ebd70c7dSAdrian Hunterclass Thread(QThread): 181ebd70c7dSAdrian Hunter 182ebd70c7dSAdrian Hunter done = Signal(object) 183ebd70c7dSAdrian Hunter 184ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 185ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 186ebd70c7dSAdrian Hunter self.task = task 187ebd70c7dSAdrian Hunter self.param = param 188ebd70c7dSAdrian Hunter 189ebd70c7dSAdrian Hunter def run(self): 190ebd70c7dSAdrian Hunter while True: 191ebd70c7dSAdrian Hunter if self.param is None: 192ebd70c7dSAdrian Hunter done, result = self.task() 193ebd70c7dSAdrian Hunter else: 194ebd70c7dSAdrian Hunter done, result = self.task(self.param) 195ebd70c7dSAdrian Hunter self.done.emit(result) 196ebd70c7dSAdrian Hunter if done: 197ebd70c7dSAdrian Hunter break 198ebd70c7dSAdrian Hunter 199031c2a00SAdrian Hunter# Tree data model 200031c2a00SAdrian Hunter 201031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 202031c2a00SAdrian Hunter 2034a0979d4SAdrian Hunter def __init__(self, glb, params, parent=None): 204031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 205a448ba23SAdrian Hunter self.glb = glb 2064a0979d4SAdrian Hunter self.params = params 207a448ba23SAdrian Hunter self.root = self.GetRoot() 208031c2a00SAdrian Hunter self.last_row_read = 0 209031c2a00SAdrian Hunter 210031c2a00SAdrian Hunter def Item(self, parent): 211031c2a00SAdrian Hunter if parent.isValid(): 212031c2a00SAdrian Hunter return parent.internalPointer() 213031c2a00SAdrian Hunter else: 214031c2a00SAdrian Hunter return self.root 215031c2a00SAdrian Hunter 216031c2a00SAdrian Hunter def rowCount(self, parent): 217031c2a00SAdrian Hunter result = self.Item(parent).childCount() 218031c2a00SAdrian Hunter if result < 0: 219031c2a00SAdrian Hunter result = 0 220031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 221031c2a00SAdrian Hunter return result 222031c2a00SAdrian Hunter 223031c2a00SAdrian Hunter def hasChildren(self, parent): 224031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 225031c2a00SAdrian Hunter 226031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 227031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 228031c2a00SAdrian Hunter return self.columnAlignment(section) 229031c2a00SAdrian Hunter if role != Qt.DisplayRole: 230031c2a00SAdrian Hunter return None 231031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 232031c2a00SAdrian Hunter return None 233031c2a00SAdrian Hunter return self.columnHeader(section) 234031c2a00SAdrian Hunter 235031c2a00SAdrian Hunter def parent(self, child): 236031c2a00SAdrian Hunter child_item = child.internalPointer() 237031c2a00SAdrian Hunter if child_item is self.root: 238031c2a00SAdrian Hunter return QModelIndex() 239031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 240031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 241031c2a00SAdrian Hunter 242031c2a00SAdrian Hunter def index(self, row, column, parent): 243031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 244031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 245031c2a00SAdrian Hunter 246031c2a00SAdrian Hunter def DisplayData(self, item, index): 247031c2a00SAdrian Hunter return item.getData(index.column()) 248031c2a00SAdrian Hunter 2498392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2508392b74bSAdrian Hunter if row > self.last_row_read: 2518392b74bSAdrian Hunter self.last_row_read = row 2528392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2538392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2548392b74bSAdrian Hunter 2558392b74bSAdrian Hunter def columnAlignment(self, column): 2568392b74bSAdrian Hunter return Qt.AlignLeft 2578392b74bSAdrian Hunter 2588392b74bSAdrian Hunter def columnFont(self, column): 2598392b74bSAdrian Hunter return None 2608392b74bSAdrian Hunter 2618392b74bSAdrian Hunter def data(self, index, role): 2628392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2638392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2648392b74bSAdrian Hunter if role == Qt.FontRole: 2658392b74bSAdrian Hunter return self.columnFont(index.column()) 2668392b74bSAdrian Hunter if role != Qt.DisplayRole: 2678392b74bSAdrian Hunter return None 2688392b74bSAdrian Hunter item = index.internalPointer() 2698392b74bSAdrian Hunter return self.DisplayData(item, index) 2708392b74bSAdrian Hunter 2718392b74bSAdrian Hunter# Table data model 2728392b74bSAdrian Hunter 2738392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2748392b74bSAdrian Hunter 2758392b74bSAdrian Hunter def __init__(self, parent=None): 2768392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2778392b74bSAdrian Hunter self.child_count = 0 2788392b74bSAdrian Hunter self.child_items = [] 2798392b74bSAdrian Hunter self.last_row_read = 0 2808392b74bSAdrian Hunter 2818392b74bSAdrian Hunter def Item(self, parent): 2828392b74bSAdrian Hunter if parent.isValid(): 2838392b74bSAdrian Hunter return parent.internalPointer() 2848392b74bSAdrian Hunter else: 2858392b74bSAdrian Hunter return self 2868392b74bSAdrian Hunter 2878392b74bSAdrian Hunter def rowCount(self, parent): 2888392b74bSAdrian Hunter return self.child_count 2898392b74bSAdrian Hunter 2908392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2918392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2928392b74bSAdrian Hunter return self.columnAlignment(section) 2938392b74bSAdrian Hunter if role != Qt.DisplayRole: 2948392b74bSAdrian Hunter return None 2958392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2968392b74bSAdrian Hunter return None 2978392b74bSAdrian Hunter return self.columnHeader(section) 2988392b74bSAdrian Hunter 2998392b74bSAdrian Hunter def index(self, row, column, parent): 3008392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 3018392b74bSAdrian Hunter 3028392b74bSAdrian Hunter def DisplayData(self, item, index): 3038392b74bSAdrian Hunter return item.getData(index.column()) 3048392b74bSAdrian Hunter 3058392b74bSAdrian Hunter def FetchIfNeeded(self, row): 3068392b74bSAdrian Hunter if row > self.last_row_read: 3078392b74bSAdrian Hunter self.last_row_read = row 3088392b74bSAdrian Hunter if row + 10 >= self.child_count: 3098392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 3108392b74bSAdrian Hunter 311031c2a00SAdrian Hunter def columnAlignment(self, column): 312031c2a00SAdrian Hunter return Qt.AlignLeft 313031c2a00SAdrian Hunter 314031c2a00SAdrian Hunter def columnFont(self, column): 315031c2a00SAdrian Hunter return None 316031c2a00SAdrian Hunter 317031c2a00SAdrian Hunter def data(self, index, role): 318031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 319031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 320031c2a00SAdrian Hunter if role == Qt.FontRole: 321031c2a00SAdrian Hunter return self.columnFont(index.column()) 322031c2a00SAdrian Hunter if role != Qt.DisplayRole: 323031c2a00SAdrian Hunter return None 324031c2a00SAdrian Hunter item = index.internalPointer() 325031c2a00SAdrian Hunter return self.DisplayData(item, index) 326031c2a00SAdrian Hunter 3271beb5c7bSAdrian Hunter# Model cache 3281beb5c7bSAdrian Hunter 3291beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 3301beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 3311beb5c7bSAdrian Hunter 3321beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 3331beb5c7bSAdrian Hunter model_cache_lock.acquire() 3341beb5c7bSAdrian Hunter try: 3351beb5c7bSAdrian Hunter model = model_cache[model_name] 3361beb5c7bSAdrian Hunter except: 3371beb5c7bSAdrian Hunter model = None 3381beb5c7bSAdrian Hunter if model is None: 3391beb5c7bSAdrian Hunter model = create_fn() 3401beb5c7bSAdrian Hunter model_cache[model_name] = model 3411beb5c7bSAdrian Hunter model_cache_lock.release() 3421beb5c7bSAdrian Hunter return model 3431beb5c7bSAdrian Hunter 344181ea40aSAdrian Hunterdef LookupModel(model_name): 345181ea40aSAdrian Hunter model_cache_lock.acquire() 346181ea40aSAdrian Hunter try: 347181ea40aSAdrian Hunter model = model_cache[model_name] 348181ea40aSAdrian Hunter except: 349181ea40aSAdrian Hunter model = None 350181ea40aSAdrian Hunter model_cache_lock.release() 351181ea40aSAdrian Hunter return model 352181ea40aSAdrian Hunter 353ebd70c7dSAdrian Hunter# Find bar 354ebd70c7dSAdrian Hunter 355ebd70c7dSAdrian Hunterclass FindBar(): 356ebd70c7dSAdrian Hunter 357ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 358ebd70c7dSAdrian Hunter self.finder = finder 359ebd70c7dSAdrian Hunter self.context = [] 360ebd70c7dSAdrian Hunter self.last_value = None 361ebd70c7dSAdrian Hunter self.last_pattern = None 362ebd70c7dSAdrian Hunter 363ebd70c7dSAdrian Hunter label = QLabel("Find:") 364ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 365ebd70c7dSAdrian Hunter 366ebd70c7dSAdrian Hunter self.textbox = QComboBox() 367ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 368ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 369ebd70c7dSAdrian Hunter 370ebd70c7dSAdrian Hunter self.progress = QProgressBar() 371ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 372ebd70c7dSAdrian Hunter self.progress.hide() 373ebd70c7dSAdrian Hunter 374ebd70c7dSAdrian Hunter if is_reg_expr: 375ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 376ebd70c7dSAdrian Hunter else: 377ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 378ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 379ebd70c7dSAdrian Hunter 380ebd70c7dSAdrian Hunter self.next_button = QToolButton() 381ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 382ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 383ebd70c7dSAdrian Hunter 384ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 385ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 386ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 387ebd70c7dSAdrian Hunter 388ebd70c7dSAdrian Hunter self.close_button = QToolButton() 389ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 390ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 391ebd70c7dSAdrian Hunter 392ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 393ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 394ebd70c7dSAdrian Hunter 395ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 396ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 397ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 398ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 399ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 400ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 401ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 402ebd70c7dSAdrian Hunter 403ebd70c7dSAdrian Hunter self.bar = QWidget() 40426688729SAdrian Hunter self.bar.setLayout(self.hbox) 405ebd70c7dSAdrian Hunter self.bar.hide() 406ebd70c7dSAdrian Hunter 407ebd70c7dSAdrian Hunter def Widget(self): 408ebd70c7dSAdrian Hunter return self.bar 409ebd70c7dSAdrian Hunter 410ebd70c7dSAdrian Hunter def Activate(self): 411ebd70c7dSAdrian Hunter self.bar.show() 41280b3fb64SAdrian Hunter self.textbox.lineEdit().selectAll() 413ebd70c7dSAdrian Hunter self.textbox.setFocus() 414ebd70c7dSAdrian Hunter 415ebd70c7dSAdrian Hunter def Deactivate(self): 416ebd70c7dSAdrian Hunter self.bar.hide() 417ebd70c7dSAdrian Hunter 418ebd70c7dSAdrian Hunter def Busy(self): 419ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 420ebd70c7dSAdrian Hunter self.pattern.hide() 421ebd70c7dSAdrian Hunter self.next_button.hide() 422ebd70c7dSAdrian Hunter self.prev_button.hide() 423ebd70c7dSAdrian Hunter self.progress.show() 424ebd70c7dSAdrian Hunter 425ebd70c7dSAdrian Hunter def Idle(self): 426ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 427ebd70c7dSAdrian Hunter self.progress.hide() 428ebd70c7dSAdrian Hunter self.pattern.show() 429ebd70c7dSAdrian Hunter self.next_button.show() 430ebd70c7dSAdrian Hunter self.prev_button.show() 431ebd70c7dSAdrian Hunter 432ebd70c7dSAdrian Hunter def Find(self, direction): 433ebd70c7dSAdrian Hunter value = self.textbox.currentText() 434ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 435ebd70c7dSAdrian Hunter self.last_value = value 436ebd70c7dSAdrian Hunter self.last_pattern = pattern 437ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 438ebd70c7dSAdrian Hunter 439ebd70c7dSAdrian Hunter def ValueChanged(self): 440ebd70c7dSAdrian Hunter value = self.textbox.currentText() 441ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 442ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 443ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 444ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 445ebd70c7dSAdrian Hunter if data == None: 446ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 447ebd70c7dSAdrian Hunter else: 448ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 449ebd70c7dSAdrian Hunter self.Find(0) 450ebd70c7dSAdrian Hunter 451ebd70c7dSAdrian Hunter def NextPrev(self, direction): 452ebd70c7dSAdrian Hunter value = self.textbox.currentText() 453ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 454ebd70c7dSAdrian Hunter if value != self.last_value: 455ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 456ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 457ebd70c7dSAdrian Hunter if index < 0: 458ebd70c7dSAdrian Hunter index = self.textbox.count() 459ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 460ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 461ebd70c7dSAdrian Hunter return 462ebd70c7dSAdrian Hunter else: 463ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 464ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 465ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 466ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 467ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 468ebd70c7dSAdrian Hunter self.Find(direction) 469ebd70c7dSAdrian Hunter 470ebd70c7dSAdrian Hunter def NotFound(self): 471ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 472ebd70c7dSAdrian Hunter 473031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 474031c2a00SAdrian Hunter 475031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 476031c2a00SAdrian Hunter 4774a0979d4SAdrian Hunter def __init__(self, glb, params, row, parent_item): 478031c2a00SAdrian Hunter self.glb = glb 4794a0979d4SAdrian Hunter self.params = params 480031c2a00SAdrian Hunter self.row = row 481031c2a00SAdrian Hunter self.parent_item = parent_item 48226688729SAdrian Hunter self.query_done = False 483031c2a00SAdrian Hunter self.child_count = 0 484031c2a00SAdrian Hunter self.child_items = [] 4853ac641f4SAdrian Hunter if parent_item: 4863ac641f4SAdrian Hunter self.level = parent_item.level + 1 4873ac641f4SAdrian Hunter else: 4883ac641f4SAdrian Hunter self.level = 0 489031c2a00SAdrian Hunter 490031c2a00SAdrian Hunter def getChildItem(self, row): 491031c2a00SAdrian Hunter return self.child_items[row] 492031c2a00SAdrian Hunter 493031c2a00SAdrian Hunter def getParentItem(self): 494031c2a00SAdrian Hunter return self.parent_item 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunter def getRow(self): 497031c2a00SAdrian Hunter return self.row 498031c2a00SAdrian Hunter 499031c2a00SAdrian Hunter def childCount(self): 500031c2a00SAdrian Hunter if not self.query_done: 501031c2a00SAdrian Hunter self.Select() 502031c2a00SAdrian Hunter if not self.child_count: 503031c2a00SAdrian Hunter return -1 504031c2a00SAdrian Hunter return self.child_count 505031c2a00SAdrian Hunter 506031c2a00SAdrian Hunter def hasChildren(self): 507031c2a00SAdrian Hunter if not self.query_done: 508031c2a00SAdrian Hunter return True 509031c2a00SAdrian Hunter return self.child_count > 0 510031c2a00SAdrian Hunter 511031c2a00SAdrian Hunter def getData(self, column): 512031c2a00SAdrian Hunter return self.data[column] 513031c2a00SAdrian Hunter 514031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 515031c2a00SAdrian Hunter 516031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 517031c2a00SAdrian Hunter 51838a846d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): 5194a0979d4SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 520031c2a00SAdrian Hunter self.comm_id = comm_id 521031c2a00SAdrian Hunter self.thread_id = thread_id 522031c2a00SAdrian Hunter self.call_path_id = call_path_id 52338a846d4SAdrian Hunter self.insn_cnt = insn_cnt 52438a846d4SAdrian Hunter self.cyc_cnt = cyc_cnt 525031c2a00SAdrian Hunter self.branch_count = branch_count 526031c2a00SAdrian Hunter self.time = time 527031c2a00SAdrian Hunter 528031c2a00SAdrian Hunter def Select(self): 52926688729SAdrian Hunter self.query_done = True 530031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 53138a846d4SAdrian Hunter if self.params.have_ipc: 53238a846d4SAdrian Hunter ipc_str = ", SUM(insn_count), SUM(cyc_count)" 53338a846d4SAdrian Hunter else: 53438a846d4SAdrian Hunter ipc_str = "" 53538a846d4SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)" 536031c2a00SAdrian Hunter " FROM calls" 537031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 538031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 539031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 540031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 541031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 542031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 543031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 544031c2a00SAdrian Hunter " ORDER BY call_path_id") 545031c2a00SAdrian Hunter while query.next(): 54638a846d4SAdrian Hunter if self.params.have_ipc: 54738a846d4SAdrian Hunter insn_cnt = int(query.value(5)) 54838a846d4SAdrian Hunter cyc_cnt = int(query.value(6)) 54938a846d4SAdrian Hunter branch_count = int(query.value(7)) 55038a846d4SAdrian Hunter else: 55138a846d4SAdrian Hunter insn_cnt = 0 55238a846d4SAdrian Hunter cyc_cnt = 0 55338a846d4SAdrian Hunter branch_count = int(query.value(5)) 55438a846d4SAdrian 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) 555031c2a00SAdrian Hunter self.child_items.append(child_item) 556031c2a00SAdrian Hunter self.child_count += 1 557031c2a00SAdrian Hunter 558031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 559031c2a00SAdrian Hunter 560031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 561031c2a00SAdrian Hunter 56238a846d4SAdrian 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): 56338a846d4SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) 564031c2a00SAdrian Hunter dso = dsoname(dso) 56538a846d4SAdrian Hunter if self.params.have_ipc: 56638a846d4SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 56738a846d4SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 56838a846d4SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 56938a846d4SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 57038a846d4SAdrian 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 ] 57138a846d4SAdrian Hunter else: 572031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 573031c2a00SAdrian Hunter self.dbid = call_path_id 574031c2a00SAdrian Hunter 575031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 576031c2a00SAdrian Hunter 577031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 578031c2a00SAdrian Hunter 5794a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 58038a846d4SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) 58138a846d4SAdrian Hunter if self.params.have_ipc: 58238a846d4SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 58338a846d4SAdrian Hunter else: 584031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 585031c2a00SAdrian Hunter self.dbid = thread_id 586031c2a00SAdrian Hunter 587031c2a00SAdrian Hunter def Select(self): 588031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 589031c2a00SAdrian Hunter for child_item in self.child_items: 590031c2a00SAdrian Hunter self.time += child_item.time 59138a846d4SAdrian Hunter self.insn_cnt += child_item.insn_cnt 59238a846d4SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 593031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 594031c2a00SAdrian Hunter for child_item in self.child_items: 595031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 59638a846d4SAdrian Hunter if self.params.have_ipc: 59738a846d4SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 59838a846d4SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 59938a846d4SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 60038a846d4SAdrian Hunter else: 601031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 602031c2a00SAdrian Hunter 603031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 604031c2a00SAdrian Hunter 605031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 606031c2a00SAdrian Hunter 6074a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 6084a0979d4SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) 60938a846d4SAdrian Hunter if self.params.have_ipc: 61038a846d4SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 61138a846d4SAdrian Hunter else: 612031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 613031c2a00SAdrian Hunter self.dbid = comm_id 614031c2a00SAdrian Hunter 615031c2a00SAdrian Hunter def Select(self): 61626688729SAdrian Hunter self.query_done = True 617031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 618031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 619031c2a00SAdrian Hunter " FROM comm_threads" 620031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 621031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 622031c2a00SAdrian Hunter while query.next(): 6234a0979d4SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 624031c2a00SAdrian Hunter self.child_items.append(child_item) 625031c2a00SAdrian Hunter self.child_count += 1 626031c2a00SAdrian Hunter 627031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 628031c2a00SAdrian Hunter 629031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 630031c2a00SAdrian Hunter 6314a0979d4SAdrian Hunter def __init__(self, glb, params): 6324a0979d4SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, params, 0, None) 633031c2a00SAdrian Hunter self.dbid = 0 63426688729SAdrian Hunter self.query_done = True 63526c11206SAdrian Hunter if_has_calls = "" 63626c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 63726c11206SAdrian Hunter if_has_calls = " WHERE has_calls = TRUE" 638031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 63926c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 640031c2a00SAdrian Hunter while query.next(): 641031c2a00SAdrian Hunter if not query.value(0): 642031c2a00SAdrian Hunter continue 6434a0979d4SAdrian Hunter child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 644031c2a00SAdrian Hunter self.child_items.append(child_item) 645031c2a00SAdrian Hunter self.child_count += 1 646031c2a00SAdrian Hunter 6474a0979d4SAdrian Hunter# Call graph model parameters 6484a0979d4SAdrian Hunter 6494a0979d4SAdrian Hunterclass CallGraphModelParams(): 6504a0979d4SAdrian Hunter 6514a0979d4SAdrian Hunter def __init__(self, glb, parent=None): 6524a0979d4SAdrian Hunter self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") 6534a0979d4SAdrian Hunter 654254c0d82SAdrian Hunter# Context-sensitive call graph data model base 655031c2a00SAdrian Hunter 656254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 657031c2a00SAdrian Hunter 658031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 6594a0979d4SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) 660031c2a00SAdrian Hunter 661ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 662ebd70c7dSAdrian Hunter if pattern: 663ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 664ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 665ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 666ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 667ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 668ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 669ebd70c7dSAdrian Hunter # Escape % and _ 670ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 671ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 672ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 673ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 674ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 675ebd70c7dSAdrian Hunter else: 676ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 677ebd70c7dSAdrian Hunter else: 678ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 679254c0d82SAdrian Hunter self.DoFindSelect(query, match) 680ebd70c7dSAdrian Hunter 681ebd70c7dSAdrian Hunter def Found(self, query, found): 682ebd70c7dSAdrian Hunter if found: 683ebd70c7dSAdrian Hunter return self.FindPath(query) 684ebd70c7dSAdrian Hunter return [] 685ebd70c7dSAdrian Hunter 686ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 687ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 688ebd70c7dSAdrian Hunter found = query.first() 689ebd70c7dSAdrian Hunter else: 690ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 691ebd70c7dSAdrian Hunter found = query.next() 692ebd70c7dSAdrian Hunter return self.Found(query, found) 693ebd70c7dSAdrian Hunter 694ebd70c7dSAdrian Hunter def FindNext(self, query): 695ebd70c7dSAdrian Hunter found = query.next() 696ebd70c7dSAdrian Hunter if not found: 697ebd70c7dSAdrian Hunter found = query.first() 698ebd70c7dSAdrian Hunter return self.Found(query, found) 699ebd70c7dSAdrian Hunter 700ebd70c7dSAdrian Hunter def FindPrev(self, query): 701ebd70c7dSAdrian Hunter found = query.previous() 702ebd70c7dSAdrian Hunter if not found: 703ebd70c7dSAdrian Hunter found = query.last() 704ebd70c7dSAdrian Hunter return self.Found(query, found) 705ebd70c7dSAdrian Hunter 706ebd70c7dSAdrian Hunter def FindThread(self, c): 707ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 708ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 709ebd70c7dSAdrian Hunter elif c.direction > 0: 710ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 711ebd70c7dSAdrian Hunter else: 712ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 713ebd70c7dSAdrian Hunter return (True, ids) 714ebd70c7dSAdrian Hunter 715ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 716ebd70c7dSAdrian Hunter class Context(): 717ebd70c7dSAdrian Hunter def __init__(self, *x): 718ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 719ebd70c7dSAdrian Hunter def Update(self, *x): 720ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 721ebd70c7dSAdrian Hunter if len(context): 722ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 723ebd70c7dSAdrian Hunter else: 724ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 725ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 726ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 727ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 728ebd70c7dSAdrian Hunter thread.start() 729ebd70c7dSAdrian Hunter 730ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 731ebd70c7dSAdrian Hunter callback(ids) 732ebd70c7dSAdrian Hunter 733254c0d82SAdrian Hunter# Context-sensitive call graph data model 734254c0d82SAdrian Hunter 735254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 736254c0d82SAdrian Hunter 737254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 738254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 739254c0d82SAdrian Hunter 740254c0d82SAdrian Hunter def GetRoot(self): 7414a0979d4SAdrian Hunter return CallGraphRootItem(self.glb, self.params) 742254c0d82SAdrian Hunter 743254c0d82SAdrian Hunter def columnCount(self, parent=None): 74438a846d4SAdrian Hunter if self.params.have_ipc: 74538a846d4SAdrian Hunter return 12 74638a846d4SAdrian Hunter else: 747254c0d82SAdrian Hunter return 7 748254c0d82SAdrian Hunter 749254c0d82SAdrian Hunter def columnHeader(self, column): 75038a846d4SAdrian Hunter if self.params.have_ipc: 75138a846d4SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 75238a846d4SAdrian Hunter else: 753254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 754254c0d82SAdrian Hunter return headers[column] 755254c0d82SAdrian Hunter 756254c0d82SAdrian Hunter def columnAlignment(self, column): 75738a846d4SAdrian Hunter if self.params.have_ipc: 75838a846d4SAdrian 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 ] 75938a846d4SAdrian Hunter else: 760254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 761254c0d82SAdrian Hunter return alignment[column] 762254c0d82SAdrian Hunter 763254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 764254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 765254c0d82SAdrian Hunter " FROM calls" 766254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 767254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 768254c0d82SAdrian Hunter " WHERE symbols.name" + match + 769254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 770254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 771254c0d82SAdrian Hunter 772254c0d82SAdrian Hunter def FindPath(self, query): 773254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 774254c0d82SAdrian Hunter # to open the tree at the right place. 775254c0d82SAdrian Hunter ids = [] 776254c0d82SAdrian Hunter parent_id = query.value(0) 777254c0d82SAdrian Hunter while parent_id: 778254c0d82SAdrian Hunter ids.insert(0, parent_id) 779254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 780254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 781254c0d82SAdrian Hunter " FROM call_paths" 782254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 783254c0d82SAdrian Hunter if not q2.next(): 784254c0d82SAdrian Hunter break 785254c0d82SAdrian Hunter parent_id = q2.value(0) 786254c0d82SAdrian Hunter # The call path root is not used 787254c0d82SAdrian Hunter if ids[0] == 1: 788254c0d82SAdrian Hunter del ids[0] 789254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 790254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 791254c0d82SAdrian Hunter return ids 792254c0d82SAdrian Hunter 793ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 794ae8b887cSAdrian Hunter 795ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 796ae8b887cSAdrian Hunter 797*da4264f5SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, call_time, time, insn_cnt, cyc_cnt, branch_count, parent_item): 7984a0979d4SAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 799ae8b887cSAdrian Hunter self.comm_id = comm_id 800ae8b887cSAdrian Hunter self.thread_id = thread_id 801ae8b887cSAdrian Hunter self.calls_id = calls_id 802*da4264f5SAdrian Hunter self.call_time = call_time 803*da4264f5SAdrian Hunter self.time = time 804b3b66079SAdrian Hunter self.insn_cnt = insn_cnt 805b3b66079SAdrian Hunter self.cyc_cnt = cyc_cnt 806ae8b887cSAdrian Hunter self.branch_count = branch_count 807ae8b887cSAdrian Hunter 808ae8b887cSAdrian Hunter def Select(self): 80926688729SAdrian Hunter self.query_done = True 810ae8b887cSAdrian Hunter if self.calls_id == 0: 811ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 812ae8b887cSAdrian Hunter else: 813ae8b887cSAdrian Hunter comm_thread = "" 814b3b66079SAdrian Hunter if self.params.have_ipc: 815b3b66079SAdrian Hunter ipc_str = ", insn_count, cyc_count" 816b3b66079SAdrian Hunter else: 817b3b66079SAdrian Hunter ipc_str = "" 818ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 819b3b66079SAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count" 820ae8b887cSAdrian Hunter " FROM calls" 821ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 822ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 823ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 824ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 825ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 826ae8b887cSAdrian Hunter while query.next(): 827b3b66079SAdrian Hunter if self.params.have_ipc: 828b3b66079SAdrian Hunter insn_cnt = int(query.value(5)) 829b3b66079SAdrian Hunter cyc_cnt = int(query.value(6)) 830b3b66079SAdrian Hunter branch_count = int(query.value(7)) 831b3b66079SAdrian Hunter else: 832b3b66079SAdrian Hunter insn_cnt = 0 833b3b66079SAdrian Hunter cyc_cnt = 0 834b3b66079SAdrian Hunter branch_count = int(query.value(5)) 835b3b66079SAdrian 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) 836ae8b887cSAdrian Hunter self.child_items.append(child_item) 837ae8b887cSAdrian Hunter self.child_count += 1 838ae8b887cSAdrian Hunter 839ae8b887cSAdrian Hunter# Call tree data model level three item 840ae8b887cSAdrian Hunter 841ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 842ae8b887cSAdrian Hunter 843*da4264f5SAdrian 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): 844*da4264f5SAdrian 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) 845ae8b887cSAdrian Hunter dso = dsoname(dso) 846b3b66079SAdrian Hunter if self.params.have_ipc: 847b3b66079SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 848b3b66079SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 849b3b66079SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 850b3b66079SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 851*da4264f5SAdrian 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 ] 852b3b66079SAdrian Hunter else: 853*da4264f5SAdrian 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) ] 854ae8b887cSAdrian Hunter self.dbid = calls_id 855ae8b887cSAdrian Hunter 856ae8b887cSAdrian Hunter# Call tree data model level two item 857ae8b887cSAdrian Hunter 858ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 859ae8b887cSAdrian Hunter 8604a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 861*da4264f5SAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, 0, parent_item) 862b3b66079SAdrian Hunter if self.params.have_ipc: 863b3b66079SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 864b3b66079SAdrian Hunter else: 865ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 866ae8b887cSAdrian Hunter self.dbid = thread_id 867ae8b887cSAdrian Hunter 868ae8b887cSAdrian Hunter def Select(self): 869ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 870ae8b887cSAdrian Hunter for child_item in self.child_items: 871ae8b887cSAdrian Hunter self.time += child_item.time 872b3b66079SAdrian Hunter self.insn_cnt += child_item.insn_cnt 873b3b66079SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 874ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 875ae8b887cSAdrian Hunter for child_item in self.child_items: 876ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 877b3b66079SAdrian Hunter if self.params.have_ipc: 878b3b66079SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 879b3b66079SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 880b3b66079SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 881b3b66079SAdrian Hunter else: 882ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 883ae8b887cSAdrian Hunter 884ae8b887cSAdrian Hunter# Call tree data model level one item 885ae8b887cSAdrian Hunter 886ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 887ae8b887cSAdrian Hunter 8884a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 8894a0979d4SAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) 890b3b66079SAdrian Hunter if self.params.have_ipc: 891b3b66079SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 892b3b66079SAdrian Hunter else: 893ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 894ae8b887cSAdrian Hunter self.dbid = comm_id 895ae8b887cSAdrian Hunter 896ae8b887cSAdrian Hunter def Select(self): 89726688729SAdrian Hunter self.query_done = True 898ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 899ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 900ae8b887cSAdrian Hunter " FROM comm_threads" 901ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 902ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 903ae8b887cSAdrian Hunter while query.next(): 9044a0979d4SAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 905ae8b887cSAdrian Hunter self.child_items.append(child_item) 906ae8b887cSAdrian Hunter self.child_count += 1 907ae8b887cSAdrian Hunter 908ae8b887cSAdrian Hunter# Call tree data model root item 909ae8b887cSAdrian Hunter 910ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 911ae8b887cSAdrian Hunter 9124a0979d4SAdrian Hunter def __init__(self, glb, params): 9134a0979d4SAdrian Hunter super(CallTreeRootItem, self).__init__(glb, params, 0, None) 914ae8b887cSAdrian Hunter self.dbid = 0 91526688729SAdrian Hunter self.query_done = True 91626c11206SAdrian Hunter if_has_calls = "" 91726c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 91826c11206SAdrian Hunter if_has_calls = " WHERE has_calls = TRUE" 919ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 92026c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 921ae8b887cSAdrian Hunter while query.next(): 922ae8b887cSAdrian Hunter if not query.value(0): 923ae8b887cSAdrian Hunter continue 9244a0979d4SAdrian Hunter child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 925ae8b887cSAdrian Hunter self.child_items.append(child_item) 926ae8b887cSAdrian Hunter self.child_count += 1 927ae8b887cSAdrian Hunter 928ae8b887cSAdrian Hunter# Call Tree data model 929ae8b887cSAdrian Hunter 930ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 931ae8b887cSAdrian Hunter 932ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 933ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 934ae8b887cSAdrian Hunter 935ae8b887cSAdrian Hunter def GetRoot(self): 9364a0979d4SAdrian Hunter return CallTreeRootItem(self.glb, self.params) 937ae8b887cSAdrian Hunter 938ae8b887cSAdrian Hunter def columnCount(self, parent=None): 939b3b66079SAdrian Hunter if self.params.have_ipc: 940b3b66079SAdrian Hunter return 12 941b3b66079SAdrian Hunter else: 942ae8b887cSAdrian Hunter return 7 943ae8b887cSAdrian Hunter 944ae8b887cSAdrian Hunter def columnHeader(self, column): 945b3b66079SAdrian Hunter if self.params.have_ipc: 946b3b66079SAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 947b3b66079SAdrian Hunter else: 948ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 949ae8b887cSAdrian Hunter return headers[column] 950ae8b887cSAdrian Hunter 951ae8b887cSAdrian Hunter def columnAlignment(self, column): 952b3b66079SAdrian Hunter if self.params.have_ipc: 953b3b66079SAdrian 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 ] 954b3b66079SAdrian Hunter else: 955ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 956ae8b887cSAdrian Hunter return alignment[column] 957ae8b887cSAdrian Hunter 958ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 959ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 960ae8b887cSAdrian Hunter " FROM calls" 961ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 962ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 963ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 964ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 965ae8b887cSAdrian Hunter 966ae8b887cSAdrian Hunter def FindPath(self, query): 967ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 968ae8b887cSAdrian Hunter # to open the tree at the right place. 969ae8b887cSAdrian Hunter ids = [] 970ae8b887cSAdrian Hunter parent_id = query.value(0) 971ae8b887cSAdrian Hunter while parent_id: 972ae8b887cSAdrian Hunter ids.insert(0, parent_id) 973ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 974ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 975ae8b887cSAdrian Hunter " FROM calls" 976ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 977ae8b887cSAdrian Hunter if not q2.next(): 978ae8b887cSAdrian Hunter break 979ae8b887cSAdrian Hunter parent_id = q2.value(0) 980ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 981ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 982ae8b887cSAdrian Hunter return ids 983ae8b887cSAdrian Hunter 98442c303ffSAdrian Hunter# Vertical layout 98542c303ffSAdrian Hunter 98642c303ffSAdrian Hunterclass HBoxLayout(QHBoxLayout): 98742c303ffSAdrian Hunter 98842c303ffSAdrian Hunter def __init__(self, *children): 98942c303ffSAdrian Hunter super(HBoxLayout, self).__init__() 99042c303ffSAdrian Hunter 99142c303ffSAdrian Hunter self.layout().setContentsMargins(0, 0, 0, 0) 99242c303ffSAdrian Hunter for child in children: 99342c303ffSAdrian Hunter if child.isWidgetType(): 99442c303ffSAdrian Hunter self.layout().addWidget(child) 99542c303ffSAdrian Hunter else: 99642c303ffSAdrian Hunter self.layout().addLayout(child) 99742c303ffSAdrian Hunter 99842c303ffSAdrian Hunter# Horizontal layout 99942c303ffSAdrian Hunter 100042c303ffSAdrian Hunterclass VBoxLayout(QVBoxLayout): 100142c303ffSAdrian Hunter 100242c303ffSAdrian Hunter def __init__(self, *children): 100342c303ffSAdrian Hunter super(VBoxLayout, self).__init__() 100442c303ffSAdrian Hunter 100542c303ffSAdrian Hunter self.layout().setContentsMargins(0, 0, 0, 0) 100642c303ffSAdrian Hunter for child in children: 100742c303ffSAdrian Hunter if child.isWidgetType(): 100842c303ffSAdrian Hunter self.layout().addWidget(child) 100942c303ffSAdrian Hunter else: 101042c303ffSAdrian Hunter self.layout().addLayout(child) 101142c303ffSAdrian Hunter 101242c303ffSAdrian Hunter# Vertical layout widget 1013ebd70c7dSAdrian Hunter 1014ebd70c7dSAdrian Hunterclass VBox(): 1015ebd70c7dSAdrian Hunter 101642c303ffSAdrian Hunter def __init__(self, *children): 1017ebd70c7dSAdrian Hunter self.vbox = QWidget() 101842c303ffSAdrian Hunter self.vbox.setLayout(VBoxLayout(*children)) 1019ebd70c7dSAdrian Hunter 1020ebd70c7dSAdrian Hunter def Widget(self): 1021ebd70c7dSAdrian Hunter return self.vbox 1022ebd70c7dSAdrian Hunter 1023a731cc4cSAdrian Hunter# Tree window base 10241beb5c7bSAdrian Hunter 1025a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 10261beb5c7bSAdrian Hunter 1027a731cc4cSAdrian Hunter def __init__(self, parent=None): 1028a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 10291beb5c7bSAdrian Hunter 1030a731cc4cSAdrian Hunter self.model = None 1031a731cc4cSAdrian Hunter self.find_bar = None 10321beb5c7bSAdrian Hunter 1033be6e7471SAdrian Hunter self.view = QTreeView() 103496c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 103596c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 1036be6e7471SAdrian Hunter 10379bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 10389bc4e4bfSAdrian Hunter 1039ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 1040ebd70c7dSAdrian Hunter if not len(ids): 1041ebd70c7dSAdrian Hunter return False 1042ebd70c7dSAdrian Hunter parent = QModelIndex() 1043ebd70c7dSAdrian Hunter for dbid in ids: 1044ebd70c7dSAdrian Hunter found = False 1045ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 1046ebd70c7dSAdrian Hunter for row in xrange(n): 1047ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 1048ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 1049ebd70c7dSAdrian Hunter found = True 1050ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 1051ebd70c7dSAdrian Hunter parent = child 1052ebd70c7dSAdrian Hunter break 1053ebd70c7dSAdrian Hunter if not found: 1054ebd70c7dSAdrian Hunter break 1055ebd70c7dSAdrian Hunter return found 1056ebd70c7dSAdrian Hunter 1057ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 1058ebd70c7dSAdrian Hunter self.view.setFocus() 1059ebd70c7dSAdrian Hunter self.find_bar.Busy() 1060ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 1061ebd70c7dSAdrian Hunter 1062ebd70c7dSAdrian Hunter def FindDone(self, ids): 1063ebd70c7dSAdrian Hunter found = True 1064ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 1065ebd70c7dSAdrian Hunter found = False 1066ebd70c7dSAdrian Hunter self.find_bar.Idle() 1067ebd70c7dSAdrian Hunter if not found: 1068ebd70c7dSAdrian Hunter self.find_bar.NotFound() 1069ebd70c7dSAdrian Hunter 1070a731cc4cSAdrian Hunter 1071a731cc4cSAdrian Hunter# Context-sensitive call graph window 1072a731cc4cSAdrian Hunter 1073a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 1074a731cc4cSAdrian Hunter 1075a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 1076a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 1077a731cc4cSAdrian Hunter 1078a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 1079a731cc4cSAdrian Hunter 1080a731cc4cSAdrian Hunter self.view.setModel(self.model) 1081a731cc4cSAdrian Hunter 1082a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 1083a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 1084a731cc4cSAdrian Hunter 1085a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 1086a731cc4cSAdrian Hunter 1087a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1088a731cc4cSAdrian Hunter 1089a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1090a731cc4cSAdrian Hunter 1091a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 1092a731cc4cSAdrian Hunter 1093ae8b887cSAdrian Hunter# Call tree window 1094ae8b887cSAdrian Hunter 1095ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 1096ae8b887cSAdrian Hunter 1097ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 1098ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 1099ae8b887cSAdrian Hunter 1100ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 1101ae8b887cSAdrian Hunter 1102ae8b887cSAdrian Hunter self.view.setModel(self.model) 1103ae8b887cSAdrian Hunter 1104ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 1105ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 1106ae8b887cSAdrian Hunter 1107ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 1108ae8b887cSAdrian Hunter 1109ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1110ae8b887cSAdrian Hunter 1111ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1112ae8b887cSAdrian Hunter 1113ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 1114ae8b887cSAdrian Hunter 11158392b74bSAdrian Hunter# Child data item finder 11168392b74bSAdrian Hunter 11178392b74bSAdrian Hunterclass ChildDataItemFinder(): 11188392b74bSAdrian Hunter 11198392b74bSAdrian Hunter def __init__(self, root): 11208392b74bSAdrian Hunter self.root = root 11218392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 11228392b74bSAdrian Hunter self.rows = [] 11238392b74bSAdrian Hunter self.pos = 0 11248392b74bSAdrian Hunter 11258392b74bSAdrian Hunter def FindSelect(self): 11268392b74bSAdrian Hunter self.rows = [] 11278392b74bSAdrian Hunter if self.pattern: 11288392b74bSAdrian Hunter pattern = re.compile(self.value) 11298392b74bSAdrian Hunter for child in self.root.child_items: 11308392b74bSAdrian Hunter for column_data in child.data: 11318392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 11328392b74bSAdrian Hunter self.rows.append(child.row) 11338392b74bSAdrian Hunter break 11348392b74bSAdrian Hunter else: 11358392b74bSAdrian Hunter for child in self.root.child_items: 11368392b74bSAdrian Hunter for column_data in child.data: 11378392b74bSAdrian Hunter if self.value in str(column_data): 11388392b74bSAdrian Hunter self.rows.append(child.row) 11398392b74bSAdrian Hunter break 11408392b74bSAdrian Hunter 11418392b74bSAdrian Hunter def FindValue(self): 11428392b74bSAdrian Hunter self.pos = 0 11438392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 11448392b74bSAdrian Hunter self.FindSelect() 11458392b74bSAdrian Hunter if not len(self.rows): 11468392b74bSAdrian Hunter return -1 11478392b74bSAdrian Hunter return self.rows[self.pos] 11488392b74bSAdrian Hunter 11498392b74bSAdrian Hunter def FindThread(self): 11508392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 11518392b74bSAdrian Hunter row = self.FindValue() 11528392b74bSAdrian Hunter elif len(self.rows): 11538392b74bSAdrian Hunter if self.direction > 0: 11548392b74bSAdrian Hunter self.pos += 1 11558392b74bSAdrian Hunter if self.pos >= len(self.rows): 11568392b74bSAdrian Hunter self.pos = 0 11578392b74bSAdrian Hunter else: 11588392b74bSAdrian Hunter self.pos -= 1 11598392b74bSAdrian Hunter if self.pos < 0: 11608392b74bSAdrian Hunter self.pos = len(self.rows) - 1 11618392b74bSAdrian Hunter row = self.rows[self.pos] 11628392b74bSAdrian Hunter else: 11638392b74bSAdrian Hunter row = -1 11648392b74bSAdrian Hunter return (True, row) 11658392b74bSAdrian Hunter 11668392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 11678392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 11688392b74bSAdrian Hunter # Use a thread so the UI is not blocked 11698392b74bSAdrian Hunter thread = Thread(self.FindThread) 11708392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 11718392b74bSAdrian Hunter thread.start() 11728392b74bSAdrian Hunter 11738392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 11748392b74bSAdrian Hunter callback(row) 11758392b74bSAdrian Hunter 11768392b74bSAdrian Hunter# Number of database records to fetch in one go 11778392b74bSAdrian Hunter 11788392b74bSAdrian Hunterglb_chunk_sz = 10000 11798392b74bSAdrian Hunter 11808392b74bSAdrian Hunter# Background process for SQL data fetcher 11818392b74bSAdrian Hunter 11828392b74bSAdrian Hunterclass SQLFetcherProcess(): 11838392b74bSAdrian Hunter 11848392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 11858392b74bSAdrian Hunter # Need a unique connection name 11868392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 11878392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 11888392b74bSAdrian Hunter self.sql = sql 11898392b74bSAdrian Hunter self.buffer = buffer 11908392b74bSAdrian Hunter self.head = head 11918392b74bSAdrian Hunter self.tail = tail 11928392b74bSAdrian Hunter self.fetch_count = fetch_count 11938392b74bSAdrian Hunter self.fetching_done = fetching_done 11948392b74bSAdrian Hunter self.process_target = process_target 11958392b74bSAdrian Hunter self.wait_event = wait_event 11968392b74bSAdrian Hunter self.fetched_event = fetched_event 11978392b74bSAdrian Hunter self.prep = prep 11988392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 11998392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 12008392b74bSAdrian Hunter self.last_id = -1 12018392b74bSAdrian Hunter self.fetched = 0 12028392b74bSAdrian Hunter self.more = True 12038392b74bSAdrian Hunter self.local_head = self.head.value 12048392b74bSAdrian Hunter self.local_tail = self.tail.value 12058392b74bSAdrian Hunter 12068392b74bSAdrian Hunter def Select(self): 12078392b74bSAdrian Hunter if self.query_limit: 12088392b74bSAdrian Hunter if self.query_limit == 1: 12098392b74bSAdrian Hunter return 12108392b74bSAdrian Hunter self.query_limit -= 1 12118392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 12128392b74bSAdrian Hunter QueryExec(self.query, stmt) 12138392b74bSAdrian Hunter 12148392b74bSAdrian Hunter def Next(self): 12158392b74bSAdrian Hunter if not self.query.next(): 12168392b74bSAdrian Hunter self.Select() 12178392b74bSAdrian Hunter if not self.query.next(): 12188392b74bSAdrian Hunter return None 12198392b74bSAdrian Hunter self.last_id = self.query.value(0) 12208392b74bSAdrian Hunter return self.prep(self.query) 12218392b74bSAdrian Hunter 12228392b74bSAdrian Hunter def WaitForTarget(self): 12238392b74bSAdrian Hunter while True: 12248392b74bSAdrian Hunter self.wait_event.clear() 12258392b74bSAdrian Hunter target = self.process_target.value 12268392b74bSAdrian Hunter if target > self.fetched or target < 0: 12278392b74bSAdrian Hunter break 12288392b74bSAdrian Hunter self.wait_event.wait() 12298392b74bSAdrian Hunter return target 12308392b74bSAdrian Hunter 12318392b74bSAdrian Hunter def HasSpace(self, sz): 12328392b74bSAdrian Hunter if self.local_tail <= self.local_head: 12338392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 12348392b74bSAdrian Hunter if space > sz: 12358392b74bSAdrian Hunter return True 12368392b74bSAdrian Hunter if space >= glb_nsz: 12378392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1238beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 12398392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 12408392b74bSAdrian Hunter self.local_head = 0 12418392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 12428392b74bSAdrian Hunter return True 12438392b74bSAdrian Hunter return False 12448392b74bSAdrian Hunter 12458392b74bSAdrian Hunter def WaitForSpace(self, sz): 12468392b74bSAdrian Hunter if self.HasSpace(sz): 12478392b74bSAdrian Hunter return 12488392b74bSAdrian Hunter while True: 12498392b74bSAdrian Hunter self.wait_event.clear() 12508392b74bSAdrian Hunter self.local_tail = self.tail.value 12518392b74bSAdrian Hunter if self.HasSpace(sz): 12528392b74bSAdrian Hunter return 12538392b74bSAdrian Hunter self.wait_event.wait() 12548392b74bSAdrian Hunter 12558392b74bSAdrian Hunter def AddToBuffer(self, obj): 1256beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 12578392b74bSAdrian Hunter n = len(d) 1258beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 12598392b74bSAdrian Hunter sz = n + glb_nsz 12608392b74bSAdrian Hunter self.WaitForSpace(sz) 12618392b74bSAdrian Hunter pos = self.local_head 12628392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 12638392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 12648392b74bSAdrian Hunter self.local_head += sz 12658392b74bSAdrian Hunter 12668392b74bSAdrian Hunter def FetchBatch(self, batch_size): 12678392b74bSAdrian Hunter fetched = 0 12688392b74bSAdrian Hunter while batch_size > fetched: 12698392b74bSAdrian Hunter obj = self.Next() 12708392b74bSAdrian Hunter if obj is None: 12718392b74bSAdrian Hunter self.more = False 12728392b74bSAdrian Hunter break 12738392b74bSAdrian Hunter self.AddToBuffer(obj) 12748392b74bSAdrian Hunter fetched += 1 12758392b74bSAdrian Hunter if fetched: 12768392b74bSAdrian Hunter self.fetched += fetched 12778392b74bSAdrian Hunter with self.fetch_count.get_lock(): 12788392b74bSAdrian Hunter self.fetch_count.value += fetched 12798392b74bSAdrian Hunter self.head.value = self.local_head 12808392b74bSAdrian Hunter self.fetched_event.set() 12818392b74bSAdrian Hunter 12828392b74bSAdrian Hunter def Run(self): 12838392b74bSAdrian Hunter while self.more: 12848392b74bSAdrian Hunter target = self.WaitForTarget() 12858392b74bSAdrian Hunter if target < 0: 12868392b74bSAdrian Hunter break 12878392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 12888392b74bSAdrian Hunter self.FetchBatch(batch_size) 12898392b74bSAdrian Hunter self.fetching_done.value = True 12908392b74bSAdrian Hunter self.fetched_event.set() 12918392b74bSAdrian Hunter 12928392b74bSAdrian Hunterdef SQLFetcherFn(*x): 12938392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 12948392b74bSAdrian Hunter process.Run() 12958392b74bSAdrian Hunter 12968392b74bSAdrian Hunter# SQL data fetcher 12978392b74bSAdrian Hunter 12988392b74bSAdrian Hunterclass SQLFetcher(QObject): 12998392b74bSAdrian Hunter 13008392b74bSAdrian Hunter done = Signal(object) 13018392b74bSAdrian Hunter 13028392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 13038392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 13048392b74bSAdrian Hunter self.process_data = process_data 13058392b74bSAdrian Hunter self.more = True 13068392b74bSAdrian Hunter self.target = 0 13078392b74bSAdrian Hunter self.last_target = 0 13088392b74bSAdrian Hunter self.fetched = 0 13098392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 13108392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 13118392b74bSAdrian Hunter self.head = Value(c_longlong) 13128392b74bSAdrian Hunter self.tail = Value(c_longlong) 13138392b74bSAdrian Hunter self.local_tail = 0 13148392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 13158392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 13168392b74bSAdrian Hunter self.last_count = 0 13178392b74bSAdrian Hunter self.process_target = Value(c_longlong) 13188392b74bSAdrian Hunter self.wait_event = Event() 13198392b74bSAdrian Hunter self.fetched_event = Event() 13208392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 13218392b74bSAdrian 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)) 13228392b74bSAdrian Hunter self.process.start() 13238392b74bSAdrian Hunter self.thread = Thread(self.Thread) 13248392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 13258392b74bSAdrian Hunter self.thread.start() 13268392b74bSAdrian Hunter 13278392b74bSAdrian Hunter def Shutdown(self): 13288392b74bSAdrian Hunter # Tell the thread and process to exit 13298392b74bSAdrian Hunter self.process_target.value = -1 13308392b74bSAdrian Hunter self.wait_event.set() 13318392b74bSAdrian Hunter self.more = False 13328392b74bSAdrian Hunter self.fetching_done.value = True 13338392b74bSAdrian Hunter self.fetched_event.set() 13348392b74bSAdrian Hunter 13358392b74bSAdrian Hunter def Thread(self): 13368392b74bSAdrian Hunter if not self.more: 13378392b74bSAdrian Hunter return True, 0 13388392b74bSAdrian Hunter while True: 13398392b74bSAdrian Hunter self.fetched_event.clear() 13408392b74bSAdrian Hunter fetch_count = self.fetch_count.value 13418392b74bSAdrian Hunter if fetch_count != self.last_count: 13428392b74bSAdrian Hunter break 13438392b74bSAdrian Hunter if self.fetching_done.value: 13448392b74bSAdrian Hunter self.more = False 13458392b74bSAdrian Hunter return True, 0 13468392b74bSAdrian Hunter self.fetched_event.wait() 13478392b74bSAdrian Hunter count = fetch_count - self.last_count 13488392b74bSAdrian Hunter self.last_count = fetch_count 13498392b74bSAdrian Hunter self.fetched += count 13508392b74bSAdrian Hunter return False, count 13518392b74bSAdrian Hunter 13528392b74bSAdrian Hunter def Fetch(self, nr): 13538392b74bSAdrian Hunter if not self.more: 13548392b74bSAdrian Hunter # -1 inidcates there are no more 13558392b74bSAdrian Hunter return -1 13568392b74bSAdrian Hunter result = self.fetched 13578392b74bSAdrian Hunter extra = result + nr - self.target 13588392b74bSAdrian Hunter if extra > 0: 13598392b74bSAdrian Hunter self.target += extra 13608392b74bSAdrian Hunter # process_target < 0 indicates shutting down 13618392b74bSAdrian Hunter if self.process_target.value >= 0: 13628392b74bSAdrian Hunter self.process_target.value = self.target 13638392b74bSAdrian Hunter self.wait_event.set() 13648392b74bSAdrian Hunter return result 13658392b74bSAdrian Hunter 13668392b74bSAdrian Hunter def RemoveFromBuffer(self): 13678392b74bSAdrian Hunter pos = self.local_tail 13688392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 13698392b74bSAdrian Hunter pos = 0 1370beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 13718392b74bSAdrian Hunter if n == 0: 13728392b74bSAdrian Hunter pos = 0 1373beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 13748392b74bSAdrian Hunter pos += glb_nsz 1375beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 13768392b74bSAdrian Hunter self.local_tail = pos + n 13778392b74bSAdrian Hunter return obj 13788392b74bSAdrian Hunter 13798392b74bSAdrian Hunter def ProcessData(self, count): 13808392b74bSAdrian Hunter for i in xrange(count): 13818392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 13828392b74bSAdrian Hunter self.process_data(obj) 13838392b74bSAdrian Hunter self.tail.value = self.local_tail 13848392b74bSAdrian Hunter self.wait_event.set() 13858392b74bSAdrian Hunter self.done.emit(count) 13868392b74bSAdrian Hunter 13878392b74bSAdrian Hunter# Fetch more records bar 13888392b74bSAdrian Hunter 13898392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 13908392b74bSAdrian Hunter 13918392b74bSAdrian Hunter def __init__(self, model, parent): 13928392b74bSAdrian Hunter self.model = model 13938392b74bSAdrian Hunter 13948392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 13958392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13968392b74bSAdrian Hunter 13978392b74bSAdrian Hunter self.fetch_count = QSpinBox() 13988392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 13998392b74bSAdrian Hunter self.fetch_count.setValue(10) 14008392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 14018392b74bSAdrian Hunter 14028392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 14038392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 14048392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 14058392b74bSAdrian Hunter 14068392b74bSAdrian Hunter self.progress = QProgressBar() 14078392b74bSAdrian Hunter self.progress.setRange(0, 100) 14088392b74bSAdrian Hunter self.progress.hide() 14098392b74bSAdrian Hunter 14108392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 14118392b74bSAdrian Hunter self.done_label.hide() 14128392b74bSAdrian Hunter 14138392b74bSAdrian Hunter self.spacer = QLabel("") 14148392b74bSAdrian Hunter 14158392b74bSAdrian Hunter self.close_button = QToolButton() 14168392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 14178392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 14188392b74bSAdrian Hunter 14198392b74bSAdrian Hunter self.hbox = QHBoxLayout() 14208392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 14218392b74bSAdrian Hunter 14228392b74bSAdrian Hunter self.hbox.addWidget(self.label) 14238392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 14248392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 14258392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 14268392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 14278392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 14288392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 14298392b74bSAdrian Hunter 14308392b74bSAdrian Hunter self.bar = QWidget() 143126688729SAdrian Hunter self.bar.setLayout(self.hbox) 14328392b74bSAdrian Hunter self.bar.show() 14338392b74bSAdrian Hunter 14348392b74bSAdrian Hunter self.in_progress = False 14358392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 14368392b74bSAdrian Hunter 14378392b74bSAdrian Hunter self.done = False 14388392b74bSAdrian Hunter 14398392b74bSAdrian Hunter if not model.HasMoreRecords(): 14408392b74bSAdrian Hunter self.Done() 14418392b74bSAdrian Hunter 14428392b74bSAdrian Hunter def Widget(self): 14438392b74bSAdrian Hunter return self.bar 14448392b74bSAdrian Hunter 14458392b74bSAdrian Hunter def Activate(self): 14468392b74bSAdrian Hunter self.bar.show() 14478392b74bSAdrian Hunter self.fetch.setFocus() 14488392b74bSAdrian Hunter 14498392b74bSAdrian Hunter def Deactivate(self): 14508392b74bSAdrian Hunter self.bar.hide() 14518392b74bSAdrian Hunter 14528392b74bSAdrian Hunter def Enable(self, enable): 14538392b74bSAdrian Hunter self.fetch.setEnabled(enable) 14548392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 14558392b74bSAdrian Hunter 14568392b74bSAdrian Hunter def Busy(self): 14578392b74bSAdrian Hunter self.Enable(False) 14588392b74bSAdrian Hunter self.fetch.hide() 14598392b74bSAdrian Hunter self.spacer.hide() 14608392b74bSAdrian Hunter self.progress.show() 14618392b74bSAdrian Hunter 14628392b74bSAdrian Hunter def Idle(self): 14638392b74bSAdrian Hunter self.in_progress = False 14648392b74bSAdrian Hunter self.Enable(True) 14658392b74bSAdrian Hunter self.progress.hide() 14668392b74bSAdrian Hunter self.fetch.show() 14678392b74bSAdrian Hunter self.spacer.show() 14688392b74bSAdrian Hunter 14698392b74bSAdrian Hunter def Target(self): 14708392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 14718392b74bSAdrian Hunter 14728392b74bSAdrian Hunter def Done(self): 14738392b74bSAdrian Hunter self.done = True 14748392b74bSAdrian Hunter self.Idle() 14758392b74bSAdrian Hunter self.label.hide() 14768392b74bSAdrian Hunter self.fetch_count.hide() 14778392b74bSAdrian Hunter self.fetch.hide() 14788392b74bSAdrian Hunter self.spacer.hide() 14798392b74bSAdrian Hunter self.done_label.show() 14808392b74bSAdrian Hunter 14818392b74bSAdrian Hunter def Progress(self, count): 14828392b74bSAdrian Hunter if self.in_progress: 14838392b74bSAdrian Hunter if count: 14848392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 14858392b74bSAdrian Hunter if percent >= 100: 14868392b74bSAdrian Hunter self.Idle() 14878392b74bSAdrian Hunter else: 14888392b74bSAdrian Hunter self.progress.setValue(percent) 14898392b74bSAdrian Hunter if not count: 14908392b74bSAdrian Hunter # Count value of zero means no more records 14918392b74bSAdrian Hunter self.Done() 14928392b74bSAdrian Hunter 14938392b74bSAdrian Hunter def FetchMoreRecords(self): 14948392b74bSAdrian Hunter if self.done: 14958392b74bSAdrian Hunter return 14968392b74bSAdrian Hunter self.progress.setValue(0) 14978392b74bSAdrian Hunter self.Busy() 14988392b74bSAdrian Hunter self.in_progress = True 14998392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 15008392b74bSAdrian Hunter 150176099f98SAdrian Hunter# Brance data model level two item 150276099f98SAdrian Hunter 150376099f98SAdrian Hunterclass BranchLevelTwoItem(): 150476099f98SAdrian Hunter 1505530e22fdSAdrian Hunter def __init__(self, row, col, text, parent_item): 150676099f98SAdrian Hunter self.row = row 150776099f98SAdrian Hunter self.parent_item = parent_item 1508530e22fdSAdrian Hunter self.data = [""] * (col + 1) 1509530e22fdSAdrian Hunter self.data[col] = text 151076099f98SAdrian Hunter self.level = 2 151176099f98SAdrian Hunter 151276099f98SAdrian Hunter def getParentItem(self): 151376099f98SAdrian Hunter return self.parent_item 151476099f98SAdrian Hunter 151576099f98SAdrian Hunter def getRow(self): 151676099f98SAdrian Hunter return self.row 151776099f98SAdrian Hunter 151876099f98SAdrian Hunter def childCount(self): 151976099f98SAdrian Hunter return 0 152076099f98SAdrian Hunter 152176099f98SAdrian Hunter def hasChildren(self): 152276099f98SAdrian Hunter return False 152376099f98SAdrian Hunter 152476099f98SAdrian Hunter def getData(self, column): 152576099f98SAdrian Hunter return self.data[column] 152676099f98SAdrian Hunter 152776099f98SAdrian Hunter# Brance data model level one item 152876099f98SAdrian Hunter 152976099f98SAdrian Hunterclass BranchLevelOneItem(): 153076099f98SAdrian Hunter 153176099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 153276099f98SAdrian Hunter self.glb = glb 153376099f98SAdrian Hunter self.row = row 153476099f98SAdrian Hunter self.parent_item = parent_item 153576099f98SAdrian Hunter self.child_count = 0 153676099f98SAdrian Hunter self.child_items = [] 153776099f98SAdrian Hunter self.data = data[1:] 153876099f98SAdrian Hunter self.dbid = data[0] 153976099f98SAdrian Hunter self.level = 1 154076099f98SAdrian Hunter self.query_done = False 1541530e22fdSAdrian Hunter self.br_col = len(self.data) - 1 154276099f98SAdrian Hunter 154376099f98SAdrian Hunter def getChildItem(self, row): 154476099f98SAdrian Hunter return self.child_items[row] 154576099f98SAdrian Hunter 154676099f98SAdrian Hunter def getParentItem(self): 154776099f98SAdrian Hunter return self.parent_item 154876099f98SAdrian Hunter 154976099f98SAdrian Hunter def getRow(self): 155076099f98SAdrian Hunter return self.row 155176099f98SAdrian Hunter 155276099f98SAdrian Hunter def Select(self): 155376099f98SAdrian Hunter self.query_done = True 155476099f98SAdrian Hunter 155576099f98SAdrian Hunter if not self.glb.have_disassembler: 155676099f98SAdrian Hunter return 155776099f98SAdrian Hunter 155876099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 155976099f98SAdrian Hunter 156076099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 156176099f98SAdrian Hunter " FROM samples" 156276099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 156376099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 156476099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 156576099f98SAdrian Hunter if not query.next(): 156676099f98SAdrian Hunter return 156776099f98SAdrian Hunter cpu = query.value(0) 156876099f98SAdrian Hunter dso = query.value(1) 156976099f98SAdrian Hunter sym = query.value(2) 157076099f98SAdrian Hunter if dso == 0 or sym == 0: 157176099f98SAdrian Hunter return 157276099f98SAdrian Hunter off = query.value(3) 157376099f98SAdrian Hunter short_name = query.value(4) 157476099f98SAdrian Hunter long_name = query.value(5) 157576099f98SAdrian Hunter build_id = query.value(6) 157676099f98SAdrian Hunter sym_start = query.value(7) 157776099f98SAdrian Hunter ip = query.value(8) 157876099f98SAdrian Hunter 157976099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 158076099f98SAdrian Hunter " FROM samples" 158176099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 158276099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 158376099f98SAdrian Hunter " ORDER BY samples.id" 158476099f98SAdrian Hunter " LIMIT 1") 158576099f98SAdrian Hunter if not query.next(): 158676099f98SAdrian Hunter return 158776099f98SAdrian Hunter if query.value(0) != dso: 158876099f98SAdrian Hunter # Cannot disassemble from one dso to another 158976099f98SAdrian Hunter return 159076099f98SAdrian Hunter bsym = query.value(1) 159176099f98SAdrian Hunter boff = query.value(2) 159276099f98SAdrian Hunter bsym_start = query.value(3) 159376099f98SAdrian Hunter if bsym == 0: 159476099f98SAdrian Hunter return 159576099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 159676099f98SAdrian Hunter if tot <= 0 or tot > 16384: 159776099f98SAdrian Hunter return 159876099f98SAdrian Hunter 159976099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 160076099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 160176099f98SAdrian Hunter if not f: 160276099f98SAdrian Hunter return 160376099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 160476099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 160576099f98SAdrian Hunter 160676099f98SAdrian Hunter buf_sz = tot + 16 160776099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 160876099f98SAdrian Hunter f.seek(sym_start + off) 160976099f98SAdrian Hunter buf.value = f.read(buf_sz) 161076099f98SAdrian Hunter buf_ptr = addressof(buf) 161176099f98SAdrian Hunter i = 0 161276099f98SAdrian Hunter while tot > 0: 161376099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 161476099f98SAdrian Hunter if cnt: 161576099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 161676099f98SAdrian Hunter for k in xrange(cnt): 161776099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 161876099f98SAdrian Hunter i += 1 161976099f98SAdrian Hunter while k < 15: 162076099f98SAdrian Hunter byte_str += " " 162176099f98SAdrian Hunter k += 1 1622530e22fdSAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) 162376099f98SAdrian Hunter self.child_count += 1 162476099f98SAdrian Hunter else: 162576099f98SAdrian Hunter return 162676099f98SAdrian Hunter buf_ptr += cnt 162776099f98SAdrian Hunter tot -= cnt 162876099f98SAdrian Hunter buf_sz -= cnt 162976099f98SAdrian Hunter ip += cnt 163076099f98SAdrian Hunter 163176099f98SAdrian Hunter def childCount(self): 163276099f98SAdrian Hunter if not self.query_done: 163376099f98SAdrian Hunter self.Select() 163476099f98SAdrian Hunter if not self.child_count: 163576099f98SAdrian Hunter return -1 163676099f98SAdrian Hunter return self.child_count 163776099f98SAdrian Hunter 163876099f98SAdrian Hunter def hasChildren(self): 163976099f98SAdrian Hunter if not self.query_done: 164076099f98SAdrian Hunter return True 164176099f98SAdrian Hunter return self.child_count > 0 164276099f98SAdrian Hunter 164376099f98SAdrian Hunter def getData(self, column): 164476099f98SAdrian Hunter return self.data[column] 164576099f98SAdrian Hunter 164676099f98SAdrian Hunter# Brance data model root item 164776099f98SAdrian Hunter 164876099f98SAdrian Hunterclass BranchRootItem(): 164976099f98SAdrian Hunter 165076099f98SAdrian Hunter def __init__(self): 165176099f98SAdrian Hunter self.child_count = 0 165276099f98SAdrian Hunter self.child_items = [] 165376099f98SAdrian Hunter self.level = 0 165476099f98SAdrian Hunter 165576099f98SAdrian Hunter def getChildItem(self, row): 165676099f98SAdrian Hunter return self.child_items[row] 165776099f98SAdrian Hunter 165876099f98SAdrian Hunter def getParentItem(self): 165976099f98SAdrian Hunter return None 166076099f98SAdrian Hunter 166176099f98SAdrian Hunter def getRow(self): 166276099f98SAdrian Hunter return 0 166376099f98SAdrian Hunter 166476099f98SAdrian Hunter def childCount(self): 166576099f98SAdrian Hunter return self.child_count 166676099f98SAdrian Hunter 166776099f98SAdrian Hunter def hasChildren(self): 166876099f98SAdrian Hunter return self.child_count > 0 166976099f98SAdrian Hunter 167076099f98SAdrian Hunter def getData(self, column): 167176099f98SAdrian Hunter return "" 167276099f98SAdrian Hunter 1673530e22fdSAdrian Hunter# Calculate instructions per cycle 1674530e22fdSAdrian Hunter 1675530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt): 1676530e22fdSAdrian Hunter if cyc_cnt and insn_cnt: 1677530e22fdSAdrian Hunter ipc = Decimal(float(insn_cnt) / cyc_cnt) 1678530e22fdSAdrian Hunter ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) 1679530e22fdSAdrian Hunter else: 1680530e22fdSAdrian Hunter ipc = "0" 1681530e22fdSAdrian Hunter return ipc 1682530e22fdSAdrian Hunter 168376099f98SAdrian Hunter# Branch data preparation 168476099f98SAdrian Hunter 1685530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data): 1686530e22fdSAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 1687530e22fdSAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 1688530e22fdSAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 1689530e22fdSAdrian Hunter " (" + dsoname(query.value(15)) + ")") 1690530e22fdSAdrian Hunter 1691530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data): 1692530e22fdSAdrian Hunter insn_cnt = query.value(16) 1693530e22fdSAdrian Hunter cyc_cnt = query.value(17) 1694530e22fdSAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 1695530e22fdSAdrian Hunter data.append(insn_cnt) 1696530e22fdSAdrian Hunter data.append(cyc_cnt) 1697530e22fdSAdrian Hunter data.append(ipc) 1698530e22fdSAdrian Hunter 169976099f98SAdrian Hunterdef BranchDataPrep(query): 170076099f98SAdrian Hunter data = [] 170176099f98SAdrian Hunter for i in xrange(0, 8): 170276099f98SAdrian Hunter data.append(query.value(i)) 1703530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 170476099f98SAdrian Hunter return data 170576099f98SAdrian Hunter 17068453c936SAdrian Hunterdef BranchDataPrepWA(query): 17078453c936SAdrian Hunter data = [] 17088453c936SAdrian Hunter data.append(query.value(0)) 17098453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 17108453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 17118453c936SAdrian Hunter for i in xrange(2, 8): 17128453c936SAdrian Hunter data.append(query.value(i)) 1713530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1714530e22fdSAdrian Hunter return data 1715530e22fdSAdrian Hunter 1716530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query): 1717530e22fdSAdrian Hunter data = [] 1718530e22fdSAdrian Hunter for i in xrange(0, 8): 1719530e22fdSAdrian Hunter data.append(query.value(i)) 1720530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1721530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1722530e22fdSAdrian Hunter return data 1723530e22fdSAdrian Hunter 1724530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query): 1725530e22fdSAdrian Hunter data = [] 1726530e22fdSAdrian Hunter data.append(query.value(0)) 1727530e22fdSAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 1728530e22fdSAdrian Hunter data.append("{:>19}".format(query.value(1))) 1729530e22fdSAdrian Hunter for i in xrange(2, 8): 1730530e22fdSAdrian Hunter data.append(query.value(i)) 1731530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1732530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 17338453c936SAdrian Hunter return data 17348453c936SAdrian Hunter 173576099f98SAdrian Hunter# Branch data model 173676099f98SAdrian Hunter 173776099f98SAdrian Hunterclass BranchModel(TreeModel): 173876099f98SAdrian Hunter 173976099f98SAdrian Hunter progress = Signal(object) 174076099f98SAdrian Hunter 174176099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 17424a0979d4SAdrian Hunter super(BranchModel, self).__init__(glb, None, parent) 174376099f98SAdrian Hunter self.event_id = event_id 174476099f98SAdrian Hunter self.more = True 174576099f98SAdrian Hunter self.populated = 0 1746530e22fdSAdrian Hunter self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") 1747530e22fdSAdrian Hunter if self.have_ipc: 1748530e22fdSAdrian Hunter select_ipc = ", insn_count, cyc_count" 1749530e22fdSAdrian Hunter prep_fn = BranchDataWithIPCPrep 1750530e22fdSAdrian Hunter prep_wa_fn = BranchDataWithIPCPrepWA 1751530e22fdSAdrian Hunter else: 1752530e22fdSAdrian Hunter select_ipc = "" 1753530e22fdSAdrian Hunter prep_fn = BranchDataPrep 1754530e22fdSAdrian Hunter prep_wa_fn = BranchDataPrepWA 175576099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 175676099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 175776099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 175876099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 1759530e22fdSAdrian Hunter + select_ipc + 176076099f98SAdrian Hunter " FROM samples" 176176099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 176276099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 176376099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 176476099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 176576099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 176676099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 176776099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 176876099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 176976099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 177076099f98SAdrian Hunter " ORDER BY samples.id" 177176099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 17728453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 1773530e22fdSAdrian Hunter prep = prep_fn 17748453c936SAdrian Hunter else: 1775530e22fdSAdrian Hunter prep = prep_wa_fn 17768453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 177776099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 177876099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 177976099f98SAdrian Hunter 1780a448ba23SAdrian Hunter def GetRoot(self): 1781a448ba23SAdrian Hunter return BranchRootItem() 1782a448ba23SAdrian Hunter 178376099f98SAdrian Hunter def columnCount(self, parent=None): 1784530e22fdSAdrian Hunter if self.have_ipc: 1785530e22fdSAdrian Hunter return 11 1786530e22fdSAdrian Hunter else: 178776099f98SAdrian Hunter return 8 178876099f98SAdrian Hunter 178976099f98SAdrian Hunter def columnHeader(self, column): 1790530e22fdSAdrian Hunter if self.have_ipc: 1791530e22fdSAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] 1792530e22fdSAdrian Hunter else: 179376099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 179476099f98SAdrian Hunter 179576099f98SAdrian Hunter def columnFont(self, column): 1796530e22fdSAdrian Hunter if self.have_ipc: 1797530e22fdSAdrian Hunter br_col = 10 1798530e22fdSAdrian Hunter else: 1799530e22fdSAdrian Hunter br_col = 7 1800530e22fdSAdrian Hunter if column != br_col: 180176099f98SAdrian Hunter return None 180276099f98SAdrian Hunter return QFont("Monospace") 180376099f98SAdrian Hunter 180476099f98SAdrian Hunter def DisplayData(self, item, index): 180576099f98SAdrian Hunter if item.level == 1: 180676099f98SAdrian Hunter self.FetchIfNeeded(item.row) 180776099f98SAdrian Hunter return item.getData(index.column()) 180876099f98SAdrian Hunter 180976099f98SAdrian Hunter def AddSample(self, data): 181076099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 181176099f98SAdrian Hunter self.root.child_items.append(child) 181276099f98SAdrian Hunter self.populated += 1 181376099f98SAdrian Hunter 181476099f98SAdrian Hunter def Update(self, fetched): 181576099f98SAdrian Hunter if not fetched: 181676099f98SAdrian Hunter self.more = False 181776099f98SAdrian Hunter self.progress.emit(0) 181876099f98SAdrian Hunter child_count = self.root.child_count 181976099f98SAdrian Hunter count = self.populated - child_count 182076099f98SAdrian Hunter if count > 0: 182176099f98SAdrian Hunter parent = QModelIndex() 182276099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 182376099f98SAdrian Hunter self.insertRows(child_count, count, parent) 182476099f98SAdrian Hunter self.root.child_count += count 182576099f98SAdrian Hunter self.endInsertRows() 182676099f98SAdrian Hunter self.progress.emit(self.root.child_count) 182776099f98SAdrian Hunter 182876099f98SAdrian Hunter def FetchMoreRecords(self, count): 182976099f98SAdrian Hunter current = self.root.child_count 183076099f98SAdrian Hunter if self.more: 183176099f98SAdrian Hunter self.fetcher.Fetch(count) 183276099f98SAdrian Hunter else: 183376099f98SAdrian Hunter self.progress.emit(0) 183476099f98SAdrian Hunter return current 183576099f98SAdrian Hunter 183676099f98SAdrian Hunter def HasMoreRecords(self): 183776099f98SAdrian Hunter return self.more 183876099f98SAdrian Hunter 18390bf0947aSAdrian Hunter# Report Variables 18400bf0947aSAdrian Hunter 18410bf0947aSAdrian Hunterclass ReportVars(): 18420bf0947aSAdrian Hunter 1843cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1844947cc38dSAdrian Hunter self.name = name 18450bf0947aSAdrian Hunter self.where_clause = where_clause 1846cd358012SAdrian Hunter self.limit = limit 18470bf0947aSAdrian Hunter 18480bf0947aSAdrian Hunter def UniqueId(self): 1849cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 18500bf0947aSAdrian Hunter 185176099f98SAdrian Hunter# Branch window 185276099f98SAdrian Hunter 185376099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 185476099f98SAdrian Hunter 1855947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 185676099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 185776099f98SAdrian Hunter 18580bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 185976099f98SAdrian Hunter 18600bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 186176099f98SAdrian Hunter 186276099f98SAdrian Hunter self.view = QTreeView() 186376099f98SAdrian Hunter self.view.setUniformRowHeights(True) 186496c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 186596c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 186676099f98SAdrian Hunter self.view.setModel(self.model) 186776099f98SAdrian Hunter 186876099f98SAdrian Hunter self.ResizeColumnsToContents() 186976099f98SAdrian Hunter 18709bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 18719bc4e4bfSAdrian Hunter 187276099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 187376099f98SAdrian Hunter 187476099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 187576099f98SAdrian Hunter 187676099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 187776099f98SAdrian Hunter 187876099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 187976099f98SAdrian Hunter 188076099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 188176099f98SAdrian Hunter 1882947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 188376099f98SAdrian Hunter 188476099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 188576099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 188676099f98SAdrian Hunter # so implement a crude alternative 188776099f98SAdrian Hunter mm = "MM" if column else "MMMM" 188876099f98SAdrian Hunter font = self.view.font() 188976099f98SAdrian Hunter metrics = QFontMetrics(font) 189076099f98SAdrian Hunter max = 0 189176099f98SAdrian Hunter for row in xrange(n): 189276099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 189376099f98SAdrian Hunter len = metrics.width(str(val) + mm) 189476099f98SAdrian Hunter max = len if len > max else max 189576099f98SAdrian Hunter val = self.model.columnHeader(column) 189676099f98SAdrian Hunter len = metrics.width(str(val) + mm) 189776099f98SAdrian Hunter max = len if len > max else max 189876099f98SAdrian Hunter self.view.setColumnWidth(column, max) 189976099f98SAdrian Hunter 190076099f98SAdrian Hunter def ResizeColumnsToContents(self): 190176099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 190276099f98SAdrian Hunter if n < 1: 190376099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 190476099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 190576099f98SAdrian Hunter return 190676099f98SAdrian Hunter columns = self.model.columnCount() 190776099f98SAdrian Hunter for i in xrange(columns): 190876099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 190976099f98SAdrian Hunter 191076099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 191176099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 191276099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 191376099f98SAdrian Hunter self.ResizeColumnsToContents() 191476099f98SAdrian Hunter 191576099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 191676099f98SAdrian Hunter self.view.setFocus() 191776099f98SAdrian Hunter self.find_bar.Busy() 191876099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 191976099f98SAdrian Hunter 192076099f98SAdrian Hunter def FindDone(self, row): 192176099f98SAdrian Hunter self.find_bar.Idle() 192276099f98SAdrian Hunter if row >= 0: 192376099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 192476099f98SAdrian Hunter else: 192576099f98SAdrian Hunter self.find_bar.NotFound() 192676099f98SAdrian Hunter 19271c3ca1b3SAdrian Hunter# Line edit data item 19281c3ca1b3SAdrian Hunter 19291c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 19301c3ca1b3SAdrian Hunter 1931cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 19321c3ca1b3SAdrian Hunter self.glb = glb 19331c3ca1b3SAdrian Hunter self.label = label 19341c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 19351c3ca1b3SAdrian Hunter self.parent = parent 19361c3ca1b3SAdrian Hunter self.id = id 19371c3ca1b3SAdrian Hunter 1938cd358012SAdrian Hunter self.value = default 19391c3ca1b3SAdrian Hunter 1940cd358012SAdrian Hunter self.widget = QLineEdit(default) 19411c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 19421c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 19431c3ca1b3SAdrian Hunter self.red = False 19441c3ca1b3SAdrian Hunter self.error = "" 19451c3ca1b3SAdrian Hunter self.validated = True 19461c3ca1b3SAdrian Hunter 19471c3ca1b3SAdrian Hunter if placeholder_text: 19481c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 19491c3ca1b3SAdrian Hunter 19501c3ca1b3SAdrian Hunter def TurnTextRed(self): 19511c3ca1b3SAdrian Hunter if not self.red: 19521c3ca1b3SAdrian Hunter palette = QPalette() 19531c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 19541c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 19551c3ca1b3SAdrian Hunter self.red = True 19561c3ca1b3SAdrian Hunter 19571c3ca1b3SAdrian Hunter def TurnTextNormal(self): 19581c3ca1b3SAdrian Hunter if self.red: 19591c3ca1b3SAdrian Hunter palette = QPalette() 19601c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 19611c3ca1b3SAdrian Hunter self.red = False 19621c3ca1b3SAdrian Hunter 19631c3ca1b3SAdrian Hunter def InvalidValue(self, value): 19641c3ca1b3SAdrian Hunter self.value = "" 19651c3ca1b3SAdrian Hunter self.TurnTextRed() 19661c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 19671c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 19681c3ca1b3SAdrian Hunter 19691c3ca1b3SAdrian Hunter def Invalidate(self): 19701c3ca1b3SAdrian Hunter self.validated = False 19711c3ca1b3SAdrian Hunter 19721c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19731c3ca1b3SAdrian Hunter self.value = input_string.strip() 19741c3ca1b3SAdrian Hunter 19751c3ca1b3SAdrian Hunter def Validate(self): 19761c3ca1b3SAdrian Hunter self.validated = True 19771c3ca1b3SAdrian Hunter self.error = "" 19781c3ca1b3SAdrian Hunter self.TurnTextNormal() 19791c3ca1b3SAdrian Hunter self.parent.ClearMessage() 19801c3ca1b3SAdrian Hunter input_string = self.widget.text() 19811c3ca1b3SAdrian Hunter if not len(input_string.strip()): 19821c3ca1b3SAdrian Hunter self.value = "" 19831c3ca1b3SAdrian Hunter return 19841c3ca1b3SAdrian Hunter self.DoValidate(input_string) 19851c3ca1b3SAdrian Hunter 19861c3ca1b3SAdrian Hunter def IsValid(self): 19871c3ca1b3SAdrian Hunter if not self.validated: 19881c3ca1b3SAdrian Hunter self.Validate() 19891c3ca1b3SAdrian Hunter if len(self.error): 19901c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 19911c3ca1b3SAdrian Hunter return False 19921c3ca1b3SAdrian Hunter return True 19931c3ca1b3SAdrian Hunter 19941c3ca1b3SAdrian Hunter def IsNumber(self, value): 19951c3ca1b3SAdrian Hunter try: 19961c3ca1b3SAdrian Hunter x = int(value) 19971c3ca1b3SAdrian Hunter except: 19981c3ca1b3SAdrian Hunter x = 0 19991c3ca1b3SAdrian Hunter return str(x) == value 20001c3ca1b3SAdrian Hunter 20011c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 20021c3ca1b3SAdrian Hunter 20031c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 20041c3ca1b3SAdrian Hunter 20051c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 20061c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 20071c3ca1b3SAdrian Hunter 20081c3ca1b3SAdrian Hunter self.column_name = column_name 20091c3ca1b3SAdrian Hunter 20101c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 20111c3ca1b3SAdrian Hunter singles = [] 20121c3ca1b3SAdrian Hunter ranges = [] 20131c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 20141c3ca1b3SAdrian Hunter if "-" in value: 20151c3ca1b3SAdrian Hunter vrange = value.split("-") 20161c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 20171c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20181c3ca1b3SAdrian Hunter ranges.append(vrange) 20191c3ca1b3SAdrian Hunter else: 20201c3ca1b3SAdrian Hunter if not self.IsNumber(value): 20211c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20221c3ca1b3SAdrian Hunter singles.append(value) 20231c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 20241c3ca1b3SAdrian Hunter if len(singles): 20251c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 20261c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 20271c3ca1b3SAdrian Hunter 2028cd358012SAdrian Hunter# Positive integer dialog data item 2029cd358012SAdrian Hunter 2030cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 2031cd358012SAdrian Hunter 2032cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 2033cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 2034cd358012SAdrian Hunter 2035cd358012SAdrian Hunter def DoValidate(self, input_string): 2036cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 2037cd358012SAdrian Hunter return self.InvalidValue(input_string) 2038cd358012SAdrian Hunter value = int(input_string.strip()) 2039cd358012SAdrian Hunter if value <= 0: 2040cd358012SAdrian Hunter return self.InvalidValue(input_string) 2041cd358012SAdrian Hunter self.value = str(value) 2042cd358012SAdrian Hunter 20431c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 20441c3ca1b3SAdrian Hunter 20451c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 20461c3ca1b3SAdrian Hunter 20471c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 20481c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 20491c3ca1b3SAdrian Hunter 20501c3ca1b3SAdrian Hunter self.table_name = table_name 20511c3ca1b3SAdrian Hunter self.match_column = match_column 20521c3ca1b3SAdrian Hunter self.column_name1 = column_name1 20531c3ca1b3SAdrian Hunter self.column_name2 = column_name2 20541c3ca1b3SAdrian Hunter 20551c3ca1b3SAdrian Hunter def ValueToIds(self, value): 20561c3ca1b3SAdrian Hunter ids = [] 20571c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 20581c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 20591c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 20601c3ca1b3SAdrian Hunter if ret: 20611c3ca1b3SAdrian Hunter while query.next(): 20621c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 20631c3ca1b3SAdrian Hunter return ids 20641c3ca1b3SAdrian Hunter 20651c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 20661c3ca1b3SAdrian Hunter all_ids = [] 20671c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 20681c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 20691c3ca1b3SAdrian Hunter if len(ids): 20701c3ca1b3SAdrian Hunter all_ids.extend(ids) 20711c3ca1b3SAdrian Hunter else: 20721c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20731c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 20741c3ca1b3SAdrian Hunter if self.column_name2: 20751c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 20761c3ca1b3SAdrian Hunter 20771c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 20781c3ca1b3SAdrian Hunter 20791c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 20801c3ca1b3SAdrian Hunter 20811c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 20821c3ca1b3SAdrian Hunter self.column_name = column_name 20831c3ca1b3SAdrian Hunter 20841c3ca1b3SAdrian Hunter self.last_id = 0 20851c3ca1b3SAdrian Hunter self.first_time = 0 20861c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 20871c3ca1b3SAdrian Hunter 20881c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 20891c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 20901c3ca1b3SAdrian Hunter if query.next(): 20911c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 20929a9dae36SAdrian Hunter self.first_time = int(glb.HostStartTime()) 20939a9dae36SAdrian Hunter self.last_time = int(glb.HostFinishTime()) 20941c3ca1b3SAdrian Hunter if placeholder_text: 20951c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 20961c3ca1b3SAdrian Hunter 20971c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 20981c3ca1b3SAdrian Hunter 20991c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 21001c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 21011c3ca1b3SAdrian Hunter if query.next(): 21021c3ca1b3SAdrian Hunter return True, int(query.value(0)) 21031c3ca1b3SAdrian Hunter else: 21041c3ca1b3SAdrian Hunter return False, 0 21051c3ca1b3SAdrian Hunter 21061c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 21071c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 21081c3ca1b3SAdrian Hunter while True: 21091c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 21101c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 21111c3ca1b3SAdrian Hunter if not query.next(): 21121c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 21131c3ca1b3SAdrian Hunter if not ok: 21141c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 21151c3ca1b3SAdrian Hunter if not ok: 21161c3ca1b3SAdrian Hunter return str(higher_id) 21171c3ca1b3SAdrian Hunter next_id = dbid 21181c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 21191c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 21201c3ca1b3SAdrian Hunter if get_floor: 21211c3ca1b3SAdrian Hunter if target_time > next_time: 21221c3ca1b3SAdrian Hunter lower_id = next_id 21231c3ca1b3SAdrian Hunter else: 21241c3ca1b3SAdrian Hunter higher_id = next_id 21251c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 21261c3ca1b3SAdrian Hunter return str(higher_id) 21271c3ca1b3SAdrian Hunter else: 21281c3ca1b3SAdrian Hunter if target_time >= next_time: 21291c3ca1b3SAdrian Hunter lower_id = next_id 21301c3ca1b3SAdrian Hunter else: 21311c3ca1b3SAdrian Hunter higher_id = next_id 21321c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 21331c3ca1b3SAdrian Hunter return str(lower_id) 21341c3ca1b3SAdrian Hunter 21351c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 21361c3ca1b3SAdrian Hunter mult = 1 21371c3ca1b3SAdrian Hunter suffix = val[-2:] 21381c3ca1b3SAdrian Hunter if suffix == "ms": 21391c3ca1b3SAdrian Hunter mult = 1000000 21401c3ca1b3SAdrian Hunter elif suffix == "us": 21411c3ca1b3SAdrian Hunter mult = 1000 21421c3ca1b3SAdrian Hunter elif suffix == "ns": 21431c3ca1b3SAdrian Hunter mult = 1 21441c3ca1b3SAdrian Hunter else: 21451c3ca1b3SAdrian Hunter return val 21461c3ca1b3SAdrian Hunter val = val[:-2].strip() 21471c3ca1b3SAdrian Hunter if not self.IsNumber(val): 21481c3ca1b3SAdrian Hunter return val 21491c3ca1b3SAdrian Hunter val = int(val) * mult 21501c3ca1b3SAdrian Hunter if val >= 0: 21511c3ca1b3SAdrian Hunter val += self.first_time 21521c3ca1b3SAdrian Hunter else: 21531c3ca1b3SAdrian Hunter val += self.last_time 21541c3ca1b3SAdrian Hunter return str(val) 21551c3ca1b3SAdrian Hunter 21561c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 21571c3ca1b3SAdrian Hunter if vrange[0] == "": 21581c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 21591c3ca1b3SAdrian Hunter if vrange[1] == "": 21601c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 21611c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 21621c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 21631c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 21641c3ca1b3SAdrian Hunter return False 21651c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 21661c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 21671c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 21681c3ca1b3SAdrian Hunter return False 21691c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 21701c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 21711c3ca1b3SAdrian Hunter return True 21721c3ca1b3SAdrian Hunter 21731c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 21741c3ca1b3SAdrian Hunter n = value.count("-") 21751c3ca1b3SAdrian Hunter if n == 1: 21761c3ca1b3SAdrian Hunter pass 21771c3ca1b3SAdrian Hunter elif n == 2: 21781c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 21791c3ca1b3SAdrian Hunter n = 1 21801c3ca1b3SAdrian Hunter elif n == 3: 21811c3ca1b3SAdrian Hunter n = 2 21821c3ca1b3SAdrian Hunter else: 21831c3ca1b3SAdrian Hunter return False 21841c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 21851c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 21861c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 21871c3ca1b3SAdrian Hunter ranges.append(vrange) 21881c3ca1b3SAdrian Hunter return True 21891c3ca1b3SAdrian Hunter return False 21901c3ca1b3SAdrian Hunter 21911c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 21921c3ca1b3SAdrian Hunter ranges = [] 21931c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 21941c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 21951c3ca1b3SAdrian Hunter return self.InvalidValue(value) 21961c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 21971c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 21981c3ca1b3SAdrian Hunter 21990924cd68SAdrian Hunter# Report Dialog Base 2200210cf1f9SAdrian Hunter 22010924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 2202210cf1f9SAdrian Hunter 22030924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 22040924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2205210cf1f9SAdrian Hunter 2206210cf1f9SAdrian Hunter self.glb = glb 2207210cf1f9SAdrian Hunter 22080bf0947aSAdrian Hunter self.report_vars = ReportVars() 2209210cf1f9SAdrian Hunter 22100924cd68SAdrian Hunter self.setWindowTitle(title) 2211210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2212210cf1f9SAdrian Hunter 22131c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2214210cf1f9SAdrian Hunter 22150924cd68SAdrian Hunter self.partial = partial 22160924cd68SAdrian Hunter 2217210cf1f9SAdrian Hunter self.grid = QGridLayout() 2218210cf1f9SAdrian Hunter 2219210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2220210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2221210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2222210cf1f9SAdrian Hunter 2223210cf1f9SAdrian Hunter self.status = QLabel() 2224210cf1f9SAdrian Hunter 2225210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2226210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2227210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2228210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2229210cf1f9SAdrian Hunter 2230210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2231210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2232210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2233210cf1f9SAdrian Hunter 2234210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2235210cf1f9SAdrian Hunter #self.hbox.addStretch() 2236210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2237210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2238210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2239210cf1f9SAdrian Hunter 2240210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2241210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2242210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2243210cf1f9SAdrian Hunter 224426688729SAdrian Hunter self.setLayout(self.vbox) 2245210cf1f9SAdrian Hunter 2246210cf1f9SAdrian Hunter def Ok(self): 22470bf0947aSAdrian Hunter vars = self.report_vars 22481c3ca1b3SAdrian Hunter for d in self.data_items: 22491c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 22501c3ca1b3SAdrian Hunter vars.name = d.value 2251947cc38dSAdrian Hunter if not vars.name: 2252210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2253210cf1f9SAdrian Hunter return 2254210cf1f9SAdrian Hunter for d in self.data_items: 2255210cf1f9SAdrian Hunter if not d.IsValid(): 2256210cf1f9SAdrian Hunter return 2257210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2258cd358012SAdrian Hunter if d.id == "LIMIT": 2259cd358012SAdrian Hunter vars.limit = d.value 2260cd358012SAdrian Hunter elif len(d.value): 22610bf0947aSAdrian Hunter if len(vars.where_clause): 22620bf0947aSAdrian Hunter vars.where_clause += " AND " 22630bf0947aSAdrian Hunter vars.where_clause += d.value 22640bf0947aSAdrian Hunter if len(vars.where_clause): 22650924cd68SAdrian Hunter if self.partial: 22660bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2267210cf1f9SAdrian Hunter else: 22680bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2269210cf1f9SAdrian Hunter self.accept() 2270210cf1f9SAdrian Hunter 2271210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2272210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2273210cf1f9SAdrian Hunter 2274210cf1f9SAdrian Hunter def ClearMessage(self): 2275210cf1f9SAdrian Hunter self.status.setText("") 2276210cf1f9SAdrian Hunter 22770924cd68SAdrian Hunter# Selected branch report creation dialog 22780924cd68SAdrian Hunter 22790924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 22800924cd68SAdrian Hunter 22810924cd68SAdrian Hunter def __init__(self, glb, parent=None): 22820924cd68SAdrian Hunter title = "Selected Branches" 22831c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 22841c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 22851c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 22861c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 22871c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 22881c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 22891c3ca1b3SAdrian 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), 22901c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 22911c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 22920924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 22930924cd68SAdrian Hunter 229476099f98SAdrian Hunter# Event list 229576099f98SAdrian Hunter 229676099f98SAdrian Hunterdef GetEventList(db): 229776099f98SAdrian Hunter events = [] 229876099f98SAdrian Hunter query = QSqlQuery(db) 229976099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 230076099f98SAdrian Hunter while query.next(): 230176099f98SAdrian Hunter events.append(query.value(0)) 230276099f98SAdrian Hunter return events 230376099f98SAdrian Hunter 2304655cb952SAdrian Hunter# Is a table selectable 2305655cb952SAdrian Hunter 2306530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"): 2307655cb952SAdrian Hunter query = QSqlQuery(db) 2308655cb952SAdrian Hunter try: 2309530e22fdSAdrian Hunter QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") 2310655cb952SAdrian Hunter except: 2311655cb952SAdrian Hunter return False 2312655cb952SAdrian Hunter return True 2313655cb952SAdrian Hunter 23148392b74bSAdrian Hunter# SQL table data model item 23158392b74bSAdrian Hunter 23168392b74bSAdrian Hunterclass SQLTableItem(): 23178392b74bSAdrian Hunter 23188392b74bSAdrian Hunter def __init__(self, row, data): 23198392b74bSAdrian Hunter self.row = row 23208392b74bSAdrian Hunter self.data = data 23218392b74bSAdrian Hunter 23228392b74bSAdrian Hunter def getData(self, column): 23238392b74bSAdrian Hunter return self.data[column] 23248392b74bSAdrian Hunter 23258392b74bSAdrian Hunter# SQL table data model 23268392b74bSAdrian Hunter 23278392b74bSAdrian Hunterclass SQLTableModel(TableModel): 23288392b74bSAdrian Hunter 23298392b74bSAdrian Hunter progress = Signal(object) 23308392b74bSAdrian Hunter 23318c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 23328392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 23338392b74bSAdrian Hunter self.glb = glb 23348392b74bSAdrian Hunter self.more = True 23358392b74bSAdrian Hunter self.populated = 0 23368c90fef9SAdrian Hunter self.column_headers = column_headers 23378453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 23388392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 23398392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 23408392b74bSAdrian Hunter 23418392b74bSAdrian Hunter def DisplayData(self, item, index): 23428392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 23438392b74bSAdrian Hunter return item.getData(index.column()) 23448392b74bSAdrian Hunter 23458392b74bSAdrian Hunter def AddSample(self, data): 23468392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 23478392b74bSAdrian Hunter self.child_items.append(child) 23488392b74bSAdrian Hunter self.populated += 1 23498392b74bSAdrian Hunter 23508392b74bSAdrian Hunter def Update(self, fetched): 23518392b74bSAdrian Hunter if not fetched: 23528392b74bSAdrian Hunter self.more = False 23538392b74bSAdrian Hunter self.progress.emit(0) 23548392b74bSAdrian Hunter child_count = self.child_count 23558392b74bSAdrian Hunter count = self.populated - child_count 23568392b74bSAdrian Hunter if count > 0: 23578392b74bSAdrian Hunter parent = QModelIndex() 23588392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 23598392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 23608392b74bSAdrian Hunter self.child_count += count 23618392b74bSAdrian Hunter self.endInsertRows() 23628392b74bSAdrian Hunter self.progress.emit(self.child_count) 23638392b74bSAdrian Hunter 23648392b74bSAdrian Hunter def FetchMoreRecords(self, count): 23658392b74bSAdrian Hunter current = self.child_count 23668392b74bSAdrian Hunter if self.more: 23678392b74bSAdrian Hunter self.fetcher.Fetch(count) 23688392b74bSAdrian Hunter else: 23698392b74bSAdrian Hunter self.progress.emit(0) 23708392b74bSAdrian Hunter return current 23718392b74bSAdrian Hunter 23728392b74bSAdrian Hunter def HasMoreRecords(self): 23738392b74bSAdrian Hunter return self.more 23748392b74bSAdrian Hunter 23758c90fef9SAdrian Hunter def columnCount(self, parent=None): 23768c90fef9SAdrian Hunter return len(self.column_headers) 23778c90fef9SAdrian Hunter 23788c90fef9SAdrian Hunter def columnHeader(self, column): 23798c90fef9SAdrian Hunter return self.column_headers[column] 23808c90fef9SAdrian Hunter 23818453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 23828453c936SAdrian Hunter data = [] 23838453c936SAdrian Hunter for i in xrange(count): 23848453c936SAdrian Hunter data.append(query.value(i)) 23858453c936SAdrian Hunter return data 23868453c936SAdrian Hunter 23878392b74bSAdrian Hunter# SQL automatic table data model 23888392b74bSAdrian Hunter 23898392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 23908392b74bSAdrian Hunter 23918392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 23928392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 23938392b74bSAdrian Hunter if table_name == "comm_threads_view": 23948392b74bSAdrian Hunter # For now, comm_threads_view has no id column 23958392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 23968c90fef9SAdrian Hunter column_headers = [] 23978392b74bSAdrian Hunter query = QSqlQuery(glb.db) 23988392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 23998392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 24008392b74bSAdrian Hunter while query.next(): 24018c90fef9SAdrian Hunter column_headers.append(query.value(1)) 24028392b74bSAdrian Hunter if table_name == "sqlite_master": 24038392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 24048392b74bSAdrian Hunter else: 24058392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 24068392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 24078392b74bSAdrian Hunter select_table_name = table_name[19:] 24088392b74bSAdrian Hunter schema = "information_schema" 24098392b74bSAdrian Hunter else: 24108392b74bSAdrian Hunter select_table_name = table_name 24118392b74bSAdrian Hunter schema = "public" 24128392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 24138392b74bSAdrian Hunter while query.next(): 24148c90fef9SAdrian Hunter column_headers.append(query.value(0)) 24158453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 24168453c936SAdrian Hunter if table_name == "samples_view": 24178453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 24188453c936SAdrian Hunter if table_name == "samples": 24198453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 24208c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 24218392b74bSAdrian Hunter 24228453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 24238453c936SAdrian Hunter data = [] 24248453c936SAdrian Hunter data.append(query.value(0)) 24258453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 24268453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 24278453c936SAdrian Hunter for i in xrange(2, count): 24288453c936SAdrian Hunter data.append(query.value(i)) 24298453c936SAdrian Hunter return data 24308453c936SAdrian Hunter 24318453c936SAdrian Hunter def samples_DataPrep(self, query, count): 24328453c936SAdrian Hunter data = [] 24338453c936SAdrian Hunter for i in xrange(9): 24348453c936SAdrian Hunter data.append(query.value(i)) 24358453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 24368453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 24378453c936SAdrian Hunter for i in xrange(10, count): 24388453c936SAdrian Hunter data.append(query.value(i)) 24398453c936SAdrian Hunter return data 24408453c936SAdrian Hunter 24418392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 24428392b74bSAdrian Hunter 24438392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 24448392b74bSAdrian Hunter 24458392b74bSAdrian Hunter def __init__(self, parent=None): 24468392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 24478392b74bSAdrian Hunter 24488392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 24498392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 24508392b74bSAdrian Hunter # so implement a crude alternative 24518392b74bSAdrian Hunter font = self.view.font() 24528392b74bSAdrian Hunter metrics = QFontMetrics(font) 24538392b74bSAdrian Hunter max = 0 24548392b74bSAdrian Hunter for row in xrange(n): 24558392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 24568392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 24578392b74bSAdrian Hunter max = len if len > max else max 24588392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 24598392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 24608392b74bSAdrian Hunter max = len if len > max else max 24618392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 24628392b74bSAdrian Hunter 24638392b74bSAdrian Hunter def ResizeColumnsToContents(self): 24648392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 24658392b74bSAdrian Hunter if n < 1: 24668392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 24678392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 24688392b74bSAdrian Hunter return 24698392b74bSAdrian Hunter columns = self.data_model.columnCount() 24708392b74bSAdrian Hunter for i in xrange(columns): 24718392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 24728392b74bSAdrian Hunter 24738392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 24748392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 24758392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 24768392b74bSAdrian Hunter self.ResizeColumnsToContents() 24778392b74bSAdrian Hunter 247896c43b9aSAdrian Hunter# Convert value to CSV 247996c43b9aSAdrian Hunter 248096c43b9aSAdrian Hunterdef ToCSValue(val): 248196c43b9aSAdrian Hunter if '"' in val: 248296c43b9aSAdrian Hunter val = val.replace('"', '""') 248396c43b9aSAdrian Hunter if "," in val or '"' in val: 248496c43b9aSAdrian Hunter val = '"' + val + '"' 248596c43b9aSAdrian Hunter return val 248696c43b9aSAdrian Hunter 248796c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 248896c43b9aSAdrian Hunter 248996c43b9aSAdrian Hunterglb_max_cols = 1000 249096c43b9aSAdrian Hunter 249196c43b9aSAdrian Hunterdef RowColumnKey(a): 249296c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 249396c43b9aSAdrian Hunter 249496c43b9aSAdrian Hunter# Copy selected table cells to clipboard 249596c43b9aSAdrian Hunter 249696c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 249796c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 249896c43b9aSAdrian Hunter idx_cnt = len(indexes) 249996c43b9aSAdrian Hunter if not idx_cnt: 250096c43b9aSAdrian Hunter return 250196c43b9aSAdrian Hunter if idx_cnt == 1: 250296c43b9aSAdrian Hunter with_hdr=False 250396c43b9aSAdrian Hunter min_row = indexes[0].row() 250496c43b9aSAdrian Hunter max_row = indexes[0].row() 250596c43b9aSAdrian Hunter min_col = indexes[0].column() 250696c43b9aSAdrian Hunter max_col = indexes[0].column() 250796c43b9aSAdrian Hunter for i in indexes: 250896c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 250996c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 251096c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 251196c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 251296c43b9aSAdrian Hunter if max_col > glb_max_cols: 251396c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 251496c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 251596c43b9aSAdrian Hunter for i in indexes: 251696c43b9aSAdrian Hunter c = i.column() - min_col 251796c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 251896c43b9aSAdrian Hunter text = "" 251996c43b9aSAdrian Hunter pad = "" 252096c43b9aSAdrian Hunter sep = "" 252196c43b9aSAdrian Hunter if with_hdr: 252296c43b9aSAdrian Hunter model = indexes[0].model() 252396c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 252496c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 252596c43b9aSAdrian Hunter if as_csv: 252696c43b9aSAdrian Hunter text += sep + ToCSValue(val) 252796c43b9aSAdrian Hunter sep = "," 252896c43b9aSAdrian Hunter else: 252996c43b9aSAdrian Hunter c = col - min_col 253096c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 253196c43b9aSAdrian Hunter width = max_width[c] 253296c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 253396c43b9aSAdrian Hunter if align & Qt.AlignRight: 253496c43b9aSAdrian Hunter val = val.rjust(width) 253596c43b9aSAdrian Hunter text += pad + sep + val 253696c43b9aSAdrian Hunter pad = " " * (width - len(val)) 253796c43b9aSAdrian Hunter sep = " " 253896c43b9aSAdrian Hunter text += "\n" 253996c43b9aSAdrian Hunter pad = "" 254096c43b9aSAdrian Hunter sep = "" 254196c43b9aSAdrian Hunter last_row = min_row 254296c43b9aSAdrian Hunter for i in indexes: 254396c43b9aSAdrian Hunter if i.row() > last_row: 254496c43b9aSAdrian Hunter last_row = i.row() 254596c43b9aSAdrian Hunter text += "\n" 254696c43b9aSAdrian Hunter pad = "" 254796c43b9aSAdrian Hunter sep = "" 254896c43b9aSAdrian Hunter if as_csv: 254996c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 255096c43b9aSAdrian Hunter sep = "," 255196c43b9aSAdrian Hunter else: 255296c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 255396c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 255496c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 255596c43b9aSAdrian Hunter else: 255696c43b9aSAdrian Hunter val = str(i.data()) 255796c43b9aSAdrian Hunter text += pad + sep + val 255896c43b9aSAdrian Hunter pad = " " * (width - len(val)) 255996c43b9aSAdrian Hunter sep = " " 256096c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 256196c43b9aSAdrian Hunter 256296c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 256396c43b9aSAdrian Hunter indexes = view.selectedIndexes() 256496c43b9aSAdrian Hunter if not len(indexes): 256596c43b9aSAdrian Hunter return 256696c43b9aSAdrian Hunter 256796c43b9aSAdrian Hunter selection = view.selectionModel() 256896c43b9aSAdrian Hunter 256996c43b9aSAdrian Hunter first = None 257096c43b9aSAdrian Hunter for i in indexes: 257196c43b9aSAdrian Hunter above = view.indexAbove(i) 257296c43b9aSAdrian Hunter if not selection.isSelected(above): 257396c43b9aSAdrian Hunter first = i 257496c43b9aSAdrian Hunter break 257596c43b9aSAdrian Hunter 257696c43b9aSAdrian Hunter if first is None: 257796c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 257896c43b9aSAdrian Hunter 257996c43b9aSAdrian Hunter model = first.model() 258096c43b9aSAdrian Hunter row_cnt = 0 258196c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 258296c43b9aSAdrian Hunter max_width = [0] * col_cnt 258396c43b9aSAdrian Hunter 258496c43b9aSAdrian Hunter indent_sz = 2 258596c43b9aSAdrian Hunter indent_str = " " * indent_sz 258696c43b9aSAdrian Hunter 258796c43b9aSAdrian Hunter expanded_mark_sz = 2 258896c43b9aSAdrian Hunter if sys.version_info[0] == 3: 258996c43b9aSAdrian Hunter expanded_mark = "\u25BC " 259096c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 259196c43b9aSAdrian Hunter else: 259296c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 259396c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 259496c43b9aSAdrian Hunter leaf_mark = " " 259596c43b9aSAdrian Hunter 259696c43b9aSAdrian Hunter if not as_csv: 259796c43b9aSAdrian Hunter pos = first 259896c43b9aSAdrian Hunter while True: 259996c43b9aSAdrian Hunter row_cnt += 1 260096c43b9aSAdrian Hunter row = pos.row() 260196c43b9aSAdrian Hunter for c in range(col_cnt): 260296c43b9aSAdrian Hunter i = pos.sibling(row, c) 260396c43b9aSAdrian Hunter if c: 260496c43b9aSAdrian Hunter n = len(str(i.data())) 260596c43b9aSAdrian Hunter else: 260696c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 260796c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 260896c43b9aSAdrian Hunter n += expanded_mark_sz 260996c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 261096c43b9aSAdrian Hunter pos = view.indexBelow(pos) 261196c43b9aSAdrian Hunter if not selection.isSelected(pos): 261296c43b9aSAdrian Hunter break 261396c43b9aSAdrian Hunter 261496c43b9aSAdrian Hunter text = "" 261596c43b9aSAdrian Hunter pad = "" 261696c43b9aSAdrian Hunter sep = "" 261796c43b9aSAdrian Hunter if with_hdr: 261896c43b9aSAdrian Hunter for c in range(col_cnt): 261996c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 262096c43b9aSAdrian Hunter if as_csv: 262196c43b9aSAdrian Hunter text += sep + ToCSValue(val) 262296c43b9aSAdrian Hunter sep = "," 262396c43b9aSAdrian Hunter else: 262496c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 262596c43b9aSAdrian Hunter width = max_width[c] 262696c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 262796c43b9aSAdrian Hunter if align & Qt.AlignRight: 262896c43b9aSAdrian Hunter val = val.rjust(width) 262996c43b9aSAdrian Hunter text += pad + sep + val 263096c43b9aSAdrian Hunter pad = " " * (width - len(val)) 263196c43b9aSAdrian Hunter sep = " " 263296c43b9aSAdrian Hunter text += "\n" 263396c43b9aSAdrian Hunter pad = "" 263496c43b9aSAdrian Hunter sep = "" 263596c43b9aSAdrian Hunter 263696c43b9aSAdrian Hunter pos = first 263796c43b9aSAdrian Hunter while True: 263896c43b9aSAdrian Hunter row = pos.row() 263996c43b9aSAdrian Hunter for c in range(col_cnt): 264096c43b9aSAdrian Hunter i = pos.sibling(row, c) 264196c43b9aSAdrian Hunter val = str(i.data()) 264296c43b9aSAdrian Hunter if not c: 264396c43b9aSAdrian Hunter if model.hasChildren(i): 264496c43b9aSAdrian Hunter if view.isExpanded(i): 264596c43b9aSAdrian Hunter mark = expanded_mark 264696c43b9aSAdrian Hunter else: 264796c43b9aSAdrian Hunter mark = not_expanded_mark 264896c43b9aSAdrian Hunter else: 264996c43b9aSAdrian Hunter mark = leaf_mark 265096c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 265196c43b9aSAdrian Hunter if as_csv: 265296c43b9aSAdrian Hunter text += sep + ToCSValue(val) 265396c43b9aSAdrian Hunter sep = "," 265496c43b9aSAdrian Hunter else: 265596c43b9aSAdrian Hunter width = max_width[c] 265696c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 265796c43b9aSAdrian Hunter val = val.rjust(width) 265896c43b9aSAdrian Hunter text += pad + sep + val 265996c43b9aSAdrian Hunter pad = " " * (width - len(val)) 266096c43b9aSAdrian Hunter sep = " " 266196c43b9aSAdrian Hunter pos = view.indexBelow(pos) 266296c43b9aSAdrian Hunter if not selection.isSelected(pos): 266396c43b9aSAdrian Hunter break 266496c43b9aSAdrian Hunter text = text.rstrip() + "\n" 266596c43b9aSAdrian Hunter pad = "" 266696c43b9aSAdrian Hunter sep = "" 266796c43b9aSAdrian Hunter 266896c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 266996c43b9aSAdrian Hunter 267096c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 267196c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 267296c43b9aSAdrian Hunter 267396c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 267496c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 267596c43b9aSAdrian Hunter 267696c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 267796c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 267896c43b9aSAdrian Hunter 26799bc4e4bfSAdrian Hunter# Context menu 26809bc4e4bfSAdrian Hunter 26819bc4e4bfSAdrian Hunterclass ContextMenu(object): 26829bc4e4bfSAdrian Hunter 26839bc4e4bfSAdrian Hunter def __init__(self, view): 26849bc4e4bfSAdrian Hunter self.view = view 26859bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 26869bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 26879bc4e4bfSAdrian Hunter 26889bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 26899bc4e4bfSAdrian Hunter menu = QMenu(self.view) 26909bc4e4bfSAdrian Hunter self.AddActions(menu) 26919bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 26929bc4e4bfSAdrian Hunter 26939bc4e4bfSAdrian Hunter def AddCopy(self, menu): 26949bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 26959bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 26969bc4e4bfSAdrian Hunter 26979bc4e4bfSAdrian Hunter def AddActions(self, menu): 26989bc4e4bfSAdrian Hunter self.AddCopy(menu) 26999bc4e4bfSAdrian Hunter 27009bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 27019bc4e4bfSAdrian Hunter 27029bc4e4bfSAdrian Hunter def __init__(self, view): 27039bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 27049bc4e4bfSAdrian Hunter 27059bc4e4bfSAdrian Hunter def AddActions(self, menu): 27069bc4e4bfSAdrian Hunter i = self.view.currentIndex() 27079bc4e4bfSAdrian Hunter text = str(i.data()).strip() 27089bc4e4bfSAdrian Hunter if len(text): 27099bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 27109bc4e4bfSAdrian Hunter self.AddCopy(menu) 27119bc4e4bfSAdrian Hunter 27128392b74bSAdrian Hunter# Table window 27138392b74bSAdrian Hunter 27148392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 27158392b74bSAdrian Hunter 27168392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 27178392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 27188392b74bSAdrian Hunter 27198392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 27208392b74bSAdrian Hunter 27218392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 27228392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 27238392b74bSAdrian Hunter 27248392b74bSAdrian Hunter self.view = QTableView() 27258392b74bSAdrian Hunter self.view.setModel(self.model) 27268392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 27278392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 27288392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 27298392b74bSAdrian Hunter self.view.setSortingEnabled(True) 273096c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 273196c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 27328392b74bSAdrian Hunter 27338392b74bSAdrian Hunter self.ResizeColumnsToContents() 27348392b74bSAdrian Hunter 27359bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 27369bc4e4bfSAdrian Hunter 27378392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 27388392b74bSAdrian Hunter 27398392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 27408392b74bSAdrian Hunter 27418392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 27428392b74bSAdrian Hunter 27438392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 27448392b74bSAdrian Hunter 27458392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 27468392b74bSAdrian Hunter 27478392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 27488392b74bSAdrian Hunter 27498392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 27508392b74bSAdrian Hunter self.view.setFocus() 27518392b74bSAdrian Hunter self.find_bar.Busy() 27528392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 27538392b74bSAdrian Hunter 27548392b74bSAdrian Hunter def FindDone(self, row): 27558392b74bSAdrian Hunter self.find_bar.Idle() 27568392b74bSAdrian Hunter if row >= 0: 275735fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 27588392b74bSAdrian Hunter else: 27598392b74bSAdrian Hunter self.find_bar.NotFound() 27608392b74bSAdrian Hunter 27618392b74bSAdrian Hunter# Table list 27628392b74bSAdrian Hunter 27638392b74bSAdrian Hunterdef GetTableList(glb): 27648392b74bSAdrian Hunter tables = [] 27658392b74bSAdrian Hunter query = QSqlQuery(glb.db) 27668392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 27678392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 27688392b74bSAdrian Hunter else: 27698392b74bSAdrian 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") 27708392b74bSAdrian Hunter while query.next(): 27718392b74bSAdrian Hunter tables.append(query.value(0)) 27728392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 27738392b74bSAdrian Hunter tables.append("sqlite_master") 27748392b74bSAdrian Hunter else: 27758392b74bSAdrian Hunter tables.append("information_schema.tables") 27768392b74bSAdrian Hunter tables.append("information_schema.views") 27778392b74bSAdrian Hunter tables.append("information_schema.columns") 27788392b74bSAdrian Hunter return tables 27798392b74bSAdrian Hunter 2780cd358012SAdrian Hunter# Top Calls data model 2781cd358012SAdrian Hunter 2782cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2783cd358012SAdrian Hunter 2784cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2785cd358012SAdrian Hunter text = "" 2786cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2787cd358012SAdrian Hunter text = "::text" 2788cd358012SAdrian Hunter limit = "" 2789cd358012SAdrian Hunter if len(report_vars.limit): 2790cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2791cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2792cd358012SAdrian Hunter " CASE" 2793cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2794cd358012SAdrian Hunter " ELSE short_name" 2795cd358012SAdrian Hunter " END AS dso," 2796cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2797cd358012SAdrian Hunter " CASE" 2798cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2799cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2800cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2801cd358012SAdrian Hunter " ELSE ''" + text + 2802cd358012SAdrian Hunter " END AS flags" 2803cd358012SAdrian Hunter " FROM calls" 2804cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2805cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2806cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2807cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2808cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2809cd358012SAdrian Hunter report_vars.where_clause + 2810cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2811cd358012SAdrian Hunter limit 2812cd358012SAdrian Hunter ) 2813cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2814cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2815cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2816cd358012SAdrian Hunter 2817cd358012SAdrian Hunter def columnAlignment(self, column): 2818cd358012SAdrian Hunter return self.alignment[column] 2819cd358012SAdrian Hunter 2820cd358012SAdrian Hunter# Top Calls report creation dialog 2821cd358012SAdrian Hunter 2822cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2823cd358012SAdrian Hunter 2824cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2825cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2826cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2827cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2828cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2829cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2830cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2831cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2832cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2833cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2834cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2835cd358012SAdrian Hunter 2836cd358012SAdrian Hunter# Top Calls window 2837cd358012SAdrian Hunter 2838cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2839cd358012SAdrian Hunter 2840cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2841cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2842cd358012SAdrian Hunter 2843cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2844cd358012SAdrian Hunter self.model = self.data_model 2845cd358012SAdrian Hunter 2846cd358012SAdrian Hunter self.view = QTableView() 2847cd358012SAdrian Hunter self.view.setModel(self.model) 2848cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2849cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 285096c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 285196c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2852cd358012SAdrian Hunter 28539bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 28549bc4e4bfSAdrian Hunter 2855cd358012SAdrian Hunter self.ResizeColumnsToContents() 2856cd358012SAdrian Hunter 2857cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2858cd358012SAdrian Hunter 2859cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2860cd358012SAdrian Hunter 2861cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2862cd358012SAdrian Hunter 2863cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2864cd358012SAdrian Hunter 2865cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2866cd358012SAdrian Hunter 2867cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2868cd358012SAdrian Hunter 2869cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2870cd358012SAdrian Hunter self.view.setFocus() 2871cd358012SAdrian Hunter self.find_bar.Busy() 2872cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2873cd358012SAdrian Hunter 2874cd358012SAdrian Hunter def FindDone(self, row): 2875cd358012SAdrian Hunter self.find_bar.Idle() 2876cd358012SAdrian Hunter if row >= 0: 2877cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2878cd358012SAdrian Hunter else: 2879cd358012SAdrian Hunter self.find_bar.NotFound() 2880cd358012SAdrian Hunter 28811beb5c7bSAdrian Hunter# Action Definition 28821beb5c7bSAdrian Hunter 28831beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 28841beb5c7bSAdrian Hunter action = QAction(label, parent) 28851beb5c7bSAdrian Hunter if shortcut != None: 28861beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 28871beb5c7bSAdrian Hunter action.setStatusTip(tip) 28881beb5c7bSAdrian Hunter action.triggered.connect(callback) 28891beb5c7bSAdrian Hunter return action 28901beb5c7bSAdrian Hunter 28911beb5c7bSAdrian Hunter# Typical application actions 28921beb5c7bSAdrian Hunter 28931beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 28941beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 28951beb5c7bSAdrian Hunter 28961beb5c7bSAdrian Hunter# Typical MDI actions 28971beb5c7bSAdrian Hunter 28981beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 28991beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 29001beb5c7bSAdrian Hunter 29011beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 29021beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 29031beb5c7bSAdrian Hunter 29041beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 29051beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 29061beb5c7bSAdrian Hunter 29071beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 29081beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 29091beb5c7bSAdrian Hunter 29101beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 29111beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 29121beb5c7bSAdrian Hunter 29131beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 29141beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 29151beb5c7bSAdrian Hunter 29161beb5c7bSAdrian Hunter# Typical MDI window menu 29171beb5c7bSAdrian Hunter 29181beb5c7bSAdrian Hunterclass WindowMenu(): 29191beb5c7bSAdrian Hunter 29201beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 29211beb5c7bSAdrian Hunter self.mdi_area = mdi_area 29221beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 29231beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 29241beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 29251beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 29261beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 29271beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 29281beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 29291beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 29301beb5c7bSAdrian Hunter 29311beb5c7bSAdrian Hunter def Update(self): 29321beb5c7bSAdrian Hunter self.window_menu.clear() 29331beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 29341beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 29351beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 29361beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 29371beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 29381beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 29391beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 29401beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 29411beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 29421beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 29431beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29441beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 29451beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 29461beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29471beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 29481beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 29491beb5c7bSAdrian Hunter if sub_window_count == 0: 29501beb5c7bSAdrian Hunter return 29511beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29521beb5c7bSAdrian Hunter nr = 1 29531beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 29541beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 29551beb5c7bSAdrian Hunter if nr < 10: 29561beb5c7bSAdrian Hunter label = "&" + label 29571beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 29581beb5c7bSAdrian Hunter action.setCheckable(True) 29591beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 2960df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 29611beb5c7bSAdrian Hunter self.window_menu.addAction(action) 29621beb5c7bSAdrian Hunter nr += 1 29631beb5c7bSAdrian Hunter 29641beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 29651beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 29661beb5c7bSAdrian Hunter 296765b24292SAdrian Hunter# Help text 296865b24292SAdrian Hunter 296965b24292SAdrian Hunterglb_help_text = """ 297065b24292SAdrian Hunter<h1>Contents</h1> 297165b24292SAdrian Hunter<style> 297265b24292SAdrian Hunterp.c1 { 297365b24292SAdrian Hunter text-indent: 40px; 297465b24292SAdrian Hunter} 297565b24292SAdrian Hunterp.c2 { 297665b24292SAdrian Hunter text-indent: 80px; 297765b24292SAdrian Hunter} 297865b24292SAdrian Hunter} 297965b24292SAdrian Hunter</style> 298065b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 298165b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2982ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2983ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2984ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2985ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 298665b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 298765b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 298865b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 298965b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 299065b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 299165b24292SAdrian Hunterwidths to suit will display something like: 299265b24292SAdrian Hunter<pre> 299365b24292SAdrian Hunter Call Graph: pt_example 299465b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 299565b24292SAdrian Hunterv- ls 299665b24292SAdrian Hunter v- 2638:2638 299765b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 299865b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 299965b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 300065b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 300165b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 300265b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 300365b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 300465b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 300565b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 300665b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 300765b24292SAdrian Hunter</pre> 300865b24292SAdrian Hunter<h3>Points to note:</h3> 300965b24292SAdrian Hunter<ul> 301065b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 301165b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 301265b24292SAdrian Hunter<li>Subsequent levels are functions</li> 301365b24292SAdrian Hunter<li>'Count' is the number of calls</li> 301465b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 301565b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 301665b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 301765b24292SAdrian Hunter</ul> 301865b24292SAdrian Hunter<h3>Find</h3> 301965b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 302065b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 3021ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 3022ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 3023ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 3024ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 302565b24292SAdrian HunterThe All branches report displays all branches in chronological order. 302665b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 302765b24292SAdrian Hunter<h3>Disassembly</h3> 302865b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 302965b24292SAdrian Hunter<ol> 303065b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 303165b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 303265b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 303365b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 303465b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 303565b24292SAdrian Hunter</ol> 303665b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 303765b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 303865b24292SAdrian Hunter<pre> 303965b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 304065b24292SAdrian Huntergit clone https://github.com/intelxed/xed 304165b24292SAdrian Huntercd xed 304265b24292SAdrian Hunter./mfile.py --share 304365b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 304465b24292SAdrian Huntersudo ldconfig 304565b24292SAdrian Hunter</pre> 3046530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3> 3047530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. 3048530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. 3049530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known. 3050530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period 3051530e22fdSAdrian Huntersince the previous displayed 'IPC'. 305265b24292SAdrian Hunter<h3>Find</h3> 305365b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 305465b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 305565b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 3056ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 305765b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 305865b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 3059ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 306065b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 306165b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 306265b24292SAdrian Hunter<pre> 306365b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 306465b24292SAdrian Hunter 100us-200us From 100us to 200us 306565b24292SAdrian Hunter 10ms- From 10ms to the end 306665b24292SAdrian Hunter -100ns The first 100ns 306765b24292SAdrian Hunter -10ms- The last 10ms 306865b24292SAdrian Hunter</pre> 306965b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 3070ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 3071cd358012SAdrian 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. 3072cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 3073cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 307465b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 307565b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 307665b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 307765b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 307865b24292SAdrian Hunterbut that can be slow for large tables. 307965b24292SAdrian Hunter<p>There are also tables of database meta-information. 308065b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 308165b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 308265b24292SAdrian Hunter<h3>Find</h3> 308365b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 308465b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 308565b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 308635fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 308735fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 308865b24292SAdrian Hunter""" 308965b24292SAdrian Hunter 309065b24292SAdrian Hunter# Help window 309165b24292SAdrian Hunter 309265b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 309365b24292SAdrian Hunter 309465b24292SAdrian Hunter def __init__(self, glb, parent=None): 309565b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 309665b24292SAdrian Hunter 309765b24292SAdrian Hunter self.text = QTextBrowser() 309865b24292SAdrian Hunter self.text.setHtml(glb_help_text) 309965b24292SAdrian Hunter self.text.setReadOnly(True) 310065b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 310165b24292SAdrian Hunter 310265b24292SAdrian Hunter self.setWidget(self.text) 310365b24292SAdrian Hunter 310465b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 310565b24292SAdrian Hunter 310665b24292SAdrian Hunter# Main window that only displays the help text 310765b24292SAdrian Hunter 310865b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 310965b24292SAdrian Hunter 311065b24292SAdrian Hunter def __init__(self, parent=None): 311165b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 311265b24292SAdrian Hunter 311365b24292SAdrian Hunter self.setMinimumSize(200, 100) 311465b24292SAdrian Hunter self.resize(800, 600) 311565b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 311665b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 311765b24292SAdrian Hunter 311865b24292SAdrian Hunter self.text = QTextBrowser() 311965b24292SAdrian Hunter self.text.setHtml(glb_help_text) 312065b24292SAdrian Hunter self.text.setReadOnly(True) 312165b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 312265b24292SAdrian Hunter 312365b24292SAdrian Hunter self.setCentralWidget(self.text) 312465b24292SAdrian Hunter 3125b62d18abSAdrian Hunter# PostqreSQL server version 3126b62d18abSAdrian Hunter 3127b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 3128b62d18abSAdrian Hunter query = QSqlQuery(db) 3129b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 3130b62d18abSAdrian Hunter if query.next(): 3131b62d18abSAdrian Hunter v_str = query.value(0) 3132b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 3133b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 3134b62d18abSAdrian Hunter return v_list[1] 3135b62d18abSAdrian Hunter return v_str 3136b62d18abSAdrian Hunter return "Unknown" 3137b62d18abSAdrian Hunter 3138b62d18abSAdrian Hunter# SQLite version 3139b62d18abSAdrian Hunter 3140b62d18abSAdrian Hunterdef SQLiteVersion(db): 3141b62d18abSAdrian Hunter query = QSqlQuery(db) 3142b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 3143b62d18abSAdrian Hunter if query.next(): 3144b62d18abSAdrian Hunter return query.value(0) 3145b62d18abSAdrian Hunter return "Unknown" 3146b62d18abSAdrian Hunter 3147b62d18abSAdrian Hunter# About dialog 3148b62d18abSAdrian Hunter 3149b62d18abSAdrian Hunterclass AboutDialog(QDialog): 3150b62d18abSAdrian Hunter 3151b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 3152b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 3153b62d18abSAdrian Hunter 3154b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 3155b62d18abSAdrian Hunter self.setMinimumWidth(300) 3156b62d18abSAdrian Hunter 3157b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 3158b62d18abSAdrian Hunter 3159b62d18abSAdrian Hunter text = "<pre>" 3160b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 3161b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 3162b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 3163b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 3164b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 3165b62d18abSAdrian Hunter else: 3166b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 3167b62d18abSAdrian Hunter text += "</pre>" 3168b62d18abSAdrian Hunter 3169b62d18abSAdrian Hunter self.text = QTextBrowser() 3170b62d18abSAdrian Hunter self.text.setHtml(text) 3171b62d18abSAdrian Hunter self.text.setReadOnly(True) 3172b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 3173b62d18abSAdrian Hunter 3174b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 3175b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 3176b62d18abSAdrian Hunter 317726688729SAdrian Hunter self.setLayout(self.vbox) 3178b62d18abSAdrian Hunter 317982f68e28SAdrian Hunter# Font resize 318082f68e28SAdrian Hunter 318182f68e28SAdrian Hunterdef ResizeFont(widget, diff): 318282f68e28SAdrian Hunter font = widget.font() 318382f68e28SAdrian Hunter sz = font.pointSize() 318482f68e28SAdrian Hunter font.setPointSize(sz + diff) 318582f68e28SAdrian Hunter widget.setFont(font) 318682f68e28SAdrian Hunter 318782f68e28SAdrian Hunterdef ShrinkFont(widget): 318882f68e28SAdrian Hunter ResizeFont(widget, -1) 318982f68e28SAdrian Hunter 319082f68e28SAdrian Hunterdef EnlargeFont(widget): 319182f68e28SAdrian Hunter ResizeFont(widget, 1) 319282f68e28SAdrian Hunter 31931beb5c7bSAdrian Hunter# Unique name for sub-windows 31941beb5c7bSAdrian Hunter 31951beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 31961beb5c7bSAdrian Hunter if nr > 1: 31971beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 31981beb5c7bSAdrian Hunter return name 31991beb5c7bSAdrian Hunter 32001beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 32011beb5c7bSAdrian Hunter nr = 1 32021beb5c7bSAdrian Hunter while True: 32031beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 32041beb5c7bSAdrian Hunter ok = True 32051beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 32061beb5c7bSAdrian Hunter if sub_window.name == unique_name: 32071beb5c7bSAdrian Hunter ok = False 32081beb5c7bSAdrian Hunter break 32091beb5c7bSAdrian Hunter if ok: 32101beb5c7bSAdrian Hunter return unique_name 32111beb5c7bSAdrian Hunter nr += 1 32121beb5c7bSAdrian Hunter 32131beb5c7bSAdrian Hunter# Add a sub-window 32141beb5c7bSAdrian Hunter 32151beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 32161beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 32171beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 32181beb5c7bSAdrian Hunter sub_window.resize(800, 600) 32191beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 32201beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 32211beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 32221beb5c7bSAdrian Hunter sub_window.name = unique_name 32231beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 32241beb5c7bSAdrian Hunter sub_window.show() 32251beb5c7bSAdrian Hunter 3226031c2a00SAdrian Hunter# Main window 3227031c2a00SAdrian Hunter 3228031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 3229031c2a00SAdrian Hunter 3230031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 3231031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 3232031c2a00SAdrian Hunter 3233031c2a00SAdrian Hunter self.glb = glb 3234031c2a00SAdrian Hunter 32351beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 3236031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 3237031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 3238031c2a00SAdrian Hunter 32391beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 32401beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 32411beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 3242031c2a00SAdrian Hunter 32431beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 3244031c2a00SAdrian Hunter 32451beb5c7bSAdrian Hunter menu = self.menuBar() 3246031c2a00SAdrian Hunter 32471beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 32481beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 32491beb5c7bSAdrian Hunter 3250ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 325196c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 325296c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 3253ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 32548392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 325582f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 325682f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 3257ebd70c7dSAdrian Hunter 32581beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 3259655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 32601beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 32611beb5c7bSAdrian Hunter 3262ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3263ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3264ae8b887cSAdrian Hunter 326576099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 326676099f98SAdrian Hunter 3267cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3268cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3269cd358012SAdrian Hunter 32708392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 32718392b74bSAdrian Hunter 32721beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 32731beb5c7bSAdrian Hunter 327465b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 327565b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 3276b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 327765b24292SAdrian Hunter 32784b208453SAdrian Hunter def Try(self, fn): 32794b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 32804b208453SAdrian Hunter if win: 32814b208453SAdrian Hunter try: 32824b208453SAdrian Hunter fn(win.view) 32834b208453SAdrian Hunter except: 32844b208453SAdrian Hunter pass 32854b208453SAdrian Hunter 328696c43b9aSAdrian Hunter def CopyToClipboard(self): 328796c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 328896c43b9aSAdrian Hunter 328996c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 329096c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 329196c43b9aSAdrian Hunter 3292ebd70c7dSAdrian Hunter def Find(self): 3293ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3294ebd70c7dSAdrian Hunter if win: 3295ebd70c7dSAdrian Hunter try: 3296ebd70c7dSAdrian Hunter win.find_bar.Activate() 3297ebd70c7dSAdrian Hunter except: 3298ebd70c7dSAdrian Hunter pass 3299ebd70c7dSAdrian Hunter 33008392b74bSAdrian Hunter def FetchMoreRecords(self): 33018392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 33028392b74bSAdrian Hunter if win: 33038392b74bSAdrian Hunter try: 33048392b74bSAdrian Hunter win.fetch_bar.Activate() 33058392b74bSAdrian Hunter except: 33068392b74bSAdrian Hunter pass 33078392b74bSAdrian Hunter 330882f68e28SAdrian Hunter def ShrinkFont(self): 33094b208453SAdrian Hunter self.Try(ShrinkFont) 331082f68e28SAdrian Hunter 331182f68e28SAdrian Hunter def EnlargeFont(self): 33124b208453SAdrian Hunter self.Try(EnlargeFont) 331382f68e28SAdrian Hunter 331476099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 331576099f98SAdrian Hunter branches_events = 0 331676099f98SAdrian Hunter for event in events: 331776099f98SAdrian Hunter event = event.split(":")[0] 331876099f98SAdrian Hunter if event == "branches": 331976099f98SAdrian Hunter branches_events += 1 332076099f98SAdrian Hunter dbid = 0 332176099f98SAdrian Hunter for event in events: 332276099f98SAdrian Hunter dbid += 1 332376099f98SAdrian Hunter event = event.split(":")[0] 332476099f98SAdrian Hunter if event == "branches": 332576099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 3326df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 3327210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3328df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 332976099f98SAdrian Hunter 33308392b74bSAdrian Hunter def TableMenu(self, tables, menu): 33318392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 33328392b74bSAdrian Hunter for table in tables: 3333df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 33348392b74bSAdrian Hunter 33351beb5c7bSAdrian Hunter def NewCallGraph(self): 33361beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3337031c2a00SAdrian Hunter 3338ae8b887cSAdrian Hunter def NewCallTree(self): 3339ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3340ae8b887cSAdrian Hunter 3341cd358012SAdrian Hunter def NewTopCalls(self): 3342cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3343cd358012SAdrian Hunter ret = dialog.exec_() 3344cd358012SAdrian Hunter if ret: 3345cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3346cd358012SAdrian Hunter 334776099f98SAdrian Hunter def NewBranchView(self, event_id): 3348947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 334976099f98SAdrian Hunter 3350210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3351210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3352210cf1f9SAdrian Hunter ret = dialog.exec_() 3353210cf1f9SAdrian Hunter if ret: 3354947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3355210cf1f9SAdrian Hunter 33568392b74bSAdrian Hunter def NewTableView(self, table_name): 33578392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 33588392b74bSAdrian Hunter 335965b24292SAdrian Hunter def Help(self): 336065b24292SAdrian Hunter HelpWindow(self.glb, self) 336165b24292SAdrian Hunter 3362b62d18abSAdrian Hunter def About(self): 3363b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 3364b62d18abSAdrian Hunter dialog.exec_() 3365b62d18abSAdrian Hunter 336676099f98SAdrian Hunter# XED Disassembler 336776099f98SAdrian Hunter 336876099f98SAdrian Hunterclass xed_state_t(Structure): 336976099f98SAdrian Hunter 337076099f98SAdrian Hunter _fields_ = [ 337176099f98SAdrian Hunter ("mode", c_int), 337276099f98SAdrian Hunter ("width", c_int) 337376099f98SAdrian Hunter ] 337476099f98SAdrian Hunter 337576099f98SAdrian Hunterclass XEDInstruction(): 337676099f98SAdrian Hunter 337776099f98SAdrian Hunter def __init__(self, libxed): 337876099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 337976099f98SAdrian Hunter xedd_t = c_byte * 512 338076099f98SAdrian Hunter self.xedd = xedd_t() 338176099f98SAdrian Hunter self.xedp = addressof(self.xedd) 338276099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 338376099f98SAdrian Hunter self.state = xed_state_t() 338476099f98SAdrian Hunter self.statep = addressof(self.state) 338576099f98SAdrian Hunter # Buffer for disassembled instruction text 338676099f98SAdrian Hunter self.buffer = create_string_buffer(256) 338776099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 338876099f98SAdrian Hunter 338976099f98SAdrian Hunterclass LibXED(): 339076099f98SAdrian Hunter 339176099f98SAdrian Hunter def __init__(self): 33925ed4419dSAdrian Hunter try: 339376099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 33945ed4419dSAdrian Hunter except: 33955ed4419dSAdrian Hunter self.libxed = None 33965ed4419dSAdrian Hunter if not self.libxed: 33975ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 339876099f98SAdrian Hunter 339976099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 340076099f98SAdrian Hunter self.xed_tables_init.restype = None 340176099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 340276099f98SAdrian Hunter 340376099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 340476099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 340576099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 340676099f98SAdrian Hunter 340776099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 340876099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 340976099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 341076099f98SAdrian Hunter 341176099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 341276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 341376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 341476099f98SAdrian Hunter 341576099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 341676099f98SAdrian Hunter self.xed_decode.restype = c_int 341776099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 341876099f98SAdrian Hunter 341976099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 342076099f98SAdrian Hunter self.xed_format_context.restype = c_uint 342176099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 342276099f98SAdrian Hunter 342376099f98SAdrian Hunter self.xed_tables_init() 342476099f98SAdrian Hunter 342576099f98SAdrian Hunter def Instruction(self): 342676099f98SAdrian Hunter return XEDInstruction(self) 342776099f98SAdrian Hunter 342876099f98SAdrian Hunter def SetMode(self, inst, mode): 342976099f98SAdrian Hunter if mode: 343076099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 343176099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 343276099f98SAdrian Hunter else: 343376099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 343476099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 343576099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 343676099f98SAdrian Hunter 343776099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 343876099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 343976099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 344076099f98SAdrian Hunter if err: 344176099f98SAdrian Hunter return 0, "" 344276099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 344376099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 344476099f98SAdrian Hunter if not ok: 344576099f98SAdrian Hunter return 0, "" 3446606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3447606bd60aSAdrian Hunter result = inst.buffer.value 3448606bd60aSAdrian Hunter else: 3449606bd60aSAdrian Hunter result = inst.buffer.value.decode() 345076099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 345176099f98SAdrian Hunter # For now, assume the length is in byte 166 3452606bd60aSAdrian Hunter return inst.xedd[166], result 345376099f98SAdrian Hunter 345476099f98SAdrian Hunterdef TryOpen(file_name): 345576099f98SAdrian Hunter try: 345676099f98SAdrian Hunter return open(file_name, "rb") 345776099f98SAdrian Hunter except: 345876099f98SAdrian Hunter return None 345976099f98SAdrian Hunter 346076099f98SAdrian Hunterdef Is64Bit(f): 346176099f98SAdrian Hunter result = sizeof(c_void_p) 346276099f98SAdrian Hunter # ELF support only 346376099f98SAdrian Hunter pos = f.tell() 346476099f98SAdrian Hunter f.seek(0) 346576099f98SAdrian Hunter header = f.read(7) 346676099f98SAdrian Hunter f.seek(pos) 346776099f98SAdrian Hunter magic = header[0:4] 3468606bd60aSAdrian Hunter if sys.version_info[0] == 2: 346976099f98SAdrian Hunter eclass = ord(header[4]) 347076099f98SAdrian Hunter encoding = ord(header[5]) 347176099f98SAdrian Hunter version = ord(header[6]) 3472606bd60aSAdrian Hunter else: 3473606bd60aSAdrian Hunter eclass = header[4] 3474606bd60aSAdrian Hunter encoding = header[5] 3475606bd60aSAdrian Hunter version = header[6] 347676099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 347776099f98SAdrian Hunter result = True if eclass == 2 else False 347876099f98SAdrian Hunter return result 347976099f98SAdrian Hunter 3480031c2a00SAdrian Hunter# Global data 3481031c2a00SAdrian Hunter 3482031c2a00SAdrian Hunterclass Glb(): 3483031c2a00SAdrian Hunter 3484031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3485031c2a00SAdrian Hunter self.dbref = dbref 3486031c2a00SAdrian Hunter self.db = db 3487031c2a00SAdrian Hunter self.dbname = dbname 348876099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 348976099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 349076099f98SAdrian Hunter if self.buildid_dir: 349176099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 349276099f98SAdrian Hunter else: 349376099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3494031c2a00SAdrian Hunter self.app = None 3495031c2a00SAdrian Hunter self.mainwindow = None 34968392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 349776099f98SAdrian Hunter try: 349876099f98SAdrian Hunter self.disassembler = LibXED() 349976099f98SAdrian Hunter self.have_disassembler = True 350076099f98SAdrian Hunter except: 350176099f98SAdrian Hunter self.have_disassembler = False 35029a9dae36SAdrian Hunter self.host_machine_id = 0 35039a9dae36SAdrian Hunter self.host_start_time = 0 35049a9dae36SAdrian Hunter self.host_finish_time = 0 350576099f98SAdrian Hunter 350676099f98SAdrian Hunter def FileFromBuildId(self, build_id): 350776099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 350876099f98SAdrian Hunter return TryOpen(file_name) 350976099f98SAdrian Hunter 351076099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 351176099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 351276099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 351376099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 351476099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 351576099f98SAdrian Hunter if f: 351676099f98SAdrian Hunter return f 351776099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 351876099f98SAdrian Hunter f = TryOpen(long_name) 351976099f98SAdrian Hunter if f: 352076099f98SAdrian Hunter return f 352176099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 352276099f98SAdrian Hunter if f: 352376099f98SAdrian Hunter return f 352476099f98SAdrian Hunter return None 35258392b74bSAdrian Hunter 35268392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 35278392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 35288392b74bSAdrian Hunter 35298392b74bSAdrian Hunter # Shutdown any background processes or threads 35308392b74bSAdrian Hunter def ShutdownInstances(self): 35318392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 35328392b74bSAdrian Hunter try: 35338392b74bSAdrian Hunter x.Shutdown() 35348392b74bSAdrian Hunter except: 35358392b74bSAdrian Hunter pass 3536031c2a00SAdrian Hunter 35379a9dae36SAdrian Hunter def GetHostMachineId(self): 35389a9dae36SAdrian Hunter query = QSqlQuery(self.db) 35399a9dae36SAdrian Hunter QueryExec(query, "SELECT id FROM machines WHERE pid = -1") 35409a9dae36SAdrian Hunter if query.next(): 35419a9dae36SAdrian Hunter self.host_machine_id = query.value(0) 35429a9dae36SAdrian Hunter else: 35439a9dae36SAdrian Hunter self.host_machine_id = 0 35449a9dae36SAdrian Hunter return self.host_machine_id 35459a9dae36SAdrian Hunter 35469a9dae36SAdrian Hunter def HostMachineId(self): 35479a9dae36SAdrian Hunter if self.host_machine_id: 35489a9dae36SAdrian Hunter return self.host_machine_id 35499a9dae36SAdrian Hunter return self.GetHostMachineId() 35509a9dae36SAdrian Hunter 35519a9dae36SAdrian Hunter def SelectValue(self, sql): 35529a9dae36SAdrian Hunter query = QSqlQuery(self.db) 35539a9dae36SAdrian Hunter try: 35549a9dae36SAdrian Hunter QueryExec(query, sql) 35559a9dae36SAdrian Hunter except: 35569a9dae36SAdrian Hunter return None 35579a9dae36SAdrian Hunter if query.next(): 35589a9dae36SAdrian Hunter return Decimal(query.value(0)) 35599a9dae36SAdrian Hunter return None 35609a9dae36SAdrian Hunter 35619a9dae36SAdrian Hunter def SwitchesMinTime(self, machine_id): 35629a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 35639a9dae36SAdrian Hunter " FROM context_switches" 35649a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 35659a9dae36SAdrian Hunter " ORDER BY id LIMIT 1") 35669a9dae36SAdrian Hunter 35679a9dae36SAdrian Hunter def SwitchesMaxTime(self, machine_id): 35689a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 35699a9dae36SAdrian Hunter " FROM context_switches" 35709a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 35719a9dae36SAdrian Hunter " ORDER BY id DESC LIMIT 1") 35729a9dae36SAdrian Hunter 35739a9dae36SAdrian Hunter def SamplesMinTime(self, machine_id): 35749a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 35759a9dae36SAdrian Hunter " FROM samples" 35769a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 35779a9dae36SAdrian Hunter " ORDER BY id LIMIT 1") 35789a9dae36SAdrian Hunter 35799a9dae36SAdrian Hunter def SamplesMaxTime(self, machine_id): 35809a9dae36SAdrian Hunter return self.SelectValue("SELECT time" 35819a9dae36SAdrian Hunter " FROM samples" 35829a9dae36SAdrian Hunter " WHERE time != 0 AND machine_id = " + str(machine_id) + 35839a9dae36SAdrian Hunter " ORDER BY id DESC LIMIT 1") 35849a9dae36SAdrian Hunter 35859a9dae36SAdrian Hunter def CallsMinTime(self, machine_id): 35869a9dae36SAdrian Hunter return self.SelectValue("SELECT calls.call_time" 35879a9dae36SAdrian Hunter " FROM calls" 35889a9dae36SAdrian Hunter " INNER JOIN threads ON threads.thread_id = calls.thread_id" 35899a9dae36SAdrian Hunter " WHERE calls.call_time != 0 AND threads.machine_id = " + str(machine_id) + 35909a9dae36SAdrian Hunter " ORDER BY calls.id LIMIT 1") 35919a9dae36SAdrian Hunter 35929a9dae36SAdrian Hunter def CallsMaxTime(self, machine_id): 35939a9dae36SAdrian Hunter return self.SelectValue("SELECT calls.return_time" 35949a9dae36SAdrian Hunter " FROM calls" 35959a9dae36SAdrian Hunter " INNER JOIN threads ON threads.thread_id = calls.thread_id" 35969a9dae36SAdrian Hunter " WHERE calls.return_time != 0 AND threads.machine_id = " + str(machine_id) + 35979a9dae36SAdrian Hunter " ORDER BY calls.return_time DESC LIMIT 1") 35989a9dae36SAdrian Hunter 35999a9dae36SAdrian Hunter def GetStartTime(self, machine_id): 36009a9dae36SAdrian Hunter t0 = self.SwitchesMinTime(machine_id) 36019a9dae36SAdrian Hunter t1 = self.SamplesMinTime(machine_id) 36029a9dae36SAdrian Hunter t2 = self.CallsMinTime(machine_id) 36039a9dae36SAdrian Hunter if t0 is None or (not(t1 is None) and t1 < t0): 36049a9dae36SAdrian Hunter t0 = t1 36059a9dae36SAdrian Hunter if t0 is None or (not(t2 is None) and t2 < t0): 36069a9dae36SAdrian Hunter t0 = t2 36079a9dae36SAdrian Hunter return t0 36089a9dae36SAdrian Hunter 36099a9dae36SAdrian Hunter def GetFinishTime(self, machine_id): 36109a9dae36SAdrian Hunter t0 = self.SwitchesMaxTime(machine_id) 36119a9dae36SAdrian Hunter t1 = self.SamplesMaxTime(machine_id) 36129a9dae36SAdrian Hunter t2 = self.CallsMaxTime(machine_id) 36139a9dae36SAdrian Hunter if t0 is None or (not(t1 is None) and t1 > t0): 36149a9dae36SAdrian Hunter t0 = t1 36159a9dae36SAdrian Hunter if t0 is None or (not(t2 is None) and t2 > t0): 36169a9dae36SAdrian Hunter t0 = t2 36179a9dae36SAdrian Hunter return t0 36189a9dae36SAdrian Hunter 36199a9dae36SAdrian Hunter def HostStartTime(self): 36209a9dae36SAdrian Hunter if self.host_start_time: 36219a9dae36SAdrian Hunter return self.host_start_time 36229a9dae36SAdrian Hunter self.host_start_time = self.GetStartTime(self.HostMachineId()) 36239a9dae36SAdrian Hunter return self.host_start_time 36249a9dae36SAdrian Hunter 36259a9dae36SAdrian Hunter def HostFinishTime(self): 36269a9dae36SAdrian Hunter if self.host_finish_time: 36279a9dae36SAdrian Hunter return self.host_finish_time 36289a9dae36SAdrian Hunter self.host_finish_time = self.GetFinishTime(self.HostMachineId()) 36299a9dae36SAdrian Hunter return self.host_finish_time 36309a9dae36SAdrian Hunter 36319a9dae36SAdrian Hunter def StartTime(self, machine_id): 36329a9dae36SAdrian Hunter if machine_id == self.HostMachineId(): 36339a9dae36SAdrian Hunter return self.HostStartTime() 36349a9dae36SAdrian Hunter return self.GetStartTime(machine_id) 36359a9dae36SAdrian Hunter 36369a9dae36SAdrian Hunter def FinishTime(self, machine_id): 36379a9dae36SAdrian Hunter if machine_id == self.HostMachineId(): 36389a9dae36SAdrian Hunter return self.HostFinishTime() 36399a9dae36SAdrian Hunter return self.GetFinishTime(machine_id) 36409a9dae36SAdrian Hunter 3641031c2a00SAdrian Hunter# Database reference 3642031c2a00SAdrian Hunter 3643031c2a00SAdrian Hunterclass DBRef(): 3644031c2a00SAdrian Hunter 3645031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3646031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3647031c2a00SAdrian Hunter self.dbname = dbname 3648031c2a00SAdrian Hunter 3649031c2a00SAdrian Hunter def Open(self, connection_name): 3650031c2a00SAdrian Hunter dbname = self.dbname 3651031c2a00SAdrian Hunter if self.is_sqlite3: 3652031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3653031c2a00SAdrian Hunter else: 3654031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3655031c2a00SAdrian Hunter opts = dbname.split() 3656031c2a00SAdrian Hunter for opt in opts: 3657031c2a00SAdrian Hunter if "=" in opt: 3658031c2a00SAdrian Hunter opt = opt.split("=") 3659031c2a00SAdrian Hunter if opt[0] == "hostname": 3660031c2a00SAdrian Hunter db.setHostName(opt[1]) 3661031c2a00SAdrian Hunter elif opt[0] == "port": 3662031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3663031c2a00SAdrian Hunter elif opt[0] == "username": 3664031c2a00SAdrian Hunter db.setUserName(opt[1]) 3665031c2a00SAdrian Hunter elif opt[0] == "password": 3666031c2a00SAdrian Hunter db.setPassword(opt[1]) 3667031c2a00SAdrian Hunter elif opt[0] == "dbname": 3668031c2a00SAdrian Hunter dbname = opt[1] 3669031c2a00SAdrian Hunter else: 3670031c2a00SAdrian Hunter dbname = opt 3671031c2a00SAdrian Hunter 3672031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3673031c2a00SAdrian Hunter if not db.open(): 3674031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3675031c2a00SAdrian Hunter return db, dbname 3676031c2a00SAdrian Hunter 3677031c2a00SAdrian Hunter# Main 3678031c2a00SAdrian Hunter 3679031c2a00SAdrian Hunterdef Main(): 36801ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 36811ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 36821ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 3683df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 36841ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 36851ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 36861ed7f47fSAdrian Hunter args = ap.parse_args() 3687031c2a00SAdrian Hunter 36881ed7f47fSAdrian Hunter if args.help_only: 368965b24292SAdrian Hunter app = QApplication(sys.argv) 369065b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 369165b24292SAdrian Hunter mainwindow.show() 369265b24292SAdrian Hunter err = app.exec_() 369365b24292SAdrian Hunter sys.exit(err) 3694031c2a00SAdrian Hunter 36951ed7f47fSAdrian Hunter dbname = args.dbname 36961ed7f47fSAdrian Hunter if dbname is None: 36971ed7f47fSAdrian Hunter ap.print_usage() 36981ed7f47fSAdrian Hunter print("Too few arguments") 36991ed7f47fSAdrian Hunter sys.exit(1) 37001ed7f47fSAdrian Hunter 3701031c2a00SAdrian Hunter is_sqlite3 = False 3702031c2a00SAdrian Hunter try: 3703beda0e72STony Jones f = open(dbname, "rb") 3704beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3705031c2a00SAdrian Hunter is_sqlite3 = True 3706031c2a00SAdrian Hunter f.close() 3707031c2a00SAdrian Hunter except: 3708031c2a00SAdrian Hunter pass 3709031c2a00SAdrian Hunter 3710031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3711031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3712031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3713031c2a00SAdrian Hunter app = QApplication(sys.argv) 3714031c2a00SAdrian Hunter glb.app = app 3715031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3716031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3717031c2a00SAdrian Hunter mainwindow.show() 3718031c2a00SAdrian Hunter err = app.exec_() 37198392b74bSAdrian Hunter glb.ShutdownInstances() 3720031c2a00SAdrian Hunter db.close() 3721031c2a00SAdrian Hunter sys.exit(err) 3722031c2a00SAdrian Hunter 3723031c2a00SAdrian Hunterif __name__ == "__main__": 3724031c2a00SAdrian Hunter Main() 3725