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 344ebd70c7dSAdrian Hunter# Find bar 345ebd70c7dSAdrian Hunter 346ebd70c7dSAdrian Hunterclass FindBar(): 347ebd70c7dSAdrian Hunter 348ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 349ebd70c7dSAdrian Hunter self.finder = finder 350ebd70c7dSAdrian Hunter self.context = [] 351ebd70c7dSAdrian Hunter self.last_value = None 352ebd70c7dSAdrian Hunter self.last_pattern = None 353ebd70c7dSAdrian Hunter 354ebd70c7dSAdrian Hunter label = QLabel("Find:") 355ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 356ebd70c7dSAdrian Hunter 357ebd70c7dSAdrian Hunter self.textbox = QComboBox() 358ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 359ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 360ebd70c7dSAdrian Hunter 361ebd70c7dSAdrian Hunter self.progress = QProgressBar() 362ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 363ebd70c7dSAdrian Hunter self.progress.hide() 364ebd70c7dSAdrian Hunter 365ebd70c7dSAdrian Hunter if is_reg_expr: 366ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 367ebd70c7dSAdrian Hunter else: 368ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 369ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 370ebd70c7dSAdrian Hunter 371ebd70c7dSAdrian Hunter self.next_button = QToolButton() 372ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 373ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 374ebd70c7dSAdrian Hunter 375ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 376ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 377ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 378ebd70c7dSAdrian Hunter 379ebd70c7dSAdrian Hunter self.close_button = QToolButton() 380ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 381ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 382ebd70c7dSAdrian Hunter 383ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 384ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 385ebd70c7dSAdrian Hunter 386ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 387ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 388ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 389ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 390ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 391ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 392ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 393ebd70c7dSAdrian Hunter 394ebd70c7dSAdrian Hunter self.bar = QWidget() 39526688729SAdrian Hunter self.bar.setLayout(self.hbox) 396ebd70c7dSAdrian Hunter self.bar.hide() 397ebd70c7dSAdrian Hunter 398ebd70c7dSAdrian Hunter def Widget(self): 399ebd70c7dSAdrian Hunter return self.bar 400ebd70c7dSAdrian Hunter 401ebd70c7dSAdrian Hunter def Activate(self): 402ebd70c7dSAdrian Hunter self.bar.show() 40380b3fb64SAdrian Hunter self.textbox.lineEdit().selectAll() 404ebd70c7dSAdrian Hunter self.textbox.setFocus() 405ebd70c7dSAdrian Hunter 406ebd70c7dSAdrian Hunter def Deactivate(self): 407ebd70c7dSAdrian Hunter self.bar.hide() 408ebd70c7dSAdrian Hunter 409ebd70c7dSAdrian Hunter def Busy(self): 410ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 411ebd70c7dSAdrian Hunter self.pattern.hide() 412ebd70c7dSAdrian Hunter self.next_button.hide() 413ebd70c7dSAdrian Hunter self.prev_button.hide() 414ebd70c7dSAdrian Hunter self.progress.show() 415ebd70c7dSAdrian Hunter 416ebd70c7dSAdrian Hunter def Idle(self): 417ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 418ebd70c7dSAdrian Hunter self.progress.hide() 419ebd70c7dSAdrian Hunter self.pattern.show() 420ebd70c7dSAdrian Hunter self.next_button.show() 421ebd70c7dSAdrian Hunter self.prev_button.show() 422ebd70c7dSAdrian Hunter 423ebd70c7dSAdrian Hunter def Find(self, direction): 424ebd70c7dSAdrian Hunter value = self.textbox.currentText() 425ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 426ebd70c7dSAdrian Hunter self.last_value = value 427ebd70c7dSAdrian Hunter self.last_pattern = pattern 428ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 429ebd70c7dSAdrian Hunter 430ebd70c7dSAdrian Hunter def ValueChanged(self): 431ebd70c7dSAdrian Hunter value = self.textbox.currentText() 432ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 433ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 434ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 435ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 436ebd70c7dSAdrian Hunter if data == None: 437ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 438ebd70c7dSAdrian Hunter else: 439ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 440ebd70c7dSAdrian Hunter self.Find(0) 441ebd70c7dSAdrian Hunter 442ebd70c7dSAdrian Hunter def NextPrev(self, direction): 443ebd70c7dSAdrian Hunter value = self.textbox.currentText() 444ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 445ebd70c7dSAdrian Hunter if value != self.last_value: 446ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 447ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 448ebd70c7dSAdrian Hunter if index < 0: 449ebd70c7dSAdrian Hunter index = self.textbox.count() 450ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 451ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 452ebd70c7dSAdrian Hunter return 453ebd70c7dSAdrian Hunter else: 454ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 455ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 456ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 457ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 458ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 459ebd70c7dSAdrian Hunter self.Find(direction) 460ebd70c7dSAdrian Hunter 461ebd70c7dSAdrian Hunter def NotFound(self): 462ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 463ebd70c7dSAdrian Hunter 464031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 465031c2a00SAdrian Hunter 466031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 467031c2a00SAdrian Hunter 4684a0979d4SAdrian Hunter def __init__(self, glb, params, row, parent_item): 469031c2a00SAdrian Hunter self.glb = glb 4704a0979d4SAdrian Hunter self.params = params 471031c2a00SAdrian Hunter self.row = row 472031c2a00SAdrian Hunter self.parent_item = parent_item 47326688729SAdrian Hunter self.query_done = False 474031c2a00SAdrian Hunter self.child_count = 0 475031c2a00SAdrian Hunter self.child_items = [] 4763ac641f4SAdrian Hunter if parent_item: 4773ac641f4SAdrian Hunter self.level = parent_item.level + 1 4783ac641f4SAdrian Hunter else: 4793ac641f4SAdrian Hunter self.level = 0 480031c2a00SAdrian Hunter 481031c2a00SAdrian Hunter def getChildItem(self, row): 482031c2a00SAdrian Hunter return self.child_items[row] 483031c2a00SAdrian Hunter 484031c2a00SAdrian Hunter def getParentItem(self): 485031c2a00SAdrian Hunter return self.parent_item 486031c2a00SAdrian Hunter 487031c2a00SAdrian Hunter def getRow(self): 488031c2a00SAdrian Hunter return self.row 489031c2a00SAdrian Hunter 490031c2a00SAdrian Hunter def childCount(self): 491031c2a00SAdrian Hunter if not self.query_done: 492031c2a00SAdrian Hunter self.Select() 493031c2a00SAdrian Hunter if not self.child_count: 494031c2a00SAdrian Hunter return -1 495031c2a00SAdrian Hunter return self.child_count 496031c2a00SAdrian Hunter 497031c2a00SAdrian Hunter def hasChildren(self): 498031c2a00SAdrian Hunter if not self.query_done: 499031c2a00SAdrian Hunter return True 500031c2a00SAdrian Hunter return self.child_count > 0 501031c2a00SAdrian Hunter 502031c2a00SAdrian Hunter def getData(self, column): 503031c2a00SAdrian Hunter return self.data[column] 504031c2a00SAdrian Hunter 505031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 506031c2a00SAdrian Hunter 507031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 508031c2a00SAdrian Hunter 50938a846d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): 5104a0979d4SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 511031c2a00SAdrian Hunter self.comm_id = comm_id 512031c2a00SAdrian Hunter self.thread_id = thread_id 513031c2a00SAdrian Hunter self.call_path_id = call_path_id 51438a846d4SAdrian Hunter self.insn_cnt = insn_cnt 51538a846d4SAdrian Hunter self.cyc_cnt = cyc_cnt 516031c2a00SAdrian Hunter self.branch_count = branch_count 517031c2a00SAdrian Hunter self.time = time 518031c2a00SAdrian Hunter 519031c2a00SAdrian Hunter def Select(self): 52026688729SAdrian Hunter self.query_done = True 521031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 52238a846d4SAdrian Hunter if self.params.have_ipc: 52338a846d4SAdrian Hunter ipc_str = ", SUM(insn_count), SUM(cyc_count)" 52438a846d4SAdrian Hunter else: 52538a846d4SAdrian Hunter ipc_str = "" 52638a846d4SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)" 527031c2a00SAdrian Hunter " FROM calls" 528031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 529031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 530031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 531031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 532031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 533031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 534031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 535031c2a00SAdrian Hunter " ORDER BY call_path_id") 536031c2a00SAdrian Hunter while query.next(): 53738a846d4SAdrian Hunter if self.params.have_ipc: 53838a846d4SAdrian Hunter insn_cnt = int(query.value(5)) 53938a846d4SAdrian Hunter cyc_cnt = int(query.value(6)) 54038a846d4SAdrian Hunter branch_count = int(query.value(7)) 54138a846d4SAdrian Hunter else: 54238a846d4SAdrian Hunter insn_cnt = 0 54338a846d4SAdrian Hunter cyc_cnt = 0 54438a846d4SAdrian Hunter branch_count = int(query.value(5)) 54538a846d4SAdrian 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) 546031c2a00SAdrian Hunter self.child_items.append(child_item) 547031c2a00SAdrian Hunter self.child_count += 1 548031c2a00SAdrian Hunter 549031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 550031c2a00SAdrian Hunter 551031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 552031c2a00SAdrian Hunter 55338a846d4SAdrian 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): 55438a846d4SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) 555031c2a00SAdrian Hunter dso = dsoname(dso) 55638a846d4SAdrian Hunter if self.params.have_ipc: 55738a846d4SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 55838a846d4SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 55938a846d4SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 56038a846d4SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 56138a846d4SAdrian 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 ] 56238a846d4SAdrian Hunter else: 563031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 564031c2a00SAdrian Hunter self.dbid = call_path_id 565031c2a00SAdrian Hunter 566031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 567031c2a00SAdrian Hunter 568031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 569031c2a00SAdrian Hunter 5704a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 57138a846d4SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) 57238a846d4SAdrian Hunter if self.params.have_ipc: 57338a846d4SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 57438a846d4SAdrian Hunter else: 575031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 576031c2a00SAdrian Hunter self.dbid = thread_id 577031c2a00SAdrian Hunter 578031c2a00SAdrian Hunter def Select(self): 579031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 580031c2a00SAdrian Hunter for child_item in self.child_items: 581031c2a00SAdrian Hunter self.time += child_item.time 58238a846d4SAdrian Hunter self.insn_cnt += child_item.insn_cnt 58338a846d4SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 584031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 585031c2a00SAdrian Hunter for child_item in self.child_items: 586031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 58738a846d4SAdrian Hunter if self.params.have_ipc: 58838a846d4SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 58938a846d4SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 59038a846d4SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 59138a846d4SAdrian Hunter else: 592031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 593031c2a00SAdrian Hunter 594031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 595031c2a00SAdrian Hunter 596031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 597031c2a00SAdrian Hunter 5984a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 5994a0979d4SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) 60038a846d4SAdrian Hunter if self.params.have_ipc: 60138a846d4SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 60238a846d4SAdrian Hunter else: 603031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 604031c2a00SAdrian Hunter self.dbid = comm_id 605031c2a00SAdrian Hunter 606031c2a00SAdrian Hunter def Select(self): 60726688729SAdrian Hunter self.query_done = True 608031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 609031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 610031c2a00SAdrian Hunter " FROM comm_threads" 611031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 612031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 613031c2a00SAdrian Hunter while query.next(): 6144a0979d4SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 615031c2a00SAdrian Hunter self.child_items.append(child_item) 616031c2a00SAdrian Hunter self.child_count += 1 617031c2a00SAdrian Hunter 618031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 619031c2a00SAdrian Hunter 620031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 621031c2a00SAdrian Hunter 6224a0979d4SAdrian Hunter def __init__(self, glb, params): 6234a0979d4SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, params, 0, None) 624031c2a00SAdrian Hunter self.dbid = 0 62526688729SAdrian Hunter self.query_done = True 626*26c11206SAdrian Hunter if_has_calls = "" 627*26c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 628*26c11206SAdrian Hunter if_has_calls = " WHERE has_calls = TRUE" 629031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 630*26c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 631031c2a00SAdrian Hunter while query.next(): 632031c2a00SAdrian Hunter if not query.value(0): 633031c2a00SAdrian Hunter continue 6344a0979d4SAdrian Hunter child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 635031c2a00SAdrian Hunter self.child_items.append(child_item) 636031c2a00SAdrian Hunter self.child_count += 1 637031c2a00SAdrian Hunter 6384a0979d4SAdrian Hunter# Call graph model parameters 6394a0979d4SAdrian Hunter 6404a0979d4SAdrian Hunterclass CallGraphModelParams(): 6414a0979d4SAdrian Hunter 6424a0979d4SAdrian Hunter def __init__(self, glb, parent=None): 6434a0979d4SAdrian Hunter self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") 6444a0979d4SAdrian Hunter 645254c0d82SAdrian Hunter# Context-sensitive call graph data model base 646031c2a00SAdrian Hunter 647254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 648031c2a00SAdrian Hunter 649031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 6504a0979d4SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) 651031c2a00SAdrian Hunter 652ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 653ebd70c7dSAdrian Hunter if pattern: 654ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 655ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 656ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 657ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 658ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 659ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 660ebd70c7dSAdrian Hunter # Escape % and _ 661ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 662ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 663ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 664ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 665ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 666ebd70c7dSAdrian Hunter else: 667ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 668ebd70c7dSAdrian Hunter else: 669ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 670254c0d82SAdrian Hunter self.DoFindSelect(query, match) 671ebd70c7dSAdrian Hunter 672ebd70c7dSAdrian Hunter def Found(self, query, found): 673ebd70c7dSAdrian Hunter if found: 674ebd70c7dSAdrian Hunter return self.FindPath(query) 675ebd70c7dSAdrian Hunter return [] 676ebd70c7dSAdrian Hunter 677ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 678ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 679ebd70c7dSAdrian Hunter found = query.first() 680ebd70c7dSAdrian Hunter else: 681ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 682ebd70c7dSAdrian Hunter found = query.next() 683ebd70c7dSAdrian Hunter return self.Found(query, found) 684ebd70c7dSAdrian Hunter 685ebd70c7dSAdrian Hunter def FindNext(self, query): 686ebd70c7dSAdrian Hunter found = query.next() 687ebd70c7dSAdrian Hunter if not found: 688ebd70c7dSAdrian Hunter found = query.first() 689ebd70c7dSAdrian Hunter return self.Found(query, found) 690ebd70c7dSAdrian Hunter 691ebd70c7dSAdrian Hunter def FindPrev(self, query): 692ebd70c7dSAdrian Hunter found = query.previous() 693ebd70c7dSAdrian Hunter if not found: 694ebd70c7dSAdrian Hunter found = query.last() 695ebd70c7dSAdrian Hunter return self.Found(query, found) 696ebd70c7dSAdrian Hunter 697ebd70c7dSAdrian Hunter def FindThread(self, c): 698ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 699ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 700ebd70c7dSAdrian Hunter elif c.direction > 0: 701ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 702ebd70c7dSAdrian Hunter else: 703ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 704ebd70c7dSAdrian Hunter return (True, ids) 705ebd70c7dSAdrian Hunter 706ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 707ebd70c7dSAdrian Hunter class Context(): 708ebd70c7dSAdrian Hunter def __init__(self, *x): 709ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 710ebd70c7dSAdrian Hunter def Update(self, *x): 711ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 712ebd70c7dSAdrian Hunter if len(context): 713ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 714ebd70c7dSAdrian Hunter else: 715ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 716ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 717ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 718ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 719ebd70c7dSAdrian Hunter thread.start() 720ebd70c7dSAdrian Hunter 721ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 722ebd70c7dSAdrian Hunter callback(ids) 723ebd70c7dSAdrian Hunter 724254c0d82SAdrian Hunter# Context-sensitive call graph data model 725254c0d82SAdrian Hunter 726254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 727254c0d82SAdrian Hunter 728254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 729254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 730254c0d82SAdrian Hunter 731254c0d82SAdrian Hunter def GetRoot(self): 7324a0979d4SAdrian Hunter return CallGraphRootItem(self.glb, self.params) 733254c0d82SAdrian Hunter 734254c0d82SAdrian Hunter def columnCount(self, parent=None): 73538a846d4SAdrian Hunter if self.params.have_ipc: 73638a846d4SAdrian Hunter return 12 73738a846d4SAdrian Hunter else: 738254c0d82SAdrian Hunter return 7 739254c0d82SAdrian Hunter 740254c0d82SAdrian Hunter def columnHeader(self, column): 74138a846d4SAdrian Hunter if self.params.have_ipc: 74238a846d4SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 74338a846d4SAdrian Hunter else: 744254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 745254c0d82SAdrian Hunter return headers[column] 746254c0d82SAdrian Hunter 747254c0d82SAdrian Hunter def columnAlignment(self, column): 74838a846d4SAdrian Hunter if self.params.have_ipc: 74938a846d4SAdrian 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 ] 75038a846d4SAdrian Hunter else: 751254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 752254c0d82SAdrian Hunter return alignment[column] 753254c0d82SAdrian Hunter 754254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 755254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 756254c0d82SAdrian Hunter " FROM calls" 757254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 758254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 759254c0d82SAdrian Hunter " WHERE symbols.name" + match + 760254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 761254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 762254c0d82SAdrian Hunter 763254c0d82SAdrian Hunter def FindPath(self, query): 764254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 765254c0d82SAdrian Hunter # to open the tree at the right place. 766254c0d82SAdrian Hunter ids = [] 767254c0d82SAdrian Hunter parent_id = query.value(0) 768254c0d82SAdrian Hunter while parent_id: 769254c0d82SAdrian Hunter ids.insert(0, parent_id) 770254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 771254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 772254c0d82SAdrian Hunter " FROM call_paths" 773254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 774254c0d82SAdrian Hunter if not q2.next(): 775254c0d82SAdrian Hunter break 776254c0d82SAdrian Hunter parent_id = q2.value(0) 777254c0d82SAdrian Hunter # The call path root is not used 778254c0d82SAdrian Hunter if ids[0] == 1: 779254c0d82SAdrian Hunter del ids[0] 780254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 781254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 782254c0d82SAdrian Hunter return ids 783254c0d82SAdrian Hunter 784ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 785ae8b887cSAdrian Hunter 786ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 787ae8b887cSAdrian Hunter 788b3b66079SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): 7894a0979d4SAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 790ae8b887cSAdrian Hunter self.comm_id = comm_id 791ae8b887cSAdrian Hunter self.thread_id = thread_id 792ae8b887cSAdrian Hunter self.calls_id = calls_id 793b3b66079SAdrian Hunter self.insn_cnt = insn_cnt 794b3b66079SAdrian Hunter self.cyc_cnt = cyc_cnt 795ae8b887cSAdrian Hunter self.branch_count = branch_count 796ae8b887cSAdrian Hunter self.time = time 797ae8b887cSAdrian Hunter 798ae8b887cSAdrian Hunter def Select(self): 79926688729SAdrian Hunter self.query_done = True 800ae8b887cSAdrian Hunter if self.calls_id == 0: 801ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 802ae8b887cSAdrian Hunter else: 803ae8b887cSAdrian Hunter comm_thread = "" 804b3b66079SAdrian Hunter if self.params.have_ipc: 805b3b66079SAdrian Hunter ipc_str = ", insn_count, cyc_count" 806b3b66079SAdrian Hunter else: 807b3b66079SAdrian Hunter ipc_str = "" 808ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 809b3b66079SAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count" 810ae8b887cSAdrian Hunter " FROM calls" 811ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 812ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 813ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 814ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 815ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 816ae8b887cSAdrian Hunter while query.next(): 817b3b66079SAdrian Hunter if self.params.have_ipc: 818b3b66079SAdrian Hunter insn_cnt = int(query.value(5)) 819b3b66079SAdrian Hunter cyc_cnt = int(query.value(6)) 820b3b66079SAdrian Hunter branch_count = int(query.value(7)) 821b3b66079SAdrian Hunter else: 822b3b66079SAdrian Hunter insn_cnt = 0 823b3b66079SAdrian Hunter cyc_cnt = 0 824b3b66079SAdrian Hunter branch_count = int(query.value(5)) 825b3b66079SAdrian 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) 826ae8b887cSAdrian Hunter self.child_items.append(child_item) 827ae8b887cSAdrian Hunter self.child_count += 1 828ae8b887cSAdrian Hunter 829ae8b887cSAdrian Hunter# Call tree data model level three item 830ae8b887cSAdrian Hunter 831ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 832ae8b887cSAdrian Hunter 833b3b66079SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item): 834b3b66079SAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) 835ae8b887cSAdrian Hunter dso = dsoname(dso) 836b3b66079SAdrian Hunter if self.params.have_ipc: 837b3b66079SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 838b3b66079SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 839b3b66079SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 840b3b66079SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 841b3b66079SAdrian 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 ] 842b3b66079SAdrian Hunter else: 843ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 844ae8b887cSAdrian Hunter self.dbid = calls_id 845ae8b887cSAdrian Hunter 846ae8b887cSAdrian Hunter# Call tree data model level two item 847ae8b887cSAdrian Hunter 848ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 849ae8b887cSAdrian Hunter 8504a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 851b3b66079SAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item) 852b3b66079SAdrian Hunter if self.params.have_ipc: 853b3b66079SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 854b3b66079SAdrian Hunter else: 855ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 856ae8b887cSAdrian Hunter self.dbid = thread_id 857ae8b887cSAdrian Hunter 858ae8b887cSAdrian Hunter def Select(self): 859ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 860ae8b887cSAdrian Hunter for child_item in self.child_items: 861ae8b887cSAdrian Hunter self.time += child_item.time 862b3b66079SAdrian Hunter self.insn_cnt += child_item.insn_cnt 863b3b66079SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 864ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 865ae8b887cSAdrian Hunter for child_item in self.child_items: 866ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 867b3b66079SAdrian Hunter if self.params.have_ipc: 868b3b66079SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 869b3b66079SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 870b3b66079SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 871b3b66079SAdrian Hunter else: 872ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 873ae8b887cSAdrian Hunter 874ae8b887cSAdrian Hunter# Call tree data model level one item 875ae8b887cSAdrian Hunter 876ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 877ae8b887cSAdrian Hunter 8784a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 8794a0979d4SAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) 880b3b66079SAdrian Hunter if self.params.have_ipc: 881b3b66079SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 882b3b66079SAdrian Hunter else: 883ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 884ae8b887cSAdrian Hunter self.dbid = comm_id 885ae8b887cSAdrian Hunter 886ae8b887cSAdrian Hunter def Select(self): 88726688729SAdrian Hunter self.query_done = True 888ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 889ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 890ae8b887cSAdrian Hunter " FROM comm_threads" 891ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 892ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 893ae8b887cSAdrian Hunter while query.next(): 8944a0979d4SAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 895ae8b887cSAdrian Hunter self.child_items.append(child_item) 896ae8b887cSAdrian Hunter self.child_count += 1 897ae8b887cSAdrian Hunter 898ae8b887cSAdrian Hunter# Call tree data model root item 899ae8b887cSAdrian Hunter 900ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 901ae8b887cSAdrian Hunter 9024a0979d4SAdrian Hunter def __init__(self, glb, params): 9034a0979d4SAdrian Hunter super(CallTreeRootItem, self).__init__(glb, params, 0, None) 904ae8b887cSAdrian Hunter self.dbid = 0 90526688729SAdrian Hunter self.query_done = True 906*26c11206SAdrian Hunter if_has_calls = "" 907*26c11206SAdrian Hunter if IsSelectable(glb.db, "comms", columns = "has_calls"): 908*26c11206SAdrian Hunter if_has_calls = " WHERE has_calls = TRUE" 909ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 910*26c11206SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls) 911ae8b887cSAdrian Hunter while query.next(): 912ae8b887cSAdrian Hunter if not query.value(0): 913ae8b887cSAdrian Hunter continue 9144a0979d4SAdrian Hunter child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 915ae8b887cSAdrian Hunter self.child_items.append(child_item) 916ae8b887cSAdrian Hunter self.child_count += 1 917ae8b887cSAdrian Hunter 918ae8b887cSAdrian Hunter# Call Tree data model 919ae8b887cSAdrian Hunter 920ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 921ae8b887cSAdrian Hunter 922ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 923ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 924ae8b887cSAdrian Hunter 925ae8b887cSAdrian Hunter def GetRoot(self): 9264a0979d4SAdrian Hunter return CallTreeRootItem(self.glb, self.params) 927ae8b887cSAdrian Hunter 928ae8b887cSAdrian Hunter def columnCount(self, parent=None): 929b3b66079SAdrian Hunter if self.params.have_ipc: 930b3b66079SAdrian Hunter return 12 931b3b66079SAdrian Hunter else: 932ae8b887cSAdrian Hunter return 7 933ae8b887cSAdrian Hunter 934ae8b887cSAdrian Hunter def columnHeader(self, column): 935b3b66079SAdrian Hunter if self.params.have_ipc: 936b3b66079SAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 937b3b66079SAdrian Hunter else: 938ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 939ae8b887cSAdrian Hunter return headers[column] 940ae8b887cSAdrian Hunter 941ae8b887cSAdrian Hunter def columnAlignment(self, column): 942b3b66079SAdrian Hunter if self.params.have_ipc: 943b3b66079SAdrian 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 ] 944b3b66079SAdrian Hunter else: 945ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 946ae8b887cSAdrian Hunter return alignment[column] 947ae8b887cSAdrian Hunter 948ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 949ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 950ae8b887cSAdrian Hunter " FROM calls" 951ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 952ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 953ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 954ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 955ae8b887cSAdrian Hunter 956ae8b887cSAdrian Hunter def FindPath(self, query): 957ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 958ae8b887cSAdrian Hunter # to open the tree at the right place. 959ae8b887cSAdrian Hunter ids = [] 960ae8b887cSAdrian Hunter parent_id = query.value(0) 961ae8b887cSAdrian Hunter while parent_id: 962ae8b887cSAdrian Hunter ids.insert(0, parent_id) 963ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 964ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 965ae8b887cSAdrian Hunter " FROM calls" 966ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 967ae8b887cSAdrian Hunter if not q2.next(): 968ae8b887cSAdrian Hunter break 969ae8b887cSAdrian Hunter parent_id = q2.value(0) 970ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 971ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 972ae8b887cSAdrian Hunter return ids 973ae8b887cSAdrian Hunter 974ebd70c7dSAdrian Hunter# Vertical widget layout 975ebd70c7dSAdrian Hunter 976ebd70c7dSAdrian Hunterclass VBox(): 977ebd70c7dSAdrian Hunter 978ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 979ebd70c7dSAdrian Hunter self.vbox = QWidget() 98026688729SAdrian Hunter self.vbox.setLayout(QVBoxLayout()) 981ebd70c7dSAdrian Hunter 982ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 983ebd70c7dSAdrian Hunter 984ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 985ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 986ebd70c7dSAdrian Hunter if w3: 987ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 988ebd70c7dSAdrian Hunter 989ebd70c7dSAdrian Hunter def Widget(self): 990ebd70c7dSAdrian Hunter return self.vbox 991ebd70c7dSAdrian Hunter 992a731cc4cSAdrian Hunter# Tree window base 9931beb5c7bSAdrian Hunter 994a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 9951beb5c7bSAdrian Hunter 996a731cc4cSAdrian Hunter def __init__(self, parent=None): 997a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 9981beb5c7bSAdrian Hunter 999a731cc4cSAdrian Hunter self.model = None 1000a731cc4cSAdrian Hunter self.find_bar = None 10011beb5c7bSAdrian Hunter 1002be6e7471SAdrian Hunter self.view = QTreeView() 100396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 100496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 1005be6e7471SAdrian Hunter 10069bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 10079bc4e4bfSAdrian Hunter 1008ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 1009ebd70c7dSAdrian Hunter if not len(ids): 1010ebd70c7dSAdrian Hunter return False 1011ebd70c7dSAdrian Hunter parent = QModelIndex() 1012ebd70c7dSAdrian Hunter for dbid in ids: 1013ebd70c7dSAdrian Hunter found = False 1014ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 1015ebd70c7dSAdrian Hunter for row in xrange(n): 1016ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 1017ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 1018ebd70c7dSAdrian Hunter found = True 1019ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 1020ebd70c7dSAdrian Hunter parent = child 1021ebd70c7dSAdrian Hunter break 1022ebd70c7dSAdrian Hunter if not found: 1023ebd70c7dSAdrian Hunter break 1024ebd70c7dSAdrian Hunter return found 1025ebd70c7dSAdrian Hunter 1026ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 1027ebd70c7dSAdrian Hunter self.view.setFocus() 1028ebd70c7dSAdrian Hunter self.find_bar.Busy() 1029ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 1030ebd70c7dSAdrian Hunter 1031ebd70c7dSAdrian Hunter def FindDone(self, ids): 1032ebd70c7dSAdrian Hunter found = True 1033ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 1034ebd70c7dSAdrian Hunter found = False 1035ebd70c7dSAdrian Hunter self.find_bar.Idle() 1036ebd70c7dSAdrian Hunter if not found: 1037ebd70c7dSAdrian Hunter self.find_bar.NotFound() 1038ebd70c7dSAdrian Hunter 1039a731cc4cSAdrian Hunter 1040a731cc4cSAdrian Hunter# Context-sensitive call graph window 1041a731cc4cSAdrian Hunter 1042a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 1043a731cc4cSAdrian Hunter 1044a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 1045a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 1046a731cc4cSAdrian Hunter 1047a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 1048a731cc4cSAdrian Hunter 1049a731cc4cSAdrian Hunter self.view.setModel(self.model) 1050a731cc4cSAdrian Hunter 1051a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 1052a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 1053a731cc4cSAdrian Hunter 1054a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 1055a731cc4cSAdrian Hunter 1056a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1057a731cc4cSAdrian Hunter 1058a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1059a731cc4cSAdrian Hunter 1060a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 1061a731cc4cSAdrian Hunter 1062ae8b887cSAdrian Hunter# Call tree window 1063ae8b887cSAdrian Hunter 1064ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 1065ae8b887cSAdrian Hunter 1066ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 1067ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 1068ae8b887cSAdrian Hunter 1069ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 1070ae8b887cSAdrian Hunter 1071ae8b887cSAdrian Hunter self.view.setModel(self.model) 1072ae8b887cSAdrian Hunter 1073ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 1074ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 1075ae8b887cSAdrian Hunter 1076ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 1077ae8b887cSAdrian Hunter 1078ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1079ae8b887cSAdrian Hunter 1080ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1081ae8b887cSAdrian Hunter 1082ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 1083ae8b887cSAdrian Hunter 10848392b74bSAdrian Hunter# Child data item finder 10858392b74bSAdrian Hunter 10868392b74bSAdrian Hunterclass ChildDataItemFinder(): 10878392b74bSAdrian Hunter 10888392b74bSAdrian Hunter def __init__(self, root): 10898392b74bSAdrian Hunter self.root = root 10908392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 10918392b74bSAdrian Hunter self.rows = [] 10928392b74bSAdrian Hunter self.pos = 0 10938392b74bSAdrian Hunter 10948392b74bSAdrian Hunter def FindSelect(self): 10958392b74bSAdrian Hunter self.rows = [] 10968392b74bSAdrian Hunter if self.pattern: 10978392b74bSAdrian Hunter pattern = re.compile(self.value) 10988392b74bSAdrian Hunter for child in self.root.child_items: 10998392b74bSAdrian Hunter for column_data in child.data: 11008392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 11018392b74bSAdrian Hunter self.rows.append(child.row) 11028392b74bSAdrian Hunter break 11038392b74bSAdrian Hunter else: 11048392b74bSAdrian Hunter for child in self.root.child_items: 11058392b74bSAdrian Hunter for column_data in child.data: 11068392b74bSAdrian Hunter if self.value in str(column_data): 11078392b74bSAdrian Hunter self.rows.append(child.row) 11088392b74bSAdrian Hunter break 11098392b74bSAdrian Hunter 11108392b74bSAdrian Hunter def FindValue(self): 11118392b74bSAdrian Hunter self.pos = 0 11128392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 11138392b74bSAdrian Hunter self.FindSelect() 11148392b74bSAdrian Hunter if not len(self.rows): 11158392b74bSAdrian Hunter return -1 11168392b74bSAdrian Hunter return self.rows[self.pos] 11178392b74bSAdrian Hunter 11188392b74bSAdrian Hunter def FindThread(self): 11198392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 11208392b74bSAdrian Hunter row = self.FindValue() 11218392b74bSAdrian Hunter elif len(self.rows): 11228392b74bSAdrian Hunter if self.direction > 0: 11238392b74bSAdrian Hunter self.pos += 1 11248392b74bSAdrian Hunter if self.pos >= len(self.rows): 11258392b74bSAdrian Hunter self.pos = 0 11268392b74bSAdrian Hunter else: 11278392b74bSAdrian Hunter self.pos -= 1 11288392b74bSAdrian Hunter if self.pos < 0: 11298392b74bSAdrian Hunter self.pos = len(self.rows) - 1 11308392b74bSAdrian Hunter row = self.rows[self.pos] 11318392b74bSAdrian Hunter else: 11328392b74bSAdrian Hunter row = -1 11338392b74bSAdrian Hunter return (True, row) 11348392b74bSAdrian Hunter 11358392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 11368392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 11378392b74bSAdrian Hunter # Use a thread so the UI is not blocked 11388392b74bSAdrian Hunter thread = Thread(self.FindThread) 11398392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 11408392b74bSAdrian Hunter thread.start() 11418392b74bSAdrian Hunter 11428392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 11438392b74bSAdrian Hunter callback(row) 11448392b74bSAdrian Hunter 11458392b74bSAdrian Hunter# Number of database records to fetch in one go 11468392b74bSAdrian Hunter 11478392b74bSAdrian Hunterglb_chunk_sz = 10000 11488392b74bSAdrian Hunter 11498392b74bSAdrian Hunter# Background process for SQL data fetcher 11508392b74bSAdrian Hunter 11518392b74bSAdrian Hunterclass SQLFetcherProcess(): 11528392b74bSAdrian Hunter 11538392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 11548392b74bSAdrian Hunter # Need a unique connection name 11558392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 11568392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 11578392b74bSAdrian Hunter self.sql = sql 11588392b74bSAdrian Hunter self.buffer = buffer 11598392b74bSAdrian Hunter self.head = head 11608392b74bSAdrian Hunter self.tail = tail 11618392b74bSAdrian Hunter self.fetch_count = fetch_count 11628392b74bSAdrian Hunter self.fetching_done = fetching_done 11638392b74bSAdrian Hunter self.process_target = process_target 11648392b74bSAdrian Hunter self.wait_event = wait_event 11658392b74bSAdrian Hunter self.fetched_event = fetched_event 11668392b74bSAdrian Hunter self.prep = prep 11678392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 11688392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 11698392b74bSAdrian Hunter self.last_id = -1 11708392b74bSAdrian Hunter self.fetched = 0 11718392b74bSAdrian Hunter self.more = True 11728392b74bSAdrian Hunter self.local_head = self.head.value 11738392b74bSAdrian Hunter self.local_tail = self.tail.value 11748392b74bSAdrian Hunter 11758392b74bSAdrian Hunter def Select(self): 11768392b74bSAdrian Hunter if self.query_limit: 11778392b74bSAdrian Hunter if self.query_limit == 1: 11788392b74bSAdrian Hunter return 11798392b74bSAdrian Hunter self.query_limit -= 1 11808392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 11818392b74bSAdrian Hunter QueryExec(self.query, stmt) 11828392b74bSAdrian Hunter 11838392b74bSAdrian Hunter def Next(self): 11848392b74bSAdrian Hunter if not self.query.next(): 11858392b74bSAdrian Hunter self.Select() 11868392b74bSAdrian Hunter if not self.query.next(): 11878392b74bSAdrian Hunter return None 11888392b74bSAdrian Hunter self.last_id = self.query.value(0) 11898392b74bSAdrian Hunter return self.prep(self.query) 11908392b74bSAdrian Hunter 11918392b74bSAdrian Hunter def WaitForTarget(self): 11928392b74bSAdrian Hunter while True: 11938392b74bSAdrian Hunter self.wait_event.clear() 11948392b74bSAdrian Hunter target = self.process_target.value 11958392b74bSAdrian Hunter if target > self.fetched or target < 0: 11968392b74bSAdrian Hunter break 11978392b74bSAdrian Hunter self.wait_event.wait() 11988392b74bSAdrian Hunter return target 11998392b74bSAdrian Hunter 12008392b74bSAdrian Hunter def HasSpace(self, sz): 12018392b74bSAdrian Hunter if self.local_tail <= self.local_head: 12028392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 12038392b74bSAdrian Hunter if space > sz: 12048392b74bSAdrian Hunter return True 12058392b74bSAdrian Hunter if space >= glb_nsz: 12068392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1207beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 12088392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 12098392b74bSAdrian Hunter self.local_head = 0 12108392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 12118392b74bSAdrian Hunter return True 12128392b74bSAdrian Hunter return False 12138392b74bSAdrian Hunter 12148392b74bSAdrian Hunter def WaitForSpace(self, sz): 12158392b74bSAdrian Hunter if self.HasSpace(sz): 12168392b74bSAdrian Hunter return 12178392b74bSAdrian Hunter while True: 12188392b74bSAdrian Hunter self.wait_event.clear() 12198392b74bSAdrian Hunter self.local_tail = self.tail.value 12208392b74bSAdrian Hunter if self.HasSpace(sz): 12218392b74bSAdrian Hunter return 12228392b74bSAdrian Hunter self.wait_event.wait() 12238392b74bSAdrian Hunter 12248392b74bSAdrian Hunter def AddToBuffer(self, obj): 1225beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 12268392b74bSAdrian Hunter n = len(d) 1227beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 12288392b74bSAdrian Hunter sz = n + glb_nsz 12298392b74bSAdrian Hunter self.WaitForSpace(sz) 12308392b74bSAdrian Hunter pos = self.local_head 12318392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 12328392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 12338392b74bSAdrian Hunter self.local_head += sz 12348392b74bSAdrian Hunter 12358392b74bSAdrian Hunter def FetchBatch(self, batch_size): 12368392b74bSAdrian Hunter fetched = 0 12378392b74bSAdrian Hunter while batch_size > fetched: 12388392b74bSAdrian Hunter obj = self.Next() 12398392b74bSAdrian Hunter if obj is None: 12408392b74bSAdrian Hunter self.more = False 12418392b74bSAdrian Hunter break 12428392b74bSAdrian Hunter self.AddToBuffer(obj) 12438392b74bSAdrian Hunter fetched += 1 12448392b74bSAdrian Hunter if fetched: 12458392b74bSAdrian Hunter self.fetched += fetched 12468392b74bSAdrian Hunter with self.fetch_count.get_lock(): 12478392b74bSAdrian Hunter self.fetch_count.value += fetched 12488392b74bSAdrian Hunter self.head.value = self.local_head 12498392b74bSAdrian Hunter self.fetched_event.set() 12508392b74bSAdrian Hunter 12518392b74bSAdrian Hunter def Run(self): 12528392b74bSAdrian Hunter while self.more: 12538392b74bSAdrian Hunter target = self.WaitForTarget() 12548392b74bSAdrian Hunter if target < 0: 12558392b74bSAdrian Hunter break 12568392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 12578392b74bSAdrian Hunter self.FetchBatch(batch_size) 12588392b74bSAdrian Hunter self.fetching_done.value = True 12598392b74bSAdrian Hunter self.fetched_event.set() 12608392b74bSAdrian Hunter 12618392b74bSAdrian Hunterdef SQLFetcherFn(*x): 12628392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 12638392b74bSAdrian Hunter process.Run() 12648392b74bSAdrian Hunter 12658392b74bSAdrian Hunter# SQL data fetcher 12668392b74bSAdrian Hunter 12678392b74bSAdrian Hunterclass SQLFetcher(QObject): 12688392b74bSAdrian Hunter 12698392b74bSAdrian Hunter done = Signal(object) 12708392b74bSAdrian Hunter 12718392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 12728392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 12738392b74bSAdrian Hunter self.process_data = process_data 12748392b74bSAdrian Hunter self.more = True 12758392b74bSAdrian Hunter self.target = 0 12768392b74bSAdrian Hunter self.last_target = 0 12778392b74bSAdrian Hunter self.fetched = 0 12788392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 12798392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 12808392b74bSAdrian Hunter self.head = Value(c_longlong) 12818392b74bSAdrian Hunter self.tail = Value(c_longlong) 12828392b74bSAdrian Hunter self.local_tail = 0 12838392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 12848392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 12858392b74bSAdrian Hunter self.last_count = 0 12868392b74bSAdrian Hunter self.process_target = Value(c_longlong) 12878392b74bSAdrian Hunter self.wait_event = Event() 12888392b74bSAdrian Hunter self.fetched_event = Event() 12898392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 12908392b74bSAdrian 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)) 12918392b74bSAdrian Hunter self.process.start() 12928392b74bSAdrian Hunter self.thread = Thread(self.Thread) 12938392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 12948392b74bSAdrian Hunter self.thread.start() 12958392b74bSAdrian Hunter 12968392b74bSAdrian Hunter def Shutdown(self): 12978392b74bSAdrian Hunter # Tell the thread and process to exit 12988392b74bSAdrian Hunter self.process_target.value = -1 12998392b74bSAdrian Hunter self.wait_event.set() 13008392b74bSAdrian Hunter self.more = False 13018392b74bSAdrian Hunter self.fetching_done.value = True 13028392b74bSAdrian Hunter self.fetched_event.set() 13038392b74bSAdrian Hunter 13048392b74bSAdrian Hunter def Thread(self): 13058392b74bSAdrian Hunter if not self.more: 13068392b74bSAdrian Hunter return True, 0 13078392b74bSAdrian Hunter while True: 13088392b74bSAdrian Hunter self.fetched_event.clear() 13098392b74bSAdrian Hunter fetch_count = self.fetch_count.value 13108392b74bSAdrian Hunter if fetch_count != self.last_count: 13118392b74bSAdrian Hunter break 13128392b74bSAdrian Hunter if self.fetching_done.value: 13138392b74bSAdrian Hunter self.more = False 13148392b74bSAdrian Hunter return True, 0 13158392b74bSAdrian Hunter self.fetched_event.wait() 13168392b74bSAdrian Hunter count = fetch_count - self.last_count 13178392b74bSAdrian Hunter self.last_count = fetch_count 13188392b74bSAdrian Hunter self.fetched += count 13198392b74bSAdrian Hunter return False, count 13208392b74bSAdrian Hunter 13218392b74bSAdrian Hunter def Fetch(self, nr): 13228392b74bSAdrian Hunter if not self.more: 13238392b74bSAdrian Hunter # -1 inidcates there are no more 13248392b74bSAdrian Hunter return -1 13258392b74bSAdrian Hunter result = self.fetched 13268392b74bSAdrian Hunter extra = result + nr - self.target 13278392b74bSAdrian Hunter if extra > 0: 13288392b74bSAdrian Hunter self.target += extra 13298392b74bSAdrian Hunter # process_target < 0 indicates shutting down 13308392b74bSAdrian Hunter if self.process_target.value >= 0: 13318392b74bSAdrian Hunter self.process_target.value = self.target 13328392b74bSAdrian Hunter self.wait_event.set() 13338392b74bSAdrian Hunter return result 13348392b74bSAdrian Hunter 13358392b74bSAdrian Hunter def RemoveFromBuffer(self): 13368392b74bSAdrian Hunter pos = self.local_tail 13378392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 13388392b74bSAdrian Hunter pos = 0 1339beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 13408392b74bSAdrian Hunter if n == 0: 13418392b74bSAdrian Hunter pos = 0 1342beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 13438392b74bSAdrian Hunter pos += glb_nsz 1344beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 13458392b74bSAdrian Hunter self.local_tail = pos + n 13468392b74bSAdrian Hunter return obj 13478392b74bSAdrian Hunter 13488392b74bSAdrian Hunter def ProcessData(self, count): 13498392b74bSAdrian Hunter for i in xrange(count): 13508392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 13518392b74bSAdrian Hunter self.process_data(obj) 13528392b74bSAdrian Hunter self.tail.value = self.local_tail 13538392b74bSAdrian Hunter self.wait_event.set() 13548392b74bSAdrian Hunter self.done.emit(count) 13558392b74bSAdrian Hunter 13568392b74bSAdrian Hunter# Fetch more records bar 13578392b74bSAdrian Hunter 13588392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 13598392b74bSAdrian Hunter 13608392b74bSAdrian Hunter def __init__(self, model, parent): 13618392b74bSAdrian Hunter self.model = model 13628392b74bSAdrian Hunter 13638392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 13648392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13658392b74bSAdrian Hunter 13668392b74bSAdrian Hunter self.fetch_count = QSpinBox() 13678392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 13688392b74bSAdrian Hunter self.fetch_count.setValue(10) 13698392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13708392b74bSAdrian Hunter 13718392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 13728392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13738392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 13748392b74bSAdrian Hunter 13758392b74bSAdrian Hunter self.progress = QProgressBar() 13768392b74bSAdrian Hunter self.progress.setRange(0, 100) 13778392b74bSAdrian Hunter self.progress.hide() 13788392b74bSAdrian Hunter 13798392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 13808392b74bSAdrian Hunter self.done_label.hide() 13818392b74bSAdrian Hunter 13828392b74bSAdrian Hunter self.spacer = QLabel("") 13838392b74bSAdrian Hunter 13848392b74bSAdrian Hunter self.close_button = QToolButton() 13858392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 13868392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 13878392b74bSAdrian Hunter 13888392b74bSAdrian Hunter self.hbox = QHBoxLayout() 13898392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 13908392b74bSAdrian Hunter 13918392b74bSAdrian Hunter self.hbox.addWidget(self.label) 13928392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 13938392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 13948392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 13958392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 13968392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 13978392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 13988392b74bSAdrian Hunter 13998392b74bSAdrian Hunter self.bar = QWidget() 140026688729SAdrian Hunter self.bar.setLayout(self.hbox) 14018392b74bSAdrian Hunter self.bar.show() 14028392b74bSAdrian Hunter 14038392b74bSAdrian Hunter self.in_progress = False 14048392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 14058392b74bSAdrian Hunter 14068392b74bSAdrian Hunter self.done = False 14078392b74bSAdrian Hunter 14088392b74bSAdrian Hunter if not model.HasMoreRecords(): 14098392b74bSAdrian Hunter self.Done() 14108392b74bSAdrian Hunter 14118392b74bSAdrian Hunter def Widget(self): 14128392b74bSAdrian Hunter return self.bar 14138392b74bSAdrian Hunter 14148392b74bSAdrian Hunter def Activate(self): 14158392b74bSAdrian Hunter self.bar.show() 14168392b74bSAdrian Hunter self.fetch.setFocus() 14178392b74bSAdrian Hunter 14188392b74bSAdrian Hunter def Deactivate(self): 14198392b74bSAdrian Hunter self.bar.hide() 14208392b74bSAdrian Hunter 14218392b74bSAdrian Hunter def Enable(self, enable): 14228392b74bSAdrian Hunter self.fetch.setEnabled(enable) 14238392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 14248392b74bSAdrian Hunter 14258392b74bSAdrian Hunter def Busy(self): 14268392b74bSAdrian Hunter self.Enable(False) 14278392b74bSAdrian Hunter self.fetch.hide() 14288392b74bSAdrian Hunter self.spacer.hide() 14298392b74bSAdrian Hunter self.progress.show() 14308392b74bSAdrian Hunter 14318392b74bSAdrian Hunter def Idle(self): 14328392b74bSAdrian Hunter self.in_progress = False 14338392b74bSAdrian Hunter self.Enable(True) 14348392b74bSAdrian Hunter self.progress.hide() 14358392b74bSAdrian Hunter self.fetch.show() 14368392b74bSAdrian Hunter self.spacer.show() 14378392b74bSAdrian Hunter 14388392b74bSAdrian Hunter def Target(self): 14398392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 14408392b74bSAdrian Hunter 14418392b74bSAdrian Hunter def Done(self): 14428392b74bSAdrian Hunter self.done = True 14438392b74bSAdrian Hunter self.Idle() 14448392b74bSAdrian Hunter self.label.hide() 14458392b74bSAdrian Hunter self.fetch_count.hide() 14468392b74bSAdrian Hunter self.fetch.hide() 14478392b74bSAdrian Hunter self.spacer.hide() 14488392b74bSAdrian Hunter self.done_label.show() 14498392b74bSAdrian Hunter 14508392b74bSAdrian Hunter def Progress(self, count): 14518392b74bSAdrian Hunter if self.in_progress: 14528392b74bSAdrian Hunter if count: 14538392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 14548392b74bSAdrian Hunter if percent >= 100: 14558392b74bSAdrian Hunter self.Idle() 14568392b74bSAdrian Hunter else: 14578392b74bSAdrian Hunter self.progress.setValue(percent) 14588392b74bSAdrian Hunter if not count: 14598392b74bSAdrian Hunter # Count value of zero means no more records 14608392b74bSAdrian Hunter self.Done() 14618392b74bSAdrian Hunter 14628392b74bSAdrian Hunter def FetchMoreRecords(self): 14638392b74bSAdrian Hunter if self.done: 14648392b74bSAdrian Hunter return 14658392b74bSAdrian Hunter self.progress.setValue(0) 14668392b74bSAdrian Hunter self.Busy() 14678392b74bSAdrian Hunter self.in_progress = True 14688392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 14698392b74bSAdrian Hunter 147076099f98SAdrian Hunter# Brance data model level two item 147176099f98SAdrian Hunter 147276099f98SAdrian Hunterclass BranchLevelTwoItem(): 147376099f98SAdrian Hunter 1474530e22fdSAdrian Hunter def __init__(self, row, col, text, parent_item): 147576099f98SAdrian Hunter self.row = row 147676099f98SAdrian Hunter self.parent_item = parent_item 1477530e22fdSAdrian Hunter self.data = [""] * (col + 1) 1478530e22fdSAdrian Hunter self.data[col] = text 147976099f98SAdrian Hunter self.level = 2 148076099f98SAdrian Hunter 148176099f98SAdrian Hunter def getParentItem(self): 148276099f98SAdrian Hunter return self.parent_item 148376099f98SAdrian Hunter 148476099f98SAdrian Hunter def getRow(self): 148576099f98SAdrian Hunter return self.row 148676099f98SAdrian Hunter 148776099f98SAdrian Hunter def childCount(self): 148876099f98SAdrian Hunter return 0 148976099f98SAdrian Hunter 149076099f98SAdrian Hunter def hasChildren(self): 149176099f98SAdrian Hunter return False 149276099f98SAdrian Hunter 149376099f98SAdrian Hunter def getData(self, column): 149476099f98SAdrian Hunter return self.data[column] 149576099f98SAdrian Hunter 149676099f98SAdrian Hunter# Brance data model level one item 149776099f98SAdrian Hunter 149876099f98SAdrian Hunterclass BranchLevelOneItem(): 149976099f98SAdrian Hunter 150076099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 150176099f98SAdrian Hunter self.glb = glb 150276099f98SAdrian Hunter self.row = row 150376099f98SAdrian Hunter self.parent_item = parent_item 150476099f98SAdrian Hunter self.child_count = 0 150576099f98SAdrian Hunter self.child_items = [] 150676099f98SAdrian Hunter self.data = data[1:] 150776099f98SAdrian Hunter self.dbid = data[0] 150876099f98SAdrian Hunter self.level = 1 150976099f98SAdrian Hunter self.query_done = False 1510530e22fdSAdrian Hunter self.br_col = len(self.data) - 1 151176099f98SAdrian Hunter 151276099f98SAdrian Hunter def getChildItem(self, row): 151376099f98SAdrian Hunter return self.child_items[row] 151476099f98SAdrian Hunter 151576099f98SAdrian Hunter def getParentItem(self): 151676099f98SAdrian Hunter return self.parent_item 151776099f98SAdrian Hunter 151876099f98SAdrian Hunter def getRow(self): 151976099f98SAdrian Hunter return self.row 152076099f98SAdrian Hunter 152176099f98SAdrian Hunter def Select(self): 152276099f98SAdrian Hunter self.query_done = True 152376099f98SAdrian Hunter 152476099f98SAdrian Hunter if not self.glb.have_disassembler: 152576099f98SAdrian Hunter return 152676099f98SAdrian Hunter 152776099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 152876099f98SAdrian Hunter 152976099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 153076099f98SAdrian Hunter " FROM samples" 153176099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 153276099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 153376099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 153476099f98SAdrian Hunter if not query.next(): 153576099f98SAdrian Hunter return 153676099f98SAdrian Hunter cpu = query.value(0) 153776099f98SAdrian Hunter dso = query.value(1) 153876099f98SAdrian Hunter sym = query.value(2) 153976099f98SAdrian Hunter if dso == 0 or sym == 0: 154076099f98SAdrian Hunter return 154176099f98SAdrian Hunter off = query.value(3) 154276099f98SAdrian Hunter short_name = query.value(4) 154376099f98SAdrian Hunter long_name = query.value(5) 154476099f98SAdrian Hunter build_id = query.value(6) 154576099f98SAdrian Hunter sym_start = query.value(7) 154676099f98SAdrian Hunter ip = query.value(8) 154776099f98SAdrian Hunter 154876099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 154976099f98SAdrian Hunter " FROM samples" 155076099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 155176099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 155276099f98SAdrian Hunter " ORDER BY samples.id" 155376099f98SAdrian Hunter " LIMIT 1") 155476099f98SAdrian Hunter if not query.next(): 155576099f98SAdrian Hunter return 155676099f98SAdrian Hunter if query.value(0) != dso: 155776099f98SAdrian Hunter # Cannot disassemble from one dso to another 155876099f98SAdrian Hunter return 155976099f98SAdrian Hunter bsym = query.value(1) 156076099f98SAdrian Hunter boff = query.value(2) 156176099f98SAdrian Hunter bsym_start = query.value(3) 156276099f98SAdrian Hunter if bsym == 0: 156376099f98SAdrian Hunter return 156476099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 156576099f98SAdrian Hunter if tot <= 0 or tot > 16384: 156676099f98SAdrian Hunter return 156776099f98SAdrian Hunter 156876099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 156976099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 157076099f98SAdrian Hunter if not f: 157176099f98SAdrian Hunter return 157276099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 157376099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 157476099f98SAdrian Hunter 157576099f98SAdrian Hunter buf_sz = tot + 16 157676099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 157776099f98SAdrian Hunter f.seek(sym_start + off) 157876099f98SAdrian Hunter buf.value = f.read(buf_sz) 157976099f98SAdrian Hunter buf_ptr = addressof(buf) 158076099f98SAdrian Hunter i = 0 158176099f98SAdrian Hunter while tot > 0: 158276099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 158376099f98SAdrian Hunter if cnt: 158476099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 158576099f98SAdrian Hunter for k in xrange(cnt): 158676099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 158776099f98SAdrian Hunter i += 1 158876099f98SAdrian Hunter while k < 15: 158976099f98SAdrian Hunter byte_str += " " 159076099f98SAdrian Hunter k += 1 1591530e22fdSAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) 159276099f98SAdrian Hunter self.child_count += 1 159376099f98SAdrian Hunter else: 159476099f98SAdrian Hunter return 159576099f98SAdrian Hunter buf_ptr += cnt 159676099f98SAdrian Hunter tot -= cnt 159776099f98SAdrian Hunter buf_sz -= cnt 159876099f98SAdrian Hunter ip += cnt 159976099f98SAdrian Hunter 160076099f98SAdrian Hunter def childCount(self): 160176099f98SAdrian Hunter if not self.query_done: 160276099f98SAdrian Hunter self.Select() 160376099f98SAdrian Hunter if not self.child_count: 160476099f98SAdrian Hunter return -1 160576099f98SAdrian Hunter return self.child_count 160676099f98SAdrian Hunter 160776099f98SAdrian Hunter def hasChildren(self): 160876099f98SAdrian Hunter if not self.query_done: 160976099f98SAdrian Hunter return True 161076099f98SAdrian Hunter return self.child_count > 0 161176099f98SAdrian Hunter 161276099f98SAdrian Hunter def getData(self, column): 161376099f98SAdrian Hunter return self.data[column] 161476099f98SAdrian Hunter 161576099f98SAdrian Hunter# Brance data model root item 161676099f98SAdrian Hunter 161776099f98SAdrian Hunterclass BranchRootItem(): 161876099f98SAdrian Hunter 161976099f98SAdrian Hunter def __init__(self): 162076099f98SAdrian Hunter self.child_count = 0 162176099f98SAdrian Hunter self.child_items = [] 162276099f98SAdrian Hunter self.level = 0 162376099f98SAdrian Hunter 162476099f98SAdrian Hunter def getChildItem(self, row): 162576099f98SAdrian Hunter return self.child_items[row] 162676099f98SAdrian Hunter 162776099f98SAdrian Hunter def getParentItem(self): 162876099f98SAdrian Hunter return None 162976099f98SAdrian Hunter 163076099f98SAdrian Hunter def getRow(self): 163176099f98SAdrian Hunter return 0 163276099f98SAdrian Hunter 163376099f98SAdrian Hunter def childCount(self): 163476099f98SAdrian Hunter return self.child_count 163576099f98SAdrian Hunter 163676099f98SAdrian Hunter def hasChildren(self): 163776099f98SAdrian Hunter return self.child_count > 0 163876099f98SAdrian Hunter 163976099f98SAdrian Hunter def getData(self, column): 164076099f98SAdrian Hunter return "" 164176099f98SAdrian Hunter 1642530e22fdSAdrian Hunter# Calculate instructions per cycle 1643530e22fdSAdrian Hunter 1644530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt): 1645530e22fdSAdrian Hunter if cyc_cnt and insn_cnt: 1646530e22fdSAdrian Hunter ipc = Decimal(float(insn_cnt) / cyc_cnt) 1647530e22fdSAdrian Hunter ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) 1648530e22fdSAdrian Hunter else: 1649530e22fdSAdrian Hunter ipc = "0" 1650530e22fdSAdrian Hunter return ipc 1651530e22fdSAdrian Hunter 165276099f98SAdrian Hunter# Branch data preparation 165376099f98SAdrian Hunter 1654530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data): 1655530e22fdSAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 1656530e22fdSAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 1657530e22fdSAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 1658530e22fdSAdrian Hunter " (" + dsoname(query.value(15)) + ")") 1659530e22fdSAdrian Hunter 1660530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data): 1661530e22fdSAdrian Hunter insn_cnt = query.value(16) 1662530e22fdSAdrian Hunter cyc_cnt = query.value(17) 1663530e22fdSAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 1664530e22fdSAdrian Hunter data.append(insn_cnt) 1665530e22fdSAdrian Hunter data.append(cyc_cnt) 1666530e22fdSAdrian Hunter data.append(ipc) 1667530e22fdSAdrian Hunter 166876099f98SAdrian Hunterdef BranchDataPrep(query): 166976099f98SAdrian Hunter data = [] 167076099f98SAdrian Hunter for i in xrange(0, 8): 167176099f98SAdrian Hunter data.append(query.value(i)) 1672530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 167376099f98SAdrian Hunter return data 167476099f98SAdrian Hunter 16758453c936SAdrian Hunterdef BranchDataPrepWA(query): 16768453c936SAdrian Hunter data = [] 16778453c936SAdrian Hunter data.append(query.value(0)) 16788453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 16798453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 16808453c936SAdrian Hunter for i in xrange(2, 8): 16818453c936SAdrian Hunter data.append(query.value(i)) 1682530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1683530e22fdSAdrian Hunter return data 1684530e22fdSAdrian Hunter 1685530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query): 1686530e22fdSAdrian Hunter data = [] 1687530e22fdSAdrian Hunter for i in xrange(0, 8): 1688530e22fdSAdrian Hunter data.append(query.value(i)) 1689530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1690530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1691530e22fdSAdrian Hunter return data 1692530e22fdSAdrian Hunter 1693530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query): 1694530e22fdSAdrian Hunter data = [] 1695530e22fdSAdrian Hunter data.append(query.value(0)) 1696530e22fdSAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 1697530e22fdSAdrian Hunter data.append("{:>19}".format(query.value(1))) 1698530e22fdSAdrian Hunter for i in xrange(2, 8): 1699530e22fdSAdrian Hunter data.append(query.value(i)) 1700530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1701530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 17028453c936SAdrian Hunter return data 17038453c936SAdrian Hunter 170476099f98SAdrian Hunter# Branch data model 170576099f98SAdrian Hunter 170676099f98SAdrian Hunterclass BranchModel(TreeModel): 170776099f98SAdrian Hunter 170876099f98SAdrian Hunter progress = Signal(object) 170976099f98SAdrian Hunter 171076099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 17114a0979d4SAdrian Hunter super(BranchModel, self).__init__(glb, None, parent) 171276099f98SAdrian Hunter self.event_id = event_id 171376099f98SAdrian Hunter self.more = True 171476099f98SAdrian Hunter self.populated = 0 1715530e22fdSAdrian Hunter self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") 1716530e22fdSAdrian Hunter if self.have_ipc: 1717530e22fdSAdrian Hunter select_ipc = ", insn_count, cyc_count" 1718530e22fdSAdrian Hunter prep_fn = BranchDataWithIPCPrep 1719530e22fdSAdrian Hunter prep_wa_fn = BranchDataWithIPCPrepWA 1720530e22fdSAdrian Hunter else: 1721530e22fdSAdrian Hunter select_ipc = "" 1722530e22fdSAdrian Hunter prep_fn = BranchDataPrep 1723530e22fdSAdrian Hunter prep_wa_fn = BranchDataPrepWA 172476099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 172576099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 172676099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 172776099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 1728530e22fdSAdrian Hunter + select_ipc + 172976099f98SAdrian Hunter " FROM samples" 173076099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 173176099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 173276099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 173376099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 173476099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 173576099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 173676099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 173776099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 173876099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 173976099f98SAdrian Hunter " ORDER BY samples.id" 174076099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 17418453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 1742530e22fdSAdrian Hunter prep = prep_fn 17438453c936SAdrian Hunter else: 1744530e22fdSAdrian Hunter prep = prep_wa_fn 17458453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 174676099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 174776099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 174876099f98SAdrian Hunter 1749a448ba23SAdrian Hunter def GetRoot(self): 1750a448ba23SAdrian Hunter return BranchRootItem() 1751a448ba23SAdrian Hunter 175276099f98SAdrian Hunter def columnCount(self, parent=None): 1753530e22fdSAdrian Hunter if self.have_ipc: 1754530e22fdSAdrian Hunter return 11 1755530e22fdSAdrian Hunter else: 175676099f98SAdrian Hunter return 8 175776099f98SAdrian Hunter 175876099f98SAdrian Hunter def columnHeader(self, column): 1759530e22fdSAdrian Hunter if self.have_ipc: 1760530e22fdSAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] 1761530e22fdSAdrian Hunter else: 176276099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 176376099f98SAdrian Hunter 176476099f98SAdrian Hunter def columnFont(self, column): 1765530e22fdSAdrian Hunter if self.have_ipc: 1766530e22fdSAdrian Hunter br_col = 10 1767530e22fdSAdrian Hunter else: 1768530e22fdSAdrian Hunter br_col = 7 1769530e22fdSAdrian Hunter if column != br_col: 177076099f98SAdrian Hunter return None 177176099f98SAdrian Hunter return QFont("Monospace") 177276099f98SAdrian Hunter 177376099f98SAdrian Hunter def DisplayData(self, item, index): 177476099f98SAdrian Hunter if item.level == 1: 177576099f98SAdrian Hunter self.FetchIfNeeded(item.row) 177676099f98SAdrian Hunter return item.getData(index.column()) 177776099f98SAdrian Hunter 177876099f98SAdrian Hunter def AddSample(self, data): 177976099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 178076099f98SAdrian Hunter self.root.child_items.append(child) 178176099f98SAdrian Hunter self.populated += 1 178276099f98SAdrian Hunter 178376099f98SAdrian Hunter def Update(self, fetched): 178476099f98SAdrian Hunter if not fetched: 178576099f98SAdrian Hunter self.more = False 178676099f98SAdrian Hunter self.progress.emit(0) 178776099f98SAdrian Hunter child_count = self.root.child_count 178876099f98SAdrian Hunter count = self.populated - child_count 178976099f98SAdrian Hunter if count > 0: 179076099f98SAdrian Hunter parent = QModelIndex() 179176099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 179276099f98SAdrian Hunter self.insertRows(child_count, count, parent) 179376099f98SAdrian Hunter self.root.child_count += count 179476099f98SAdrian Hunter self.endInsertRows() 179576099f98SAdrian Hunter self.progress.emit(self.root.child_count) 179676099f98SAdrian Hunter 179776099f98SAdrian Hunter def FetchMoreRecords(self, count): 179876099f98SAdrian Hunter current = self.root.child_count 179976099f98SAdrian Hunter if self.more: 180076099f98SAdrian Hunter self.fetcher.Fetch(count) 180176099f98SAdrian Hunter else: 180276099f98SAdrian Hunter self.progress.emit(0) 180376099f98SAdrian Hunter return current 180476099f98SAdrian Hunter 180576099f98SAdrian Hunter def HasMoreRecords(self): 180676099f98SAdrian Hunter return self.more 180776099f98SAdrian Hunter 18080bf0947aSAdrian Hunter# Report Variables 18090bf0947aSAdrian Hunter 18100bf0947aSAdrian Hunterclass ReportVars(): 18110bf0947aSAdrian Hunter 1812cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1813947cc38dSAdrian Hunter self.name = name 18140bf0947aSAdrian Hunter self.where_clause = where_clause 1815cd358012SAdrian Hunter self.limit = limit 18160bf0947aSAdrian Hunter 18170bf0947aSAdrian Hunter def UniqueId(self): 1818cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 18190bf0947aSAdrian Hunter 182076099f98SAdrian Hunter# Branch window 182176099f98SAdrian Hunter 182276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 182376099f98SAdrian Hunter 1824947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 182576099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 182676099f98SAdrian Hunter 18270bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 182876099f98SAdrian Hunter 18290bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 183076099f98SAdrian Hunter 183176099f98SAdrian Hunter self.view = QTreeView() 183276099f98SAdrian Hunter self.view.setUniformRowHeights(True) 183396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 183496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 183576099f98SAdrian Hunter self.view.setModel(self.model) 183676099f98SAdrian Hunter 183776099f98SAdrian Hunter self.ResizeColumnsToContents() 183876099f98SAdrian Hunter 18399bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 18409bc4e4bfSAdrian Hunter 184176099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 184276099f98SAdrian Hunter 184376099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 184476099f98SAdrian Hunter 184576099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 184676099f98SAdrian Hunter 184776099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 184876099f98SAdrian Hunter 184976099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 185076099f98SAdrian Hunter 1851947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 185276099f98SAdrian Hunter 185376099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 185476099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 185576099f98SAdrian Hunter # so implement a crude alternative 185676099f98SAdrian Hunter mm = "MM" if column else "MMMM" 185776099f98SAdrian Hunter font = self.view.font() 185876099f98SAdrian Hunter metrics = QFontMetrics(font) 185976099f98SAdrian Hunter max = 0 186076099f98SAdrian Hunter for row in xrange(n): 186176099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 186276099f98SAdrian Hunter len = metrics.width(str(val) + mm) 186376099f98SAdrian Hunter max = len if len > max else max 186476099f98SAdrian Hunter val = self.model.columnHeader(column) 186576099f98SAdrian Hunter len = metrics.width(str(val) + mm) 186676099f98SAdrian Hunter max = len if len > max else max 186776099f98SAdrian Hunter self.view.setColumnWidth(column, max) 186876099f98SAdrian Hunter 186976099f98SAdrian Hunter def ResizeColumnsToContents(self): 187076099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 187176099f98SAdrian Hunter if n < 1: 187276099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 187376099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 187476099f98SAdrian Hunter return 187576099f98SAdrian Hunter columns = self.model.columnCount() 187676099f98SAdrian Hunter for i in xrange(columns): 187776099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 187876099f98SAdrian Hunter 187976099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 188076099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 188176099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 188276099f98SAdrian Hunter self.ResizeColumnsToContents() 188376099f98SAdrian Hunter 188476099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 188576099f98SAdrian Hunter self.view.setFocus() 188676099f98SAdrian Hunter self.find_bar.Busy() 188776099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 188876099f98SAdrian Hunter 188976099f98SAdrian Hunter def FindDone(self, row): 189076099f98SAdrian Hunter self.find_bar.Idle() 189176099f98SAdrian Hunter if row >= 0: 189276099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 189376099f98SAdrian Hunter else: 189476099f98SAdrian Hunter self.find_bar.NotFound() 189576099f98SAdrian Hunter 18961c3ca1b3SAdrian Hunter# Line edit data item 18971c3ca1b3SAdrian Hunter 18981c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 18991c3ca1b3SAdrian Hunter 1900cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 19011c3ca1b3SAdrian Hunter self.glb = glb 19021c3ca1b3SAdrian Hunter self.label = label 19031c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 19041c3ca1b3SAdrian Hunter self.parent = parent 19051c3ca1b3SAdrian Hunter self.id = id 19061c3ca1b3SAdrian Hunter 1907cd358012SAdrian Hunter self.value = default 19081c3ca1b3SAdrian Hunter 1909cd358012SAdrian Hunter self.widget = QLineEdit(default) 19101c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 19111c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 19121c3ca1b3SAdrian Hunter self.red = False 19131c3ca1b3SAdrian Hunter self.error = "" 19141c3ca1b3SAdrian Hunter self.validated = True 19151c3ca1b3SAdrian Hunter 19161c3ca1b3SAdrian Hunter if placeholder_text: 19171c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 19181c3ca1b3SAdrian Hunter 19191c3ca1b3SAdrian Hunter def TurnTextRed(self): 19201c3ca1b3SAdrian Hunter if not self.red: 19211c3ca1b3SAdrian Hunter palette = QPalette() 19221c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 19231c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 19241c3ca1b3SAdrian Hunter self.red = True 19251c3ca1b3SAdrian Hunter 19261c3ca1b3SAdrian Hunter def TurnTextNormal(self): 19271c3ca1b3SAdrian Hunter if self.red: 19281c3ca1b3SAdrian Hunter palette = QPalette() 19291c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 19301c3ca1b3SAdrian Hunter self.red = False 19311c3ca1b3SAdrian Hunter 19321c3ca1b3SAdrian Hunter def InvalidValue(self, value): 19331c3ca1b3SAdrian Hunter self.value = "" 19341c3ca1b3SAdrian Hunter self.TurnTextRed() 19351c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 19361c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 19371c3ca1b3SAdrian Hunter 19381c3ca1b3SAdrian Hunter def Invalidate(self): 19391c3ca1b3SAdrian Hunter self.validated = False 19401c3ca1b3SAdrian Hunter 19411c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19421c3ca1b3SAdrian Hunter self.value = input_string.strip() 19431c3ca1b3SAdrian Hunter 19441c3ca1b3SAdrian Hunter def Validate(self): 19451c3ca1b3SAdrian Hunter self.validated = True 19461c3ca1b3SAdrian Hunter self.error = "" 19471c3ca1b3SAdrian Hunter self.TurnTextNormal() 19481c3ca1b3SAdrian Hunter self.parent.ClearMessage() 19491c3ca1b3SAdrian Hunter input_string = self.widget.text() 19501c3ca1b3SAdrian Hunter if not len(input_string.strip()): 19511c3ca1b3SAdrian Hunter self.value = "" 19521c3ca1b3SAdrian Hunter return 19531c3ca1b3SAdrian Hunter self.DoValidate(input_string) 19541c3ca1b3SAdrian Hunter 19551c3ca1b3SAdrian Hunter def IsValid(self): 19561c3ca1b3SAdrian Hunter if not self.validated: 19571c3ca1b3SAdrian Hunter self.Validate() 19581c3ca1b3SAdrian Hunter if len(self.error): 19591c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 19601c3ca1b3SAdrian Hunter return False 19611c3ca1b3SAdrian Hunter return True 19621c3ca1b3SAdrian Hunter 19631c3ca1b3SAdrian Hunter def IsNumber(self, value): 19641c3ca1b3SAdrian Hunter try: 19651c3ca1b3SAdrian Hunter x = int(value) 19661c3ca1b3SAdrian Hunter except: 19671c3ca1b3SAdrian Hunter x = 0 19681c3ca1b3SAdrian Hunter return str(x) == value 19691c3ca1b3SAdrian Hunter 19701c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 19711c3ca1b3SAdrian Hunter 19721c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 19731c3ca1b3SAdrian Hunter 19741c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 19751c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 19761c3ca1b3SAdrian Hunter 19771c3ca1b3SAdrian Hunter self.column_name = column_name 19781c3ca1b3SAdrian Hunter 19791c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19801c3ca1b3SAdrian Hunter singles = [] 19811c3ca1b3SAdrian Hunter ranges = [] 19821c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19831c3ca1b3SAdrian Hunter if "-" in value: 19841c3ca1b3SAdrian Hunter vrange = value.split("-") 19851c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 19861c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19871c3ca1b3SAdrian Hunter ranges.append(vrange) 19881c3ca1b3SAdrian Hunter else: 19891c3ca1b3SAdrian Hunter if not self.IsNumber(value): 19901c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19911c3ca1b3SAdrian Hunter singles.append(value) 19921c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 19931c3ca1b3SAdrian Hunter if len(singles): 19941c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 19951c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 19961c3ca1b3SAdrian Hunter 1997cd358012SAdrian Hunter# Positive integer dialog data item 1998cd358012SAdrian Hunter 1999cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 2000cd358012SAdrian Hunter 2001cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 2002cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 2003cd358012SAdrian Hunter 2004cd358012SAdrian Hunter def DoValidate(self, input_string): 2005cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 2006cd358012SAdrian Hunter return self.InvalidValue(input_string) 2007cd358012SAdrian Hunter value = int(input_string.strip()) 2008cd358012SAdrian Hunter if value <= 0: 2009cd358012SAdrian Hunter return self.InvalidValue(input_string) 2010cd358012SAdrian Hunter self.value = str(value) 2011cd358012SAdrian Hunter 20121c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 20131c3ca1b3SAdrian Hunter 20141c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 20151c3ca1b3SAdrian Hunter 20161c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 20171c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 20181c3ca1b3SAdrian Hunter 20191c3ca1b3SAdrian Hunter self.table_name = table_name 20201c3ca1b3SAdrian Hunter self.match_column = match_column 20211c3ca1b3SAdrian Hunter self.column_name1 = column_name1 20221c3ca1b3SAdrian Hunter self.column_name2 = column_name2 20231c3ca1b3SAdrian Hunter 20241c3ca1b3SAdrian Hunter def ValueToIds(self, value): 20251c3ca1b3SAdrian Hunter ids = [] 20261c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 20271c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 20281c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 20291c3ca1b3SAdrian Hunter if ret: 20301c3ca1b3SAdrian Hunter while query.next(): 20311c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 20321c3ca1b3SAdrian Hunter return ids 20331c3ca1b3SAdrian Hunter 20341c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 20351c3ca1b3SAdrian Hunter all_ids = [] 20361c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 20371c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 20381c3ca1b3SAdrian Hunter if len(ids): 20391c3ca1b3SAdrian Hunter all_ids.extend(ids) 20401c3ca1b3SAdrian Hunter else: 20411c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20421c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 20431c3ca1b3SAdrian Hunter if self.column_name2: 20441c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 20451c3ca1b3SAdrian Hunter 20461c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 20471c3ca1b3SAdrian Hunter 20481c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 20491c3ca1b3SAdrian Hunter 20501c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 20511c3ca1b3SAdrian Hunter self.column_name = column_name 20521c3ca1b3SAdrian Hunter 20531c3ca1b3SAdrian Hunter self.last_id = 0 20541c3ca1b3SAdrian Hunter self.first_time = 0 20551c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 20561c3ca1b3SAdrian Hunter 20571c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 20581c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 20591c3ca1b3SAdrian Hunter if query.next(): 20601c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 20611c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 20621c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 20631c3ca1b3SAdrian Hunter if query.next(): 20641c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 20651c3ca1b3SAdrian Hunter if placeholder_text: 20661c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 20671c3ca1b3SAdrian Hunter 20681c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 20691c3ca1b3SAdrian Hunter 20701c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 20711c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 20721c3ca1b3SAdrian Hunter if query.next(): 20731c3ca1b3SAdrian Hunter return True, int(query.value(0)) 20741c3ca1b3SAdrian Hunter else: 20751c3ca1b3SAdrian Hunter return False, 0 20761c3ca1b3SAdrian Hunter 20771c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 20781c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 20791c3ca1b3SAdrian Hunter while True: 20801c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 20811c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 20821c3ca1b3SAdrian Hunter if not query.next(): 20831c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 20841c3ca1b3SAdrian Hunter if not ok: 20851c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 20861c3ca1b3SAdrian Hunter if not ok: 20871c3ca1b3SAdrian Hunter return str(higher_id) 20881c3ca1b3SAdrian Hunter next_id = dbid 20891c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 20901c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 20911c3ca1b3SAdrian Hunter if get_floor: 20921c3ca1b3SAdrian Hunter if target_time > next_time: 20931c3ca1b3SAdrian Hunter lower_id = next_id 20941c3ca1b3SAdrian Hunter else: 20951c3ca1b3SAdrian Hunter higher_id = next_id 20961c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 20971c3ca1b3SAdrian Hunter return str(higher_id) 20981c3ca1b3SAdrian Hunter else: 20991c3ca1b3SAdrian Hunter if target_time >= next_time: 21001c3ca1b3SAdrian Hunter lower_id = next_id 21011c3ca1b3SAdrian Hunter else: 21021c3ca1b3SAdrian Hunter higher_id = next_id 21031c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 21041c3ca1b3SAdrian Hunter return str(lower_id) 21051c3ca1b3SAdrian Hunter 21061c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 21071c3ca1b3SAdrian Hunter mult = 1 21081c3ca1b3SAdrian Hunter suffix = val[-2:] 21091c3ca1b3SAdrian Hunter if suffix == "ms": 21101c3ca1b3SAdrian Hunter mult = 1000000 21111c3ca1b3SAdrian Hunter elif suffix == "us": 21121c3ca1b3SAdrian Hunter mult = 1000 21131c3ca1b3SAdrian Hunter elif suffix == "ns": 21141c3ca1b3SAdrian Hunter mult = 1 21151c3ca1b3SAdrian Hunter else: 21161c3ca1b3SAdrian Hunter return val 21171c3ca1b3SAdrian Hunter val = val[:-2].strip() 21181c3ca1b3SAdrian Hunter if not self.IsNumber(val): 21191c3ca1b3SAdrian Hunter return val 21201c3ca1b3SAdrian Hunter val = int(val) * mult 21211c3ca1b3SAdrian Hunter if val >= 0: 21221c3ca1b3SAdrian Hunter val += self.first_time 21231c3ca1b3SAdrian Hunter else: 21241c3ca1b3SAdrian Hunter val += self.last_time 21251c3ca1b3SAdrian Hunter return str(val) 21261c3ca1b3SAdrian Hunter 21271c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 21281c3ca1b3SAdrian Hunter if vrange[0] == "": 21291c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 21301c3ca1b3SAdrian Hunter if vrange[1] == "": 21311c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 21321c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 21331c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 21341c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 21351c3ca1b3SAdrian Hunter return False 21361c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 21371c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 21381c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 21391c3ca1b3SAdrian Hunter return False 21401c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 21411c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 21421c3ca1b3SAdrian Hunter return True 21431c3ca1b3SAdrian Hunter 21441c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 21451c3ca1b3SAdrian Hunter n = value.count("-") 21461c3ca1b3SAdrian Hunter if n == 1: 21471c3ca1b3SAdrian Hunter pass 21481c3ca1b3SAdrian Hunter elif n == 2: 21491c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 21501c3ca1b3SAdrian Hunter n = 1 21511c3ca1b3SAdrian Hunter elif n == 3: 21521c3ca1b3SAdrian Hunter n = 2 21531c3ca1b3SAdrian Hunter else: 21541c3ca1b3SAdrian Hunter return False 21551c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 21561c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 21571c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 21581c3ca1b3SAdrian Hunter ranges.append(vrange) 21591c3ca1b3SAdrian Hunter return True 21601c3ca1b3SAdrian Hunter return False 21611c3ca1b3SAdrian Hunter 21621c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 21631c3ca1b3SAdrian Hunter ranges = [] 21641c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 21651c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 21661c3ca1b3SAdrian Hunter return self.InvalidValue(value) 21671c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 21681c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 21691c3ca1b3SAdrian Hunter 21700924cd68SAdrian Hunter# Report Dialog Base 2171210cf1f9SAdrian Hunter 21720924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 2173210cf1f9SAdrian Hunter 21740924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 21750924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2176210cf1f9SAdrian Hunter 2177210cf1f9SAdrian Hunter self.glb = glb 2178210cf1f9SAdrian Hunter 21790bf0947aSAdrian Hunter self.report_vars = ReportVars() 2180210cf1f9SAdrian Hunter 21810924cd68SAdrian Hunter self.setWindowTitle(title) 2182210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2183210cf1f9SAdrian Hunter 21841c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2185210cf1f9SAdrian Hunter 21860924cd68SAdrian Hunter self.partial = partial 21870924cd68SAdrian Hunter 2188210cf1f9SAdrian Hunter self.grid = QGridLayout() 2189210cf1f9SAdrian Hunter 2190210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2191210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2192210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2193210cf1f9SAdrian Hunter 2194210cf1f9SAdrian Hunter self.status = QLabel() 2195210cf1f9SAdrian Hunter 2196210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2197210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2198210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2199210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2200210cf1f9SAdrian Hunter 2201210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2202210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2203210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2204210cf1f9SAdrian Hunter 2205210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2206210cf1f9SAdrian Hunter #self.hbox.addStretch() 2207210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2208210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2209210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2210210cf1f9SAdrian Hunter 2211210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2212210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2213210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2214210cf1f9SAdrian Hunter 221526688729SAdrian Hunter self.setLayout(self.vbox) 2216210cf1f9SAdrian Hunter 2217210cf1f9SAdrian Hunter def Ok(self): 22180bf0947aSAdrian Hunter vars = self.report_vars 22191c3ca1b3SAdrian Hunter for d in self.data_items: 22201c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 22211c3ca1b3SAdrian Hunter vars.name = d.value 2222947cc38dSAdrian Hunter if not vars.name: 2223210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2224210cf1f9SAdrian Hunter return 2225210cf1f9SAdrian Hunter for d in self.data_items: 2226210cf1f9SAdrian Hunter if not d.IsValid(): 2227210cf1f9SAdrian Hunter return 2228210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2229cd358012SAdrian Hunter if d.id == "LIMIT": 2230cd358012SAdrian Hunter vars.limit = d.value 2231cd358012SAdrian Hunter elif len(d.value): 22320bf0947aSAdrian Hunter if len(vars.where_clause): 22330bf0947aSAdrian Hunter vars.where_clause += " AND " 22340bf0947aSAdrian Hunter vars.where_clause += d.value 22350bf0947aSAdrian Hunter if len(vars.where_clause): 22360924cd68SAdrian Hunter if self.partial: 22370bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2238210cf1f9SAdrian Hunter else: 22390bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2240210cf1f9SAdrian Hunter self.accept() 2241210cf1f9SAdrian Hunter 2242210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2243210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2244210cf1f9SAdrian Hunter 2245210cf1f9SAdrian Hunter def ClearMessage(self): 2246210cf1f9SAdrian Hunter self.status.setText("") 2247210cf1f9SAdrian Hunter 22480924cd68SAdrian Hunter# Selected branch report creation dialog 22490924cd68SAdrian Hunter 22500924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 22510924cd68SAdrian Hunter 22520924cd68SAdrian Hunter def __init__(self, glb, parent=None): 22530924cd68SAdrian Hunter title = "Selected Branches" 22541c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 22551c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 22561c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 22571c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 22581c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 22591c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 22601c3ca1b3SAdrian 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), 22611c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 22621c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 22630924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 22640924cd68SAdrian Hunter 226576099f98SAdrian Hunter# Event list 226676099f98SAdrian Hunter 226776099f98SAdrian Hunterdef GetEventList(db): 226876099f98SAdrian Hunter events = [] 226976099f98SAdrian Hunter query = QSqlQuery(db) 227076099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 227176099f98SAdrian Hunter while query.next(): 227276099f98SAdrian Hunter events.append(query.value(0)) 227376099f98SAdrian Hunter return events 227476099f98SAdrian Hunter 2275655cb952SAdrian Hunter# Is a table selectable 2276655cb952SAdrian Hunter 2277530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"): 2278655cb952SAdrian Hunter query = QSqlQuery(db) 2279655cb952SAdrian Hunter try: 2280530e22fdSAdrian Hunter QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") 2281655cb952SAdrian Hunter except: 2282655cb952SAdrian Hunter return False 2283655cb952SAdrian Hunter return True 2284655cb952SAdrian Hunter 22858392b74bSAdrian Hunter# SQL table data model item 22868392b74bSAdrian Hunter 22878392b74bSAdrian Hunterclass SQLTableItem(): 22888392b74bSAdrian Hunter 22898392b74bSAdrian Hunter def __init__(self, row, data): 22908392b74bSAdrian Hunter self.row = row 22918392b74bSAdrian Hunter self.data = data 22928392b74bSAdrian Hunter 22938392b74bSAdrian Hunter def getData(self, column): 22948392b74bSAdrian Hunter return self.data[column] 22958392b74bSAdrian Hunter 22968392b74bSAdrian Hunter# SQL table data model 22978392b74bSAdrian Hunter 22988392b74bSAdrian Hunterclass SQLTableModel(TableModel): 22998392b74bSAdrian Hunter 23008392b74bSAdrian Hunter progress = Signal(object) 23018392b74bSAdrian Hunter 23028c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 23038392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 23048392b74bSAdrian Hunter self.glb = glb 23058392b74bSAdrian Hunter self.more = True 23068392b74bSAdrian Hunter self.populated = 0 23078c90fef9SAdrian Hunter self.column_headers = column_headers 23088453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 23098392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 23108392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 23118392b74bSAdrian Hunter 23128392b74bSAdrian Hunter def DisplayData(self, item, index): 23138392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 23148392b74bSAdrian Hunter return item.getData(index.column()) 23158392b74bSAdrian Hunter 23168392b74bSAdrian Hunter def AddSample(self, data): 23178392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 23188392b74bSAdrian Hunter self.child_items.append(child) 23198392b74bSAdrian Hunter self.populated += 1 23208392b74bSAdrian Hunter 23218392b74bSAdrian Hunter def Update(self, fetched): 23228392b74bSAdrian Hunter if not fetched: 23238392b74bSAdrian Hunter self.more = False 23248392b74bSAdrian Hunter self.progress.emit(0) 23258392b74bSAdrian Hunter child_count = self.child_count 23268392b74bSAdrian Hunter count = self.populated - child_count 23278392b74bSAdrian Hunter if count > 0: 23288392b74bSAdrian Hunter parent = QModelIndex() 23298392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 23308392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 23318392b74bSAdrian Hunter self.child_count += count 23328392b74bSAdrian Hunter self.endInsertRows() 23338392b74bSAdrian Hunter self.progress.emit(self.child_count) 23348392b74bSAdrian Hunter 23358392b74bSAdrian Hunter def FetchMoreRecords(self, count): 23368392b74bSAdrian Hunter current = self.child_count 23378392b74bSAdrian Hunter if self.more: 23388392b74bSAdrian Hunter self.fetcher.Fetch(count) 23398392b74bSAdrian Hunter else: 23408392b74bSAdrian Hunter self.progress.emit(0) 23418392b74bSAdrian Hunter return current 23428392b74bSAdrian Hunter 23438392b74bSAdrian Hunter def HasMoreRecords(self): 23448392b74bSAdrian Hunter return self.more 23458392b74bSAdrian Hunter 23468c90fef9SAdrian Hunter def columnCount(self, parent=None): 23478c90fef9SAdrian Hunter return len(self.column_headers) 23488c90fef9SAdrian Hunter 23498c90fef9SAdrian Hunter def columnHeader(self, column): 23508c90fef9SAdrian Hunter return self.column_headers[column] 23518c90fef9SAdrian Hunter 23528453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 23538453c936SAdrian Hunter data = [] 23548453c936SAdrian Hunter for i in xrange(count): 23558453c936SAdrian Hunter data.append(query.value(i)) 23568453c936SAdrian Hunter return data 23578453c936SAdrian Hunter 23588392b74bSAdrian Hunter# SQL automatic table data model 23598392b74bSAdrian Hunter 23608392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 23618392b74bSAdrian Hunter 23628392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 23638392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 23648392b74bSAdrian Hunter if table_name == "comm_threads_view": 23658392b74bSAdrian Hunter # For now, comm_threads_view has no id column 23668392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 23678c90fef9SAdrian Hunter column_headers = [] 23688392b74bSAdrian Hunter query = QSqlQuery(glb.db) 23698392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 23708392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 23718392b74bSAdrian Hunter while query.next(): 23728c90fef9SAdrian Hunter column_headers.append(query.value(1)) 23738392b74bSAdrian Hunter if table_name == "sqlite_master": 23748392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 23758392b74bSAdrian Hunter else: 23768392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 23778392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 23788392b74bSAdrian Hunter select_table_name = table_name[19:] 23798392b74bSAdrian Hunter schema = "information_schema" 23808392b74bSAdrian Hunter else: 23818392b74bSAdrian Hunter select_table_name = table_name 23828392b74bSAdrian Hunter schema = "public" 23838392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 23848392b74bSAdrian Hunter while query.next(): 23858c90fef9SAdrian Hunter column_headers.append(query.value(0)) 23868453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 23878453c936SAdrian Hunter if table_name == "samples_view": 23888453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 23898453c936SAdrian Hunter if table_name == "samples": 23908453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 23918c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 23928392b74bSAdrian Hunter 23938453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 23948453c936SAdrian Hunter data = [] 23958453c936SAdrian Hunter data.append(query.value(0)) 23968453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 23978453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 23988453c936SAdrian Hunter for i in xrange(2, count): 23998453c936SAdrian Hunter data.append(query.value(i)) 24008453c936SAdrian Hunter return data 24018453c936SAdrian Hunter 24028453c936SAdrian Hunter def samples_DataPrep(self, query, count): 24038453c936SAdrian Hunter data = [] 24048453c936SAdrian Hunter for i in xrange(9): 24058453c936SAdrian Hunter data.append(query.value(i)) 24068453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 24078453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 24088453c936SAdrian Hunter for i in xrange(10, count): 24098453c936SAdrian Hunter data.append(query.value(i)) 24108453c936SAdrian Hunter return data 24118453c936SAdrian Hunter 24128392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 24138392b74bSAdrian Hunter 24148392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 24158392b74bSAdrian Hunter 24168392b74bSAdrian Hunter def __init__(self, parent=None): 24178392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 24188392b74bSAdrian Hunter 24198392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 24208392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 24218392b74bSAdrian Hunter # so implement a crude alternative 24228392b74bSAdrian Hunter font = self.view.font() 24238392b74bSAdrian Hunter metrics = QFontMetrics(font) 24248392b74bSAdrian Hunter max = 0 24258392b74bSAdrian Hunter for row in xrange(n): 24268392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 24278392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 24288392b74bSAdrian Hunter max = len if len > max else max 24298392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 24308392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 24318392b74bSAdrian Hunter max = len if len > max else max 24328392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 24338392b74bSAdrian Hunter 24348392b74bSAdrian Hunter def ResizeColumnsToContents(self): 24358392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 24368392b74bSAdrian Hunter if n < 1: 24378392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 24388392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 24398392b74bSAdrian Hunter return 24408392b74bSAdrian Hunter columns = self.data_model.columnCount() 24418392b74bSAdrian Hunter for i in xrange(columns): 24428392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 24438392b74bSAdrian Hunter 24448392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 24458392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 24468392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 24478392b74bSAdrian Hunter self.ResizeColumnsToContents() 24488392b74bSAdrian Hunter 244996c43b9aSAdrian Hunter# Convert value to CSV 245096c43b9aSAdrian Hunter 245196c43b9aSAdrian Hunterdef ToCSValue(val): 245296c43b9aSAdrian Hunter if '"' in val: 245396c43b9aSAdrian Hunter val = val.replace('"', '""') 245496c43b9aSAdrian Hunter if "," in val or '"' in val: 245596c43b9aSAdrian Hunter val = '"' + val + '"' 245696c43b9aSAdrian Hunter return val 245796c43b9aSAdrian Hunter 245896c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 245996c43b9aSAdrian Hunter 246096c43b9aSAdrian Hunterglb_max_cols = 1000 246196c43b9aSAdrian Hunter 246296c43b9aSAdrian Hunterdef RowColumnKey(a): 246396c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 246496c43b9aSAdrian Hunter 246596c43b9aSAdrian Hunter# Copy selected table cells to clipboard 246696c43b9aSAdrian Hunter 246796c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 246896c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 246996c43b9aSAdrian Hunter idx_cnt = len(indexes) 247096c43b9aSAdrian Hunter if not idx_cnt: 247196c43b9aSAdrian Hunter return 247296c43b9aSAdrian Hunter if idx_cnt == 1: 247396c43b9aSAdrian Hunter with_hdr=False 247496c43b9aSAdrian Hunter min_row = indexes[0].row() 247596c43b9aSAdrian Hunter max_row = indexes[0].row() 247696c43b9aSAdrian Hunter min_col = indexes[0].column() 247796c43b9aSAdrian Hunter max_col = indexes[0].column() 247896c43b9aSAdrian Hunter for i in indexes: 247996c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 248096c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 248196c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 248296c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 248396c43b9aSAdrian Hunter if max_col > glb_max_cols: 248496c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 248596c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 248696c43b9aSAdrian Hunter for i in indexes: 248796c43b9aSAdrian Hunter c = i.column() - min_col 248896c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 248996c43b9aSAdrian Hunter text = "" 249096c43b9aSAdrian Hunter pad = "" 249196c43b9aSAdrian Hunter sep = "" 249296c43b9aSAdrian Hunter if with_hdr: 249396c43b9aSAdrian Hunter model = indexes[0].model() 249496c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 249596c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 249696c43b9aSAdrian Hunter if as_csv: 249796c43b9aSAdrian Hunter text += sep + ToCSValue(val) 249896c43b9aSAdrian Hunter sep = "," 249996c43b9aSAdrian Hunter else: 250096c43b9aSAdrian Hunter c = col - min_col 250196c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 250296c43b9aSAdrian Hunter width = max_width[c] 250396c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 250496c43b9aSAdrian Hunter if align & Qt.AlignRight: 250596c43b9aSAdrian Hunter val = val.rjust(width) 250696c43b9aSAdrian Hunter text += pad + sep + val 250796c43b9aSAdrian Hunter pad = " " * (width - len(val)) 250896c43b9aSAdrian Hunter sep = " " 250996c43b9aSAdrian Hunter text += "\n" 251096c43b9aSAdrian Hunter pad = "" 251196c43b9aSAdrian Hunter sep = "" 251296c43b9aSAdrian Hunter last_row = min_row 251396c43b9aSAdrian Hunter for i in indexes: 251496c43b9aSAdrian Hunter if i.row() > last_row: 251596c43b9aSAdrian Hunter last_row = i.row() 251696c43b9aSAdrian Hunter text += "\n" 251796c43b9aSAdrian Hunter pad = "" 251896c43b9aSAdrian Hunter sep = "" 251996c43b9aSAdrian Hunter if as_csv: 252096c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 252196c43b9aSAdrian Hunter sep = "," 252296c43b9aSAdrian Hunter else: 252396c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 252496c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 252596c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 252696c43b9aSAdrian Hunter else: 252796c43b9aSAdrian Hunter val = str(i.data()) 252896c43b9aSAdrian Hunter text += pad + sep + val 252996c43b9aSAdrian Hunter pad = " " * (width - len(val)) 253096c43b9aSAdrian Hunter sep = " " 253196c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 253296c43b9aSAdrian Hunter 253396c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 253496c43b9aSAdrian Hunter indexes = view.selectedIndexes() 253596c43b9aSAdrian Hunter if not len(indexes): 253696c43b9aSAdrian Hunter return 253796c43b9aSAdrian Hunter 253896c43b9aSAdrian Hunter selection = view.selectionModel() 253996c43b9aSAdrian Hunter 254096c43b9aSAdrian Hunter first = None 254196c43b9aSAdrian Hunter for i in indexes: 254296c43b9aSAdrian Hunter above = view.indexAbove(i) 254396c43b9aSAdrian Hunter if not selection.isSelected(above): 254496c43b9aSAdrian Hunter first = i 254596c43b9aSAdrian Hunter break 254696c43b9aSAdrian Hunter 254796c43b9aSAdrian Hunter if first is None: 254896c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 254996c43b9aSAdrian Hunter 255096c43b9aSAdrian Hunter model = first.model() 255196c43b9aSAdrian Hunter row_cnt = 0 255296c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 255396c43b9aSAdrian Hunter max_width = [0] * col_cnt 255496c43b9aSAdrian Hunter 255596c43b9aSAdrian Hunter indent_sz = 2 255696c43b9aSAdrian Hunter indent_str = " " * indent_sz 255796c43b9aSAdrian Hunter 255896c43b9aSAdrian Hunter expanded_mark_sz = 2 255996c43b9aSAdrian Hunter if sys.version_info[0] == 3: 256096c43b9aSAdrian Hunter expanded_mark = "\u25BC " 256196c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 256296c43b9aSAdrian Hunter else: 256396c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 256496c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 256596c43b9aSAdrian Hunter leaf_mark = " " 256696c43b9aSAdrian Hunter 256796c43b9aSAdrian Hunter if not as_csv: 256896c43b9aSAdrian Hunter pos = first 256996c43b9aSAdrian Hunter while True: 257096c43b9aSAdrian Hunter row_cnt += 1 257196c43b9aSAdrian Hunter row = pos.row() 257296c43b9aSAdrian Hunter for c in range(col_cnt): 257396c43b9aSAdrian Hunter i = pos.sibling(row, c) 257496c43b9aSAdrian Hunter if c: 257596c43b9aSAdrian Hunter n = len(str(i.data())) 257696c43b9aSAdrian Hunter else: 257796c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 257896c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 257996c43b9aSAdrian Hunter n += expanded_mark_sz 258096c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 258196c43b9aSAdrian Hunter pos = view.indexBelow(pos) 258296c43b9aSAdrian Hunter if not selection.isSelected(pos): 258396c43b9aSAdrian Hunter break 258496c43b9aSAdrian Hunter 258596c43b9aSAdrian Hunter text = "" 258696c43b9aSAdrian Hunter pad = "" 258796c43b9aSAdrian Hunter sep = "" 258896c43b9aSAdrian Hunter if with_hdr: 258996c43b9aSAdrian Hunter for c in range(col_cnt): 259096c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 259196c43b9aSAdrian Hunter if as_csv: 259296c43b9aSAdrian Hunter text += sep + ToCSValue(val) 259396c43b9aSAdrian Hunter sep = "," 259496c43b9aSAdrian Hunter else: 259596c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 259696c43b9aSAdrian Hunter width = max_width[c] 259796c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 259896c43b9aSAdrian Hunter if align & Qt.AlignRight: 259996c43b9aSAdrian Hunter val = val.rjust(width) 260096c43b9aSAdrian Hunter text += pad + sep + val 260196c43b9aSAdrian Hunter pad = " " * (width - len(val)) 260296c43b9aSAdrian Hunter sep = " " 260396c43b9aSAdrian Hunter text += "\n" 260496c43b9aSAdrian Hunter pad = "" 260596c43b9aSAdrian Hunter sep = "" 260696c43b9aSAdrian Hunter 260796c43b9aSAdrian Hunter pos = first 260896c43b9aSAdrian Hunter while True: 260996c43b9aSAdrian Hunter row = pos.row() 261096c43b9aSAdrian Hunter for c in range(col_cnt): 261196c43b9aSAdrian Hunter i = pos.sibling(row, c) 261296c43b9aSAdrian Hunter val = str(i.data()) 261396c43b9aSAdrian Hunter if not c: 261496c43b9aSAdrian Hunter if model.hasChildren(i): 261596c43b9aSAdrian Hunter if view.isExpanded(i): 261696c43b9aSAdrian Hunter mark = expanded_mark 261796c43b9aSAdrian Hunter else: 261896c43b9aSAdrian Hunter mark = not_expanded_mark 261996c43b9aSAdrian Hunter else: 262096c43b9aSAdrian Hunter mark = leaf_mark 262196c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 262296c43b9aSAdrian Hunter if as_csv: 262396c43b9aSAdrian Hunter text += sep + ToCSValue(val) 262496c43b9aSAdrian Hunter sep = "," 262596c43b9aSAdrian Hunter else: 262696c43b9aSAdrian Hunter width = max_width[c] 262796c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & 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 pos = view.indexBelow(pos) 263396c43b9aSAdrian Hunter if not selection.isSelected(pos): 263496c43b9aSAdrian Hunter break 263596c43b9aSAdrian Hunter text = text.rstrip() + "\n" 263696c43b9aSAdrian Hunter pad = "" 263796c43b9aSAdrian Hunter sep = "" 263896c43b9aSAdrian Hunter 263996c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 264096c43b9aSAdrian Hunter 264196c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 264296c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 264396c43b9aSAdrian Hunter 264496c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 264596c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 264696c43b9aSAdrian Hunter 264796c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 264896c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 264996c43b9aSAdrian Hunter 26509bc4e4bfSAdrian Hunter# Context menu 26519bc4e4bfSAdrian Hunter 26529bc4e4bfSAdrian Hunterclass ContextMenu(object): 26539bc4e4bfSAdrian Hunter 26549bc4e4bfSAdrian Hunter def __init__(self, view): 26559bc4e4bfSAdrian Hunter self.view = view 26569bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 26579bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 26589bc4e4bfSAdrian Hunter 26599bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 26609bc4e4bfSAdrian Hunter menu = QMenu(self.view) 26619bc4e4bfSAdrian Hunter self.AddActions(menu) 26629bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 26639bc4e4bfSAdrian Hunter 26649bc4e4bfSAdrian Hunter def AddCopy(self, menu): 26659bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 26669bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 26679bc4e4bfSAdrian Hunter 26689bc4e4bfSAdrian Hunter def AddActions(self, menu): 26699bc4e4bfSAdrian Hunter self.AddCopy(menu) 26709bc4e4bfSAdrian Hunter 26719bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 26729bc4e4bfSAdrian Hunter 26739bc4e4bfSAdrian Hunter def __init__(self, view): 26749bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 26759bc4e4bfSAdrian Hunter 26769bc4e4bfSAdrian Hunter def AddActions(self, menu): 26779bc4e4bfSAdrian Hunter i = self.view.currentIndex() 26789bc4e4bfSAdrian Hunter text = str(i.data()).strip() 26799bc4e4bfSAdrian Hunter if len(text): 26809bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 26819bc4e4bfSAdrian Hunter self.AddCopy(menu) 26829bc4e4bfSAdrian Hunter 26838392b74bSAdrian Hunter# Table window 26848392b74bSAdrian Hunter 26858392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 26868392b74bSAdrian Hunter 26878392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 26888392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 26898392b74bSAdrian Hunter 26908392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 26918392b74bSAdrian Hunter 26928392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 26938392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 26948392b74bSAdrian Hunter 26958392b74bSAdrian Hunter self.view = QTableView() 26968392b74bSAdrian Hunter self.view.setModel(self.model) 26978392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 26988392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 26998392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 27008392b74bSAdrian Hunter self.view.setSortingEnabled(True) 270196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 270296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 27038392b74bSAdrian Hunter 27048392b74bSAdrian Hunter self.ResizeColumnsToContents() 27058392b74bSAdrian Hunter 27069bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 27079bc4e4bfSAdrian Hunter 27088392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 27098392b74bSAdrian Hunter 27108392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 27118392b74bSAdrian Hunter 27128392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 27138392b74bSAdrian Hunter 27148392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 27158392b74bSAdrian Hunter 27168392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 27178392b74bSAdrian Hunter 27188392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 27198392b74bSAdrian Hunter 27208392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 27218392b74bSAdrian Hunter self.view.setFocus() 27228392b74bSAdrian Hunter self.find_bar.Busy() 27238392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 27248392b74bSAdrian Hunter 27258392b74bSAdrian Hunter def FindDone(self, row): 27268392b74bSAdrian Hunter self.find_bar.Idle() 27278392b74bSAdrian Hunter if row >= 0: 272835fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 27298392b74bSAdrian Hunter else: 27308392b74bSAdrian Hunter self.find_bar.NotFound() 27318392b74bSAdrian Hunter 27328392b74bSAdrian Hunter# Table list 27338392b74bSAdrian Hunter 27348392b74bSAdrian Hunterdef GetTableList(glb): 27358392b74bSAdrian Hunter tables = [] 27368392b74bSAdrian Hunter query = QSqlQuery(glb.db) 27378392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 27388392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 27398392b74bSAdrian Hunter else: 27408392b74bSAdrian 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") 27418392b74bSAdrian Hunter while query.next(): 27428392b74bSAdrian Hunter tables.append(query.value(0)) 27438392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 27448392b74bSAdrian Hunter tables.append("sqlite_master") 27458392b74bSAdrian Hunter else: 27468392b74bSAdrian Hunter tables.append("information_schema.tables") 27478392b74bSAdrian Hunter tables.append("information_schema.views") 27488392b74bSAdrian Hunter tables.append("information_schema.columns") 27498392b74bSAdrian Hunter return tables 27508392b74bSAdrian Hunter 2751cd358012SAdrian Hunter# Top Calls data model 2752cd358012SAdrian Hunter 2753cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2754cd358012SAdrian Hunter 2755cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2756cd358012SAdrian Hunter text = "" 2757cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2758cd358012SAdrian Hunter text = "::text" 2759cd358012SAdrian Hunter limit = "" 2760cd358012SAdrian Hunter if len(report_vars.limit): 2761cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2762cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2763cd358012SAdrian Hunter " CASE" 2764cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2765cd358012SAdrian Hunter " ELSE short_name" 2766cd358012SAdrian Hunter " END AS dso," 2767cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2768cd358012SAdrian Hunter " CASE" 2769cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2770cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2771cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2772cd358012SAdrian Hunter " ELSE ''" + text + 2773cd358012SAdrian Hunter " END AS flags" 2774cd358012SAdrian Hunter " FROM calls" 2775cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2776cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2777cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2778cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2779cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2780cd358012SAdrian Hunter report_vars.where_clause + 2781cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2782cd358012SAdrian Hunter limit 2783cd358012SAdrian Hunter ) 2784cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2785cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2786cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2787cd358012SAdrian Hunter 2788cd358012SAdrian Hunter def columnAlignment(self, column): 2789cd358012SAdrian Hunter return self.alignment[column] 2790cd358012SAdrian Hunter 2791cd358012SAdrian Hunter# Top Calls report creation dialog 2792cd358012SAdrian Hunter 2793cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2794cd358012SAdrian Hunter 2795cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2796cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2797cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2798cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2799cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2800cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2801cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2802cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2803cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2804cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2805cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2806cd358012SAdrian Hunter 2807cd358012SAdrian Hunter# Top Calls window 2808cd358012SAdrian Hunter 2809cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2810cd358012SAdrian Hunter 2811cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2812cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2813cd358012SAdrian Hunter 2814cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2815cd358012SAdrian Hunter self.model = self.data_model 2816cd358012SAdrian Hunter 2817cd358012SAdrian Hunter self.view = QTableView() 2818cd358012SAdrian Hunter self.view.setModel(self.model) 2819cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2820cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 282196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 282296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2823cd358012SAdrian Hunter 28249bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 28259bc4e4bfSAdrian Hunter 2826cd358012SAdrian Hunter self.ResizeColumnsToContents() 2827cd358012SAdrian Hunter 2828cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2829cd358012SAdrian Hunter 2830cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2831cd358012SAdrian Hunter 2832cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2833cd358012SAdrian Hunter 2834cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2835cd358012SAdrian Hunter 2836cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2837cd358012SAdrian Hunter 2838cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2839cd358012SAdrian Hunter 2840cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2841cd358012SAdrian Hunter self.view.setFocus() 2842cd358012SAdrian Hunter self.find_bar.Busy() 2843cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2844cd358012SAdrian Hunter 2845cd358012SAdrian Hunter def FindDone(self, row): 2846cd358012SAdrian Hunter self.find_bar.Idle() 2847cd358012SAdrian Hunter if row >= 0: 2848cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2849cd358012SAdrian Hunter else: 2850cd358012SAdrian Hunter self.find_bar.NotFound() 2851cd358012SAdrian Hunter 28521beb5c7bSAdrian Hunter# Action Definition 28531beb5c7bSAdrian Hunter 28541beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 28551beb5c7bSAdrian Hunter action = QAction(label, parent) 28561beb5c7bSAdrian Hunter if shortcut != None: 28571beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 28581beb5c7bSAdrian Hunter action.setStatusTip(tip) 28591beb5c7bSAdrian Hunter action.triggered.connect(callback) 28601beb5c7bSAdrian Hunter return action 28611beb5c7bSAdrian Hunter 28621beb5c7bSAdrian Hunter# Typical application actions 28631beb5c7bSAdrian Hunter 28641beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 28651beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 28661beb5c7bSAdrian Hunter 28671beb5c7bSAdrian Hunter# Typical MDI actions 28681beb5c7bSAdrian Hunter 28691beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 28701beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 28711beb5c7bSAdrian Hunter 28721beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 28731beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 28741beb5c7bSAdrian Hunter 28751beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 28761beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 28771beb5c7bSAdrian Hunter 28781beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 28791beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 28801beb5c7bSAdrian Hunter 28811beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 28821beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 28831beb5c7bSAdrian Hunter 28841beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 28851beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 28861beb5c7bSAdrian Hunter 28871beb5c7bSAdrian Hunter# Typical MDI window menu 28881beb5c7bSAdrian Hunter 28891beb5c7bSAdrian Hunterclass WindowMenu(): 28901beb5c7bSAdrian Hunter 28911beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 28921beb5c7bSAdrian Hunter self.mdi_area = mdi_area 28931beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 28941beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 28951beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 28961beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 28971beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 28981beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 28991beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 29001beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 29011beb5c7bSAdrian Hunter 29021beb5c7bSAdrian Hunter def Update(self): 29031beb5c7bSAdrian Hunter self.window_menu.clear() 29041beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 29051beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 29061beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 29071beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 29081beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 29091beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 29101beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 29111beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 29121beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 29131beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 29141beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29151beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 29161beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 29171beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29181beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 29191beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 29201beb5c7bSAdrian Hunter if sub_window_count == 0: 29211beb5c7bSAdrian Hunter return 29221beb5c7bSAdrian Hunter self.window_menu.addSeparator() 29231beb5c7bSAdrian Hunter nr = 1 29241beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 29251beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 29261beb5c7bSAdrian Hunter if nr < 10: 29271beb5c7bSAdrian Hunter label = "&" + label 29281beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 29291beb5c7bSAdrian Hunter action.setCheckable(True) 29301beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 2931df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 29321beb5c7bSAdrian Hunter self.window_menu.addAction(action) 29331beb5c7bSAdrian Hunter nr += 1 29341beb5c7bSAdrian Hunter 29351beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 29361beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 29371beb5c7bSAdrian Hunter 293865b24292SAdrian Hunter# Help text 293965b24292SAdrian Hunter 294065b24292SAdrian Hunterglb_help_text = """ 294165b24292SAdrian Hunter<h1>Contents</h1> 294265b24292SAdrian Hunter<style> 294365b24292SAdrian Hunterp.c1 { 294465b24292SAdrian Hunter text-indent: 40px; 294565b24292SAdrian Hunter} 294665b24292SAdrian Hunterp.c2 { 294765b24292SAdrian Hunter text-indent: 80px; 294865b24292SAdrian Hunter} 294965b24292SAdrian Hunter} 295065b24292SAdrian Hunter</style> 295165b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 295265b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2953ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2954ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2955ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2956ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 295765b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 295865b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 295965b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 296065b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 296165b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 296265b24292SAdrian Hunterwidths to suit will display something like: 296365b24292SAdrian Hunter<pre> 296465b24292SAdrian Hunter Call Graph: pt_example 296565b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 296665b24292SAdrian Hunterv- ls 296765b24292SAdrian Hunter v- 2638:2638 296865b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 296965b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 297065b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 297165b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 297265b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 297365b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 297465b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 297565b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 297665b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 297765b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 297865b24292SAdrian Hunter</pre> 297965b24292SAdrian Hunter<h3>Points to note:</h3> 298065b24292SAdrian Hunter<ul> 298165b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 298265b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 298365b24292SAdrian Hunter<li>Subsequent levels are functions</li> 298465b24292SAdrian Hunter<li>'Count' is the number of calls</li> 298565b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 298665b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 298765b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 298865b24292SAdrian Hunter</ul> 298965b24292SAdrian Hunter<h3>Find</h3> 299065b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 299165b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2992ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2993ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2994ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2995ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 299665b24292SAdrian HunterThe All branches report displays all branches in chronological order. 299765b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 299865b24292SAdrian Hunter<h3>Disassembly</h3> 299965b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 300065b24292SAdrian Hunter<ol> 300165b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 300265b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 300365b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 300465b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 300565b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 300665b24292SAdrian Hunter</ol> 300765b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 300865b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 300965b24292SAdrian Hunter<pre> 301065b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 301165b24292SAdrian Huntergit clone https://github.com/intelxed/xed 301265b24292SAdrian Huntercd xed 301365b24292SAdrian Hunter./mfile.py --share 301465b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 301565b24292SAdrian Huntersudo ldconfig 301665b24292SAdrian Hunter</pre> 3017530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3> 3018530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. 3019530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. 3020530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known. 3021530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period 3022530e22fdSAdrian Huntersince the previous displayed 'IPC'. 302365b24292SAdrian Hunter<h3>Find</h3> 302465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 302565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 302665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 3027ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 302865b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 302965b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 3030ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 303165b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 303265b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 303365b24292SAdrian Hunter<pre> 303465b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 303565b24292SAdrian Hunter 100us-200us From 100us to 200us 303665b24292SAdrian Hunter 10ms- From 10ms to the end 303765b24292SAdrian Hunter -100ns The first 100ns 303865b24292SAdrian Hunter -10ms- The last 10ms 303965b24292SAdrian Hunter</pre> 304065b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 3041ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 3042cd358012SAdrian 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. 3043cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 3044cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 304565b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 304665b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 304765b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 304865b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 304965b24292SAdrian Hunterbut that can be slow for large tables. 305065b24292SAdrian Hunter<p>There are also tables of database meta-information. 305165b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 305265b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 305365b24292SAdrian Hunter<h3>Find</h3> 305465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 305565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 305665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 305735fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 305835fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 305965b24292SAdrian Hunter""" 306065b24292SAdrian Hunter 306165b24292SAdrian Hunter# Help window 306265b24292SAdrian Hunter 306365b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 306465b24292SAdrian Hunter 306565b24292SAdrian Hunter def __init__(self, glb, parent=None): 306665b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 306765b24292SAdrian Hunter 306865b24292SAdrian Hunter self.text = QTextBrowser() 306965b24292SAdrian Hunter self.text.setHtml(glb_help_text) 307065b24292SAdrian Hunter self.text.setReadOnly(True) 307165b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 307265b24292SAdrian Hunter 307365b24292SAdrian Hunter self.setWidget(self.text) 307465b24292SAdrian Hunter 307565b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 307665b24292SAdrian Hunter 307765b24292SAdrian Hunter# Main window that only displays the help text 307865b24292SAdrian Hunter 307965b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 308065b24292SAdrian Hunter 308165b24292SAdrian Hunter def __init__(self, parent=None): 308265b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 308365b24292SAdrian Hunter 308465b24292SAdrian Hunter self.setMinimumSize(200, 100) 308565b24292SAdrian Hunter self.resize(800, 600) 308665b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 308765b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 308865b24292SAdrian Hunter 308965b24292SAdrian Hunter self.text = QTextBrowser() 309065b24292SAdrian Hunter self.text.setHtml(glb_help_text) 309165b24292SAdrian Hunter self.text.setReadOnly(True) 309265b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 309365b24292SAdrian Hunter 309465b24292SAdrian Hunter self.setCentralWidget(self.text) 309565b24292SAdrian Hunter 3096b62d18abSAdrian Hunter# PostqreSQL server version 3097b62d18abSAdrian Hunter 3098b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 3099b62d18abSAdrian Hunter query = QSqlQuery(db) 3100b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 3101b62d18abSAdrian Hunter if query.next(): 3102b62d18abSAdrian Hunter v_str = query.value(0) 3103b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 3104b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 3105b62d18abSAdrian Hunter return v_list[1] 3106b62d18abSAdrian Hunter return v_str 3107b62d18abSAdrian Hunter return "Unknown" 3108b62d18abSAdrian Hunter 3109b62d18abSAdrian Hunter# SQLite version 3110b62d18abSAdrian Hunter 3111b62d18abSAdrian Hunterdef SQLiteVersion(db): 3112b62d18abSAdrian Hunter query = QSqlQuery(db) 3113b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 3114b62d18abSAdrian Hunter if query.next(): 3115b62d18abSAdrian Hunter return query.value(0) 3116b62d18abSAdrian Hunter return "Unknown" 3117b62d18abSAdrian Hunter 3118b62d18abSAdrian Hunter# About dialog 3119b62d18abSAdrian Hunter 3120b62d18abSAdrian Hunterclass AboutDialog(QDialog): 3121b62d18abSAdrian Hunter 3122b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 3123b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 3124b62d18abSAdrian Hunter 3125b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 3126b62d18abSAdrian Hunter self.setMinimumWidth(300) 3127b62d18abSAdrian Hunter 3128b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 3129b62d18abSAdrian Hunter 3130b62d18abSAdrian Hunter text = "<pre>" 3131b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 3132b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 3133b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 3134b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 3135b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 3136b62d18abSAdrian Hunter else: 3137b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 3138b62d18abSAdrian Hunter text += "</pre>" 3139b62d18abSAdrian Hunter 3140b62d18abSAdrian Hunter self.text = QTextBrowser() 3141b62d18abSAdrian Hunter self.text.setHtml(text) 3142b62d18abSAdrian Hunter self.text.setReadOnly(True) 3143b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 3144b62d18abSAdrian Hunter 3145b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 3146b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 3147b62d18abSAdrian Hunter 314826688729SAdrian Hunter self.setLayout(self.vbox) 3149b62d18abSAdrian Hunter 315082f68e28SAdrian Hunter# Font resize 315182f68e28SAdrian Hunter 315282f68e28SAdrian Hunterdef ResizeFont(widget, diff): 315382f68e28SAdrian Hunter font = widget.font() 315482f68e28SAdrian Hunter sz = font.pointSize() 315582f68e28SAdrian Hunter font.setPointSize(sz + diff) 315682f68e28SAdrian Hunter widget.setFont(font) 315782f68e28SAdrian Hunter 315882f68e28SAdrian Hunterdef ShrinkFont(widget): 315982f68e28SAdrian Hunter ResizeFont(widget, -1) 316082f68e28SAdrian Hunter 316182f68e28SAdrian Hunterdef EnlargeFont(widget): 316282f68e28SAdrian Hunter ResizeFont(widget, 1) 316382f68e28SAdrian Hunter 31641beb5c7bSAdrian Hunter# Unique name for sub-windows 31651beb5c7bSAdrian Hunter 31661beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 31671beb5c7bSAdrian Hunter if nr > 1: 31681beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 31691beb5c7bSAdrian Hunter return name 31701beb5c7bSAdrian Hunter 31711beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 31721beb5c7bSAdrian Hunter nr = 1 31731beb5c7bSAdrian Hunter while True: 31741beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 31751beb5c7bSAdrian Hunter ok = True 31761beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 31771beb5c7bSAdrian Hunter if sub_window.name == unique_name: 31781beb5c7bSAdrian Hunter ok = False 31791beb5c7bSAdrian Hunter break 31801beb5c7bSAdrian Hunter if ok: 31811beb5c7bSAdrian Hunter return unique_name 31821beb5c7bSAdrian Hunter nr += 1 31831beb5c7bSAdrian Hunter 31841beb5c7bSAdrian Hunter# Add a sub-window 31851beb5c7bSAdrian Hunter 31861beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 31871beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 31881beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 31891beb5c7bSAdrian Hunter sub_window.resize(800, 600) 31901beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 31911beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 31921beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 31931beb5c7bSAdrian Hunter sub_window.name = unique_name 31941beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 31951beb5c7bSAdrian Hunter sub_window.show() 31961beb5c7bSAdrian Hunter 3197031c2a00SAdrian Hunter# Main window 3198031c2a00SAdrian Hunter 3199031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 3200031c2a00SAdrian Hunter 3201031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 3202031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 3203031c2a00SAdrian Hunter 3204031c2a00SAdrian Hunter self.glb = glb 3205031c2a00SAdrian Hunter 32061beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 3207031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 3208031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 3209031c2a00SAdrian Hunter 32101beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 32111beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 32121beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 3213031c2a00SAdrian Hunter 32141beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 3215031c2a00SAdrian Hunter 32161beb5c7bSAdrian Hunter menu = self.menuBar() 3217031c2a00SAdrian Hunter 32181beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 32191beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 32201beb5c7bSAdrian Hunter 3221ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 322296c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 322396c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 3224ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 32258392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 322682f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 322782f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 3228ebd70c7dSAdrian Hunter 32291beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 3230655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 32311beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 32321beb5c7bSAdrian Hunter 3233ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3234ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3235ae8b887cSAdrian Hunter 323676099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 323776099f98SAdrian Hunter 3238cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3239cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3240cd358012SAdrian Hunter 32418392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 32428392b74bSAdrian Hunter 32431beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 32441beb5c7bSAdrian Hunter 324565b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 324665b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 3247b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 324865b24292SAdrian Hunter 32494b208453SAdrian Hunter def Try(self, fn): 32504b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 32514b208453SAdrian Hunter if win: 32524b208453SAdrian Hunter try: 32534b208453SAdrian Hunter fn(win.view) 32544b208453SAdrian Hunter except: 32554b208453SAdrian Hunter pass 32564b208453SAdrian Hunter 325796c43b9aSAdrian Hunter def CopyToClipboard(self): 325896c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 325996c43b9aSAdrian Hunter 326096c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 326196c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 326296c43b9aSAdrian Hunter 3263ebd70c7dSAdrian Hunter def Find(self): 3264ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3265ebd70c7dSAdrian Hunter if win: 3266ebd70c7dSAdrian Hunter try: 3267ebd70c7dSAdrian Hunter win.find_bar.Activate() 3268ebd70c7dSAdrian Hunter except: 3269ebd70c7dSAdrian Hunter pass 3270ebd70c7dSAdrian Hunter 32718392b74bSAdrian Hunter def FetchMoreRecords(self): 32728392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 32738392b74bSAdrian Hunter if win: 32748392b74bSAdrian Hunter try: 32758392b74bSAdrian Hunter win.fetch_bar.Activate() 32768392b74bSAdrian Hunter except: 32778392b74bSAdrian Hunter pass 32788392b74bSAdrian Hunter 327982f68e28SAdrian Hunter def ShrinkFont(self): 32804b208453SAdrian Hunter self.Try(ShrinkFont) 328182f68e28SAdrian Hunter 328282f68e28SAdrian Hunter def EnlargeFont(self): 32834b208453SAdrian Hunter self.Try(EnlargeFont) 328482f68e28SAdrian Hunter 328576099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 328676099f98SAdrian Hunter branches_events = 0 328776099f98SAdrian Hunter for event in events: 328876099f98SAdrian Hunter event = event.split(":")[0] 328976099f98SAdrian Hunter if event == "branches": 329076099f98SAdrian Hunter branches_events += 1 329176099f98SAdrian Hunter dbid = 0 329276099f98SAdrian Hunter for event in events: 329376099f98SAdrian Hunter dbid += 1 329476099f98SAdrian Hunter event = event.split(":")[0] 329576099f98SAdrian Hunter if event == "branches": 329676099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 3297df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 3298210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3299df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 330076099f98SAdrian Hunter 33018392b74bSAdrian Hunter def TableMenu(self, tables, menu): 33028392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 33038392b74bSAdrian Hunter for table in tables: 3304df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 33058392b74bSAdrian Hunter 33061beb5c7bSAdrian Hunter def NewCallGraph(self): 33071beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3308031c2a00SAdrian Hunter 3309ae8b887cSAdrian Hunter def NewCallTree(self): 3310ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3311ae8b887cSAdrian Hunter 3312cd358012SAdrian Hunter def NewTopCalls(self): 3313cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3314cd358012SAdrian Hunter ret = dialog.exec_() 3315cd358012SAdrian Hunter if ret: 3316cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3317cd358012SAdrian Hunter 331876099f98SAdrian Hunter def NewBranchView(self, event_id): 3319947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 332076099f98SAdrian Hunter 3321210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3322210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3323210cf1f9SAdrian Hunter ret = dialog.exec_() 3324210cf1f9SAdrian Hunter if ret: 3325947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3326210cf1f9SAdrian Hunter 33278392b74bSAdrian Hunter def NewTableView(self, table_name): 33288392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 33298392b74bSAdrian Hunter 333065b24292SAdrian Hunter def Help(self): 333165b24292SAdrian Hunter HelpWindow(self.glb, self) 333265b24292SAdrian Hunter 3333b62d18abSAdrian Hunter def About(self): 3334b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 3335b62d18abSAdrian Hunter dialog.exec_() 3336b62d18abSAdrian Hunter 333776099f98SAdrian Hunter# XED Disassembler 333876099f98SAdrian Hunter 333976099f98SAdrian Hunterclass xed_state_t(Structure): 334076099f98SAdrian Hunter 334176099f98SAdrian Hunter _fields_ = [ 334276099f98SAdrian Hunter ("mode", c_int), 334376099f98SAdrian Hunter ("width", c_int) 334476099f98SAdrian Hunter ] 334576099f98SAdrian Hunter 334676099f98SAdrian Hunterclass XEDInstruction(): 334776099f98SAdrian Hunter 334876099f98SAdrian Hunter def __init__(self, libxed): 334976099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 335076099f98SAdrian Hunter xedd_t = c_byte * 512 335176099f98SAdrian Hunter self.xedd = xedd_t() 335276099f98SAdrian Hunter self.xedp = addressof(self.xedd) 335376099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 335476099f98SAdrian Hunter self.state = xed_state_t() 335576099f98SAdrian Hunter self.statep = addressof(self.state) 335676099f98SAdrian Hunter # Buffer for disassembled instruction text 335776099f98SAdrian Hunter self.buffer = create_string_buffer(256) 335876099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 335976099f98SAdrian Hunter 336076099f98SAdrian Hunterclass LibXED(): 336176099f98SAdrian Hunter 336276099f98SAdrian Hunter def __init__(self): 33635ed4419dSAdrian Hunter try: 336476099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 33655ed4419dSAdrian Hunter except: 33665ed4419dSAdrian Hunter self.libxed = None 33675ed4419dSAdrian Hunter if not self.libxed: 33685ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 336976099f98SAdrian Hunter 337076099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 337176099f98SAdrian Hunter self.xed_tables_init.restype = None 337276099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 337376099f98SAdrian Hunter 337476099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 337576099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 337676099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 337776099f98SAdrian Hunter 337876099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 337976099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 338076099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 338176099f98SAdrian Hunter 338276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 338376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 338476099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 338576099f98SAdrian Hunter 338676099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 338776099f98SAdrian Hunter self.xed_decode.restype = c_int 338876099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 338976099f98SAdrian Hunter 339076099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 339176099f98SAdrian Hunter self.xed_format_context.restype = c_uint 339276099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 339376099f98SAdrian Hunter 339476099f98SAdrian Hunter self.xed_tables_init() 339576099f98SAdrian Hunter 339676099f98SAdrian Hunter def Instruction(self): 339776099f98SAdrian Hunter return XEDInstruction(self) 339876099f98SAdrian Hunter 339976099f98SAdrian Hunter def SetMode(self, inst, mode): 340076099f98SAdrian Hunter if mode: 340176099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 340276099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 340376099f98SAdrian Hunter else: 340476099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 340576099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 340676099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 340776099f98SAdrian Hunter 340876099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 340976099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 341076099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 341176099f98SAdrian Hunter if err: 341276099f98SAdrian Hunter return 0, "" 341376099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 341476099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 341576099f98SAdrian Hunter if not ok: 341676099f98SAdrian Hunter return 0, "" 3417606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3418606bd60aSAdrian Hunter result = inst.buffer.value 3419606bd60aSAdrian Hunter else: 3420606bd60aSAdrian Hunter result = inst.buffer.value.decode() 342176099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 342276099f98SAdrian Hunter # For now, assume the length is in byte 166 3423606bd60aSAdrian Hunter return inst.xedd[166], result 342476099f98SAdrian Hunter 342576099f98SAdrian Hunterdef TryOpen(file_name): 342676099f98SAdrian Hunter try: 342776099f98SAdrian Hunter return open(file_name, "rb") 342876099f98SAdrian Hunter except: 342976099f98SAdrian Hunter return None 343076099f98SAdrian Hunter 343176099f98SAdrian Hunterdef Is64Bit(f): 343276099f98SAdrian Hunter result = sizeof(c_void_p) 343376099f98SAdrian Hunter # ELF support only 343476099f98SAdrian Hunter pos = f.tell() 343576099f98SAdrian Hunter f.seek(0) 343676099f98SAdrian Hunter header = f.read(7) 343776099f98SAdrian Hunter f.seek(pos) 343876099f98SAdrian Hunter magic = header[0:4] 3439606bd60aSAdrian Hunter if sys.version_info[0] == 2: 344076099f98SAdrian Hunter eclass = ord(header[4]) 344176099f98SAdrian Hunter encoding = ord(header[5]) 344276099f98SAdrian Hunter version = ord(header[6]) 3443606bd60aSAdrian Hunter else: 3444606bd60aSAdrian Hunter eclass = header[4] 3445606bd60aSAdrian Hunter encoding = header[5] 3446606bd60aSAdrian Hunter version = header[6] 344776099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 344876099f98SAdrian Hunter result = True if eclass == 2 else False 344976099f98SAdrian Hunter return result 345076099f98SAdrian Hunter 3451031c2a00SAdrian Hunter# Global data 3452031c2a00SAdrian Hunter 3453031c2a00SAdrian Hunterclass Glb(): 3454031c2a00SAdrian Hunter 3455031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3456031c2a00SAdrian Hunter self.dbref = dbref 3457031c2a00SAdrian Hunter self.db = db 3458031c2a00SAdrian Hunter self.dbname = dbname 345976099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 346076099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 346176099f98SAdrian Hunter if self.buildid_dir: 346276099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 346376099f98SAdrian Hunter else: 346476099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3465031c2a00SAdrian Hunter self.app = None 3466031c2a00SAdrian Hunter self.mainwindow = None 34678392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 346876099f98SAdrian Hunter try: 346976099f98SAdrian Hunter self.disassembler = LibXED() 347076099f98SAdrian Hunter self.have_disassembler = True 347176099f98SAdrian Hunter except: 347276099f98SAdrian Hunter self.have_disassembler = False 347376099f98SAdrian Hunter 347476099f98SAdrian Hunter def FileFromBuildId(self, build_id): 347576099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 347676099f98SAdrian Hunter return TryOpen(file_name) 347776099f98SAdrian Hunter 347876099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 347976099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 348076099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 348176099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 348276099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 348376099f98SAdrian Hunter if f: 348476099f98SAdrian Hunter return f 348576099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 348676099f98SAdrian Hunter f = TryOpen(long_name) 348776099f98SAdrian Hunter if f: 348876099f98SAdrian Hunter return f 348976099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 349076099f98SAdrian Hunter if f: 349176099f98SAdrian Hunter return f 349276099f98SAdrian Hunter return None 34938392b74bSAdrian Hunter 34948392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 34958392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 34968392b74bSAdrian Hunter 34978392b74bSAdrian Hunter # Shutdown any background processes or threads 34988392b74bSAdrian Hunter def ShutdownInstances(self): 34998392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 35008392b74bSAdrian Hunter try: 35018392b74bSAdrian Hunter x.Shutdown() 35028392b74bSAdrian Hunter except: 35038392b74bSAdrian Hunter pass 3504031c2a00SAdrian Hunter 3505031c2a00SAdrian Hunter# Database reference 3506031c2a00SAdrian Hunter 3507031c2a00SAdrian Hunterclass DBRef(): 3508031c2a00SAdrian Hunter 3509031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3510031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3511031c2a00SAdrian Hunter self.dbname = dbname 3512031c2a00SAdrian Hunter 3513031c2a00SAdrian Hunter def Open(self, connection_name): 3514031c2a00SAdrian Hunter dbname = self.dbname 3515031c2a00SAdrian Hunter if self.is_sqlite3: 3516031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3517031c2a00SAdrian Hunter else: 3518031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3519031c2a00SAdrian Hunter opts = dbname.split() 3520031c2a00SAdrian Hunter for opt in opts: 3521031c2a00SAdrian Hunter if "=" in opt: 3522031c2a00SAdrian Hunter opt = opt.split("=") 3523031c2a00SAdrian Hunter if opt[0] == "hostname": 3524031c2a00SAdrian Hunter db.setHostName(opt[1]) 3525031c2a00SAdrian Hunter elif opt[0] == "port": 3526031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3527031c2a00SAdrian Hunter elif opt[0] == "username": 3528031c2a00SAdrian Hunter db.setUserName(opt[1]) 3529031c2a00SAdrian Hunter elif opt[0] == "password": 3530031c2a00SAdrian Hunter db.setPassword(opt[1]) 3531031c2a00SAdrian Hunter elif opt[0] == "dbname": 3532031c2a00SAdrian Hunter dbname = opt[1] 3533031c2a00SAdrian Hunter else: 3534031c2a00SAdrian Hunter dbname = opt 3535031c2a00SAdrian Hunter 3536031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3537031c2a00SAdrian Hunter if not db.open(): 3538031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3539031c2a00SAdrian Hunter return db, dbname 3540031c2a00SAdrian Hunter 3541031c2a00SAdrian Hunter# Main 3542031c2a00SAdrian Hunter 3543031c2a00SAdrian Hunterdef Main(): 35441ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 35451ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 35461ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 3547df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 35481ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 35491ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 35501ed7f47fSAdrian Hunter args = ap.parse_args() 3551031c2a00SAdrian Hunter 35521ed7f47fSAdrian Hunter if args.help_only: 355365b24292SAdrian Hunter app = QApplication(sys.argv) 355465b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 355565b24292SAdrian Hunter mainwindow.show() 355665b24292SAdrian Hunter err = app.exec_() 355765b24292SAdrian Hunter sys.exit(err) 3558031c2a00SAdrian Hunter 35591ed7f47fSAdrian Hunter dbname = args.dbname 35601ed7f47fSAdrian Hunter if dbname is None: 35611ed7f47fSAdrian Hunter ap.print_usage() 35621ed7f47fSAdrian Hunter print("Too few arguments") 35631ed7f47fSAdrian Hunter sys.exit(1) 35641ed7f47fSAdrian Hunter 3565031c2a00SAdrian Hunter is_sqlite3 = False 3566031c2a00SAdrian Hunter try: 3567beda0e72STony Jones f = open(dbname, "rb") 3568beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3569031c2a00SAdrian Hunter is_sqlite3 = True 3570031c2a00SAdrian Hunter f.close() 3571031c2a00SAdrian Hunter except: 3572031c2a00SAdrian Hunter pass 3573031c2a00SAdrian Hunter 3574031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3575031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3576031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3577031c2a00SAdrian Hunter app = QApplication(sys.argv) 3578031c2a00SAdrian Hunter glb.app = app 3579031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3580031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3581031c2a00SAdrian Hunter mainwindow.show() 3582031c2a00SAdrian Hunter err = app.exec_() 35838392b74bSAdrian Hunter glb.ShutdownInstances() 3584031c2a00SAdrian Hunter db.close() 3585031c2a00SAdrian Hunter sys.exit(err) 3586031c2a00SAdrian Hunter 3587031c2a00SAdrian Hunterif __name__ == "__main__": 3588031c2a00SAdrian Hunter Main() 3589