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() 395ebd70c7dSAdrian 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() 403ebd70c7dSAdrian Hunter self.textbox.setFocus() 404ebd70c7dSAdrian Hunter 405ebd70c7dSAdrian Hunter def Deactivate(self): 406ebd70c7dSAdrian Hunter self.bar.hide() 407ebd70c7dSAdrian Hunter 408ebd70c7dSAdrian Hunter def Busy(self): 409ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 410ebd70c7dSAdrian Hunter self.pattern.hide() 411ebd70c7dSAdrian Hunter self.next_button.hide() 412ebd70c7dSAdrian Hunter self.prev_button.hide() 413ebd70c7dSAdrian Hunter self.progress.show() 414ebd70c7dSAdrian Hunter 415ebd70c7dSAdrian Hunter def Idle(self): 416ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 417ebd70c7dSAdrian Hunter self.progress.hide() 418ebd70c7dSAdrian Hunter self.pattern.show() 419ebd70c7dSAdrian Hunter self.next_button.show() 420ebd70c7dSAdrian Hunter self.prev_button.show() 421ebd70c7dSAdrian Hunter 422ebd70c7dSAdrian Hunter def Find(self, direction): 423ebd70c7dSAdrian Hunter value = self.textbox.currentText() 424ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 425ebd70c7dSAdrian Hunter self.last_value = value 426ebd70c7dSAdrian Hunter self.last_pattern = pattern 427ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 428ebd70c7dSAdrian Hunter 429ebd70c7dSAdrian Hunter def ValueChanged(self): 430ebd70c7dSAdrian Hunter value = self.textbox.currentText() 431ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 432ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 433ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 434ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 435ebd70c7dSAdrian Hunter if data == None: 436ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 437ebd70c7dSAdrian Hunter else: 438ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 439ebd70c7dSAdrian Hunter self.Find(0) 440ebd70c7dSAdrian Hunter 441ebd70c7dSAdrian Hunter def NextPrev(self, direction): 442ebd70c7dSAdrian Hunter value = self.textbox.currentText() 443ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 444ebd70c7dSAdrian Hunter if value != self.last_value: 445ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 446ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 447ebd70c7dSAdrian Hunter if index < 0: 448ebd70c7dSAdrian Hunter index = self.textbox.count() 449ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 450ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 451ebd70c7dSAdrian Hunter return 452ebd70c7dSAdrian Hunter else: 453ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 454ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 455ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 456ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 457ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 458ebd70c7dSAdrian Hunter self.Find(direction) 459ebd70c7dSAdrian Hunter 460ebd70c7dSAdrian Hunter def NotFound(self): 461ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 462ebd70c7dSAdrian Hunter 463031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 464031c2a00SAdrian Hunter 465031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 466031c2a00SAdrian Hunter 4674a0979d4SAdrian Hunter def __init__(self, glb, params, row, parent_item): 468031c2a00SAdrian Hunter self.glb = glb 4694a0979d4SAdrian Hunter self.params = params 470031c2a00SAdrian Hunter self.row = row 471031c2a00SAdrian Hunter self.parent_item = parent_item 472031c2a00SAdrian Hunter self.query_done = False; 473031c2a00SAdrian Hunter self.child_count = 0 474031c2a00SAdrian Hunter self.child_items = [] 4753ac641f4SAdrian Hunter if parent_item: 4763ac641f4SAdrian Hunter self.level = parent_item.level + 1 4773ac641f4SAdrian Hunter else: 4783ac641f4SAdrian Hunter self.level = 0 479031c2a00SAdrian Hunter 480031c2a00SAdrian Hunter def getChildItem(self, row): 481031c2a00SAdrian Hunter return self.child_items[row] 482031c2a00SAdrian Hunter 483031c2a00SAdrian Hunter def getParentItem(self): 484031c2a00SAdrian Hunter return self.parent_item 485031c2a00SAdrian Hunter 486031c2a00SAdrian Hunter def getRow(self): 487031c2a00SAdrian Hunter return self.row 488031c2a00SAdrian Hunter 489031c2a00SAdrian Hunter def childCount(self): 490031c2a00SAdrian Hunter if not self.query_done: 491031c2a00SAdrian Hunter self.Select() 492031c2a00SAdrian Hunter if not self.child_count: 493031c2a00SAdrian Hunter return -1 494031c2a00SAdrian Hunter return self.child_count 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunter def hasChildren(self): 497031c2a00SAdrian Hunter if not self.query_done: 498031c2a00SAdrian Hunter return True 499031c2a00SAdrian Hunter return self.child_count > 0 500031c2a00SAdrian Hunter 501031c2a00SAdrian Hunter def getData(self, column): 502031c2a00SAdrian Hunter return self.data[column] 503031c2a00SAdrian Hunter 504031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 505031c2a00SAdrian Hunter 506031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 507031c2a00SAdrian Hunter 508*38a846d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): 5094a0979d4SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 510031c2a00SAdrian Hunter self.comm_id = comm_id 511031c2a00SAdrian Hunter self.thread_id = thread_id 512031c2a00SAdrian Hunter self.call_path_id = call_path_id 513*38a846d4SAdrian Hunter self.insn_cnt = insn_cnt 514*38a846d4SAdrian Hunter self.cyc_cnt = cyc_cnt 515031c2a00SAdrian Hunter self.branch_count = branch_count 516031c2a00SAdrian Hunter self.time = time 517031c2a00SAdrian Hunter 518031c2a00SAdrian Hunter def Select(self): 519031c2a00SAdrian Hunter self.query_done = True; 520031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 521*38a846d4SAdrian Hunter if self.params.have_ipc: 522*38a846d4SAdrian Hunter ipc_str = ", SUM(insn_count), SUM(cyc_count)" 523*38a846d4SAdrian Hunter else: 524*38a846d4SAdrian Hunter ipc_str = "" 525*38a846d4SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)" 526031c2a00SAdrian Hunter " FROM calls" 527031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 528031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 529031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 530031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 531031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 532031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 533031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 534031c2a00SAdrian Hunter " ORDER BY call_path_id") 535031c2a00SAdrian Hunter while query.next(): 536*38a846d4SAdrian Hunter if self.params.have_ipc: 537*38a846d4SAdrian Hunter insn_cnt = int(query.value(5)) 538*38a846d4SAdrian Hunter cyc_cnt = int(query.value(6)) 539*38a846d4SAdrian Hunter branch_count = int(query.value(7)) 540*38a846d4SAdrian Hunter else: 541*38a846d4SAdrian Hunter insn_cnt = 0 542*38a846d4SAdrian Hunter cyc_cnt = 0 543*38a846d4SAdrian Hunter branch_count = int(query.value(5)) 544*38a846d4SAdrian 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) 545031c2a00SAdrian Hunter self.child_items.append(child_item) 546031c2a00SAdrian Hunter self.child_count += 1 547031c2a00SAdrian Hunter 548031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 549031c2a00SAdrian Hunter 550031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 551031c2a00SAdrian Hunter 552*38a846d4SAdrian 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): 553*38a846d4SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item) 554031c2a00SAdrian Hunter dso = dsoname(dso) 555*38a846d4SAdrian Hunter if self.params.have_ipc: 556*38a846d4SAdrian Hunter insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) 557*38a846d4SAdrian Hunter cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) 558*38a846d4SAdrian Hunter br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) 559*38a846d4SAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 560*38a846d4SAdrian 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 ] 561*38a846d4SAdrian Hunter else: 562031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 563031c2a00SAdrian Hunter self.dbid = call_path_id 564031c2a00SAdrian Hunter 565031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 566031c2a00SAdrian Hunter 567031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 568031c2a00SAdrian Hunter 5694a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 570*38a846d4SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) 571*38a846d4SAdrian Hunter if self.params.have_ipc: 572*38a846d4SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] 573*38a846d4SAdrian Hunter else: 574031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 575031c2a00SAdrian Hunter self.dbid = thread_id 576031c2a00SAdrian Hunter 577031c2a00SAdrian Hunter def Select(self): 578031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 579031c2a00SAdrian Hunter for child_item in self.child_items: 580031c2a00SAdrian Hunter self.time += child_item.time 581*38a846d4SAdrian Hunter self.insn_cnt += child_item.insn_cnt 582*38a846d4SAdrian Hunter self.cyc_cnt += child_item.cyc_cnt 583031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 584031c2a00SAdrian Hunter for child_item in self.child_items: 585031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 586*38a846d4SAdrian Hunter if self.params.have_ipc: 587*38a846d4SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) 588*38a846d4SAdrian Hunter child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) 589*38a846d4SAdrian Hunter child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) 590*38a846d4SAdrian Hunter else: 591031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 592031c2a00SAdrian Hunter 593031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 594031c2a00SAdrian Hunter 595031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 596031c2a00SAdrian Hunter 5974a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 5984a0979d4SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) 599*38a846d4SAdrian Hunter if self.params.have_ipc: 600*38a846d4SAdrian Hunter self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] 601*38a846d4SAdrian Hunter else: 602031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 603031c2a00SAdrian Hunter self.dbid = comm_id 604031c2a00SAdrian Hunter 605031c2a00SAdrian Hunter def Select(self): 606031c2a00SAdrian Hunter self.query_done = True; 607031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 608031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 609031c2a00SAdrian Hunter " FROM comm_threads" 610031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 611031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 612031c2a00SAdrian Hunter while query.next(): 6134a0979d4SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 614031c2a00SAdrian Hunter self.child_items.append(child_item) 615031c2a00SAdrian Hunter self.child_count += 1 616031c2a00SAdrian Hunter 617031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 618031c2a00SAdrian Hunter 619031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 620031c2a00SAdrian Hunter 6214a0979d4SAdrian Hunter def __init__(self, glb, params): 6224a0979d4SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, params, 0, None) 623031c2a00SAdrian Hunter self.dbid = 0 624031c2a00SAdrian Hunter self.query_done = True; 625031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 626031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 627031c2a00SAdrian Hunter while query.next(): 628031c2a00SAdrian Hunter if not query.value(0): 629031c2a00SAdrian Hunter continue 6304a0979d4SAdrian Hunter child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 631031c2a00SAdrian Hunter self.child_items.append(child_item) 632031c2a00SAdrian Hunter self.child_count += 1 633031c2a00SAdrian Hunter 6344a0979d4SAdrian Hunter# Call graph model parameters 6354a0979d4SAdrian Hunter 6364a0979d4SAdrian Hunterclass CallGraphModelParams(): 6374a0979d4SAdrian Hunter 6384a0979d4SAdrian Hunter def __init__(self, glb, parent=None): 6394a0979d4SAdrian Hunter self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") 6404a0979d4SAdrian Hunter 641254c0d82SAdrian Hunter# Context-sensitive call graph data model base 642031c2a00SAdrian Hunter 643254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 644031c2a00SAdrian Hunter 645031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 6464a0979d4SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) 647031c2a00SAdrian Hunter 648ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 649ebd70c7dSAdrian Hunter if pattern: 650ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 651ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 652ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 653ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 654ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 655ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 656ebd70c7dSAdrian Hunter # Escape % and _ 657ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 658ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 659ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 660ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 661ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 662ebd70c7dSAdrian Hunter else: 663ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 664ebd70c7dSAdrian Hunter else: 665ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 666254c0d82SAdrian Hunter self.DoFindSelect(query, match) 667ebd70c7dSAdrian Hunter 668ebd70c7dSAdrian Hunter def Found(self, query, found): 669ebd70c7dSAdrian Hunter if found: 670ebd70c7dSAdrian Hunter return self.FindPath(query) 671ebd70c7dSAdrian Hunter return [] 672ebd70c7dSAdrian Hunter 673ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 674ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 675ebd70c7dSAdrian Hunter found = query.first() 676ebd70c7dSAdrian Hunter else: 677ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 678ebd70c7dSAdrian Hunter found = query.next() 679ebd70c7dSAdrian Hunter return self.Found(query, found) 680ebd70c7dSAdrian Hunter 681ebd70c7dSAdrian Hunter def FindNext(self, query): 682ebd70c7dSAdrian Hunter found = query.next() 683ebd70c7dSAdrian Hunter if not found: 684ebd70c7dSAdrian Hunter found = query.first() 685ebd70c7dSAdrian Hunter return self.Found(query, found) 686ebd70c7dSAdrian Hunter 687ebd70c7dSAdrian Hunter def FindPrev(self, query): 688ebd70c7dSAdrian Hunter found = query.previous() 689ebd70c7dSAdrian Hunter if not found: 690ebd70c7dSAdrian Hunter found = query.last() 691ebd70c7dSAdrian Hunter return self.Found(query, found) 692ebd70c7dSAdrian Hunter 693ebd70c7dSAdrian Hunter def FindThread(self, c): 694ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 695ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 696ebd70c7dSAdrian Hunter elif c.direction > 0: 697ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 698ebd70c7dSAdrian Hunter else: 699ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 700ebd70c7dSAdrian Hunter return (True, ids) 701ebd70c7dSAdrian Hunter 702ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 703ebd70c7dSAdrian Hunter class Context(): 704ebd70c7dSAdrian Hunter def __init__(self, *x): 705ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 706ebd70c7dSAdrian Hunter def Update(self, *x): 707ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 708ebd70c7dSAdrian Hunter if len(context): 709ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 710ebd70c7dSAdrian Hunter else: 711ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 712ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 713ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 714ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 715ebd70c7dSAdrian Hunter thread.start() 716ebd70c7dSAdrian Hunter 717ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 718ebd70c7dSAdrian Hunter callback(ids) 719ebd70c7dSAdrian Hunter 720254c0d82SAdrian Hunter# Context-sensitive call graph data model 721254c0d82SAdrian Hunter 722254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 723254c0d82SAdrian Hunter 724254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 725254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 726254c0d82SAdrian Hunter 727254c0d82SAdrian Hunter def GetRoot(self): 7284a0979d4SAdrian Hunter return CallGraphRootItem(self.glb, self.params) 729254c0d82SAdrian Hunter 730254c0d82SAdrian Hunter def columnCount(self, parent=None): 731*38a846d4SAdrian Hunter if self.params.have_ipc: 732*38a846d4SAdrian Hunter return 12 733*38a846d4SAdrian Hunter else: 734254c0d82SAdrian Hunter return 7 735254c0d82SAdrian Hunter 736254c0d82SAdrian Hunter def columnHeader(self, column): 737*38a846d4SAdrian Hunter if self.params.have_ipc: 738*38a846d4SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] 739*38a846d4SAdrian Hunter else: 740254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 741254c0d82SAdrian Hunter return headers[column] 742254c0d82SAdrian Hunter 743254c0d82SAdrian Hunter def columnAlignment(self, column): 744*38a846d4SAdrian Hunter if self.params.have_ipc: 745*38a846d4SAdrian 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 ] 746*38a846d4SAdrian Hunter else: 747254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 748254c0d82SAdrian Hunter return alignment[column] 749254c0d82SAdrian Hunter 750254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 751254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 752254c0d82SAdrian Hunter " FROM calls" 753254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 754254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 755254c0d82SAdrian Hunter " WHERE symbols.name" + match + 756254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 757254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 758254c0d82SAdrian Hunter 759254c0d82SAdrian Hunter def FindPath(self, query): 760254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 761254c0d82SAdrian Hunter # to open the tree at the right place. 762254c0d82SAdrian Hunter ids = [] 763254c0d82SAdrian Hunter parent_id = query.value(0) 764254c0d82SAdrian Hunter while parent_id: 765254c0d82SAdrian Hunter ids.insert(0, parent_id) 766254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 767254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 768254c0d82SAdrian Hunter " FROM call_paths" 769254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 770254c0d82SAdrian Hunter if not q2.next(): 771254c0d82SAdrian Hunter break 772254c0d82SAdrian Hunter parent_id = q2.value(0) 773254c0d82SAdrian Hunter # The call path root is not used 774254c0d82SAdrian Hunter if ids[0] == 1: 775254c0d82SAdrian Hunter del ids[0] 776254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 777254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 778254c0d82SAdrian Hunter return ids 779254c0d82SAdrian Hunter 780ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 781ae8b887cSAdrian Hunter 782ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 783ae8b887cSAdrian Hunter 7844a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): 7854a0979d4SAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 786ae8b887cSAdrian Hunter self.comm_id = comm_id 787ae8b887cSAdrian Hunter self.thread_id = thread_id 788ae8b887cSAdrian Hunter self.calls_id = calls_id 789ae8b887cSAdrian Hunter self.branch_count = branch_count 790ae8b887cSAdrian Hunter self.time = time 791ae8b887cSAdrian Hunter 792ae8b887cSAdrian Hunter def Select(self): 793ae8b887cSAdrian Hunter self.query_done = True; 794ae8b887cSAdrian Hunter if self.calls_id == 0: 795ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 796ae8b887cSAdrian Hunter else: 797ae8b887cSAdrian Hunter comm_thread = "" 798ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 799ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" 800ae8b887cSAdrian Hunter " FROM calls" 801ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 802ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 803ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 804ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 805ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 806ae8b887cSAdrian Hunter while query.next(): 8074a0979d4SAdrian 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)), int(query.value(5)), self) 808ae8b887cSAdrian Hunter self.child_items.append(child_item) 809ae8b887cSAdrian Hunter self.child_count += 1 810ae8b887cSAdrian Hunter 811ae8b887cSAdrian Hunter# Call tree data model level three item 812ae8b887cSAdrian Hunter 813ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 814ae8b887cSAdrian Hunter 8154a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): 8164a0979d4SAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) 817ae8b887cSAdrian Hunter dso = dsoname(dso) 818ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 819ae8b887cSAdrian Hunter self.dbid = calls_id 820ae8b887cSAdrian Hunter 821ae8b887cSAdrian Hunter# Call tree data model level two item 822ae8b887cSAdrian Hunter 823ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 824ae8b887cSAdrian Hunter 8254a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 8264a0979d4SAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, parent_item) 827ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 828ae8b887cSAdrian Hunter self.dbid = thread_id 829ae8b887cSAdrian Hunter 830ae8b887cSAdrian Hunter def Select(self): 831ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 832ae8b887cSAdrian Hunter for child_item in self.child_items: 833ae8b887cSAdrian Hunter self.time += child_item.time 834ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 835ae8b887cSAdrian Hunter for child_item in self.child_items: 836ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 837ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 838ae8b887cSAdrian Hunter 839ae8b887cSAdrian Hunter# Call tree data model level one item 840ae8b887cSAdrian Hunter 841ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 842ae8b887cSAdrian Hunter 8434a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 8444a0979d4SAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) 845ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 846ae8b887cSAdrian Hunter self.dbid = comm_id 847ae8b887cSAdrian Hunter 848ae8b887cSAdrian Hunter def Select(self): 849ae8b887cSAdrian Hunter self.query_done = True; 850ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 851ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 852ae8b887cSAdrian Hunter " FROM comm_threads" 853ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 854ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 855ae8b887cSAdrian Hunter while query.next(): 8564a0979d4SAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 857ae8b887cSAdrian Hunter self.child_items.append(child_item) 858ae8b887cSAdrian Hunter self.child_count += 1 859ae8b887cSAdrian Hunter 860ae8b887cSAdrian Hunter# Call tree data model root item 861ae8b887cSAdrian Hunter 862ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 863ae8b887cSAdrian Hunter 8644a0979d4SAdrian Hunter def __init__(self, glb, params): 8654a0979d4SAdrian Hunter super(CallTreeRootItem, self).__init__(glb, params, 0, None) 866ae8b887cSAdrian Hunter self.dbid = 0 867ae8b887cSAdrian Hunter self.query_done = True; 868ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 869ae8b887cSAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 870ae8b887cSAdrian Hunter while query.next(): 871ae8b887cSAdrian Hunter if not query.value(0): 872ae8b887cSAdrian Hunter continue 8734a0979d4SAdrian Hunter child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 874ae8b887cSAdrian Hunter self.child_items.append(child_item) 875ae8b887cSAdrian Hunter self.child_count += 1 876ae8b887cSAdrian Hunter 877ae8b887cSAdrian Hunter# Call Tree data model 878ae8b887cSAdrian Hunter 879ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 880ae8b887cSAdrian Hunter 881ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 882ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 883ae8b887cSAdrian Hunter 884ae8b887cSAdrian Hunter def GetRoot(self): 8854a0979d4SAdrian Hunter return CallTreeRootItem(self.glb, self.params) 886ae8b887cSAdrian Hunter 887ae8b887cSAdrian Hunter def columnCount(self, parent=None): 888ae8b887cSAdrian Hunter return 7 889ae8b887cSAdrian Hunter 890ae8b887cSAdrian Hunter def columnHeader(self, column): 891ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 892ae8b887cSAdrian Hunter return headers[column] 893ae8b887cSAdrian Hunter 894ae8b887cSAdrian Hunter def columnAlignment(self, column): 895ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 896ae8b887cSAdrian Hunter return alignment[column] 897ae8b887cSAdrian Hunter 898ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 899ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 900ae8b887cSAdrian Hunter " FROM calls" 901ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 902ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 903ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 904ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 905ae8b887cSAdrian Hunter 906ae8b887cSAdrian Hunter def FindPath(self, query): 907ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 908ae8b887cSAdrian Hunter # to open the tree at the right place. 909ae8b887cSAdrian Hunter ids = [] 910ae8b887cSAdrian Hunter parent_id = query.value(0) 911ae8b887cSAdrian Hunter while parent_id: 912ae8b887cSAdrian Hunter ids.insert(0, parent_id) 913ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 914ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 915ae8b887cSAdrian Hunter " FROM calls" 916ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 917ae8b887cSAdrian Hunter if not q2.next(): 918ae8b887cSAdrian Hunter break 919ae8b887cSAdrian Hunter parent_id = q2.value(0) 920ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 921ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 922ae8b887cSAdrian Hunter return ids 923ae8b887cSAdrian Hunter 924ebd70c7dSAdrian Hunter# Vertical widget layout 925ebd70c7dSAdrian Hunter 926ebd70c7dSAdrian Hunterclass VBox(): 927ebd70c7dSAdrian Hunter 928ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 929ebd70c7dSAdrian Hunter self.vbox = QWidget() 930ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 931ebd70c7dSAdrian Hunter 932ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 933ebd70c7dSAdrian Hunter 934ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 935ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 936ebd70c7dSAdrian Hunter if w3: 937ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 938ebd70c7dSAdrian Hunter 939ebd70c7dSAdrian Hunter def Widget(self): 940ebd70c7dSAdrian Hunter return self.vbox 941ebd70c7dSAdrian Hunter 942a731cc4cSAdrian Hunter# Tree window base 9431beb5c7bSAdrian Hunter 944a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 9451beb5c7bSAdrian Hunter 946a731cc4cSAdrian Hunter def __init__(self, parent=None): 947a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 9481beb5c7bSAdrian Hunter 949a731cc4cSAdrian Hunter self.model = None 950a731cc4cSAdrian Hunter self.find_bar = None 9511beb5c7bSAdrian Hunter 952be6e7471SAdrian Hunter self.view = QTreeView() 95396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 95496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 955be6e7471SAdrian Hunter 9569bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 9579bc4e4bfSAdrian Hunter 958ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 959ebd70c7dSAdrian Hunter if not len(ids): 960ebd70c7dSAdrian Hunter return False 961ebd70c7dSAdrian Hunter parent = QModelIndex() 962ebd70c7dSAdrian Hunter for dbid in ids: 963ebd70c7dSAdrian Hunter found = False 964ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 965ebd70c7dSAdrian Hunter for row in xrange(n): 966ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 967ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 968ebd70c7dSAdrian Hunter found = True 969ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 970ebd70c7dSAdrian Hunter parent = child 971ebd70c7dSAdrian Hunter break 972ebd70c7dSAdrian Hunter if not found: 973ebd70c7dSAdrian Hunter break 974ebd70c7dSAdrian Hunter return found 975ebd70c7dSAdrian Hunter 976ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 977ebd70c7dSAdrian Hunter self.view.setFocus() 978ebd70c7dSAdrian Hunter self.find_bar.Busy() 979ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 980ebd70c7dSAdrian Hunter 981ebd70c7dSAdrian Hunter def FindDone(self, ids): 982ebd70c7dSAdrian Hunter found = True 983ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 984ebd70c7dSAdrian Hunter found = False 985ebd70c7dSAdrian Hunter self.find_bar.Idle() 986ebd70c7dSAdrian Hunter if not found: 987ebd70c7dSAdrian Hunter self.find_bar.NotFound() 988ebd70c7dSAdrian Hunter 989a731cc4cSAdrian Hunter 990a731cc4cSAdrian Hunter# Context-sensitive call graph window 991a731cc4cSAdrian Hunter 992a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 993a731cc4cSAdrian Hunter 994a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 995a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 996a731cc4cSAdrian Hunter 997a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 998a731cc4cSAdrian Hunter 999a731cc4cSAdrian Hunter self.view.setModel(self.model) 1000a731cc4cSAdrian Hunter 1001a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 1002a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 1003a731cc4cSAdrian Hunter 1004a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 1005a731cc4cSAdrian Hunter 1006a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1007a731cc4cSAdrian Hunter 1008a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1009a731cc4cSAdrian Hunter 1010a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 1011a731cc4cSAdrian Hunter 1012ae8b887cSAdrian Hunter# Call tree window 1013ae8b887cSAdrian Hunter 1014ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 1015ae8b887cSAdrian Hunter 1016ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 1017ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 1018ae8b887cSAdrian Hunter 1019ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 1020ae8b887cSAdrian Hunter 1021ae8b887cSAdrian Hunter self.view.setModel(self.model) 1022ae8b887cSAdrian Hunter 1023ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 1024ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 1025ae8b887cSAdrian Hunter 1026ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 1027ae8b887cSAdrian Hunter 1028ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 1029ae8b887cSAdrian Hunter 1030ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 1031ae8b887cSAdrian Hunter 1032ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 1033ae8b887cSAdrian Hunter 10348392b74bSAdrian Hunter# Child data item finder 10358392b74bSAdrian Hunter 10368392b74bSAdrian Hunterclass ChildDataItemFinder(): 10378392b74bSAdrian Hunter 10388392b74bSAdrian Hunter def __init__(self, root): 10398392b74bSAdrian Hunter self.root = root 10408392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 10418392b74bSAdrian Hunter self.rows = [] 10428392b74bSAdrian Hunter self.pos = 0 10438392b74bSAdrian Hunter 10448392b74bSAdrian Hunter def FindSelect(self): 10458392b74bSAdrian Hunter self.rows = [] 10468392b74bSAdrian Hunter if self.pattern: 10478392b74bSAdrian Hunter pattern = re.compile(self.value) 10488392b74bSAdrian Hunter for child in self.root.child_items: 10498392b74bSAdrian Hunter for column_data in child.data: 10508392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 10518392b74bSAdrian Hunter self.rows.append(child.row) 10528392b74bSAdrian Hunter break 10538392b74bSAdrian Hunter else: 10548392b74bSAdrian Hunter for child in self.root.child_items: 10558392b74bSAdrian Hunter for column_data in child.data: 10568392b74bSAdrian Hunter if self.value in str(column_data): 10578392b74bSAdrian Hunter self.rows.append(child.row) 10588392b74bSAdrian Hunter break 10598392b74bSAdrian Hunter 10608392b74bSAdrian Hunter def FindValue(self): 10618392b74bSAdrian Hunter self.pos = 0 10628392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 10638392b74bSAdrian Hunter self.FindSelect() 10648392b74bSAdrian Hunter if not len(self.rows): 10658392b74bSAdrian Hunter return -1 10668392b74bSAdrian Hunter return self.rows[self.pos] 10678392b74bSAdrian Hunter 10688392b74bSAdrian Hunter def FindThread(self): 10698392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 10708392b74bSAdrian Hunter row = self.FindValue() 10718392b74bSAdrian Hunter elif len(self.rows): 10728392b74bSAdrian Hunter if self.direction > 0: 10738392b74bSAdrian Hunter self.pos += 1 10748392b74bSAdrian Hunter if self.pos >= len(self.rows): 10758392b74bSAdrian Hunter self.pos = 0 10768392b74bSAdrian Hunter else: 10778392b74bSAdrian Hunter self.pos -= 1 10788392b74bSAdrian Hunter if self.pos < 0: 10798392b74bSAdrian Hunter self.pos = len(self.rows) - 1 10808392b74bSAdrian Hunter row = self.rows[self.pos] 10818392b74bSAdrian Hunter else: 10828392b74bSAdrian Hunter row = -1 10838392b74bSAdrian Hunter return (True, row) 10848392b74bSAdrian Hunter 10858392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 10868392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 10878392b74bSAdrian Hunter # Use a thread so the UI is not blocked 10888392b74bSAdrian Hunter thread = Thread(self.FindThread) 10898392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 10908392b74bSAdrian Hunter thread.start() 10918392b74bSAdrian Hunter 10928392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 10938392b74bSAdrian Hunter callback(row) 10948392b74bSAdrian Hunter 10958392b74bSAdrian Hunter# Number of database records to fetch in one go 10968392b74bSAdrian Hunter 10978392b74bSAdrian Hunterglb_chunk_sz = 10000 10988392b74bSAdrian Hunter 10998392b74bSAdrian Hunter# Background process for SQL data fetcher 11008392b74bSAdrian Hunter 11018392b74bSAdrian Hunterclass SQLFetcherProcess(): 11028392b74bSAdrian Hunter 11038392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 11048392b74bSAdrian Hunter # Need a unique connection name 11058392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 11068392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 11078392b74bSAdrian Hunter self.sql = sql 11088392b74bSAdrian Hunter self.buffer = buffer 11098392b74bSAdrian Hunter self.head = head 11108392b74bSAdrian Hunter self.tail = tail 11118392b74bSAdrian Hunter self.fetch_count = fetch_count 11128392b74bSAdrian Hunter self.fetching_done = fetching_done 11138392b74bSAdrian Hunter self.process_target = process_target 11148392b74bSAdrian Hunter self.wait_event = wait_event 11158392b74bSAdrian Hunter self.fetched_event = fetched_event 11168392b74bSAdrian Hunter self.prep = prep 11178392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 11188392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 11198392b74bSAdrian Hunter self.last_id = -1 11208392b74bSAdrian Hunter self.fetched = 0 11218392b74bSAdrian Hunter self.more = True 11228392b74bSAdrian Hunter self.local_head = self.head.value 11238392b74bSAdrian Hunter self.local_tail = self.tail.value 11248392b74bSAdrian Hunter 11258392b74bSAdrian Hunter def Select(self): 11268392b74bSAdrian Hunter if self.query_limit: 11278392b74bSAdrian Hunter if self.query_limit == 1: 11288392b74bSAdrian Hunter return 11298392b74bSAdrian Hunter self.query_limit -= 1 11308392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 11318392b74bSAdrian Hunter QueryExec(self.query, stmt) 11328392b74bSAdrian Hunter 11338392b74bSAdrian Hunter def Next(self): 11348392b74bSAdrian Hunter if not self.query.next(): 11358392b74bSAdrian Hunter self.Select() 11368392b74bSAdrian Hunter if not self.query.next(): 11378392b74bSAdrian Hunter return None 11388392b74bSAdrian Hunter self.last_id = self.query.value(0) 11398392b74bSAdrian Hunter return self.prep(self.query) 11408392b74bSAdrian Hunter 11418392b74bSAdrian Hunter def WaitForTarget(self): 11428392b74bSAdrian Hunter while True: 11438392b74bSAdrian Hunter self.wait_event.clear() 11448392b74bSAdrian Hunter target = self.process_target.value 11458392b74bSAdrian Hunter if target > self.fetched or target < 0: 11468392b74bSAdrian Hunter break 11478392b74bSAdrian Hunter self.wait_event.wait() 11488392b74bSAdrian Hunter return target 11498392b74bSAdrian Hunter 11508392b74bSAdrian Hunter def HasSpace(self, sz): 11518392b74bSAdrian Hunter if self.local_tail <= self.local_head: 11528392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 11538392b74bSAdrian Hunter if space > sz: 11548392b74bSAdrian Hunter return True 11558392b74bSAdrian Hunter if space >= glb_nsz: 11568392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1157beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 11588392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 11598392b74bSAdrian Hunter self.local_head = 0 11608392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 11618392b74bSAdrian Hunter return True 11628392b74bSAdrian Hunter return False 11638392b74bSAdrian Hunter 11648392b74bSAdrian Hunter def WaitForSpace(self, sz): 11658392b74bSAdrian Hunter if self.HasSpace(sz): 11668392b74bSAdrian Hunter return 11678392b74bSAdrian Hunter while True: 11688392b74bSAdrian Hunter self.wait_event.clear() 11698392b74bSAdrian Hunter self.local_tail = self.tail.value 11708392b74bSAdrian Hunter if self.HasSpace(sz): 11718392b74bSAdrian Hunter return 11728392b74bSAdrian Hunter self.wait_event.wait() 11738392b74bSAdrian Hunter 11748392b74bSAdrian Hunter def AddToBuffer(self, obj): 1175beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 11768392b74bSAdrian Hunter n = len(d) 1177beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 11788392b74bSAdrian Hunter sz = n + glb_nsz 11798392b74bSAdrian Hunter self.WaitForSpace(sz) 11808392b74bSAdrian Hunter pos = self.local_head 11818392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 11828392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 11838392b74bSAdrian Hunter self.local_head += sz 11848392b74bSAdrian Hunter 11858392b74bSAdrian Hunter def FetchBatch(self, batch_size): 11868392b74bSAdrian Hunter fetched = 0 11878392b74bSAdrian Hunter while batch_size > fetched: 11888392b74bSAdrian Hunter obj = self.Next() 11898392b74bSAdrian Hunter if obj is None: 11908392b74bSAdrian Hunter self.more = False 11918392b74bSAdrian Hunter break 11928392b74bSAdrian Hunter self.AddToBuffer(obj) 11938392b74bSAdrian Hunter fetched += 1 11948392b74bSAdrian Hunter if fetched: 11958392b74bSAdrian Hunter self.fetched += fetched 11968392b74bSAdrian Hunter with self.fetch_count.get_lock(): 11978392b74bSAdrian Hunter self.fetch_count.value += fetched 11988392b74bSAdrian Hunter self.head.value = self.local_head 11998392b74bSAdrian Hunter self.fetched_event.set() 12008392b74bSAdrian Hunter 12018392b74bSAdrian Hunter def Run(self): 12028392b74bSAdrian Hunter while self.more: 12038392b74bSAdrian Hunter target = self.WaitForTarget() 12048392b74bSAdrian Hunter if target < 0: 12058392b74bSAdrian Hunter break 12068392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 12078392b74bSAdrian Hunter self.FetchBatch(batch_size) 12088392b74bSAdrian Hunter self.fetching_done.value = True 12098392b74bSAdrian Hunter self.fetched_event.set() 12108392b74bSAdrian Hunter 12118392b74bSAdrian Hunterdef SQLFetcherFn(*x): 12128392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 12138392b74bSAdrian Hunter process.Run() 12148392b74bSAdrian Hunter 12158392b74bSAdrian Hunter# SQL data fetcher 12168392b74bSAdrian Hunter 12178392b74bSAdrian Hunterclass SQLFetcher(QObject): 12188392b74bSAdrian Hunter 12198392b74bSAdrian Hunter done = Signal(object) 12208392b74bSAdrian Hunter 12218392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 12228392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 12238392b74bSAdrian Hunter self.process_data = process_data 12248392b74bSAdrian Hunter self.more = True 12258392b74bSAdrian Hunter self.target = 0 12268392b74bSAdrian Hunter self.last_target = 0 12278392b74bSAdrian Hunter self.fetched = 0 12288392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 12298392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 12308392b74bSAdrian Hunter self.head = Value(c_longlong) 12318392b74bSAdrian Hunter self.tail = Value(c_longlong) 12328392b74bSAdrian Hunter self.local_tail = 0 12338392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 12348392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 12358392b74bSAdrian Hunter self.last_count = 0 12368392b74bSAdrian Hunter self.process_target = Value(c_longlong) 12378392b74bSAdrian Hunter self.wait_event = Event() 12388392b74bSAdrian Hunter self.fetched_event = Event() 12398392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 12408392b74bSAdrian 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)) 12418392b74bSAdrian Hunter self.process.start() 12428392b74bSAdrian Hunter self.thread = Thread(self.Thread) 12438392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 12448392b74bSAdrian Hunter self.thread.start() 12458392b74bSAdrian Hunter 12468392b74bSAdrian Hunter def Shutdown(self): 12478392b74bSAdrian Hunter # Tell the thread and process to exit 12488392b74bSAdrian Hunter self.process_target.value = -1 12498392b74bSAdrian Hunter self.wait_event.set() 12508392b74bSAdrian Hunter self.more = False 12518392b74bSAdrian Hunter self.fetching_done.value = True 12528392b74bSAdrian Hunter self.fetched_event.set() 12538392b74bSAdrian Hunter 12548392b74bSAdrian Hunter def Thread(self): 12558392b74bSAdrian Hunter if not self.more: 12568392b74bSAdrian Hunter return True, 0 12578392b74bSAdrian Hunter while True: 12588392b74bSAdrian Hunter self.fetched_event.clear() 12598392b74bSAdrian Hunter fetch_count = self.fetch_count.value 12608392b74bSAdrian Hunter if fetch_count != self.last_count: 12618392b74bSAdrian Hunter break 12628392b74bSAdrian Hunter if self.fetching_done.value: 12638392b74bSAdrian Hunter self.more = False 12648392b74bSAdrian Hunter return True, 0 12658392b74bSAdrian Hunter self.fetched_event.wait() 12668392b74bSAdrian Hunter count = fetch_count - self.last_count 12678392b74bSAdrian Hunter self.last_count = fetch_count 12688392b74bSAdrian Hunter self.fetched += count 12698392b74bSAdrian Hunter return False, count 12708392b74bSAdrian Hunter 12718392b74bSAdrian Hunter def Fetch(self, nr): 12728392b74bSAdrian Hunter if not self.more: 12738392b74bSAdrian Hunter # -1 inidcates there are no more 12748392b74bSAdrian Hunter return -1 12758392b74bSAdrian Hunter result = self.fetched 12768392b74bSAdrian Hunter extra = result + nr - self.target 12778392b74bSAdrian Hunter if extra > 0: 12788392b74bSAdrian Hunter self.target += extra 12798392b74bSAdrian Hunter # process_target < 0 indicates shutting down 12808392b74bSAdrian Hunter if self.process_target.value >= 0: 12818392b74bSAdrian Hunter self.process_target.value = self.target 12828392b74bSAdrian Hunter self.wait_event.set() 12838392b74bSAdrian Hunter return result 12848392b74bSAdrian Hunter 12858392b74bSAdrian Hunter def RemoveFromBuffer(self): 12868392b74bSAdrian Hunter pos = self.local_tail 12878392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 12888392b74bSAdrian Hunter pos = 0 1289beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 12908392b74bSAdrian Hunter if n == 0: 12918392b74bSAdrian Hunter pos = 0 1292beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 12938392b74bSAdrian Hunter pos += glb_nsz 1294beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 12958392b74bSAdrian Hunter self.local_tail = pos + n 12968392b74bSAdrian Hunter return obj 12978392b74bSAdrian Hunter 12988392b74bSAdrian Hunter def ProcessData(self, count): 12998392b74bSAdrian Hunter for i in xrange(count): 13008392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 13018392b74bSAdrian Hunter self.process_data(obj) 13028392b74bSAdrian Hunter self.tail.value = self.local_tail 13038392b74bSAdrian Hunter self.wait_event.set() 13048392b74bSAdrian Hunter self.done.emit(count) 13058392b74bSAdrian Hunter 13068392b74bSAdrian Hunter# Fetch more records bar 13078392b74bSAdrian Hunter 13088392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 13098392b74bSAdrian Hunter 13108392b74bSAdrian Hunter def __init__(self, model, parent): 13118392b74bSAdrian Hunter self.model = model 13128392b74bSAdrian Hunter 13138392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 13148392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13158392b74bSAdrian Hunter 13168392b74bSAdrian Hunter self.fetch_count = QSpinBox() 13178392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 13188392b74bSAdrian Hunter self.fetch_count.setValue(10) 13198392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13208392b74bSAdrian Hunter 13218392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 13228392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 13238392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 13248392b74bSAdrian Hunter 13258392b74bSAdrian Hunter self.progress = QProgressBar() 13268392b74bSAdrian Hunter self.progress.setRange(0, 100) 13278392b74bSAdrian Hunter self.progress.hide() 13288392b74bSAdrian Hunter 13298392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 13308392b74bSAdrian Hunter self.done_label.hide() 13318392b74bSAdrian Hunter 13328392b74bSAdrian Hunter self.spacer = QLabel("") 13338392b74bSAdrian Hunter 13348392b74bSAdrian Hunter self.close_button = QToolButton() 13358392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 13368392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 13378392b74bSAdrian Hunter 13388392b74bSAdrian Hunter self.hbox = QHBoxLayout() 13398392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 13408392b74bSAdrian Hunter 13418392b74bSAdrian Hunter self.hbox.addWidget(self.label) 13428392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 13438392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 13448392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 13458392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 13468392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 13478392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 13488392b74bSAdrian Hunter 13498392b74bSAdrian Hunter self.bar = QWidget() 13508392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 13518392b74bSAdrian Hunter self.bar.show() 13528392b74bSAdrian Hunter 13538392b74bSAdrian Hunter self.in_progress = False 13548392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 13558392b74bSAdrian Hunter 13568392b74bSAdrian Hunter self.done = False 13578392b74bSAdrian Hunter 13588392b74bSAdrian Hunter if not model.HasMoreRecords(): 13598392b74bSAdrian Hunter self.Done() 13608392b74bSAdrian Hunter 13618392b74bSAdrian Hunter def Widget(self): 13628392b74bSAdrian Hunter return self.bar 13638392b74bSAdrian Hunter 13648392b74bSAdrian Hunter def Activate(self): 13658392b74bSAdrian Hunter self.bar.show() 13668392b74bSAdrian Hunter self.fetch.setFocus() 13678392b74bSAdrian Hunter 13688392b74bSAdrian Hunter def Deactivate(self): 13698392b74bSAdrian Hunter self.bar.hide() 13708392b74bSAdrian Hunter 13718392b74bSAdrian Hunter def Enable(self, enable): 13728392b74bSAdrian Hunter self.fetch.setEnabled(enable) 13738392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 13748392b74bSAdrian Hunter 13758392b74bSAdrian Hunter def Busy(self): 13768392b74bSAdrian Hunter self.Enable(False) 13778392b74bSAdrian Hunter self.fetch.hide() 13788392b74bSAdrian Hunter self.spacer.hide() 13798392b74bSAdrian Hunter self.progress.show() 13808392b74bSAdrian Hunter 13818392b74bSAdrian Hunter def Idle(self): 13828392b74bSAdrian Hunter self.in_progress = False 13838392b74bSAdrian Hunter self.Enable(True) 13848392b74bSAdrian Hunter self.progress.hide() 13858392b74bSAdrian Hunter self.fetch.show() 13868392b74bSAdrian Hunter self.spacer.show() 13878392b74bSAdrian Hunter 13888392b74bSAdrian Hunter def Target(self): 13898392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 13908392b74bSAdrian Hunter 13918392b74bSAdrian Hunter def Done(self): 13928392b74bSAdrian Hunter self.done = True 13938392b74bSAdrian Hunter self.Idle() 13948392b74bSAdrian Hunter self.label.hide() 13958392b74bSAdrian Hunter self.fetch_count.hide() 13968392b74bSAdrian Hunter self.fetch.hide() 13978392b74bSAdrian Hunter self.spacer.hide() 13988392b74bSAdrian Hunter self.done_label.show() 13998392b74bSAdrian Hunter 14008392b74bSAdrian Hunter def Progress(self, count): 14018392b74bSAdrian Hunter if self.in_progress: 14028392b74bSAdrian Hunter if count: 14038392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 14048392b74bSAdrian Hunter if percent >= 100: 14058392b74bSAdrian Hunter self.Idle() 14068392b74bSAdrian Hunter else: 14078392b74bSAdrian Hunter self.progress.setValue(percent) 14088392b74bSAdrian Hunter if not count: 14098392b74bSAdrian Hunter # Count value of zero means no more records 14108392b74bSAdrian Hunter self.Done() 14118392b74bSAdrian Hunter 14128392b74bSAdrian Hunter def FetchMoreRecords(self): 14138392b74bSAdrian Hunter if self.done: 14148392b74bSAdrian Hunter return 14158392b74bSAdrian Hunter self.progress.setValue(0) 14168392b74bSAdrian Hunter self.Busy() 14178392b74bSAdrian Hunter self.in_progress = True 14188392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 14198392b74bSAdrian Hunter 142076099f98SAdrian Hunter# Brance data model level two item 142176099f98SAdrian Hunter 142276099f98SAdrian Hunterclass BranchLevelTwoItem(): 142376099f98SAdrian Hunter 1424530e22fdSAdrian Hunter def __init__(self, row, col, text, parent_item): 142576099f98SAdrian Hunter self.row = row 142676099f98SAdrian Hunter self.parent_item = parent_item 1427530e22fdSAdrian Hunter self.data = [""] * (col + 1) 1428530e22fdSAdrian Hunter self.data[col] = text 142976099f98SAdrian Hunter self.level = 2 143076099f98SAdrian Hunter 143176099f98SAdrian Hunter def getParentItem(self): 143276099f98SAdrian Hunter return self.parent_item 143376099f98SAdrian Hunter 143476099f98SAdrian Hunter def getRow(self): 143576099f98SAdrian Hunter return self.row 143676099f98SAdrian Hunter 143776099f98SAdrian Hunter def childCount(self): 143876099f98SAdrian Hunter return 0 143976099f98SAdrian Hunter 144076099f98SAdrian Hunter def hasChildren(self): 144176099f98SAdrian Hunter return False 144276099f98SAdrian Hunter 144376099f98SAdrian Hunter def getData(self, column): 144476099f98SAdrian Hunter return self.data[column] 144576099f98SAdrian Hunter 144676099f98SAdrian Hunter# Brance data model level one item 144776099f98SAdrian Hunter 144876099f98SAdrian Hunterclass BranchLevelOneItem(): 144976099f98SAdrian Hunter 145076099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 145176099f98SAdrian Hunter self.glb = glb 145276099f98SAdrian Hunter self.row = row 145376099f98SAdrian Hunter self.parent_item = parent_item 145476099f98SAdrian Hunter self.child_count = 0 145576099f98SAdrian Hunter self.child_items = [] 145676099f98SAdrian Hunter self.data = data[1:] 145776099f98SAdrian Hunter self.dbid = data[0] 145876099f98SAdrian Hunter self.level = 1 145976099f98SAdrian Hunter self.query_done = False 1460530e22fdSAdrian Hunter self.br_col = len(self.data) - 1 146176099f98SAdrian Hunter 146276099f98SAdrian Hunter def getChildItem(self, row): 146376099f98SAdrian Hunter return self.child_items[row] 146476099f98SAdrian Hunter 146576099f98SAdrian Hunter def getParentItem(self): 146676099f98SAdrian Hunter return self.parent_item 146776099f98SAdrian Hunter 146876099f98SAdrian Hunter def getRow(self): 146976099f98SAdrian Hunter return self.row 147076099f98SAdrian Hunter 147176099f98SAdrian Hunter def Select(self): 147276099f98SAdrian Hunter self.query_done = True 147376099f98SAdrian Hunter 147476099f98SAdrian Hunter if not self.glb.have_disassembler: 147576099f98SAdrian Hunter return 147676099f98SAdrian Hunter 147776099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 147876099f98SAdrian Hunter 147976099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 148076099f98SAdrian Hunter " FROM samples" 148176099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 148276099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 148376099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 148476099f98SAdrian Hunter if not query.next(): 148576099f98SAdrian Hunter return 148676099f98SAdrian Hunter cpu = query.value(0) 148776099f98SAdrian Hunter dso = query.value(1) 148876099f98SAdrian Hunter sym = query.value(2) 148976099f98SAdrian Hunter if dso == 0 or sym == 0: 149076099f98SAdrian Hunter return 149176099f98SAdrian Hunter off = query.value(3) 149276099f98SAdrian Hunter short_name = query.value(4) 149376099f98SAdrian Hunter long_name = query.value(5) 149476099f98SAdrian Hunter build_id = query.value(6) 149576099f98SAdrian Hunter sym_start = query.value(7) 149676099f98SAdrian Hunter ip = query.value(8) 149776099f98SAdrian Hunter 149876099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 149976099f98SAdrian Hunter " FROM samples" 150076099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 150176099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 150276099f98SAdrian Hunter " ORDER BY samples.id" 150376099f98SAdrian Hunter " LIMIT 1") 150476099f98SAdrian Hunter if not query.next(): 150576099f98SAdrian Hunter return 150676099f98SAdrian Hunter if query.value(0) != dso: 150776099f98SAdrian Hunter # Cannot disassemble from one dso to another 150876099f98SAdrian Hunter return 150976099f98SAdrian Hunter bsym = query.value(1) 151076099f98SAdrian Hunter boff = query.value(2) 151176099f98SAdrian Hunter bsym_start = query.value(3) 151276099f98SAdrian Hunter if bsym == 0: 151376099f98SAdrian Hunter return 151476099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 151576099f98SAdrian Hunter if tot <= 0 or tot > 16384: 151676099f98SAdrian Hunter return 151776099f98SAdrian Hunter 151876099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 151976099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 152076099f98SAdrian Hunter if not f: 152176099f98SAdrian Hunter return 152276099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 152376099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 152476099f98SAdrian Hunter 152576099f98SAdrian Hunter buf_sz = tot + 16 152676099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 152776099f98SAdrian Hunter f.seek(sym_start + off) 152876099f98SAdrian Hunter buf.value = f.read(buf_sz) 152976099f98SAdrian Hunter buf_ptr = addressof(buf) 153076099f98SAdrian Hunter i = 0 153176099f98SAdrian Hunter while tot > 0: 153276099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 153376099f98SAdrian Hunter if cnt: 153476099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 153576099f98SAdrian Hunter for k in xrange(cnt): 153676099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 153776099f98SAdrian Hunter i += 1 153876099f98SAdrian Hunter while k < 15: 153976099f98SAdrian Hunter byte_str += " " 154076099f98SAdrian Hunter k += 1 1541530e22fdSAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) 154276099f98SAdrian Hunter self.child_count += 1 154376099f98SAdrian Hunter else: 154476099f98SAdrian Hunter return 154576099f98SAdrian Hunter buf_ptr += cnt 154676099f98SAdrian Hunter tot -= cnt 154776099f98SAdrian Hunter buf_sz -= cnt 154876099f98SAdrian Hunter ip += cnt 154976099f98SAdrian Hunter 155076099f98SAdrian Hunter def childCount(self): 155176099f98SAdrian Hunter if not self.query_done: 155276099f98SAdrian Hunter self.Select() 155376099f98SAdrian Hunter if not self.child_count: 155476099f98SAdrian Hunter return -1 155576099f98SAdrian Hunter return self.child_count 155676099f98SAdrian Hunter 155776099f98SAdrian Hunter def hasChildren(self): 155876099f98SAdrian Hunter if not self.query_done: 155976099f98SAdrian Hunter return True 156076099f98SAdrian Hunter return self.child_count > 0 156176099f98SAdrian Hunter 156276099f98SAdrian Hunter def getData(self, column): 156376099f98SAdrian Hunter return self.data[column] 156476099f98SAdrian Hunter 156576099f98SAdrian Hunter# Brance data model root item 156676099f98SAdrian Hunter 156776099f98SAdrian Hunterclass BranchRootItem(): 156876099f98SAdrian Hunter 156976099f98SAdrian Hunter def __init__(self): 157076099f98SAdrian Hunter self.child_count = 0 157176099f98SAdrian Hunter self.child_items = [] 157276099f98SAdrian Hunter self.level = 0 157376099f98SAdrian Hunter 157476099f98SAdrian Hunter def getChildItem(self, row): 157576099f98SAdrian Hunter return self.child_items[row] 157676099f98SAdrian Hunter 157776099f98SAdrian Hunter def getParentItem(self): 157876099f98SAdrian Hunter return None 157976099f98SAdrian Hunter 158076099f98SAdrian Hunter def getRow(self): 158176099f98SAdrian Hunter return 0 158276099f98SAdrian Hunter 158376099f98SAdrian Hunter def childCount(self): 158476099f98SAdrian Hunter return self.child_count 158576099f98SAdrian Hunter 158676099f98SAdrian Hunter def hasChildren(self): 158776099f98SAdrian Hunter return self.child_count > 0 158876099f98SAdrian Hunter 158976099f98SAdrian Hunter def getData(self, column): 159076099f98SAdrian Hunter return "" 159176099f98SAdrian Hunter 1592530e22fdSAdrian Hunter# Calculate instructions per cycle 1593530e22fdSAdrian Hunter 1594530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt): 1595530e22fdSAdrian Hunter if cyc_cnt and insn_cnt: 1596530e22fdSAdrian Hunter ipc = Decimal(float(insn_cnt) / cyc_cnt) 1597530e22fdSAdrian Hunter ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) 1598530e22fdSAdrian Hunter else: 1599530e22fdSAdrian Hunter ipc = "0" 1600530e22fdSAdrian Hunter return ipc 1601530e22fdSAdrian Hunter 160276099f98SAdrian Hunter# Branch data preparation 160376099f98SAdrian Hunter 1604530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data): 1605530e22fdSAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 1606530e22fdSAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 1607530e22fdSAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 1608530e22fdSAdrian Hunter " (" + dsoname(query.value(15)) + ")") 1609530e22fdSAdrian Hunter 1610530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data): 1611530e22fdSAdrian Hunter insn_cnt = query.value(16) 1612530e22fdSAdrian Hunter cyc_cnt = query.value(17) 1613530e22fdSAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 1614530e22fdSAdrian Hunter data.append(insn_cnt) 1615530e22fdSAdrian Hunter data.append(cyc_cnt) 1616530e22fdSAdrian Hunter data.append(ipc) 1617530e22fdSAdrian Hunter 161876099f98SAdrian Hunterdef BranchDataPrep(query): 161976099f98SAdrian Hunter data = [] 162076099f98SAdrian Hunter for i in xrange(0, 8): 162176099f98SAdrian Hunter data.append(query.value(i)) 1622530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 162376099f98SAdrian Hunter return data 162476099f98SAdrian Hunter 16258453c936SAdrian Hunterdef BranchDataPrepWA(query): 16268453c936SAdrian Hunter data = [] 16278453c936SAdrian Hunter data.append(query.value(0)) 16288453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 16298453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 16308453c936SAdrian Hunter for i in xrange(2, 8): 16318453c936SAdrian Hunter data.append(query.value(i)) 1632530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1633530e22fdSAdrian Hunter return data 1634530e22fdSAdrian Hunter 1635530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query): 1636530e22fdSAdrian Hunter data = [] 1637530e22fdSAdrian Hunter for i in xrange(0, 8): 1638530e22fdSAdrian Hunter data.append(query.value(i)) 1639530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1640530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1641530e22fdSAdrian Hunter return data 1642530e22fdSAdrian Hunter 1643530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query): 1644530e22fdSAdrian Hunter data = [] 1645530e22fdSAdrian Hunter data.append(query.value(0)) 1646530e22fdSAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 1647530e22fdSAdrian Hunter data.append("{:>19}".format(query.value(1))) 1648530e22fdSAdrian Hunter for i in xrange(2, 8): 1649530e22fdSAdrian Hunter data.append(query.value(i)) 1650530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1651530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 16528453c936SAdrian Hunter return data 16538453c936SAdrian Hunter 165476099f98SAdrian Hunter# Branch data model 165576099f98SAdrian Hunter 165676099f98SAdrian Hunterclass BranchModel(TreeModel): 165776099f98SAdrian Hunter 165876099f98SAdrian Hunter progress = Signal(object) 165976099f98SAdrian Hunter 166076099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 16614a0979d4SAdrian Hunter super(BranchModel, self).__init__(glb, None, parent) 166276099f98SAdrian Hunter self.event_id = event_id 166376099f98SAdrian Hunter self.more = True 166476099f98SAdrian Hunter self.populated = 0 1665530e22fdSAdrian Hunter self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") 1666530e22fdSAdrian Hunter if self.have_ipc: 1667530e22fdSAdrian Hunter select_ipc = ", insn_count, cyc_count" 1668530e22fdSAdrian Hunter prep_fn = BranchDataWithIPCPrep 1669530e22fdSAdrian Hunter prep_wa_fn = BranchDataWithIPCPrepWA 1670530e22fdSAdrian Hunter else: 1671530e22fdSAdrian Hunter select_ipc = "" 1672530e22fdSAdrian Hunter prep_fn = BranchDataPrep 1673530e22fdSAdrian Hunter prep_wa_fn = BranchDataPrepWA 167476099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 167576099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 167676099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 167776099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 1678530e22fdSAdrian Hunter + select_ipc + 167976099f98SAdrian Hunter " FROM samples" 168076099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 168176099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 168276099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 168376099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 168476099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 168576099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 168676099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 168776099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 168876099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 168976099f98SAdrian Hunter " ORDER BY samples.id" 169076099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 16918453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 1692530e22fdSAdrian Hunter prep = prep_fn 16938453c936SAdrian Hunter else: 1694530e22fdSAdrian Hunter prep = prep_wa_fn 16958453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 169676099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 169776099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 169876099f98SAdrian Hunter 1699a448ba23SAdrian Hunter def GetRoot(self): 1700a448ba23SAdrian Hunter return BranchRootItem() 1701a448ba23SAdrian Hunter 170276099f98SAdrian Hunter def columnCount(self, parent=None): 1703530e22fdSAdrian Hunter if self.have_ipc: 1704530e22fdSAdrian Hunter return 11 1705530e22fdSAdrian Hunter else: 170676099f98SAdrian Hunter return 8 170776099f98SAdrian Hunter 170876099f98SAdrian Hunter def columnHeader(self, column): 1709530e22fdSAdrian Hunter if self.have_ipc: 1710530e22fdSAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] 1711530e22fdSAdrian Hunter else: 171276099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 171376099f98SAdrian Hunter 171476099f98SAdrian Hunter def columnFont(self, column): 1715530e22fdSAdrian Hunter if self.have_ipc: 1716530e22fdSAdrian Hunter br_col = 10 1717530e22fdSAdrian Hunter else: 1718530e22fdSAdrian Hunter br_col = 7 1719530e22fdSAdrian Hunter if column != br_col: 172076099f98SAdrian Hunter return None 172176099f98SAdrian Hunter return QFont("Monospace") 172276099f98SAdrian Hunter 172376099f98SAdrian Hunter def DisplayData(self, item, index): 172476099f98SAdrian Hunter if item.level == 1: 172576099f98SAdrian Hunter self.FetchIfNeeded(item.row) 172676099f98SAdrian Hunter return item.getData(index.column()) 172776099f98SAdrian Hunter 172876099f98SAdrian Hunter def AddSample(self, data): 172976099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 173076099f98SAdrian Hunter self.root.child_items.append(child) 173176099f98SAdrian Hunter self.populated += 1 173276099f98SAdrian Hunter 173376099f98SAdrian Hunter def Update(self, fetched): 173476099f98SAdrian Hunter if not fetched: 173576099f98SAdrian Hunter self.more = False 173676099f98SAdrian Hunter self.progress.emit(0) 173776099f98SAdrian Hunter child_count = self.root.child_count 173876099f98SAdrian Hunter count = self.populated - child_count 173976099f98SAdrian Hunter if count > 0: 174076099f98SAdrian Hunter parent = QModelIndex() 174176099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 174276099f98SAdrian Hunter self.insertRows(child_count, count, parent) 174376099f98SAdrian Hunter self.root.child_count += count 174476099f98SAdrian Hunter self.endInsertRows() 174576099f98SAdrian Hunter self.progress.emit(self.root.child_count) 174676099f98SAdrian Hunter 174776099f98SAdrian Hunter def FetchMoreRecords(self, count): 174876099f98SAdrian Hunter current = self.root.child_count 174976099f98SAdrian Hunter if self.more: 175076099f98SAdrian Hunter self.fetcher.Fetch(count) 175176099f98SAdrian Hunter else: 175276099f98SAdrian Hunter self.progress.emit(0) 175376099f98SAdrian Hunter return current 175476099f98SAdrian Hunter 175576099f98SAdrian Hunter def HasMoreRecords(self): 175676099f98SAdrian Hunter return self.more 175776099f98SAdrian Hunter 17580bf0947aSAdrian Hunter# Report Variables 17590bf0947aSAdrian Hunter 17600bf0947aSAdrian Hunterclass ReportVars(): 17610bf0947aSAdrian Hunter 1762cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1763947cc38dSAdrian Hunter self.name = name 17640bf0947aSAdrian Hunter self.where_clause = where_clause 1765cd358012SAdrian Hunter self.limit = limit 17660bf0947aSAdrian Hunter 17670bf0947aSAdrian Hunter def UniqueId(self): 1768cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 17690bf0947aSAdrian Hunter 177076099f98SAdrian Hunter# Branch window 177176099f98SAdrian Hunter 177276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 177376099f98SAdrian Hunter 1774947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 177576099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 177676099f98SAdrian Hunter 17770bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 177876099f98SAdrian Hunter 17790bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 178076099f98SAdrian Hunter 178176099f98SAdrian Hunter self.view = QTreeView() 178276099f98SAdrian Hunter self.view.setUniformRowHeights(True) 178396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 178496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 178576099f98SAdrian Hunter self.view.setModel(self.model) 178676099f98SAdrian Hunter 178776099f98SAdrian Hunter self.ResizeColumnsToContents() 178876099f98SAdrian Hunter 17899bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 17909bc4e4bfSAdrian Hunter 179176099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 179276099f98SAdrian Hunter 179376099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 179476099f98SAdrian Hunter 179576099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 179676099f98SAdrian Hunter 179776099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 179876099f98SAdrian Hunter 179976099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 180076099f98SAdrian Hunter 1801947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 180276099f98SAdrian Hunter 180376099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 180476099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 180576099f98SAdrian Hunter # so implement a crude alternative 180676099f98SAdrian Hunter mm = "MM" if column else "MMMM" 180776099f98SAdrian Hunter font = self.view.font() 180876099f98SAdrian Hunter metrics = QFontMetrics(font) 180976099f98SAdrian Hunter max = 0 181076099f98SAdrian Hunter for row in xrange(n): 181176099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 181276099f98SAdrian Hunter len = metrics.width(str(val) + mm) 181376099f98SAdrian Hunter max = len if len > max else max 181476099f98SAdrian Hunter val = self.model.columnHeader(column) 181576099f98SAdrian Hunter len = metrics.width(str(val) + mm) 181676099f98SAdrian Hunter max = len if len > max else max 181776099f98SAdrian Hunter self.view.setColumnWidth(column, max) 181876099f98SAdrian Hunter 181976099f98SAdrian Hunter def ResizeColumnsToContents(self): 182076099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 182176099f98SAdrian Hunter if n < 1: 182276099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 182376099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 182476099f98SAdrian Hunter return 182576099f98SAdrian Hunter columns = self.model.columnCount() 182676099f98SAdrian Hunter for i in xrange(columns): 182776099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 182876099f98SAdrian Hunter 182976099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 183076099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 183176099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 183276099f98SAdrian Hunter self.ResizeColumnsToContents() 183376099f98SAdrian Hunter 183476099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 183576099f98SAdrian Hunter self.view.setFocus() 183676099f98SAdrian Hunter self.find_bar.Busy() 183776099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 183876099f98SAdrian Hunter 183976099f98SAdrian Hunter def FindDone(self, row): 184076099f98SAdrian Hunter self.find_bar.Idle() 184176099f98SAdrian Hunter if row >= 0: 184276099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 184376099f98SAdrian Hunter else: 184476099f98SAdrian Hunter self.find_bar.NotFound() 184576099f98SAdrian Hunter 18461c3ca1b3SAdrian Hunter# Line edit data item 18471c3ca1b3SAdrian Hunter 18481c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 18491c3ca1b3SAdrian Hunter 1850cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 18511c3ca1b3SAdrian Hunter self.glb = glb 18521c3ca1b3SAdrian Hunter self.label = label 18531c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 18541c3ca1b3SAdrian Hunter self.parent = parent 18551c3ca1b3SAdrian Hunter self.id = id 18561c3ca1b3SAdrian Hunter 1857cd358012SAdrian Hunter self.value = default 18581c3ca1b3SAdrian Hunter 1859cd358012SAdrian Hunter self.widget = QLineEdit(default) 18601c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 18611c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 18621c3ca1b3SAdrian Hunter self.red = False 18631c3ca1b3SAdrian Hunter self.error = "" 18641c3ca1b3SAdrian Hunter self.validated = True 18651c3ca1b3SAdrian Hunter 18661c3ca1b3SAdrian Hunter if placeholder_text: 18671c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 18681c3ca1b3SAdrian Hunter 18691c3ca1b3SAdrian Hunter def TurnTextRed(self): 18701c3ca1b3SAdrian Hunter if not self.red: 18711c3ca1b3SAdrian Hunter palette = QPalette() 18721c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 18731c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 18741c3ca1b3SAdrian Hunter self.red = True 18751c3ca1b3SAdrian Hunter 18761c3ca1b3SAdrian Hunter def TurnTextNormal(self): 18771c3ca1b3SAdrian Hunter if self.red: 18781c3ca1b3SAdrian Hunter palette = QPalette() 18791c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 18801c3ca1b3SAdrian Hunter self.red = False 18811c3ca1b3SAdrian Hunter 18821c3ca1b3SAdrian Hunter def InvalidValue(self, value): 18831c3ca1b3SAdrian Hunter self.value = "" 18841c3ca1b3SAdrian Hunter self.TurnTextRed() 18851c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 18861c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 18871c3ca1b3SAdrian Hunter 18881c3ca1b3SAdrian Hunter def Invalidate(self): 18891c3ca1b3SAdrian Hunter self.validated = False 18901c3ca1b3SAdrian Hunter 18911c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18921c3ca1b3SAdrian Hunter self.value = input_string.strip() 18931c3ca1b3SAdrian Hunter 18941c3ca1b3SAdrian Hunter def Validate(self): 18951c3ca1b3SAdrian Hunter self.validated = True 18961c3ca1b3SAdrian Hunter self.error = "" 18971c3ca1b3SAdrian Hunter self.TurnTextNormal() 18981c3ca1b3SAdrian Hunter self.parent.ClearMessage() 18991c3ca1b3SAdrian Hunter input_string = self.widget.text() 19001c3ca1b3SAdrian Hunter if not len(input_string.strip()): 19011c3ca1b3SAdrian Hunter self.value = "" 19021c3ca1b3SAdrian Hunter return 19031c3ca1b3SAdrian Hunter self.DoValidate(input_string) 19041c3ca1b3SAdrian Hunter 19051c3ca1b3SAdrian Hunter def IsValid(self): 19061c3ca1b3SAdrian Hunter if not self.validated: 19071c3ca1b3SAdrian Hunter self.Validate() 19081c3ca1b3SAdrian Hunter if len(self.error): 19091c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 19101c3ca1b3SAdrian Hunter return False 19111c3ca1b3SAdrian Hunter return True 19121c3ca1b3SAdrian Hunter 19131c3ca1b3SAdrian Hunter def IsNumber(self, value): 19141c3ca1b3SAdrian Hunter try: 19151c3ca1b3SAdrian Hunter x = int(value) 19161c3ca1b3SAdrian Hunter except: 19171c3ca1b3SAdrian Hunter x = 0 19181c3ca1b3SAdrian Hunter return str(x) == value 19191c3ca1b3SAdrian Hunter 19201c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 19211c3ca1b3SAdrian Hunter 19221c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 19231c3ca1b3SAdrian Hunter 19241c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 19251c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 19261c3ca1b3SAdrian Hunter 19271c3ca1b3SAdrian Hunter self.column_name = column_name 19281c3ca1b3SAdrian Hunter 19291c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19301c3ca1b3SAdrian Hunter singles = [] 19311c3ca1b3SAdrian Hunter ranges = [] 19321c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19331c3ca1b3SAdrian Hunter if "-" in value: 19341c3ca1b3SAdrian Hunter vrange = value.split("-") 19351c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 19361c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19371c3ca1b3SAdrian Hunter ranges.append(vrange) 19381c3ca1b3SAdrian Hunter else: 19391c3ca1b3SAdrian Hunter if not self.IsNumber(value): 19401c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19411c3ca1b3SAdrian Hunter singles.append(value) 19421c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 19431c3ca1b3SAdrian Hunter if len(singles): 19441c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 19451c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 19461c3ca1b3SAdrian Hunter 1947cd358012SAdrian Hunter# Positive integer dialog data item 1948cd358012SAdrian Hunter 1949cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1950cd358012SAdrian Hunter 1951cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1952cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1953cd358012SAdrian Hunter 1954cd358012SAdrian Hunter def DoValidate(self, input_string): 1955cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1956cd358012SAdrian Hunter return self.InvalidValue(input_string) 1957cd358012SAdrian Hunter value = int(input_string.strip()) 1958cd358012SAdrian Hunter if value <= 0: 1959cd358012SAdrian Hunter return self.InvalidValue(input_string) 1960cd358012SAdrian Hunter self.value = str(value) 1961cd358012SAdrian Hunter 19621c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 19631c3ca1b3SAdrian Hunter 19641c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 19651c3ca1b3SAdrian Hunter 19661c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 19671c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 19681c3ca1b3SAdrian Hunter 19691c3ca1b3SAdrian Hunter self.table_name = table_name 19701c3ca1b3SAdrian Hunter self.match_column = match_column 19711c3ca1b3SAdrian Hunter self.column_name1 = column_name1 19721c3ca1b3SAdrian Hunter self.column_name2 = column_name2 19731c3ca1b3SAdrian Hunter 19741c3ca1b3SAdrian Hunter def ValueToIds(self, value): 19751c3ca1b3SAdrian Hunter ids = [] 19761c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 19771c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 19781c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 19791c3ca1b3SAdrian Hunter if ret: 19801c3ca1b3SAdrian Hunter while query.next(): 19811c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 19821c3ca1b3SAdrian Hunter return ids 19831c3ca1b3SAdrian Hunter 19841c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19851c3ca1b3SAdrian Hunter all_ids = [] 19861c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19871c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 19881c3ca1b3SAdrian Hunter if len(ids): 19891c3ca1b3SAdrian Hunter all_ids.extend(ids) 19901c3ca1b3SAdrian Hunter else: 19911c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19921c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 19931c3ca1b3SAdrian Hunter if self.column_name2: 19941c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 19951c3ca1b3SAdrian Hunter 19961c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 19971c3ca1b3SAdrian Hunter 19981c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 19991c3ca1b3SAdrian Hunter 20001c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 20011c3ca1b3SAdrian Hunter self.column_name = column_name 20021c3ca1b3SAdrian Hunter 20031c3ca1b3SAdrian Hunter self.last_id = 0 20041c3ca1b3SAdrian Hunter self.first_time = 0 20051c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 20061c3ca1b3SAdrian Hunter 20071c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 20081c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 20091c3ca1b3SAdrian Hunter if query.next(): 20101c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 20111c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 20121c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 20131c3ca1b3SAdrian Hunter if query.next(): 20141c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 20151c3ca1b3SAdrian Hunter if placeholder_text: 20161c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 20171c3ca1b3SAdrian Hunter 20181c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 20191c3ca1b3SAdrian Hunter 20201c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 20211c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 20221c3ca1b3SAdrian Hunter if query.next(): 20231c3ca1b3SAdrian Hunter return True, int(query.value(0)) 20241c3ca1b3SAdrian Hunter else: 20251c3ca1b3SAdrian Hunter return False, 0 20261c3ca1b3SAdrian Hunter 20271c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 20281c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 20291c3ca1b3SAdrian Hunter while True: 20301c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 20311c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 20321c3ca1b3SAdrian Hunter if not query.next(): 20331c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 20341c3ca1b3SAdrian Hunter if not ok: 20351c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 20361c3ca1b3SAdrian Hunter if not ok: 20371c3ca1b3SAdrian Hunter return str(higher_id) 20381c3ca1b3SAdrian Hunter next_id = dbid 20391c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 20401c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 20411c3ca1b3SAdrian Hunter if get_floor: 20421c3ca1b3SAdrian Hunter if target_time > next_time: 20431c3ca1b3SAdrian Hunter lower_id = next_id 20441c3ca1b3SAdrian Hunter else: 20451c3ca1b3SAdrian Hunter higher_id = next_id 20461c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 20471c3ca1b3SAdrian Hunter return str(higher_id) 20481c3ca1b3SAdrian Hunter else: 20491c3ca1b3SAdrian Hunter if target_time >= next_time: 20501c3ca1b3SAdrian Hunter lower_id = next_id 20511c3ca1b3SAdrian Hunter else: 20521c3ca1b3SAdrian Hunter higher_id = next_id 20531c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 20541c3ca1b3SAdrian Hunter return str(lower_id) 20551c3ca1b3SAdrian Hunter 20561c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 20571c3ca1b3SAdrian Hunter mult = 1 20581c3ca1b3SAdrian Hunter suffix = val[-2:] 20591c3ca1b3SAdrian Hunter if suffix == "ms": 20601c3ca1b3SAdrian Hunter mult = 1000000 20611c3ca1b3SAdrian Hunter elif suffix == "us": 20621c3ca1b3SAdrian Hunter mult = 1000 20631c3ca1b3SAdrian Hunter elif suffix == "ns": 20641c3ca1b3SAdrian Hunter mult = 1 20651c3ca1b3SAdrian Hunter else: 20661c3ca1b3SAdrian Hunter return val 20671c3ca1b3SAdrian Hunter val = val[:-2].strip() 20681c3ca1b3SAdrian Hunter if not self.IsNumber(val): 20691c3ca1b3SAdrian Hunter return val 20701c3ca1b3SAdrian Hunter val = int(val) * mult 20711c3ca1b3SAdrian Hunter if val >= 0: 20721c3ca1b3SAdrian Hunter val += self.first_time 20731c3ca1b3SAdrian Hunter else: 20741c3ca1b3SAdrian Hunter val += self.last_time 20751c3ca1b3SAdrian Hunter return str(val) 20761c3ca1b3SAdrian Hunter 20771c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 20781c3ca1b3SAdrian Hunter if vrange[0] == "": 20791c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 20801c3ca1b3SAdrian Hunter if vrange[1] == "": 20811c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 20821c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 20831c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 20841c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 20851c3ca1b3SAdrian Hunter return False 20861c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 20871c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 20881c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 20891c3ca1b3SAdrian Hunter return False 20901c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 20911c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 20921c3ca1b3SAdrian Hunter return True 20931c3ca1b3SAdrian Hunter 20941c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 20951c3ca1b3SAdrian Hunter n = value.count("-") 20961c3ca1b3SAdrian Hunter if n == 1: 20971c3ca1b3SAdrian Hunter pass 20981c3ca1b3SAdrian Hunter elif n == 2: 20991c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 21001c3ca1b3SAdrian Hunter n = 1 21011c3ca1b3SAdrian Hunter elif n == 3: 21021c3ca1b3SAdrian Hunter n = 2 21031c3ca1b3SAdrian Hunter else: 21041c3ca1b3SAdrian Hunter return False 21051c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 21061c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 21071c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 21081c3ca1b3SAdrian Hunter ranges.append(vrange) 21091c3ca1b3SAdrian Hunter return True 21101c3ca1b3SAdrian Hunter return False 21111c3ca1b3SAdrian Hunter 21121c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 21131c3ca1b3SAdrian Hunter ranges = [] 21141c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 21151c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 21161c3ca1b3SAdrian Hunter return self.InvalidValue(value) 21171c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 21181c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 21191c3ca1b3SAdrian Hunter 21200924cd68SAdrian Hunter# Report Dialog Base 2121210cf1f9SAdrian Hunter 21220924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 2123210cf1f9SAdrian Hunter 21240924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 21250924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2126210cf1f9SAdrian Hunter 2127210cf1f9SAdrian Hunter self.glb = glb 2128210cf1f9SAdrian Hunter 21290bf0947aSAdrian Hunter self.report_vars = ReportVars() 2130210cf1f9SAdrian Hunter 21310924cd68SAdrian Hunter self.setWindowTitle(title) 2132210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2133210cf1f9SAdrian Hunter 21341c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2135210cf1f9SAdrian Hunter 21360924cd68SAdrian Hunter self.partial = partial 21370924cd68SAdrian Hunter 2138210cf1f9SAdrian Hunter self.grid = QGridLayout() 2139210cf1f9SAdrian Hunter 2140210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2141210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2142210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2143210cf1f9SAdrian Hunter 2144210cf1f9SAdrian Hunter self.status = QLabel() 2145210cf1f9SAdrian Hunter 2146210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2147210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2148210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2149210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2150210cf1f9SAdrian Hunter 2151210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2152210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2153210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2154210cf1f9SAdrian Hunter 2155210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2156210cf1f9SAdrian Hunter #self.hbox.addStretch() 2157210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2158210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2159210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2160210cf1f9SAdrian Hunter 2161210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2162210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2163210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2164210cf1f9SAdrian Hunter 2165210cf1f9SAdrian Hunter self.setLayout(self.vbox); 2166210cf1f9SAdrian Hunter 2167210cf1f9SAdrian Hunter def Ok(self): 21680bf0947aSAdrian Hunter vars = self.report_vars 21691c3ca1b3SAdrian Hunter for d in self.data_items: 21701c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 21711c3ca1b3SAdrian Hunter vars.name = d.value 2172947cc38dSAdrian Hunter if not vars.name: 2173210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2174210cf1f9SAdrian Hunter return 2175210cf1f9SAdrian Hunter for d in self.data_items: 2176210cf1f9SAdrian Hunter if not d.IsValid(): 2177210cf1f9SAdrian Hunter return 2178210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2179cd358012SAdrian Hunter if d.id == "LIMIT": 2180cd358012SAdrian Hunter vars.limit = d.value 2181cd358012SAdrian Hunter elif len(d.value): 21820bf0947aSAdrian Hunter if len(vars.where_clause): 21830bf0947aSAdrian Hunter vars.where_clause += " AND " 21840bf0947aSAdrian Hunter vars.where_clause += d.value 21850bf0947aSAdrian Hunter if len(vars.where_clause): 21860924cd68SAdrian Hunter if self.partial: 21870bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2188210cf1f9SAdrian Hunter else: 21890bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2190210cf1f9SAdrian Hunter self.accept() 2191210cf1f9SAdrian Hunter 2192210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2193210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2194210cf1f9SAdrian Hunter 2195210cf1f9SAdrian Hunter def ClearMessage(self): 2196210cf1f9SAdrian Hunter self.status.setText("") 2197210cf1f9SAdrian Hunter 21980924cd68SAdrian Hunter# Selected branch report creation dialog 21990924cd68SAdrian Hunter 22000924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 22010924cd68SAdrian Hunter 22020924cd68SAdrian Hunter def __init__(self, glb, parent=None): 22030924cd68SAdrian Hunter title = "Selected Branches" 22041c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 22051c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 22061c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 22071c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 22081c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 22091c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 22101c3ca1b3SAdrian 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), 22111c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 22121c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 22130924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 22140924cd68SAdrian Hunter 221576099f98SAdrian Hunter# Event list 221676099f98SAdrian Hunter 221776099f98SAdrian Hunterdef GetEventList(db): 221876099f98SAdrian Hunter events = [] 221976099f98SAdrian Hunter query = QSqlQuery(db) 222076099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 222176099f98SAdrian Hunter while query.next(): 222276099f98SAdrian Hunter events.append(query.value(0)) 222376099f98SAdrian Hunter return events 222476099f98SAdrian Hunter 2225655cb952SAdrian Hunter# Is a table selectable 2226655cb952SAdrian Hunter 2227530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"): 2228655cb952SAdrian Hunter query = QSqlQuery(db) 2229655cb952SAdrian Hunter try: 2230530e22fdSAdrian Hunter QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") 2231655cb952SAdrian Hunter except: 2232655cb952SAdrian Hunter return False 2233655cb952SAdrian Hunter return True 2234655cb952SAdrian Hunter 22358392b74bSAdrian Hunter# SQL table data model item 22368392b74bSAdrian Hunter 22378392b74bSAdrian Hunterclass SQLTableItem(): 22388392b74bSAdrian Hunter 22398392b74bSAdrian Hunter def __init__(self, row, data): 22408392b74bSAdrian Hunter self.row = row 22418392b74bSAdrian Hunter self.data = data 22428392b74bSAdrian Hunter 22438392b74bSAdrian Hunter def getData(self, column): 22448392b74bSAdrian Hunter return self.data[column] 22458392b74bSAdrian Hunter 22468392b74bSAdrian Hunter# SQL table data model 22478392b74bSAdrian Hunter 22488392b74bSAdrian Hunterclass SQLTableModel(TableModel): 22498392b74bSAdrian Hunter 22508392b74bSAdrian Hunter progress = Signal(object) 22518392b74bSAdrian Hunter 22528c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 22538392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 22548392b74bSAdrian Hunter self.glb = glb 22558392b74bSAdrian Hunter self.more = True 22568392b74bSAdrian Hunter self.populated = 0 22578c90fef9SAdrian Hunter self.column_headers = column_headers 22588453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 22598392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 22608392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 22618392b74bSAdrian Hunter 22628392b74bSAdrian Hunter def DisplayData(self, item, index): 22638392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 22648392b74bSAdrian Hunter return item.getData(index.column()) 22658392b74bSAdrian Hunter 22668392b74bSAdrian Hunter def AddSample(self, data): 22678392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 22688392b74bSAdrian Hunter self.child_items.append(child) 22698392b74bSAdrian Hunter self.populated += 1 22708392b74bSAdrian Hunter 22718392b74bSAdrian Hunter def Update(self, fetched): 22728392b74bSAdrian Hunter if not fetched: 22738392b74bSAdrian Hunter self.more = False 22748392b74bSAdrian Hunter self.progress.emit(0) 22758392b74bSAdrian Hunter child_count = self.child_count 22768392b74bSAdrian Hunter count = self.populated - child_count 22778392b74bSAdrian Hunter if count > 0: 22788392b74bSAdrian Hunter parent = QModelIndex() 22798392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 22808392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 22818392b74bSAdrian Hunter self.child_count += count 22828392b74bSAdrian Hunter self.endInsertRows() 22838392b74bSAdrian Hunter self.progress.emit(self.child_count) 22848392b74bSAdrian Hunter 22858392b74bSAdrian Hunter def FetchMoreRecords(self, count): 22868392b74bSAdrian Hunter current = self.child_count 22878392b74bSAdrian Hunter if self.more: 22888392b74bSAdrian Hunter self.fetcher.Fetch(count) 22898392b74bSAdrian Hunter else: 22908392b74bSAdrian Hunter self.progress.emit(0) 22918392b74bSAdrian Hunter return current 22928392b74bSAdrian Hunter 22938392b74bSAdrian Hunter def HasMoreRecords(self): 22948392b74bSAdrian Hunter return self.more 22958392b74bSAdrian Hunter 22968c90fef9SAdrian Hunter def columnCount(self, parent=None): 22978c90fef9SAdrian Hunter return len(self.column_headers) 22988c90fef9SAdrian Hunter 22998c90fef9SAdrian Hunter def columnHeader(self, column): 23008c90fef9SAdrian Hunter return self.column_headers[column] 23018c90fef9SAdrian Hunter 23028453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 23038453c936SAdrian Hunter data = [] 23048453c936SAdrian Hunter for i in xrange(count): 23058453c936SAdrian Hunter data.append(query.value(i)) 23068453c936SAdrian Hunter return data 23078453c936SAdrian Hunter 23088392b74bSAdrian Hunter# SQL automatic table data model 23098392b74bSAdrian Hunter 23108392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 23118392b74bSAdrian Hunter 23128392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 23138392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 23148392b74bSAdrian Hunter if table_name == "comm_threads_view": 23158392b74bSAdrian Hunter # For now, comm_threads_view has no id column 23168392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 23178c90fef9SAdrian Hunter column_headers = [] 23188392b74bSAdrian Hunter query = QSqlQuery(glb.db) 23198392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 23208392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 23218392b74bSAdrian Hunter while query.next(): 23228c90fef9SAdrian Hunter column_headers.append(query.value(1)) 23238392b74bSAdrian Hunter if table_name == "sqlite_master": 23248392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 23258392b74bSAdrian Hunter else: 23268392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 23278392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 23288392b74bSAdrian Hunter select_table_name = table_name[19:] 23298392b74bSAdrian Hunter schema = "information_schema" 23308392b74bSAdrian Hunter else: 23318392b74bSAdrian Hunter select_table_name = table_name 23328392b74bSAdrian Hunter schema = "public" 23338392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 23348392b74bSAdrian Hunter while query.next(): 23358c90fef9SAdrian Hunter column_headers.append(query.value(0)) 23368453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 23378453c936SAdrian Hunter if table_name == "samples_view": 23388453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 23398453c936SAdrian Hunter if table_name == "samples": 23408453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 23418c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 23428392b74bSAdrian Hunter 23438453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 23448453c936SAdrian Hunter data = [] 23458453c936SAdrian Hunter data.append(query.value(0)) 23468453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 23478453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 23488453c936SAdrian Hunter for i in xrange(2, count): 23498453c936SAdrian Hunter data.append(query.value(i)) 23508453c936SAdrian Hunter return data 23518453c936SAdrian Hunter 23528453c936SAdrian Hunter def samples_DataPrep(self, query, count): 23538453c936SAdrian Hunter data = [] 23548453c936SAdrian Hunter for i in xrange(9): 23558453c936SAdrian Hunter data.append(query.value(i)) 23568453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 23578453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 23588453c936SAdrian Hunter for i in xrange(10, count): 23598453c936SAdrian Hunter data.append(query.value(i)) 23608453c936SAdrian Hunter return data 23618453c936SAdrian Hunter 23628392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 23638392b74bSAdrian Hunter 23648392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 23658392b74bSAdrian Hunter 23668392b74bSAdrian Hunter def __init__(self, parent=None): 23678392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 23688392b74bSAdrian Hunter 23698392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 23708392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 23718392b74bSAdrian Hunter # so implement a crude alternative 23728392b74bSAdrian Hunter font = self.view.font() 23738392b74bSAdrian Hunter metrics = QFontMetrics(font) 23748392b74bSAdrian Hunter max = 0 23758392b74bSAdrian Hunter for row in xrange(n): 23768392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 23778392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 23788392b74bSAdrian Hunter max = len if len > max else max 23798392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 23808392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 23818392b74bSAdrian Hunter max = len if len > max else max 23828392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 23838392b74bSAdrian Hunter 23848392b74bSAdrian Hunter def ResizeColumnsToContents(self): 23858392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 23868392b74bSAdrian Hunter if n < 1: 23878392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 23888392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 23898392b74bSAdrian Hunter return 23908392b74bSAdrian Hunter columns = self.data_model.columnCount() 23918392b74bSAdrian Hunter for i in xrange(columns): 23928392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 23938392b74bSAdrian Hunter 23948392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 23958392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 23968392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 23978392b74bSAdrian Hunter self.ResizeColumnsToContents() 23988392b74bSAdrian Hunter 239996c43b9aSAdrian Hunter# Convert value to CSV 240096c43b9aSAdrian Hunter 240196c43b9aSAdrian Hunterdef ToCSValue(val): 240296c43b9aSAdrian Hunter if '"' in val: 240396c43b9aSAdrian Hunter val = val.replace('"', '""') 240496c43b9aSAdrian Hunter if "," in val or '"' in val: 240596c43b9aSAdrian Hunter val = '"' + val + '"' 240696c43b9aSAdrian Hunter return val 240796c43b9aSAdrian Hunter 240896c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 240996c43b9aSAdrian Hunter 241096c43b9aSAdrian Hunterglb_max_cols = 1000 241196c43b9aSAdrian Hunter 241296c43b9aSAdrian Hunterdef RowColumnKey(a): 241396c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 241496c43b9aSAdrian Hunter 241596c43b9aSAdrian Hunter# Copy selected table cells to clipboard 241696c43b9aSAdrian Hunter 241796c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 241896c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 241996c43b9aSAdrian Hunter idx_cnt = len(indexes) 242096c43b9aSAdrian Hunter if not idx_cnt: 242196c43b9aSAdrian Hunter return 242296c43b9aSAdrian Hunter if idx_cnt == 1: 242396c43b9aSAdrian Hunter with_hdr=False 242496c43b9aSAdrian Hunter min_row = indexes[0].row() 242596c43b9aSAdrian Hunter max_row = indexes[0].row() 242696c43b9aSAdrian Hunter min_col = indexes[0].column() 242796c43b9aSAdrian Hunter max_col = indexes[0].column() 242896c43b9aSAdrian Hunter for i in indexes: 242996c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 243096c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 243196c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 243296c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 243396c43b9aSAdrian Hunter if max_col > glb_max_cols: 243496c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 243596c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 243696c43b9aSAdrian Hunter for i in indexes: 243796c43b9aSAdrian Hunter c = i.column() - min_col 243896c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 243996c43b9aSAdrian Hunter text = "" 244096c43b9aSAdrian Hunter pad = "" 244196c43b9aSAdrian Hunter sep = "" 244296c43b9aSAdrian Hunter if with_hdr: 244396c43b9aSAdrian Hunter model = indexes[0].model() 244496c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 244596c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 244696c43b9aSAdrian Hunter if as_csv: 244796c43b9aSAdrian Hunter text += sep + ToCSValue(val) 244896c43b9aSAdrian Hunter sep = "," 244996c43b9aSAdrian Hunter else: 245096c43b9aSAdrian Hunter c = col - min_col 245196c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 245296c43b9aSAdrian Hunter width = max_width[c] 245396c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 245496c43b9aSAdrian Hunter if align & Qt.AlignRight: 245596c43b9aSAdrian Hunter val = val.rjust(width) 245696c43b9aSAdrian Hunter text += pad + sep + val 245796c43b9aSAdrian Hunter pad = " " * (width - len(val)) 245896c43b9aSAdrian Hunter sep = " " 245996c43b9aSAdrian Hunter text += "\n" 246096c43b9aSAdrian Hunter pad = "" 246196c43b9aSAdrian Hunter sep = "" 246296c43b9aSAdrian Hunter last_row = min_row 246396c43b9aSAdrian Hunter for i in indexes: 246496c43b9aSAdrian Hunter if i.row() > last_row: 246596c43b9aSAdrian Hunter last_row = i.row() 246696c43b9aSAdrian Hunter text += "\n" 246796c43b9aSAdrian Hunter pad = "" 246896c43b9aSAdrian Hunter sep = "" 246996c43b9aSAdrian Hunter if as_csv: 247096c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 247196c43b9aSAdrian Hunter sep = "," 247296c43b9aSAdrian Hunter else: 247396c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 247496c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 247596c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 247696c43b9aSAdrian Hunter else: 247796c43b9aSAdrian Hunter val = str(i.data()) 247896c43b9aSAdrian Hunter text += pad + sep + val 247996c43b9aSAdrian Hunter pad = " " * (width - len(val)) 248096c43b9aSAdrian Hunter sep = " " 248196c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 248296c43b9aSAdrian Hunter 248396c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 248496c43b9aSAdrian Hunter indexes = view.selectedIndexes() 248596c43b9aSAdrian Hunter if not len(indexes): 248696c43b9aSAdrian Hunter return 248796c43b9aSAdrian Hunter 248896c43b9aSAdrian Hunter selection = view.selectionModel() 248996c43b9aSAdrian Hunter 249096c43b9aSAdrian Hunter first = None 249196c43b9aSAdrian Hunter for i in indexes: 249296c43b9aSAdrian Hunter above = view.indexAbove(i) 249396c43b9aSAdrian Hunter if not selection.isSelected(above): 249496c43b9aSAdrian Hunter first = i 249596c43b9aSAdrian Hunter break 249696c43b9aSAdrian Hunter 249796c43b9aSAdrian Hunter if first is None: 249896c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 249996c43b9aSAdrian Hunter 250096c43b9aSAdrian Hunter model = first.model() 250196c43b9aSAdrian Hunter row_cnt = 0 250296c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 250396c43b9aSAdrian Hunter max_width = [0] * col_cnt 250496c43b9aSAdrian Hunter 250596c43b9aSAdrian Hunter indent_sz = 2 250696c43b9aSAdrian Hunter indent_str = " " * indent_sz 250796c43b9aSAdrian Hunter 250896c43b9aSAdrian Hunter expanded_mark_sz = 2 250996c43b9aSAdrian Hunter if sys.version_info[0] == 3: 251096c43b9aSAdrian Hunter expanded_mark = "\u25BC " 251196c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 251296c43b9aSAdrian Hunter else: 251396c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 251496c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 251596c43b9aSAdrian Hunter leaf_mark = " " 251696c43b9aSAdrian Hunter 251796c43b9aSAdrian Hunter if not as_csv: 251896c43b9aSAdrian Hunter pos = first 251996c43b9aSAdrian Hunter while True: 252096c43b9aSAdrian Hunter row_cnt += 1 252196c43b9aSAdrian Hunter row = pos.row() 252296c43b9aSAdrian Hunter for c in range(col_cnt): 252396c43b9aSAdrian Hunter i = pos.sibling(row, c) 252496c43b9aSAdrian Hunter if c: 252596c43b9aSAdrian Hunter n = len(str(i.data())) 252696c43b9aSAdrian Hunter else: 252796c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 252896c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 252996c43b9aSAdrian Hunter n += expanded_mark_sz 253096c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 253196c43b9aSAdrian Hunter pos = view.indexBelow(pos) 253296c43b9aSAdrian Hunter if not selection.isSelected(pos): 253396c43b9aSAdrian Hunter break 253496c43b9aSAdrian Hunter 253596c43b9aSAdrian Hunter text = "" 253696c43b9aSAdrian Hunter pad = "" 253796c43b9aSAdrian Hunter sep = "" 253896c43b9aSAdrian Hunter if with_hdr: 253996c43b9aSAdrian Hunter for c in range(col_cnt): 254096c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 254196c43b9aSAdrian Hunter if as_csv: 254296c43b9aSAdrian Hunter text += sep + ToCSValue(val) 254396c43b9aSAdrian Hunter sep = "," 254496c43b9aSAdrian Hunter else: 254596c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 254696c43b9aSAdrian Hunter width = max_width[c] 254796c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 254896c43b9aSAdrian Hunter if align & Qt.AlignRight: 254996c43b9aSAdrian Hunter val = val.rjust(width) 255096c43b9aSAdrian Hunter text += pad + sep + val 255196c43b9aSAdrian Hunter pad = " " * (width - len(val)) 255296c43b9aSAdrian Hunter sep = " " 255396c43b9aSAdrian Hunter text += "\n" 255496c43b9aSAdrian Hunter pad = "" 255596c43b9aSAdrian Hunter sep = "" 255696c43b9aSAdrian Hunter 255796c43b9aSAdrian Hunter pos = first 255896c43b9aSAdrian Hunter while True: 255996c43b9aSAdrian Hunter row = pos.row() 256096c43b9aSAdrian Hunter for c in range(col_cnt): 256196c43b9aSAdrian Hunter i = pos.sibling(row, c) 256296c43b9aSAdrian Hunter val = str(i.data()) 256396c43b9aSAdrian Hunter if not c: 256496c43b9aSAdrian Hunter if model.hasChildren(i): 256596c43b9aSAdrian Hunter if view.isExpanded(i): 256696c43b9aSAdrian Hunter mark = expanded_mark 256796c43b9aSAdrian Hunter else: 256896c43b9aSAdrian Hunter mark = not_expanded_mark 256996c43b9aSAdrian Hunter else: 257096c43b9aSAdrian Hunter mark = leaf_mark 257196c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 257296c43b9aSAdrian Hunter if as_csv: 257396c43b9aSAdrian Hunter text += sep + ToCSValue(val) 257496c43b9aSAdrian Hunter sep = "," 257596c43b9aSAdrian Hunter else: 257696c43b9aSAdrian Hunter width = max_width[c] 257796c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 257896c43b9aSAdrian Hunter val = val.rjust(width) 257996c43b9aSAdrian Hunter text += pad + sep + val 258096c43b9aSAdrian Hunter pad = " " * (width - len(val)) 258196c43b9aSAdrian Hunter sep = " " 258296c43b9aSAdrian Hunter pos = view.indexBelow(pos) 258396c43b9aSAdrian Hunter if not selection.isSelected(pos): 258496c43b9aSAdrian Hunter break 258596c43b9aSAdrian Hunter text = text.rstrip() + "\n" 258696c43b9aSAdrian Hunter pad = "" 258796c43b9aSAdrian Hunter sep = "" 258896c43b9aSAdrian Hunter 258996c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 259096c43b9aSAdrian Hunter 259196c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 259296c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 259396c43b9aSAdrian Hunter 259496c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 259596c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 259696c43b9aSAdrian Hunter 259796c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 259896c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 259996c43b9aSAdrian Hunter 26009bc4e4bfSAdrian Hunter# Context menu 26019bc4e4bfSAdrian Hunter 26029bc4e4bfSAdrian Hunterclass ContextMenu(object): 26039bc4e4bfSAdrian Hunter 26049bc4e4bfSAdrian Hunter def __init__(self, view): 26059bc4e4bfSAdrian Hunter self.view = view 26069bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 26079bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 26089bc4e4bfSAdrian Hunter 26099bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 26109bc4e4bfSAdrian Hunter menu = QMenu(self.view) 26119bc4e4bfSAdrian Hunter self.AddActions(menu) 26129bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 26139bc4e4bfSAdrian Hunter 26149bc4e4bfSAdrian Hunter def AddCopy(self, menu): 26159bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 26169bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 26179bc4e4bfSAdrian Hunter 26189bc4e4bfSAdrian Hunter def AddActions(self, menu): 26199bc4e4bfSAdrian Hunter self.AddCopy(menu) 26209bc4e4bfSAdrian Hunter 26219bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 26229bc4e4bfSAdrian Hunter 26239bc4e4bfSAdrian Hunter def __init__(self, view): 26249bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 26259bc4e4bfSAdrian Hunter 26269bc4e4bfSAdrian Hunter def AddActions(self, menu): 26279bc4e4bfSAdrian Hunter i = self.view.currentIndex() 26289bc4e4bfSAdrian Hunter text = str(i.data()).strip() 26299bc4e4bfSAdrian Hunter if len(text): 26309bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 26319bc4e4bfSAdrian Hunter self.AddCopy(menu) 26329bc4e4bfSAdrian Hunter 26338392b74bSAdrian Hunter# Table window 26348392b74bSAdrian Hunter 26358392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 26368392b74bSAdrian Hunter 26378392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 26388392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 26398392b74bSAdrian Hunter 26408392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 26418392b74bSAdrian Hunter 26428392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 26438392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 26448392b74bSAdrian Hunter 26458392b74bSAdrian Hunter self.view = QTableView() 26468392b74bSAdrian Hunter self.view.setModel(self.model) 26478392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 26488392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 26498392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 26508392b74bSAdrian Hunter self.view.setSortingEnabled(True) 265196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 265296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 26538392b74bSAdrian Hunter 26548392b74bSAdrian Hunter self.ResizeColumnsToContents() 26558392b74bSAdrian Hunter 26569bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 26579bc4e4bfSAdrian Hunter 26588392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 26598392b74bSAdrian Hunter 26608392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 26618392b74bSAdrian Hunter 26628392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 26638392b74bSAdrian Hunter 26648392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 26658392b74bSAdrian Hunter 26668392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 26678392b74bSAdrian Hunter 26688392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 26698392b74bSAdrian Hunter 26708392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 26718392b74bSAdrian Hunter self.view.setFocus() 26728392b74bSAdrian Hunter self.find_bar.Busy() 26738392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 26748392b74bSAdrian Hunter 26758392b74bSAdrian Hunter def FindDone(self, row): 26768392b74bSAdrian Hunter self.find_bar.Idle() 26778392b74bSAdrian Hunter if row >= 0: 267835fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 26798392b74bSAdrian Hunter else: 26808392b74bSAdrian Hunter self.find_bar.NotFound() 26818392b74bSAdrian Hunter 26828392b74bSAdrian Hunter# Table list 26838392b74bSAdrian Hunter 26848392b74bSAdrian Hunterdef GetTableList(glb): 26858392b74bSAdrian Hunter tables = [] 26868392b74bSAdrian Hunter query = QSqlQuery(glb.db) 26878392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 26888392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 26898392b74bSAdrian Hunter else: 26908392b74bSAdrian 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") 26918392b74bSAdrian Hunter while query.next(): 26928392b74bSAdrian Hunter tables.append(query.value(0)) 26938392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 26948392b74bSAdrian Hunter tables.append("sqlite_master") 26958392b74bSAdrian Hunter else: 26968392b74bSAdrian Hunter tables.append("information_schema.tables") 26978392b74bSAdrian Hunter tables.append("information_schema.views") 26988392b74bSAdrian Hunter tables.append("information_schema.columns") 26998392b74bSAdrian Hunter return tables 27008392b74bSAdrian Hunter 2701cd358012SAdrian Hunter# Top Calls data model 2702cd358012SAdrian Hunter 2703cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2704cd358012SAdrian Hunter 2705cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2706cd358012SAdrian Hunter text = "" 2707cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2708cd358012SAdrian Hunter text = "::text" 2709cd358012SAdrian Hunter limit = "" 2710cd358012SAdrian Hunter if len(report_vars.limit): 2711cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2712cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2713cd358012SAdrian Hunter " CASE" 2714cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2715cd358012SAdrian Hunter " ELSE short_name" 2716cd358012SAdrian Hunter " END AS dso," 2717cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2718cd358012SAdrian Hunter " CASE" 2719cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2720cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2721cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2722cd358012SAdrian Hunter " ELSE ''" + text + 2723cd358012SAdrian Hunter " END AS flags" 2724cd358012SAdrian Hunter " FROM calls" 2725cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2726cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2727cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2728cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2729cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2730cd358012SAdrian Hunter report_vars.where_clause + 2731cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2732cd358012SAdrian Hunter limit 2733cd358012SAdrian Hunter ) 2734cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2735cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2736cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2737cd358012SAdrian Hunter 2738cd358012SAdrian Hunter def columnAlignment(self, column): 2739cd358012SAdrian Hunter return self.alignment[column] 2740cd358012SAdrian Hunter 2741cd358012SAdrian Hunter# Top Calls report creation dialog 2742cd358012SAdrian Hunter 2743cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2744cd358012SAdrian Hunter 2745cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2746cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2747cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2748cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2749cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2750cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2751cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2752cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2753cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2754cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2755cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2756cd358012SAdrian Hunter 2757cd358012SAdrian Hunter# Top Calls window 2758cd358012SAdrian Hunter 2759cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2760cd358012SAdrian Hunter 2761cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2762cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2763cd358012SAdrian Hunter 2764cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2765cd358012SAdrian Hunter self.model = self.data_model 2766cd358012SAdrian Hunter 2767cd358012SAdrian Hunter self.view = QTableView() 2768cd358012SAdrian Hunter self.view.setModel(self.model) 2769cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2770cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 277196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 277296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2773cd358012SAdrian Hunter 27749bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 27759bc4e4bfSAdrian Hunter 2776cd358012SAdrian Hunter self.ResizeColumnsToContents() 2777cd358012SAdrian Hunter 2778cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2779cd358012SAdrian Hunter 2780cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2781cd358012SAdrian Hunter 2782cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2783cd358012SAdrian Hunter 2784cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2785cd358012SAdrian Hunter 2786cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2787cd358012SAdrian Hunter 2788cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2789cd358012SAdrian Hunter 2790cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2791cd358012SAdrian Hunter self.view.setFocus() 2792cd358012SAdrian Hunter self.find_bar.Busy() 2793cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2794cd358012SAdrian Hunter 2795cd358012SAdrian Hunter def FindDone(self, row): 2796cd358012SAdrian Hunter self.find_bar.Idle() 2797cd358012SAdrian Hunter if row >= 0: 2798cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2799cd358012SAdrian Hunter else: 2800cd358012SAdrian Hunter self.find_bar.NotFound() 2801cd358012SAdrian Hunter 28021beb5c7bSAdrian Hunter# Action Definition 28031beb5c7bSAdrian Hunter 28041beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 28051beb5c7bSAdrian Hunter action = QAction(label, parent) 28061beb5c7bSAdrian Hunter if shortcut != None: 28071beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 28081beb5c7bSAdrian Hunter action.setStatusTip(tip) 28091beb5c7bSAdrian Hunter action.triggered.connect(callback) 28101beb5c7bSAdrian Hunter return action 28111beb5c7bSAdrian Hunter 28121beb5c7bSAdrian Hunter# Typical application actions 28131beb5c7bSAdrian Hunter 28141beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 28151beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 28161beb5c7bSAdrian Hunter 28171beb5c7bSAdrian Hunter# Typical MDI actions 28181beb5c7bSAdrian Hunter 28191beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 28201beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 28211beb5c7bSAdrian Hunter 28221beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 28231beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 28241beb5c7bSAdrian Hunter 28251beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 28261beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 28271beb5c7bSAdrian Hunter 28281beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 28291beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 28301beb5c7bSAdrian Hunter 28311beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 28321beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 28331beb5c7bSAdrian Hunter 28341beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 28351beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 28361beb5c7bSAdrian Hunter 28371beb5c7bSAdrian Hunter# Typical MDI window menu 28381beb5c7bSAdrian Hunter 28391beb5c7bSAdrian Hunterclass WindowMenu(): 28401beb5c7bSAdrian Hunter 28411beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 28421beb5c7bSAdrian Hunter self.mdi_area = mdi_area 28431beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 28441beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 28451beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 28461beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 28471beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 28481beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 28491beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 28501beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 28511beb5c7bSAdrian Hunter 28521beb5c7bSAdrian Hunter def Update(self): 28531beb5c7bSAdrian Hunter self.window_menu.clear() 28541beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 28551beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 28561beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 28571beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 28581beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 28591beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 28601beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 28611beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 28621beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 28631beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 28641beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28651beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 28661beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 28671beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28681beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 28691beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 28701beb5c7bSAdrian Hunter if sub_window_count == 0: 28711beb5c7bSAdrian Hunter return 28721beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28731beb5c7bSAdrian Hunter nr = 1 28741beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 28751beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 28761beb5c7bSAdrian Hunter if nr < 10: 28771beb5c7bSAdrian Hunter label = "&" + label 28781beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 28791beb5c7bSAdrian Hunter action.setCheckable(True) 28801beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 2881df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 28821beb5c7bSAdrian Hunter self.window_menu.addAction(action) 28831beb5c7bSAdrian Hunter nr += 1 28841beb5c7bSAdrian Hunter 28851beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 28861beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 28871beb5c7bSAdrian Hunter 288865b24292SAdrian Hunter# Help text 288965b24292SAdrian Hunter 289065b24292SAdrian Hunterglb_help_text = """ 289165b24292SAdrian Hunter<h1>Contents</h1> 289265b24292SAdrian Hunter<style> 289365b24292SAdrian Hunterp.c1 { 289465b24292SAdrian Hunter text-indent: 40px; 289565b24292SAdrian Hunter} 289665b24292SAdrian Hunterp.c2 { 289765b24292SAdrian Hunter text-indent: 80px; 289865b24292SAdrian Hunter} 289965b24292SAdrian Hunter} 290065b24292SAdrian Hunter</style> 290165b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 290265b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2903ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2904ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2905ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2906ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 290765b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 290865b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 290965b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 291065b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 291165b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 291265b24292SAdrian Hunterwidths to suit will display something like: 291365b24292SAdrian Hunter<pre> 291465b24292SAdrian Hunter Call Graph: pt_example 291565b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 291665b24292SAdrian Hunterv- ls 291765b24292SAdrian Hunter v- 2638:2638 291865b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 291965b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 292065b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 292165b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 292265b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 292365b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 292465b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 292565b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 292665b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 292765b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 292865b24292SAdrian Hunter</pre> 292965b24292SAdrian Hunter<h3>Points to note:</h3> 293065b24292SAdrian Hunter<ul> 293165b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 293265b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 293365b24292SAdrian Hunter<li>Subsequent levels are functions</li> 293465b24292SAdrian Hunter<li>'Count' is the number of calls</li> 293565b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 293665b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 293765b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 293865b24292SAdrian Hunter</ul> 293965b24292SAdrian Hunter<h3>Find</h3> 294065b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 294165b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2942ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2943ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2944ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2945ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 294665b24292SAdrian HunterThe All branches report displays all branches in chronological order. 294765b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 294865b24292SAdrian Hunter<h3>Disassembly</h3> 294965b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 295065b24292SAdrian Hunter<ol> 295165b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 295265b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 295365b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 295465b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 295565b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 295665b24292SAdrian Hunter</ol> 295765b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 295865b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 295965b24292SAdrian Hunter<pre> 296065b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 296165b24292SAdrian Huntergit clone https://github.com/intelxed/xed 296265b24292SAdrian Huntercd xed 296365b24292SAdrian Hunter./mfile.py --share 296465b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 296565b24292SAdrian Huntersudo ldconfig 296665b24292SAdrian Hunter</pre> 2967530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3> 2968530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. 2969530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. 2970530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known. 2971530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period 2972530e22fdSAdrian Huntersince the previous displayed 'IPC'. 297365b24292SAdrian Hunter<h3>Find</h3> 297465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 297565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 297665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 2977ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 297865b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 297965b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 2980ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 298165b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 298265b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 298365b24292SAdrian Hunter<pre> 298465b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 298565b24292SAdrian Hunter 100us-200us From 100us to 200us 298665b24292SAdrian Hunter 10ms- From 10ms to the end 298765b24292SAdrian Hunter -100ns The first 100ns 298865b24292SAdrian Hunter -10ms- The last 10ms 298965b24292SAdrian Hunter</pre> 299065b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2991ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 2992cd358012SAdrian 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. 2993cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2994cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 299565b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 299665b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 299765b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 299865b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 299965b24292SAdrian Hunterbut that can be slow for large tables. 300065b24292SAdrian Hunter<p>There are also tables of database meta-information. 300165b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 300265b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 300365b24292SAdrian Hunter<h3>Find</h3> 300465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 300565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 300665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 300735fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 300835fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 300965b24292SAdrian Hunter""" 301065b24292SAdrian Hunter 301165b24292SAdrian Hunter# Help window 301265b24292SAdrian Hunter 301365b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 301465b24292SAdrian Hunter 301565b24292SAdrian Hunter def __init__(self, glb, parent=None): 301665b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 301765b24292SAdrian Hunter 301865b24292SAdrian Hunter self.text = QTextBrowser() 301965b24292SAdrian Hunter self.text.setHtml(glb_help_text) 302065b24292SAdrian Hunter self.text.setReadOnly(True) 302165b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 302265b24292SAdrian Hunter 302365b24292SAdrian Hunter self.setWidget(self.text) 302465b24292SAdrian Hunter 302565b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 302665b24292SAdrian Hunter 302765b24292SAdrian Hunter# Main window that only displays the help text 302865b24292SAdrian Hunter 302965b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 303065b24292SAdrian Hunter 303165b24292SAdrian Hunter def __init__(self, parent=None): 303265b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 303365b24292SAdrian Hunter 303465b24292SAdrian Hunter self.setMinimumSize(200, 100) 303565b24292SAdrian Hunter self.resize(800, 600) 303665b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 303765b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 303865b24292SAdrian Hunter 303965b24292SAdrian Hunter self.text = QTextBrowser() 304065b24292SAdrian Hunter self.text.setHtml(glb_help_text) 304165b24292SAdrian Hunter self.text.setReadOnly(True) 304265b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 304365b24292SAdrian Hunter 304465b24292SAdrian Hunter self.setCentralWidget(self.text) 304565b24292SAdrian Hunter 3046b62d18abSAdrian Hunter# PostqreSQL server version 3047b62d18abSAdrian Hunter 3048b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 3049b62d18abSAdrian Hunter query = QSqlQuery(db) 3050b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 3051b62d18abSAdrian Hunter if query.next(): 3052b62d18abSAdrian Hunter v_str = query.value(0) 3053b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 3054b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 3055b62d18abSAdrian Hunter return v_list[1] 3056b62d18abSAdrian Hunter return v_str 3057b62d18abSAdrian Hunter return "Unknown" 3058b62d18abSAdrian Hunter 3059b62d18abSAdrian Hunter# SQLite version 3060b62d18abSAdrian Hunter 3061b62d18abSAdrian Hunterdef SQLiteVersion(db): 3062b62d18abSAdrian Hunter query = QSqlQuery(db) 3063b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 3064b62d18abSAdrian Hunter if query.next(): 3065b62d18abSAdrian Hunter return query.value(0) 3066b62d18abSAdrian Hunter return "Unknown" 3067b62d18abSAdrian Hunter 3068b62d18abSAdrian Hunter# About dialog 3069b62d18abSAdrian Hunter 3070b62d18abSAdrian Hunterclass AboutDialog(QDialog): 3071b62d18abSAdrian Hunter 3072b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 3073b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 3074b62d18abSAdrian Hunter 3075b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 3076b62d18abSAdrian Hunter self.setMinimumWidth(300) 3077b62d18abSAdrian Hunter 3078b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 3079b62d18abSAdrian Hunter 3080b62d18abSAdrian Hunter text = "<pre>" 3081b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 3082b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 3083b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 3084b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 3085b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 3086b62d18abSAdrian Hunter else: 3087b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 3088b62d18abSAdrian Hunter text += "</pre>" 3089b62d18abSAdrian Hunter 3090b62d18abSAdrian Hunter self.text = QTextBrowser() 3091b62d18abSAdrian Hunter self.text.setHtml(text) 3092b62d18abSAdrian Hunter self.text.setReadOnly(True) 3093b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 3094b62d18abSAdrian Hunter 3095b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 3096b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 3097b62d18abSAdrian Hunter 3098b62d18abSAdrian Hunter self.setLayout(self.vbox); 3099b62d18abSAdrian Hunter 310082f68e28SAdrian Hunter# Font resize 310182f68e28SAdrian Hunter 310282f68e28SAdrian Hunterdef ResizeFont(widget, diff): 310382f68e28SAdrian Hunter font = widget.font() 310482f68e28SAdrian Hunter sz = font.pointSize() 310582f68e28SAdrian Hunter font.setPointSize(sz + diff) 310682f68e28SAdrian Hunter widget.setFont(font) 310782f68e28SAdrian Hunter 310882f68e28SAdrian Hunterdef ShrinkFont(widget): 310982f68e28SAdrian Hunter ResizeFont(widget, -1) 311082f68e28SAdrian Hunter 311182f68e28SAdrian Hunterdef EnlargeFont(widget): 311282f68e28SAdrian Hunter ResizeFont(widget, 1) 311382f68e28SAdrian Hunter 31141beb5c7bSAdrian Hunter# Unique name for sub-windows 31151beb5c7bSAdrian Hunter 31161beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 31171beb5c7bSAdrian Hunter if nr > 1: 31181beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 31191beb5c7bSAdrian Hunter return name 31201beb5c7bSAdrian Hunter 31211beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 31221beb5c7bSAdrian Hunter nr = 1 31231beb5c7bSAdrian Hunter while True: 31241beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 31251beb5c7bSAdrian Hunter ok = True 31261beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 31271beb5c7bSAdrian Hunter if sub_window.name == unique_name: 31281beb5c7bSAdrian Hunter ok = False 31291beb5c7bSAdrian Hunter break 31301beb5c7bSAdrian Hunter if ok: 31311beb5c7bSAdrian Hunter return unique_name 31321beb5c7bSAdrian Hunter nr += 1 31331beb5c7bSAdrian Hunter 31341beb5c7bSAdrian Hunter# Add a sub-window 31351beb5c7bSAdrian Hunter 31361beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 31371beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 31381beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 31391beb5c7bSAdrian Hunter sub_window.resize(800, 600) 31401beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 31411beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 31421beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 31431beb5c7bSAdrian Hunter sub_window.name = unique_name 31441beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 31451beb5c7bSAdrian Hunter sub_window.show() 31461beb5c7bSAdrian Hunter 3147031c2a00SAdrian Hunter# Main window 3148031c2a00SAdrian Hunter 3149031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 3150031c2a00SAdrian Hunter 3151031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 3152031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 3153031c2a00SAdrian Hunter 3154031c2a00SAdrian Hunter self.glb = glb 3155031c2a00SAdrian Hunter 31561beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 3157031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 3158031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 3159031c2a00SAdrian Hunter 31601beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 31611beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 31621beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 3163031c2a00SAdrian Hunter 31641beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 3165031c2a00SAdrian Hunter 31661beb5c7bSAdrian Hunter menu = self.menuBar() 3167031c2a00SAdrian Hunter 31681beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 31691beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 31701beb5c7bSAdrian Hunter 3171ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 317296c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 317396c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 3174ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 31758392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 317682f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 317782f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 3178ebd70c7dSAdrian Hunter 31791beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 3180655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 31811beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 31821beb5c7bSAdrian Hunter 3183ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3184ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3185ae8b887cSAdrian Hunter 318676099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 318776099f98SAdrian Hunter 3188cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3189cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3190cd358012SAdrian Hunter 31918392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 31928392b74bSAdrian Hunter 31931beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 31941beb5c7bSAdrian Hunter 319565b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 319665b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 3197b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 319865b24292SAdrian Hunter 31994b208453SAdrian Hunter def Try(self, fn): 32004b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 32014b208453SAdrian Hunter if win: 32024b208453SAdrian Hunter try: 32034b208453SAdrian Hunter fn(win.view) 32044b208453SAdrian Hunter except: 32054b208453SAdrian Hunter pass 32064b208453SAdrian Hunter 320796c43b9aSAdrian Hunter def CopyToClipboard(self): 320896c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 320996c43b9aSAdrian Hunter 321096c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 321196c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 321296c43b9aSAdrian Hunter 3213ebd70c7dSAdrian Hunter def Find(self): 3214ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3215ebd70c7dSAdrian Hunter if win: 3216ebd70c7dSAdrian Hunter try: 3217ebd70c7dSAdrian Hunter win.find_bar.Activate() 3218ebd70c7dSAdrian Hunter except: 3219ebd70c7dSAdrian Hunter pass 3220ebd70c7dSAdrian Hunter 32218392b74bSAdrian Hunter def FetchMoreRecords(self): 32228392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 32238392b74bSAdrian Hunter if win: 32248392b74bSAdrian Hunter try: 32258392b74bSAdrian Hunter win.fetch_bar.Activate() 32268392b74bSAdrian Hunter except: 32278392b74bSAdrian Hunter pass 32288392b74bSAdrian Hunter 322982f68e28SAdrian Hunter def ShrinkFont(self): 32304b208453SAdrian Hunter self.Try(ShrinkFont) 323182f68e28SAdrian Hunter 323282f68e28SAdrian Hunter def EnlargeFont(self): 32334b208453SAdrian Hunter self.Try(EnlargeFont) 323482f68e28SAdrian Hunter 323576099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 323676099f98SAdrian Hunter branches_events = 0 323776099f98SAdrian Hunter for event in events: 323876099f98SAdrian Hunter event = event.split(":")[0] 323976099f98SAdrian Hunter if event == "branches": 324076099f98SAdrian Hunter branches_events += 1 324176099f98SAdrian Hunter dbid = 0 324276099f98SAdrian Hunter for event in events: 324376099f98SAdrian Hunter dbid += 1 324476099f98SAdrian Hunter event = event.split(":")[0] 324576099f98SAdrian Hunter if event == "branches": 324676099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 3247df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 3248210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3249df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 325076099f98SAdrian Hunter 32518392b74bSAdrian Hunter def TableMenu(self, tables, menu): 32528392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 32538392b74bSAdrian Hunter for table in tables: 3254df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 32558392b74bSAdrian Hunter 32561beb5c7bSAdrian Hunter def NewCallGraph(self): 32571beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3258031c2a00SAdrian Hunter 3259ae8b887cSAdrian Hunter def NewCallTree(self): 3260ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3261ae8b887cSAdrian Hunter 3262cd358012SAdrian Hunter def NewTopCalls(self): 3263cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3264cd358012SAdrian Hunter ret = dialog.exec_() 3265cd358012SAdrian Hunter if ret: 3266cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3267cd358012SAdrian Hunter 326876099f98SAdrian Hunter def NewBranchView(self, event_id): 3269947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 327076099f98SAdrian Hunter 3271210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3272210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3273210cf1f9SAdrian Hunter ret = dialog.exec_() 3274210cf1f9SAdrian Hunter if ret: 3275947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3276210cf1f9SAdrian Hunter 32778392b74bSAdrian Hunter def NewTableView(self, table_name): 32788392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 32798392b74bSAdrian Hunter 328065b24292SAdrian Hunter def Help(self): 328165b24292SAdrian Hunter HelpWindow(self.glb, self) 328265b24292SAdrian Hunter 3283b62d18abSAdrian Hunter def About(self): 3284b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 3285b62d18abSAdrian Hunter dialog.exec_() 3286b62d18abSAdrian Hunter 328776099f98SAdrian Hunter# XED Disassembler 328876099f98SAdrian Hunter 328976099f98SAdrian Hunterclass xed_state_t(Structure): 329076099f98SAdrian Hunter 329176099f98SAdrian Hunter _fields_ = [ 329276099f98SAdrian Hunter ("mode", c_int), 329376099f98SAdrian Hunter ("width", c_int) 329476099f98SAdrian Hunter ] 329576099f98SAdrian Hunter 329676099f98SAdrian Hunterclass XEDInstruction(): 329776099f98SAdrian Hunter 329876099f98SAdrian Hunter def __init__(self, libxed): 329976099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 330076099f98SAdrian Hunter xedd_t = c_byte * 512 330176099f98SAdrian Hunter self.xedd = xedd_t() 330276099f98SAdrian Hunter self.xedp = addressof(self.xedd) 330376099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 330476099f98SAdrian Hunter self.state = xed_state_t() 330576099f98SAdrian Hunter self.statep = addressof(self.state) 330676099f98SAdrian Hunter # Buffer for disassembled instruction text 330776099f98SAdrian Hunter self.buffer = create_string_buffer(256) 330876099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 330976099f98SAdrian Hunter 331076099f98SAdrian Hunterclass LibXED(): 331176099f98SAdrian Hunter 331276099f98SAdrian Hunter def __init__(self): 33135ed4419dSAdrian Hunter try: 331476099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 33155ed4419dSAdrian Hunter except: 33165ed4419dSAdrian Hunter self.libxed = None 33175ed4419dSAdrian Hunter if not self.libxed: 33185ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 331976099f98SAdrian Hunter 332076099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 332176099f98SAdrian Hunter self.xed_tables_init.restype = None 332276099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 332376099f98SAdrian Hunter 332476099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 332576099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 332676099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 332776099f98SAdrian Hunter 332876099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 332976099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 333076099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 333176099f98SAdrian Hunter 333276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 333376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 333476099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 333576099f98SAdrian Hunter 333676099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 333776099f98SAdrian Hunter self.xed_decode.restype = c_int 333876099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 333976099f98SAdrian Hunter 334076099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 334176099f98SAdrian Hunter self.xed_format_context.restype = c_uint 334276099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 334376099f98SAdrian Hunter 334476099f98SAdrian Hunter self.xed_tables_init() 334576099f98SAdrian Hunter 334676099f98SAdrian Hunter def Instruction(self): 334776099f98SAdrian Hunter return XEDInstruction(self) 334876099f98SAdrian Hunter 334976099f98SAdrian Hunter def SetMode(self, inst, mode): 335076099f98SAdrian Hunter if mode: 335176099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 335276099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 335376099f98SAdrian Hunter else: 335476099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 335576099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 335676099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 335776099f98SAdrian Hunter 335876099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 335976099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 336076099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 336176099f98SAdrian Hunter if err: 336276099f98SAdrian Hunter return 0, "" 336376099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 336476099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 336576099f98SAdrian Hunter if not ok: 336676099f98SAdrian Hunter return 0, "" 3367606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3368606bd60aSAdrian Hunter result = inst.buffer.value 3369606bd60aSAdrian Hunter else: 3370606bd60aSAdrian Hunter result = inst.buffer.value.decode() 337176099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 337276099f98SAdrian Hunter # For now, assume the length is in byte 166 3373606bd60aSAdrian Hunter return inst.xedd[166], result 337476099f98SAdrian Hunter 337576099f98SAdrian Hunterdef TryOpen(file_name): 337676099f98SAdrian Hunter try: 337776099f98SAdrian Hunter return open(file_name, "rb") 337876099f98SAdrian Hunter except: 337976099f98SAdrian Hunter return None 338076099f98SAdrian Hunter 338176099f98SAdrian Hunterdef Is64Bit(f): 338276099f98SAdrian Hunter result = sizeof(c_void_p) 338376099f98SAdrian Hunter # ELF support only 338476099f98SAdrian Hunter pos = f.tell() 338576099f98SAdrian Hunter f.seek(0) 338676099f98SAdrian Hunter header = f.read(7) 338776099f98SAdrian Hunter f.seek(pos) 338876099f98SAdrian Hunter magic = header[0:4] 3389606bd60aSAdrian Hunter if sys.version_info[0] == 2: 339076099f98SAdrian Hunter eclass = ord(header[4]) 339176099f98SAdrian Hunter encoding = ord(header[5]) 339276099f98SAdrian Hunter version = ord(header[6]) 3393606bd60aSAdrian Hunter else: 3394606bd60aSAdrian Hunter eclass = header[4] 3395606bd60aSAdrian Hunter encoding = header[5] 3396606bd60aSAdrian Hunter version = header[6] 339776099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 339876099f98SAdrian Hunter result = True if eclass == 2 else False 339976099f98SAdrian Hunter return result 340076099f98SAdrian Hunter 3401031c2a00SAdrian Hunter# Global data 3402031c2a00SAdrian Hunter 3403031c2a00SAdrian Hunterclass Glb(): 3404031c2a00SAdrian Hunter 3405031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3406031c2a00SAdrian Hunter self.dbref = dbref 3407031c2a00SAdrian Hunter self.db = db 3408031c2a00SAdrian Hunter self.dbname = dbname 340976099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 341076099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 341176099f98SAdrian Hunter if self.buildid_dir: 341276099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 341376099f98SAdrian Hunter else: 341476099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3415031c2a00SAdrian Hunter self.app = None 3416031c2a00SAdrian Hunter self.mainwindow = None 34178392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 341876099f98SAdrian Hunter try: 341976099f98SAdrian Hunter self.disassembler = LibXED() 342076099f98SAdrian Hunter self.have_disassembler = True 342176099f98SAdrian Hunter except: 342276099f98SAdrian Hunter self.have_disassembler = False 342376099f98SAdrian Hunter 342476099f98SAdrian Hunter def FileFromBuildId(self, build_id): 342576099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 342676099f98SAdrian Hunter return TryOpen(file_name) 342776099f98SAdrian Hunter 342876099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 342976099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 343076099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 343176099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 343276099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 343376099f98SAdrian Hunter if f: 343476099f98SAdrian Hunter return f 343576099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 343676099f98SAdrian Hunter f = TryOpen(long_name) 343776099f98SAdrian Hunter if f: 343876099f98SAdrian Hunter return f 343976099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 344076099f98SAdrian Hunter if f: 344176099f98SAdrian Hunter return f 344276099f98SAdrian Hunter return None 34438392b74bSAdrian Hunter 34448392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 34458392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 34468392b74bSAdrian Hunter 34478392b74bSAdrian Hunter # Shutdown any background processes or threads 34488392b74bSAdrian Hunter def ShutdownInstances(self): 34498392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 34508392b74bSAdrian Hunter try: 34518392b74bSAdrian Hunter x.Shutdown() 34528392b74bSAdrian Hunter except: 34538392b74bSAdrian Hunter pass 3454031c2a00SAdrian Hunter 3455031c2a00SAdrian Hunter# Database reference 3456031c2a00SAdrian Hunter 3457031c2a00SAdrian Hunterclass DBRef(): 3458031c2a00SAdrian Hunter 3459031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3460031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3461031c2a00SAdrian Hunter self.dbname = dbname 3462031c2a00SAdrian Hunter 3463031c2a00SAdrian Hunter def Open(self, connection_name): 3464031c2a00SAdrian Hunter dbname = self.dbname 3465031c2a00SAdrian Hunter if self.is_sqlite3: 3466031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3467031c2a00SAdrian Hunter else: 3468031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3469031c2a00SAdrian Hunter opts = dbname.split() 3470031c2a00SAdrian Hunter for opt in opts: 3471031c2a00SAdrian Hunter if "=" in opt: 3472031c2a00SAdrian Hunter opt = opt.split("=") 3473031c2a00SAdrian Hunter if opt[0] == "hostname": 3474031c2a00SAdrian Hunter db.setHostName(opt[1]) 3475031c2a00SAdrian Hunter elif opt[0] == "port": 3476031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3477031c2a00SAdrian Hunter elif opt[0] == "username": 3478031c2a00SAdrian Hunter db.setUserName(opt[1]) 3479031c2a00SAdrian Hunter elif opt[0] == "password": 3480031c2a00SAdrian Hunter db.setPassword(opt[1]) 3481031c2a00SAdrian Hunter elif opt[0] == "dbname": 3482031c2a00SAdrian Hunter dbname = opt[1] 3483031c2a00SAdrian Hunter else: 3484031c2a00SAdrian Hunter dbname = opt 3485031c2a00SAdrian Hunter 3486031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3487031c2a00SAdrian Hunter if not db.open(): 3488031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3489031c2a00SAdrian Hunter return db, dbname 3490031c2a00SAdrian Hunter 3491031c2a00SAdrian Hunter# Main 3492031c2a00SAdrian Hunter 3493031c2a00SAdrian Hunterdef Main(): 34941ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 34951ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 34961ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 3497df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 34981ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 34991ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 35001ed7f47fSAdrian Hunter args = ap.parse_args() 3501031c2a00SAdrian Hunter 35021ed7f47fSAdrian Hunter if args.help_only: 350365b24292SAdrian Hunter app = QApplication(sys.argv) 350465b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 350565b24292SAdrian Hunter mainwindow.show() 350665b24292SAdrian Hunter err = app.exec_() 350765b24292SAdrian Hunter sys.exit(err) 3508031c2a00SAdrian Hunter 35091ed7f47fSAdrian Hunter dbname = args.dbname 35101ed7f47fSAdrian Hunter if dbname is None: 35111ed7f47fSAdrian Hunter ap.print_usage() 35121ed7f47fSAdrian Hunter print("Too few arguments") 35131ed7f47fSAdrian Hunter sys.exit(1) 35141ed7f47fSAdrian Hunter 3515031c2a00SAdrian Hunter is_sqlite3 = False 3516031c2a00SAdrian Hunter try: 3517beda0e72STony Jones f = open(dbname, "rb") 3518beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3519031c2a00SAdrian Hunter is_sqlite3 = True 3520031c2a00SAdrian Hunter f.close() 3521031c2a00SAdrian Hunter except: 3522031c2a00SAdrian Hunter pass 3523031c2a00SAdrian Hunter 3524031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3525031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3526031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3527031c2a00SAdrian Hunter app = QApplication(sys.argv) 3528031c2a00SAdrian Hunter glb.app = app 3529031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3530031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3531031c2a00SAdrian Hunter mainwindow.show() 3532031c2a00SAdrian Hunter err = app.exec_() 35338392b74bSAdrian Hunter glb.ShutdownInstances() 3534031c2a00SAdrian Hunter db.close() 3535031c2a00SAdrian Hunter sys.exit(err) 3536031c2a00SAdrian Hunter 3537031c2a00SAdrian Hunterif __name__ == "__main__": 3538031c2a00SAdrian Hunter Main() 3539