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 203*4a0979d4SAdrian Hunter def __init__(self, glb, params, parent=None): 204031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 205a448ba23SAdrian Hunter self.glb = glb 206*4a0979d4SAdrian 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 467*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, parent_item): 468031c2a00SAdrian Hunter self.glb = glb 469*4a0979d4SAdrian 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*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 509*4a0979d4SAdrian 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 513031c2a00SAdrian Hunter self.branch_count = branch_count 514031c2a00SAdrian Hunter self.time = time 515031c2a00SAdrian Hunter 516031c2a00SAdrian Hunter def Select(self): 517031c2a00SAdrian Hunter self.query_done = True; 518031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 519031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 520031c2a00SAdrian Hunter " FROM calls" 521031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 522031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 523031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 524031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 525031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 526031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 527031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 528031c2a00SAdrian Hunter " ORDER BY call_path_id") 529031c2a00SAdrian Hunter while query.next(): 530*4a0979d4SAdrian 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)), int(query.value(5)), self) 531031c2a00SAdrian Hunter self.child_items.append(child_item) 532031c2a00SAdrian Hunter self.child_count += 1 533031c2a00SAdrian Hunter 534031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 535031c2a00SAdrian Hunter 536031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 537031c2a00SAdrian Hunter 538*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 539*4a0979d4SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 540031c2a00SAdrian Hunter dso = dsoname(dso) 541031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 542031c2a00SAdrian Hunter self.dbid = call_path_id 543031c2a00SAdrian Hunter 544031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 547031c2a00SAdrian Hunter 548*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 549*4a0979d4SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, parent_item) 550031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 551031c2a00SAdrian Hunter self.dbid = thread_id 552031c2a00SAdrian Hunter 553031c2a00SAdrian Hunter def Select(self): 554031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 555031c2a00SAdrian Hunter for child_item in self.child_items: 556031c2a00SAdrian Hunter self.time += child_item.time 557031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 558031c2a00SAdrian Hunter for child_item in self.child_items: 559031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 560031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 561031c2a00SAdrian Hunter 562031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 563031c2a00SAdrian Hunter 564031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 565031c2a00SAdrian Hunter 566*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 567*4a0979d4SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) 568031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 569031c2a00SAdrian Hunter self.dbid = comm_id 570031c2a00SAdrian Hunter 571031c2a00SAdrian Hunter def Select(self): 572031c2a00SAdrian Hunter self.query_done = True; 573031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 574031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 575031c2a00SAdrian Hunter " FROM comm_threads" 576031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 577031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 578031c2a00SAdrian Hunter while query.next(): 579*4a0979d4SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 580031c2a00SAdrian Hunter self.child_items.append(child_item) 581031c2a00SAdrian Hunter self.child_count += 1 582031c2a00SAdrian Hunter 583031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 584031c2a00SAdrian Hunter 585031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 586031c2a00SAdrian Hunter 587*4a0979d4SAdrian Hunter def __init__(self, glb, params): 588*4a0979d4SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, params, 0, None) 589031c2a00SAdrian Hunter self.dbid = 0 590031c2a00SAdrian Hunter self.query_done = True; 591031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 592031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 593031c2a00SAdrian Hunter while query.next(): 594031c2a00SAdrian Hunter if not query.value(0): 595031c2a00SAdrian Hunter continue 596*4a0979d4SAdrian Hunter child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 597031c2a00SAdrian Hunter self.child_items.append(child_item) 598031c2a00SAdrian Hunter self.child_count += 1 599031c2a00SAdrian Hunter 600*4a0979d4SAdrian Hunter# Call graph model parameters 601*4a0979d4SAdrian Hunter 602*4a0979d4SAdrian Hunterclass CallGraphModelParams(): 603*4a0979d4SAdrian Hunter 604*4a0979d4SAdrian Hunter def __init__(self, glb, parent=None): 605*4a0979d4SAdrian Hunter self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") 606*4a0979d4SAdrian Hunter 607254c0d82SAdrian Hunter# Context-sensitive call graph data model base 608031c2a00SAdrian Hunter 609254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 610031c2a00SAdrian Hunter 611031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 612*4a0979d4SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent) 613031c2a00SAdrian Hunter 614ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 615ebd70c7dSAdrian Hunter if pattern: 616ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 617ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 618ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 619ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 620ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 621ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 622ebd70c7dSAdrian Hunter # Escape % and _ 623ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 624ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 625ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 626ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 627ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 628ebd70c7dSAdrian Hunter else: 629ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 630ebd70c7dSAdrian Hunter else: 631ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 632254c0d82SAdrian Hunter self.DoFindSelect(query, match) 633ebd70c7dSAdrian Hunter 634ebd70c7dSAdrian Hunter def Found(self, query, found): 635ebd70c7dSAdrian Hunter if found: 636ebd70c7dSAdrian Hunter return self.FindPath(query) 637ebd70c7dSAdrian Hunter return [] 638ebd70c7dSAdrian Hunter 639ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 640ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 641ebd70c7dSAdrian Hunter found = query.first() 642ebd70c7dSAdrian Hunter else: 643ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 644ebd70c7dSAdrian Hunter found = query.next() 645ebd70c7dSAdrian Hunter return self.Found(query, found) 646ebd70c7dSAdrian Hunter 647ebd70c7dSAdrian Hunter def FindNext(self, query): 648ebd70c7dSAdrian Hunter found = query.next() 649ebd70c7dSAdrian Hunter if not found: 650ebd70c7dSAdrian Hunter found = query.first() 651ebd70c7dSAdrian Hunter return self.Found(query, found) 652ebd70c7dSAdrian Hunter 653ebd70c7dSAdrian Hunter def FindPrev(self, query): 654ebd70c7dSAdrian Hunter found = query.previous() 655ebd70c7dSAdrian Hunter if not found: 656ebd70c7dSAdrian Hunter found = query.last() 657ebd70c7dSAdrian Hunter return self.Found(query, found) 658ebd70c7dSAdrian Hunter 659ebd70c7dSAdrian Hunter def FindThread(self, c): 660ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 661ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 662ebd70c7dSAdrian Hunter elif c.direction > 0: 663ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 664ebd70c7dSAdrian Hunter else: 665ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 666ebd70c7dSAdrian Hunter return (True, ids) 667ebd70c7dSAdrian Hunter 668ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 669ebd70c7dSAdrian Hunter class Context(): 670ebd70c7dSAdrian Hunter def __init__(self, *x): 671ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 672ebd70c7dSAdrian Hunter def Update(self, *x): 673ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 674ebd70c7dSAdrian Hunter if len(context): 675ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 676ebd70c7dSAdrian Hunter else: 677ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 678ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 679ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 680ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 681ebd70c7dSAdrian Hunter thread.start() 682ebd70c7dSAdrian Hunter 683ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 684ebd70c7dSAdrian Hunter callback(ids) 685ebd70c7dSAdrian Hunter 686254c0d82SAdrian Hunter# Context-sensitive call graph data model 687254c0d82SAdrian Hunter 688254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 689254c0d82SAdrian Hunter 690254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 691254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 692254c0d82SAdrian Hunter 693254c0d82SAdrian Hunter def GetRoot(self): 694*4a0979d4SAdrian Hunter return CallGraphRootItem(self.glb, self.params) 695254c0d82SAdrian Hunter 696254c0d82SAdrian Hunter def columnCount(self, parent=None): 697254c0d82SAdrian Hunter return 7 698254c0d82SAdrian Hunter 699254c0d82SAdrian Hunter def columnHeader(self, column): 700254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 701254c0d82SAdrian Hunter return headers[column] 702254c0d82SAdrian Hunter 703254c0d82SAdrian Hunter def columnAlignment(self, column): 704254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 705254c0d82SAdrian Hunter return alignment[column] 706254c0d82SAdrian Hunter 707254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 708254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 709254c0d82SAdrian Hunter " FROM calls" 710254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 711254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 712254c0d82SAdrian Hunter " WHERE symbols.name" + match + 713254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 714254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 715254c0d82SAdrian Hunter 716254c0d82SAdrian Hunter def FindPath(self, query): 717254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 718254c0d82SAdrian Hunter # to open the tree at the right place. 719254c0d82SAdrian Hunter ids = [] 720254c0d82SAdrian Hunter parent_id = query.value(0) 721254c0d82SAdrian Hunter while parent_id: 722254c0d82SAdrian Hunter ids.insert(0, parent_id) 723254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 724254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 725254c0d82SAdrian Hunter " FROM call_paths" 726254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 727254c0d82SAdrian Hunter if not q2.next(): 728254c0d82SAdrian Hunter break 729254c0d82SAdrian Hunter parent_id = q2.value(0) 730254c0d82SAdrian Hunter # The call path root is not used 731254c0d82SAdrian Hunter if ids[0] == 1: 732254c0d82SAdrian Hunter del ids[0] 733254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 734254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 735254c0d82SAdrian Hunter return ids 736254c0d82SAdrian Hunter 737ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 738ae8b887cSAdrian Hunter 739ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 740ae8b887cSAdrian Hunter 741*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): 742*4a0979d4SAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item) 743ae8b887cSAdrian Hunter self.comm_id = comm_id 744ae8b887cSAdrian Hunter self.thread_id = thread_id 745ae8b887cSAdrian Hunter self.calls_id = calls_id 746ae8b887cSAdrian Hunter self.branch_count = branch_count 747ae8b887cSAdrian Hunter self.time = time 748ae8b887cSAdrian Hunter 749ae8b887cSAdrian Hunter def Select(self): 750ae8b887cSAdrian Hunter self.query_done = True; 751ae8b887cSAdrian Hunter if self.calls_id == 0: 752ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 753ae8b887cSAdrian Hunter else: 754ae8b887cSAdrian Hunter comm_thread = "" 755ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 756ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" 757ae8b887cSAdrian Hunter " FROM calls" 758ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 759ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 760ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 761ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 762ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 763ae8b887cSAdrian Hunter while query.next(): 764*4a0979d4SAdrian 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) 765ae8b887cSAdrian Hunter self.child_items.append(child_item) 766ae8b887cSAdrian Hunter self.child_count += 1 767ae8b887cSAdrian Hunter 768ae8b887cSAdrian Hunter# Call tree data model level three item 769ae8b887cSAdrian Hunter 770ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 771ae8b887cSAdrian Hunter 772*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): 773*4a0979d4SAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) 774ae8b887cSAdrian Hunter dso = dsoname(dso) 775ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 776ae8b887cSAdrian Hunter self.dbid = calls_id 777ae8b887cSAdrian Hunter 778ae8b887cSAdrian Hunter# Call tree data model level two item 779ae8b887cSAdrian Hunter 780ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 781ae8b887cSAdrian Hunter 782*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): 783*4a0979d4SAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, parent_item) 784ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 785ae8b887cSAdrian Hunter self.dbid = thread_id 786ae8b887cSAdrian Hunter 787ae8b887cSAdrian Hunter def Select(self): 788ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 789ae8b887cSAdrian Hunter for child_item in self.child_items: 790ae8b887cSAdrian Hunter self.time += child_item.time 791ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 792ae8b887cSAdrian Hunter for child_item in self.child_items: 793ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 794ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 795ae8b887cSAdrian Hunter 796ae8b887cSAdrian Hunter# Call tree data model level one item 797ae8b887cSAdrian Hunter 798ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 799ae8b887cSAdrian Hunter 800*4a0979d4SAdrian Hunter def __init__(self, glb, params, row, comm_id, comm, parent_item): 801*4a0979d4SAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) 802ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 803ae8b887cSAdrian Hunter self.dbid = comm_id 804ae8b887cSAdrian Hunter 805ae8b887cSAdrian Hunter def Select(self): 806ae8b887cSAdrian Hunter self.query_done = True; 807ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 808ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 809ae8b887cSAdrian Hunter " FROM comm_threads" 810ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 811ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 812ae8b887cSAdrian Hunter while query.next(): 813*4a0979d4SAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 814ae8b887cSAdrian Hunter self.child_items.append(child_item) 815ae8b887cSAdrian Hunter self.child_count += 1 816ae8b887cSAdrian Hunter 817ae8b887cSAdrian Hunter# Call tree data model root item 818ae8b887cSAdrian Hunter 819ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 820ae8b887cSAdrian Hunter 821*4a0979d4SAdrian Hunter def __init__(self, glb, params): 822*4a0979d4SAdrian Hunter super(CallTreeRootItem, self).__init__(glb, params, 0, None) 823ae8b887cSAdrian Hunter self.dbid = 0 824ae8b887cSAdrian Hunter self.query_done = True; 825ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 826ae8b887cSAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 827ae8b887cSAdrian Hunter while query.next(): 828ae8b887cSAdrian Hunter if not query.value(0): 829ae8b887cSAdrian Hunter continue 830*4a0979d4SAdrian Hunter child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self) 831ae8b887cSAdrian Hunter self.child_items.append(child_item) 832ae8b887cSAdrian Hunter self.child_count += 1 833ae8b887cSAdrian Hunter 834ae8b887cSAdrian Hunter# Call Tree data model 835ae8b887cSAdrian Hunter 836ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 837ae8b887cSAdrian Hunter 838ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 839ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 840ae8b887cSAdrian Hunter 841ae8b887cSAdrian Hunter def GetRoot(self): 842*4a0979d4SAdrian Hunter return CallTreeRootItem(self.glb, self.params) 843ae8b887cSAdrian Hunter 844ae8b887cSAdrian Hunter def columnCount(self, parent=None): 845ae8b887cSAdrian Hunter return 7 846ae8b887cSAdrian Hunter 847ae8b887cSAdrian Hunter def columnHeader(self, column): 848ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 849ae8b887cSAdrian Hunter return headers[column] 850ae8b887cSAdrian Hunter 851ae8b887cSAdrian Hunter def columnAlignment(self, column): 852ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 853ae8b887cSAdrian Hunter return alignment[column] 854ae8b887cSAdrian Hunter 855ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 856ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 857ae8b887cSAdrian Hunter " FROM calls" 858ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 859ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 860ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 861ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 862ae8b887cSAdrian Hunter 863ae8b887cSAdrian Hunter def FindPath(self, query): 864ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 865ae8b887cSAdrian Hunter # to open the tree at the right place. 866ae8b887cSAdrian Hunter ids = [] 867ae8b887cSAdrian Hunter parent_id = query.value(0) 868ae8b887cSAdrian Hunter while parent_id: 869ae8b887cSAdrian Hunter ids.insert(0, parent_id) 870ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 871ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 872ae8b887cSAdrian Hunter " FROM calls" 873ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 874ae8b887cSAdrian Hunter if not q2.next(): 875ae8b887cSAdrian Hunter break 876ae8b887cSAdrian Hunter parent_id = q2.value(0) 877ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 878ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 879ae8b887cSAdrian Hunter return ids 880ae8b887cSAdrian Hunter 881ebd70c7dSAdrian Hunter# Vertical widget layout 882ebd70c7dSAdrian Hunter 883ebd70c7dSAdrian Hunterclass VBox(): 884ebd70c7dSAdrian Hunter 885ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 886ebd70c7dSAdrian Hunter self.vbox = QWidget() 887ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 888ebd70c7dSAdrian Hunter 889ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 890ebd70c7dSAdrian Hunter 891ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 892ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 893ebd70c7dSAdrian Hunter if w3: 894ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 895ebd70c7dSAdrian Hunter 896ebd70c7dSAdrian Hunter def Widget(self): 897ebd70c7dSAdrian Hunter return self.vbox 898ebd70c7dSAdrian Hunter 899a731cc4cSAdrian Hunter# Tree window base 9001beb5c7bSAdrian Hunter 901a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 9021beb5c7bSAdrian Hunter 903a731cc4cSAdrian Hunter def __init__(self, parent=None): 904a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 9051beb5c7bSAdrian Hunter 906a731cc4cSAdrian Hunter self.model = None 907a731cc4cSAdrian Hunter self.find_bar = None 9081beb5c7bSAdrian Hunter 909be6e7471SAdrian Hunter self.view = QTreeView() 91096c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 91196c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 912be6e7471SAdrian Hunter 9139bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 9149bc4e4bfSAdrian Hunter 915ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 916ebd70c7dSAdrian Hunter if not len(ids): 917ebd70c7dSAdrian Hunter return False 918ebd70c7dSAdrian Hunter parent = QModelIndex() 919ebd70c7dSAdrian Hunter for dbid in ids: 920ebd70c7dSAdrian Hunter found = False 921ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 922ebd70c7dSAdrian Hunter for row in xrange(n): 923ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 924ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 925ebd70c7dSAdrian Hunter found = True 926ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 927ebd70c7dSAdrian Hunter parent = child 928ebd70c7dSAdrian Hunter break 929ebd70c7dSAdrian Hunter if not found: 930ebd70c7dSAdrian Hunter break 931ebd70c7dSAdrian Hunter return found 932ebd70c7dSAdrian Hunter 933ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 934ebd70c7dSAdrian Hunter self.view.setFocus() 935ebd70c7dSAdrian Hunter self.find_bar.Busy() 936ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 937ebd70c7dSAdrian Hunter 938ebd70c7dSAdrian Hunter def FindDone(self, ids): 939ebd70c7dSAdrian Hunter found = True 940ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 941ebd70c7dSAdrian Hunter found = False 942ebd70c7dSAdrian Hunter self.find_bar.Idle() 943ebd70c7dSAdrian Hunter if not found: 944ebd70c7dSAdrian Hunter self.find_bar.NotFound() 945ebd70c7dSAdrian Hunter 946a731cc4cSAdrian Hunter 947a731cc4cSAdrian Hunter# Context-sensitive call graph window 948a731cc4cSAdrian Hunter 949a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 950a731cc4cSAdrian Hunter 951a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 952a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 953a731cc4cSAdrian Hunter 954a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 955a731cc4cSAdrian Hunter 956a731cc4cSAdrian Hunter self.view.setModel(self.model) 957a731cc4cSAdrian Hunter 958a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 959a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 960a731cc4cSAdrian Hunter 961a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 962a731cc4cSAdrian Hunter 963a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 964a731cc4cSAdrian Hunter 965a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 966a731cc4cSAdrian Hunter 967a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 968a731cc4cSAdrian Hunter 969ae8b887cSAdrian Hunter# Call tree window 970ae8b887cSAdrian Hunter 971ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 972ae8b887cSAdrian Hunter 973ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 974ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 975ae8b887cSAdrian Hunter 976ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 977ae8b887cSAdrian Hunter 978ae8b887cSAdrian Hunter self.view.setModel(self.model) 979ae8b887cSAdrian Hunter 980ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 981ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 982ae8b887cSAdrian Hunter 983ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 984ae8b887cSAdrian Hunter 985ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 986ae8b887cSAdrian Hunter 987ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 988ae8b887cSAdrian Hunter 989ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 990ae8b887cSAdrian Hunter 9918392b74bSAdrian Hunter# Child data item finder 9928392b74bSAdrian Hunter 9938392b74bSAdrian Hunterclass ChildDataItemFinder(): 9948392b74bSAdrian Hunter 9958392b74bSAdrian Hunter def __init__(self, root): 9968392b74bSAdrian Hunter self.root = root 9978392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 9988392b74bSAdrian Hunter self.rows = [] 9998392b74bSAdrian Hunter self.pos = 0 10008392b74bSAdrian Hunter 10018392b74bSAdrian Hunter def FindSelect(self): 10028392b74bSAdrian Hunter self.rows = [] 10038392b74bSAdrian Hunter if self.pattern: 10048392b74bSAdrian Hunter pattern = re.compile(self.value) 10058392b74bSAdrian Hunter for child in self.root.child_items: 10068392b74bSAdrian Hunter for column_data in child.data: 10078392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 10088392b74bSAdrian Hunter self.rows.append(child.row) 10098392b74bSAdrian Hunter break 10108392b74bSAdrian Hunter else: 10118392b74bSAdrian Hunter for child in self.root.child_items: 10128392b74bSAdrian Hunter for column_data in child.data: 10138392b74bSAdrian Hunter if self.value in str(column_data): 10148392b74bSAdrian Hunter self.rows.append(child.row) 10158392b74bSAdrian Hunter break 10168392b74bSAdrian Hunter 10178392b74bSAdrian Hunter def FindValue(self): 10188392b74bSAdrian Hunter self.pos = 0 10198392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 10208392b74bSAdrian Hunter self.FindSelect() 10218392b74bSAdrian Hunter if not len(self.rows): 10228392b74bSAdrian Hunter return -1 10238392b74bSAdrian Hunter return self.rows[self.pos] 10248392b74bSAdrian Hunter 10258392b74bSAdrian Hunter def FindThread(self): 10268392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 10278392b74bSAdrian Hunter row = self.FindValue() 10288392b74bSAdrian Hunter elif len(self.rows): 10298392b74bSAdrian Hunter if self.direction > 0: 10308392b74bSAdrian Hunter self.pos += 1 10318392b74bSAdrian Hunter if self.pos >= len(self.rows): 10328392b74bSAdrian Hunter self.pos = 0 10338392b74bSAdrian Hunter else: 10348392b74bSAdrian Hunter self.pos -= 1 10358392b74bSAdrian Hunter if self.pos < 0: 10368392b74bSAdrian Hunter self.pos = len(self.rows) - 1 10378392b74bSAdrian Hunter row = self.rows[self.pos] 10388392b74bSAdrian Hunter else: 10398392b74bSAdrian Hunter row = -1 10408392b74bSAdrian Hunter return (True, row) 10418392b74bSAdrian Hunter 10428392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 10438392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 10448392b74bSAdrian Hunter # Use a thread so the UI is not blocked 10458392b74bSAdrian Hunter thread = Thread(self.FindThread) 10468392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 10478392b74bSAdrian Hunter thread.start() 10488392b74bSAdrian Hunter 10498392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 10508392b74bSAdrian Hunter callback(row) 10518392b74bSAdrian Hunter 10528392b74bSAdrian Hunter# Number of database records to fetch in one go 10538392b74bSAdrian Hunter 10548392b74bSAdrian Hunterglb_chunk_sz = 10000 10558392b74bSAdrian Hunter 10568392b74bSAdrian Hunter# Background process for SQL data fetcher 10578392b74bSAdrian Hunter 10588392b74bSAdrian Hunterclass SQLFetcherProcess(): 10598392b74bSAdrian Hunter 10608392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 10618392b74bSAdrian Hunter # Need a unique connection name 10628392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 10638392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 10648392b74bSAdrian Hunter self.sql = sql 10658392b74bSAdrian Hunter self.buffer = buffer 10668392b74bSAdrian Hunter self.head = head 10678392b74bSAdrian Hunter self.tail = tail 10688392b74bSAdrian Hunter self.fetch_count = fetch_count 10698392b74bSAdrian Hunter self.fetching_done = fetching_done 10708392b74bSAdrian Hunter self.process_target = process_target 10718392b74bSAdrian Hunter self.wait_event = wait_event 10728392b74bSAdrian Hunter self.fetched_event = fetched_event 10738392b74bSAdrian Hunter self.prep = prep 10748392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 10758392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 10768392b74bSAdrian Hunter self.last_id = -1 10778392b74bSAdrian Hunter self.fetched = 0 10788392b74bSAdrian Hunter self.more = True 10798392b74bSAdrian Hunter self.local_head = self.head.value 10808392b74bSAdrian Hunter self.local_tail = self.tail.value 10818392b74bSAdrian Hunter 10828392b74bSAdrian Hunter def Select(self): 10838392b74bSAdrian Hunter if self.query_limit: 10848392b74bSAdrian Hunter if self.query_limit == 1: 10858392b74bSAdrian Hunter return 10868392b74bSAdrian Hunter self.query_limit -= 1 10878392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 10888392b74bSAdrian Hunter QueryExec(self.query, stmt) 10898392b74bSAdrian Hunter 10908392b74bSAdrian Hunter def Next(self): 10918392b74bSAdrian Hunter if not self.query.next(): 10928392b74bSAdrian Hunter self.Select() 10938392b74bSAdrian Hunter if not self.query.next(): 10948392b74bSAdrian Hunter return None 10958392b74bSAdrian Hunter self.last_id = self.query.value(0) 10968392b74bSAdrian Hunter return self.prep(self.query) 10978392b74bSAdrian Hunter 10988392b74bSAdrian Hunter def WaitForTarget(self): 10998392b74bSAdrian Hunter while True: 11008392b74bSAdrian Hunter self.wait_event.clear() 11018392b74bSAdrian Hunter target = self.process_target.value 11028392b74bSAdrian Hunter if target > self.fetched or target < 0: 11038392b74bSAdrian Hunter break 11048392b74bSAdrian Hunter self.wait_event.wait() 11058392b74bSAdrian Hunter return target 11068392b74bSAdrian Hunter 11078392b74bSAdrian Hunter def HasSpace(self, sz): 11088392b74bSAdrian Hunter if self.local_tail <= self.local_head: 11098392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 11108392b74bSAdrian Hunter if space > sz: 11118392b74bSAdrian Hunter return True 11128392b74bSAdrian Hunter if space >= glb_nsz: 11138392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1114beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 11158392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 11168392b74bSAdrian Hunter self.local_head = 0 11178392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 11188392b74bSAdrian Hunter return True 11198392b74bSAdrian Hunter return False 11208392b74bSAdrian Hunter 11218392b74bSAdrian Hunter def WaitForSpace(self, sz): 11228392b74bSAdrian Hunter if self.HasSpace(sz): 11238392b74bSAdrian Hunter return 11248392b74bSAdrian Hunter while True: 11258392b74bSAdrian Hunter self.wait_event.clear() 11268392b74bSAdrian Hunter self.local_tail = self.tail.value 11278392b74bSAdrian Hunter if self.HasSpace(sz): 11288392b74bSAdrian Hunter return 11298392b74bSAdrian Hunter self.wait_event.wait() 11308392b74bSAdrian Hunter 11318392b74bSAdrian Hunter def AddToBuffer(self, obj): 1132beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 11338392b74bSAdrian Hunter n = len(d) 1134beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 11358392b74bSAdrian Hunter sz = n + glb_nsz 11368392b74bSAdrian Hunter self.WaitForSpace(sz) 11378392b74bSAdrian Hunter pos = self.local_head 11388392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 11398392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 11408392b74bSAdrian Hunter self.local_head += sz 11418392b74bSAdrian Hunter 11428392b74bSAdrian Hunter def FetchBatch(self, batch_size): 11438392b74bSAdrian Hunter fetched = 0 11448392b74bSAdrian Hunter while batch_size > fetched: 11458392b74bSAdrian Hunter obj = self.Next() 11468392b74bSAdrian Hunter if obj is None: 11478392b74bSAdrian Hunter self.more = False 11488392b74bSAdrian Hunter break 11498392b74bSAdrian Hunter self.AddToBuffer(obj) 11508392b74bSAdrian Hunter fetched += 1 11518392b74bSAdrian Hunter if fetched: 11528392b74bSAdrian Hunter self.fetched += fetched 11538392b74bSAdrian Hunter with self.fetch_count.get_lock(): 11548392b74bSAdrian Hunter self.fetch_count.value += fetched 11558392b74bSAdrian Hunter self.head.value = self.local_head 11568392b74bSAdrian Hunter self.fetched_event.set() 11578392b74bSAdrian Hunter 11588392b74bSAdrian Hunter def Run(self): 11598392b74bSAdrian Hunter while self.more: 11608392b74bSAdrian Hunter target = self.WaitForTarget() 11618392b74bSAdrian Hunter if target < 0: 11628392b74bSAdrian Hunter break 11638392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 11648392b74bSAdrian Hunter self.FetchBatch(batch_size) 11658392b74bSAdrian Hunter self.fetching_done.value = True 11668392b74bSAdrian Hunter self.fetched_event.set() 11678392b74bSAdrian Hunter 11688392b74bSAdrian Hunterdef SQLFetcherFn(*x): 11698392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 11708392b74bSAdrian Hunter process.Run() 11718392b74bSAdrian Hunter 11728392b74bSAdrian Hunter# SQL data fetcher 11738392b74bSAdrian Hunter 11748392b74bSAdrian Hunterclass SQLFetcher(QObject): 11758392b74bSAdrian Hunter 11768392b74bSAdrian Hunter done = Signal(object) 11778392b74bSAdrian Hunter 11788392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 11798392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 11808392b74bSAdrian Hunter self.process_data = process_data 11818392b74bSAdrian Hunter self.more = True 11828392b74bSAdrian Hunter self.target = 0 11838392b74bSAdrian Hunter self.last_target = 0 11848392b74bSAdrian Hunter self.fetched = 0 11858392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 11868392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 11878392b74bSAdrian Hunter self.head = Value(c_longlong) 11888392b74bSAdrian Hunter self.tail = Value(c_longlong) 11898392b74bSAdrian Hunter self.local_tail = 0 11908392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 11918392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 11928392b74bSAdrian Hunter self.last_count = 0 11938392b74bSAdrian Hunter self.process_target = Value(c_longlong) 11948392b74bSAdrian Hunter self.wait_event = Event() 11958392b74bSAdrian Hunter self.fetched_event = Event() 11968392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 11978392b74bSAdrian 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)) 11988392b74bSAdrian Hunter self.process.start() 11998392b74bSAdrian Hunter self.thread = Thread(self.Thread) 12008392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 12018392b74bSAdrian Hunter self.thread.start() 12028392b74bSAdrian Hunter 12038392b74bSAdrian Hunter def Shutdown(self): 12048392b74bSAdrian Hunter # Tell the thread and process to exit 12058392b74bSAdrian Hunter self.process_target.value = -1 12068392b74bSAdrian Hunter self.wait_event.set() 12078392b74bSAdrian Hunter self.more = False 12088392b74bSAdrian Hunter self.fetching_done.value = True 12098392b74bSAdrian Hunter self.fetched_event.set() 12108392b74bSAdrian Hunter 12118392b74bSAdrian Hunter def Thread(self): 12128392b74bSAdrian Hunter if not self.more: 12138392b74bSAdrian Hunter return True, 0 12148392b74bSAdrian Hunter while True: 12158392b74bSAdrian Hunter self.fetched_event.clear() 12168392b74bSAdrian Hunter fetch_count = self.fetch_count.value 12178392b74bSAdrian Hunter if fetch_count != self.last_count: 12188392b74bSAdrian Hunter break 12198392b74bSAdrian Hunter if self.fetching_done.value: 12208392b74bSAdrian Hunter self.more = False 12218392b74bSAdrian Hunter return True, 0 12228392b74bSAdrian Hunter self.fetched_event.wait() 12238392b74bSAdrian Hunter count = fetch_count - self.last_count 12248392b74bSAdrian Hunter self.last_count = fetch_count 12258392b74bSAdrian Hunter self.fetched += count 12268392b74bSAdrian Hunter return False, count 12278392b74bSAdrian Hunter 12288392b74bSAdrian Hunter def Fetch(self, nr): 12298392b74bSAdrian Hunter if not self.more: 12308392b74bSAdrian Hunter # -1 inidcates there are no more 12318392b74bSAdrian Hunter return -1 12328392b74bSAdrian Hunter result = self.fetched 12338392b74bSAdrian Hunter extra = result + nr - self.target 12348392b74bSAdrian Hunter if extra > 0: 12358392b74bSAdrian Hunter self.target += extra 12368392b74bSAdrian Hunter # process_target < 0 indicates shutting down 12378392b74bSAdrian Hunter if self.process_target.value >= 0: 12388392b74bSAdrian Hunter self.process_target.value = self.target 12398392b74bSAdrian Hunter self.wait_event.set() 12408392b74bSAdrian Hunter return result 12418392b74bSAdrian Hunter 12428392b74bSAdrian Hunter def RemoveFromBuffer(self): 12438392b74bSAdrian Hunter pos = self.local_tail 12448392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 12458392b74bSAdrian Hunter pos = 0 1246beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 12478392b74bSAdrian Hunter if n == 0: 12488392b74bSAdrian Hunter pos = 0 1249beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 12508392b74bSAdrian Hunter pos += glb_nsz 1251beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 12528392b74bSAdrian Hunter self.local_tail = pos + n 12538392b74bSAdrian Hunter return obj 12548392b74bSAdrian Hunter 12558392b74bSAdrian Hunter def ProcessData(self, count): 12568392b74bSAdrian Hunter for i in xrange(count): 12578392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 12588392b74bSAdrian Hunter self.process_data(obj) 12598392b74bSAdrian Hunter self.tail.value = self.local_tail 12608392b74bSAdrian Hunter self.wait_event.set() 12618392b74bSAdrian Hunter self.done.emit(count) 12628392b74bSAdrian Hunter 12638392b74bSAdrian Hunter# Fetch more records bar 12648392b74bSAdrian Hunter 12658392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 12668392b74bSAdrian Hunter 12678392b74bSAdrian Hunter def __init__(self, model, parent): 12688392b74bSAdrian Hunter self.model = model 12698392b74bSAdrian Hunter 12708392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 12718392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12728392b74bSAdrian Hunter 12738392b74bSAdrian Hunter self.fetch_count = QSpinBox() 12748392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 12758392b74bSAdrian Hunter self.fetch_count.setValue(10) 12768392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12778392b74bSAdrian Hunter 12788392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 12798392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12808392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 12818392b74bSAdrian Hunter 12828392b74bSAdrian Hunter self.progress = QProgressBar() 12838392b74bSAdrian Hunter self.progress.setRange(0, 100) 12848392b74bSAdrian Hunter self.progress.hide() 12858392b74bSAdrian Hunter 12868392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 12878392b74bSAdrian Hunter self.done_label.hide() 12888392b74bSAdrian Hunter 12898392b74bSAdrian Hunter self.spacer = QLabel("") 12908392b74bSAdrian Hunter 12918392b74bSAdrian Hunter self.close_button = QToolButton() 12928392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 12938392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 12948392b74bSAdrian Hunter 12958392b74bSAdrian Hunter self.hbox = QHBoxLayout() 12968392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 12978392b74bSAdrian Hunter 12988392b74bSAdrian Hunter self.hbox.addWidget(self.label) 12998392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 13008392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 13018392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 13028392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 13038392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 13048392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 13058392b74bSAdrian Hunter 13068392b74bSAdrian Hunter self.bar = QWidget() 13078392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 13088392b74bSAdrian Hunter self.bar.show() 13098392b74bSAdrian Hunter 13108392b74bSAdrian Hunter self.in_progress = False 13118392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 13128392b74bSAdrian Hunter 13138392b74bSAdrian Hunter self.done = False 13148392b74bSAdrian Hunter 13158392b74bSAdrian Hunter if not model.HasMoreRecords(): 13168392b74bSAdrian Hunter self.Done() 13178392b74bSAdrian Hunter 13188392b74bSAdrian Hunter def Widget(self): 13198392b74bSAdrian Hunter return self.bar 13208392b74bSAdrian Hunter 13218392b74bSAdrian Hunter def Activate(self): 13228392b74bSAdrian Hunter self.bar.show() 13238392b74bSAdrian Hunter self.fetch.setFocus() 13248392b74bSAdrian Hunter 13258392b74bSAdrian Hunter def Deactivate(self): 13268392b74bSAdrian Hunter self.bar.hide() 13278392b74bSAdrian Hunter 13288392b74bSAdrian Hunter def Enable(self, enable): 13298392b74bSAdrian Hunter self.fetch.setEnabled(enable) 13308392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 13318392b74bSAdrian Hunter 13328392b74bSAdrian Hunter def Busy(self): 13338392b74bSAdrian Hunter self.Enable(False) 13348392b74bSAdrian Hunter self.fetch.hide() 13358392b74bSAdrian Hunter self.spacer.hide() 13368392b74bSAdrian Hunter self.progress.show() 13378392b74bSAdrian Hunter 13388392b74bSAdrian Hunter def Idle(self): 13398392b74bSAdrian Hunter self.in_progress = False 13408392b74bSAdrian Hunter self.Enable(True) 13418392b74bSAdrian Hunter self.progress.hide() 13428392b74bSAdrian Hunter self.fetch.show() 13438392b74bSAdrian Hunter self.spacer.show() 13448392b74bSAdrian Hunter 13458392b74bSAdrian Hunter def Target(self): 13468392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 13478392b74bSAdrian Hunter 13488392b74bSAdrian Hunter def Done(self): 13498392b74bSAdrian Hunter self.done = True 13508392b74bSAdrian Hunter self.Idle() 13518392b74bSAdrian Hunter self.label.hide() 13528392b74bSAdrian Hunter self.fetch_count.hide() 13538392b74bSAdrian Hunter self.fetch.hide() 13548392b74bSAdrian Hunter self.spacer.hide() 13558392b74bSAdrian Hunter self.done_label.show() 13568392b74bSAdrian Hunter 13578392b74bSAdrian Hunter def Progress(self, count): 13588392b74bSAdrian Hunter if self.in_progress: 13598392b74bSAdrian Hunter if count: 13608392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 13618392b74bSAdrian Hunter if percent >= 100: 13628392b74bSAdrian Hunter self.Idle() 13638392b74bSAdrian Hunter else: 13648392b74bSAdrian Hunter self.progress.setValue(percent) 13658392b74bSAdrian Hunter if not count: 13668392b74bSAdrian Hunter # Count value of zero means no more records 13678392b74bSAdrian Hunter self.Done() 13688392b74bSAdrian Hunter 13698392b74bSAdrian Hunter def FetchMoreRecords(self): 13708392b74bSAdrian Hunter if self.done: 13718392b74bSAdrian Hunter return 13728392b74bSAdrian Hunter self.progress.setValue(0) 13738392b74bSAdrian Hunter self.Busy() 13748392b74bSAdrian Hunter self.in_progress = True 13758392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 13768392b74bSAdrian Hunter 137776099f98SAdrian Hunter# Brance data model level two item 137876099f98SAdrian Hunter 137976099f98SAdrian Hunterclass BranchLevelTwoItem(): 138076099f98SAdrian Hunter 1381530e22fdSAdrian Hunter def __init__(self, row, col, text, parent_item): 138276099f98SAdrian Hunter self.row = row 138376099f98SAdrian Hunter self.parent_item = parent_item 1384530e22fdSAdrian Hunter self.data = [""] * (col + 1) 1385530e22fdSAdrian Hunter self.data[col] = text 138676099f98SAdrian Hunter self.level = 2 138776099f98SAdrian Hunter 138876099f98SAdrian Hunter def getParentItem(self): 138976099f98SAdrian Hunter return self.parent_item 139076099f98SAdrian Hunter 139176099f98SAdrian Hunter def getRow(self): 139276099f98SAdrian Hunter return self.row 139376099f98SAdrian Hunter 139476099f98SAdrian Hunter def childCount(self): 139576099f98SAdrian Hunter return 0 139676099f98SAdrian Hunter 139776099f98SAdrian Hunter def hasChildren(self): 139876099f98SAdrian Hunter return False 139976099f98SAdrian Hunter 140076099f98SAdrian Hunter def getData(self, column): 140176099f98SAdrian Hunter return self.data[column] 140276099f98SAdrian Hunter 140376099f98SAdrian Hunter# Brance data model level one item 140476099f98SAdrian Hunter 140576099f98SAdrian Hunterclass BranchLevelOneItem(): 140676099f98SAdrian Hunter 140776099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 140876099f98SAdrian Hunter self.glb = glb 140976099f98SAdrian Hunter self.row = row 141076099f98SAdrian Hunter self.parent_item = parent_item 141176099f98SAdrian Hunter self.child_count = 0 141276099f98SAdrian Hunter self.child_items = [] 141376099f98SAdrian Hunter self.data = data[1:] 141476099f98SAdrian Hunter self.dbid = data[0] 141576099f98SAdrian Hunter self.level = 1 141676099f98SAdrian Hunter self.query_done = False 1417530e22fdSAdrian Hunter self.br_col = len(self.data) - 1 141876099f98SAdrian Hunter 141976099f98SAdrian Hunter def getChildItem(self, row): 142076099f98SAdrian Hunter return self.child_items[row] 142176099f98SAdrian Hunter 142276099f98SAdrian Hunter def getParentItem(self): 142376099f98SAdrian Hunter return self.parent_item 142476099f98SAdrian Hunter 142576099f98SAdrian Hunter def getRow(self): 142676099f98SAdrian Hunter return self.row 142776099f98SAdrian Hunter 142876099f98SAdrian Hunter def Select(self): 142976099f98SAdrian Hunter self.query_done = True 143076099f98SAdrian Hunter 143176099f98SAdrian Hunter if not self.glb.have_disassembler: 143276099f98SAdrian Hunter return 143376099f98SAdrian Hunter 143476099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 143576099f98SAdrian Hunter 143676099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 143776099f98SAdrian Hunter " FROM samples" 143876099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 143976099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 144076099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 144176099f98SAdrian Hunter if not query.next(): 144276099f98SAdrian Hunter return 144376099f98SAdrian Hunter cpu = query.value(0) 144476099f98SAdrian Hunter dso = query.value(1) 144576099f98SAdrian Hunter sym = query.value(2) 144676099f98SAdrian Hunter if dso == 0 or sym == 0: 144776099f98SAdrian Hunter return 144876099f98SAdrian Hunter off = query.value(3) 144976099f98SAdrian Hunter short_name = query.value(4) 145076099f98SAdrian Hunter long_name = query.value(5) 145176099f98SAdrian Hunter build_id = query.value(6) 145276099f98SAdrian Hunter sym_start = query.value(7) 145376099f98SAdrian Hunter ip = query.value(8) 145476099f98SAdrian Hunter 145576099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 145676099f98SAdrian Hunter " FROM samples" 145776099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 145876099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 145976099f98SAdrian Hunter " ORDER BY samples.id" 146076099f98SAdrian Hunter " LIMIT 1") 146176099f98SAdrian Hunter if not query.next(): 146276099f98SAdrian Hunter return 146376099f98SAdrian Hunter if query.value(0) != dso: 146476099f98SAdrian Hunter # Cannot disassemble from one dso to another 146576099f98SAdrian Hunter return 146676099f98SAdrian Hunter bsym = query.value(1) 146776099f98SAdrian Hunter boff = query.value(2) 146876099f98SAdrian Hunter bsym_start = query.value(3) 146976099f98SAdrian Hunter if bsym == 0: 147076099f98SAdrian Hunter return 147176099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 147276099f98SAdrian Hunter if tot <= 0 or tot > 16384: 147376099f98SAdrian Hunter return 147476099f98SAdrian Hunter 147576099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 147676099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 147776099f98SAdrian Hunter if not f: 147876099f98SAdrian Hunter return 147976099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 148076099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 148176099f98SAdrian Hunter 148276099f98SAdrian Hunter buf_sz = tot + 16 148376099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 148476099f98SAdrian Hunter f.seek(sym_start + off) 148576099f98SAdrian Hunter buf.value = f.read(buf_sz) 148676099f98SAdrian Hunter buf_ptr = addressof(buf) 148776099f98SAdrian Hunter i = 0 148876099f98SAdrian Hunter while tot > 0: 148976099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 149076099f98SAdrian Hunter if cnt: 149176099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 149276099f98SAdrian Hunter for k in xrange(cnt): 149376099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 149476099f98SAdrian Hunter i += 1 149576099f98SAdrian Hunter while k < 15: 149676099f98SAdrian Hunter byte_str += " " 149776099f98SAdrian Hunter k += 1 1498530e22fdSAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) 149976099f98SAdrian Hunter self.child_count += 1 150076099f98SAdrian Hunter else: 150176099f98SAdrian Hunter return 150276099f98SAdrian Hunter buf_ptr += cnt 150376099f98SAdrian Hunter tot -= cnt 150476099f98SAdrian Hunter buf_sz -= cnt 150576099f98SAdrian Hunter ip += cnt 150676099f98SAdrian Hunter 150776099f98SAdrian Hunter def childCount(self): 150876099f98SAdrian Hunter if not self.query_done: 150976099f98SAdrian Hunter self.Select() 151076099f98SAdrian Hunter if not self.child_count: 151176099f98SAdrian Hunter return -1 151276099f98SAdrian Hunter return self.child_count 151376099f98SAdrian Hunter 151476099f98SAdrian Hunter def hasChildren(self): 151576099f98SAdrian Hunter if not self.query_done: 151676099f98SAdrian Hunter return True 151776099f98SAdrian Hunter return self.child_count > 0 151876099f98SAdrian Hunter 151976099f98SAdrian Hunter def getData(self, column): 152076099f98SAdrian Hunter return self.data[column] 152176099f98SAdrian Hunter 152276099f98SAdrian Hunter# Brance data model root item 152376099f98SAdrian Hunter 152476099f98SAdrian Hunterclass BranchRootItem(): 152576099f98SAdrian Hunter 152676099f98SAdrian Hunter def __init__(self): 152776099f98SAdrian Hunter self.child_count = 0 152876099f98SAdrian Hunter self.child_items = [] 152976099f98SAdrian Hunter self.level = 0 153076099f98SAdrian Hunter 153176099f98SAdrian Hunter def getChildItem(self, row): 153276099f98SAdrian Hunter return self.child_items[row] 153376099f98SAdrian Hunter 153476099f98SAdrian Hunter def getParentItem(self): 153576099f98SAdrian Hunter return None 153676099f98SAdrian Hunter 153776099f98SAdrian Hunter def getRow(self): 153876099f98SAdrian Hunter return 0 153976099f98SAdrian Hunter 154076099f98SAdrian Hunter def childCount(self): 154176099f98SAdrian Hunter return self.child_count 154276099f98SAdrian Hunter 154376099f98SAdrian Hunter def hasChildren(self): 154476099f98SAdrian Hunter return self.child_count > 0 154576099f98SAdrian Hunter 154676099f98SAdrian Hunter def getData(self, column): 154776099f98SAdrian Hunter return "" 154876099f98SAdrian Hunter 1549530e22fdSAdrian Hunter# Calculate instructions per cycle 1550530e22fdSAdrian Hunter 1551530e22fdSAdrian Hunterdef CalcIPC(cyc_cnt, insn_cnt): 1552530e22fdSAdrian Hunter if cyc_cnt and insn_cnt: 1553530e22fdSAdrian Hunter ipc = Decimal(float(insn_cnt) / cyc_cnt) 1554530e22fdSAdrian Hunter ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) 1555530e22fdSAdrian Hunter else: 1556530e22fdSAdrian Hunter ipc = "0" 1557530e22fdSAdrian Hunter return ipc 1558530e22fdSAdrian Hunter 155976099f98SAdrian Hunter# Branch data preparation 156076099f98SAdrian Hunter 1561530e22fdSAdrian Hunterdef BranchDataPrepBr(query, data): 1562530e22fdSAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 1563530e22fdSAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 1564530e22fdSAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 1565530e22fdSAdrian Hunter " (" + dsoname(query.value(15)) + ")") 1566530e22fdSAdrian Hunter 1567530e22fdSAdrian Hunterdef BranchDataPrepIPC(query, data): 1568530e22fdSAdrian Hunter insn_cnt = query.value(16) 1569530e22fdSAdrian Hunter cyc_cnt = query.value(17) 1570530e22fdSAdrian Hunter ipc = CalcIPC(cyc_cnt, insn_cnt) 1571530e22fdSAdrian Hunter data.append(insn_cnt) 1572530e22fdSAdrian Hunter data.append(cyc_cnt) 1573530e22fdSAdrian Hunter data.append(ipc) 1574530e22fdSAdrian Hunter 157576099f98SAdrian Hunterdef BranchDataPrep(query): 157676099f98SAdrian Hunter data = [] 157776099f98SAdrian Hunter for i in xrange(0, 8): 157876099f98SAdrian Hunter data.append(query.value(i)) 1579530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 158076099f98SAdrian Hunter return data 158176099f98SAdrian Hunter 15828453c936SAdrian Hunterdef BranchDataPrepWA(query): 15838453c936SAdrian Hunter data = [] 15848453c936SAdrian Hunter data.append(query.value(0)) 15858453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 15868453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 15878453c936SAdrian Hunter for i in xrange(2, 8): 15888453c936SAdrian Hunter data.append(query.value(i)) 1589530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1590530e22fdSAdrian Hunter return data 1591530e22fdSAdrian Hunter 1592530e22fdSAdrian Hunterdef BranchDataWithIPCPrep(query): 1593530e22fdSAdrian Hunter data = [] 1594530e22fdSAdrian Hunter for i in xrange(0, 8): 1595530e22fdSAdrian Hunter data.append(query.value(i)) 1596530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1597530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 1598530e22fdSAdrian Hunter return data 1599530e22fdSAdrian Hunter 1600530e22fdSAdrian Hunterdef BranchDataWithIPCPrepWA(query): 1601530e22fdSAdrian Hunter data = [] 1602530e22fdSAdrian Hunter data.append(query.value(0)) 1603530e22fdSAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 1604530e22fdSAdrian Hunter data.append("{:>19}".format(query.value(1))) 1605530e22fdSAdrian Hunter for i in xrange(2, 8): 1606530e22fdSAdrian Hunter data.append(query.value(i)) 1607530e22fdSAdrian Hunter BranchDataPrepIPC(query, data) 1608530e22fdSAdrian Hunter BranchDataPrepBr(query, data) 16098453c936SAdrian Hunter return data 16108453c936SAdrian Hunter 161176099f98SAdrian Hunter# Branch data model 161276099f98SAdrian Hunter 161376099f98SAdrian Hunterclass BranchModel(TreeModel): 161476099f98SAdrian Hunter 161576099f98SAdrian Hunter progress = Signal(object) 161676099f98SAdrian Hunter 161776099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 1618*4a0979d4SAdrian Hunter super(BranchModel, self).__init__(glb, None, parent) 161976099f98SAdrian Hunter self.event_id = event_id 162076099f98SAdrian Hunter self.more = True 162176099f98SAdrian Hunter self.populated = 0 1622530e22fdSAdrian Hunter self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") 1623530e22fdSAdrian Hunter if self.have_ipc: 1624530e22fdSAdrian Hunter select_ipc = ", insn_count, cyc_count" 1625530e22fdSAdrian Hunter prep_fn = BranchDataWithIPCPrep 1626530e22fdSAdrian Hunter prep_wa_fn = BranchDataWithIPCPrepWA 1627530e22fdSAdrian Hunter else: 1628530e22fdSAdrian Hunter select_ipc = "" 1629530e22fdSAdrian Hunter prep_fn = BranchDataPrep 1630530e22fdSAdrian Hunter prep_wa_fn = BranchDataPrepWA 163176099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 163276099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 163376099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 163476099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 1635530e22fdSAdrian Hunter + select_ipc + 163676099f98SAdrian Hunter " FROM samples" 163776099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 163876099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 163976099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 164076099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 164176099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 164276099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 164376099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 164476099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 164576099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 164676099f98SAdrian Hunter " ORDER BY samples.id" 164776099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 16488453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 1649530e22fdSAdrian Hunter prep = prep_fn 16508453c936SAdrian Hunter else: 1651530e22fdSAdrian Hunter prep = prep_wa_fn 16528453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 165376099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 165476099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 165576099f98SAdrian Hunter 1656a448ba23SAdrian Hunter def GetRoot(self): 1657a448ba23SAdrian Hunter return BranchRootItem() 1658a448ba23SAdrian Hunter 165976099f98SAdrian Hunter def columnCount(self, parent=None): 1660530e22fdSAdrian Hunter if self.have_ipc: 1661530e22fdSAdrian Hunter return 11 1662530e22fdSAdrian Hunter else: 166376099f98SAdrian Hunter return 8 166476099f98SAdrian Hunter 166576099f98SAdrian Hunter def columnHeader(self, column): 1666530e22fdSAdrian Hunter if self.have_ipc: 1667530e22fdSAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] 1668530e22fdSAdrian Hunter else: 166976099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 167076099f98SAdrian Hunter 167176099f98SAdrian Hunter def columnFont(self, column): 1672530e22fdSAdrian Hunter if self.have_ipc: 1673530e22fdSAdrian Hunter br_col = 10 1674530e22fdSAdrian Hunter else: 1675530e22fdSAdrian Hunter br_col = 7 1676530e22fdSAdrian Hunter if column != br_col: 167776099f98SAdrian Hunter return None 167876099f98SAdrian Hunter return QFont("Monospace") 167976099f98SAdrian Hunter 168076099f98SAdrian Hunter def DisplayData(self, item, index): 168176099f98SAdrian Hunter if item.level == 1: 168276099f98SAdrian Hunter self.FetchIfNeeded(item.row) 168376099f98SAdrian Hunter return item.getData(index.column()) 168476099f98SAdrian Hunter 168576099f98SAdrian Hunter def AddSample(self, data): 168676099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 168776099f98SAdrian Hunter self.root.child_items.append(child) 168876099f98SAdrian Hunter self.populated += 1 168976099f98SAdrian Hunter 169076099f98SAdrian Hunter def Update(self, fetched): 169176099f98SAdrian Hunter if not fetched: 169276099f98SAdrian Hunter self.more = False 169376099f98SAdrian Hunter self.progress.emit(0) 169476099f98SAdrian Hunter child_count = self.root.child_count 169576099f98SAdrian Hunter count = self.populated - child_count 169676099f98SAdrian Hunter if count > 0: 169776099f98SAdrian Hunter parent = QModelIndex() 169876099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 169976099f98SAdrian Hunter self.insertRows(child_count, count, parent) 170076099f98SAdrian Hunter self.root.child_count += count 170176099f98SAdrian Hunter self.endInsertRows() 170276099f98SAdrian Hunter self.progress.emit(self.root.child_count) 170376099f98SAdrian Hunter 170476099f98SAdrian Hunter def FetchMoreRecords(self, count): 170576099f98SAdrian Hunter current = self.root.child_count 170676099f98SAdrian Hunter if self.more: 170776099f98SAdrian Hunter self.fetcher.Fetch(count) 170876099f98SAdrian Hunter else: 170976099f98SAdrian Hunter self.progress.emit(0) 171076099f98SAdrian Hunter return current 171176099f98SAdrian Hunter 171276099f98SAdrian Hunter def HasMoreRecords(self): 171376099f98SAdrian Hunter return self.more 171476099f98SAdrian Hunter 17150bf0947aSAdrian Hunter# Report Variables 17160bf0947aSAdrian Hunter 17170bf0947aSAdrian Hunterclass ReportVars(): 17180bf0947aSAdrian Hunter 1719cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1720947cc38dSAdrian Hunter self.name = name 17210bf0947aSAdrian Hunter self.where_clause = where_clause 1722cd358012SAdrian Hunter self.limit = limit 17230bf0947aSAdrian Hunter 17240bf0947aSAdrian Hunter def UniqueId(self): 1725cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 17260bf0947aSAdrian Hunter 172776099f98SAdrian Hunter# Branch window 172876099f98SAdrian Hunter 172976099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 173076099f98SAdrian Hunter 1731947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 173276099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 173376099f98SAdrian Hunter 17340bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 173576099f98SAdrian Hunter 17360bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 173776099f98SAdrian Hunter 173876099f98SAdrian Hunter self.view = QTreeView() 173976099f98SAdrian Hunter self.view.setUniformRowHeights(True) 174096c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 174196c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 174276099f98SAdrian Hunter self.view.setModel(self.model) 174376099f98SAdrian Hunter 174476099f98SAdrian Hunter self.ResizeColumnsToContents() 174576099f98SAdrian Hunter 17469bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 17479bc4e4bfSAdrian Hunter 174876099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 174976099f98SAdrian Hunter 175076099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 175176099f98SAdrian Hunter 175276099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 175376099f98SAdrian Hunter 175476099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 175576099f98SAdrian Hunter 175676099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 175776099f98SAdrian Hunter 1758947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 175976099f98SAdrian Hunter 176076099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 176176099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 176276099f98SAdrian Hunter # so implement a crude alternative 176376099f98SAdrian Hunter mm = "MM" if column else "MMMM" 176476099f98SAdrian Hunter font = self.view.font() 176576099f98SAdrian Hunter metrics = QFontMetrics(font) 176676099f98SAdrian Hunter max = 0 176776099f98SAdrian Hunter for row in xrange(n): 176876099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 176976099f98SAdrian Hunter len = metrics.width(str(val) + mm) 177076099f98SAdrian Hunter max = len if len > max else max 177176099f98SAdrian Hunter val = self.model.columnHeader(column) 177276099f98SAdrian Hunter len = metrics.width(str(val) + mm) 177376099f98SAdrian Hunter max = len if len > max else max 177476099f98SAdrian Hunter self.view.setColumnWidth(column, max) 177576099f98SAdrian Hunter 177676099f98SAdrian Hunter def ResizeColumnsToContents(self): 177776099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 177876099f98SAdrian Hunter if n < 1: 177976099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 178076099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 178176099f98SAdrian Hunter return 178276099f98SAdrian Hunter columns = self.model.columnCount() 178376099f98SAdrian Hunter for i in xrange(columns): 178476099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 178576099f98SAdrian Hunter 178676099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 178776099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 178876099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 178976099f98SAdrian Hunter self.ResizeColumnsToContents() 179076099f98SAdrian Hunter 179176099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 179276099f98SAdrian Hunter self.view.setFocus() 179376099f98SAdrian Hunter self.find_bar.Busy() 179476099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 179576099f98SAdrian Hunter 179676099f98SAdrian Hunter def FindDone(self, row): 179776099f98SAdrian Hunter self.find_bar.Idle() 179876099f98SAdrian Hunter if row >= 0: 179976099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 180076099f98SAdrian Hunter else: 180176099f98SAdrian Hunter self.find_bar.NotFound() 180276099f98SAdrian Hunter 18031c3ca1b3SAdrian Hunter# Line edit data item 18041c3ca1b3SAdrian Hunter 18051c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 18061c3ca1b3SAdrian Hunter 1807cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 18081c3ca1b3SAdrian Hunter self.glb = glb 18091c3ca1b3SAdrian Hunter self.label = label 18101c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 18111c3ca1b3SAdrian Hunter self.parent = parent 18121c3ca1b3SAdrian Hunter self.id = id 18131c3ca1b3SAdrian Hunter 1814cd358012SAdrian Hunter self.value = default 18151c3ca1b3SAdrian Hunter 1816cd358012SAdrian Hunter self.widget = QLineEdit(default) 18171c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 18181c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 18191c3ca1b3SAdrian Hunter self.red = False 18201c3ca1b3SAdrian Hunter self.error = "" 18211c3ca1b3SAdrian Hunter self.validated = True 18221c3ca1b3SAdrian Hunter 18231c3ca1b3SAdrian Hunter if placeholder_text: 18241c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 18251c3ca1b3SAdrian Hunter 18261c3ca1b3SAdrian Hunter def TurnTextRed(self): 18271c3ca1b3SAdrian Hunter if not self.red: 18281c3ca1b3SAdrian Hunter palette = QPalette() 18291c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 18301c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 18311c3ca1b3SAdrian Hunter self.red = True 18321c3ca1b3SAdrian Hunter 18331c3ca1b3SAdrian Hunter def TurnTextNormal(self): 18341c3ca1b3SAdrian Hunter if self.red: 18351c3ca1b3SAdrian Hunter palette = QPalette() 18361c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 18371c3ca1b3SAdrian Hunter self.red = False 18381c3ca1b3SAdrian Hunter 18391c3ca1b3SAdrian Hunter def InvalidValue(self, value): 18401c3ca1b3SAdrian Hunter self.value = "" 18411c3ca1b3SAdrian Hunter self.TurnTextRed() 18421c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 18431c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 18441c3ca1b3SAdrian Hunter 18451c3ca1b3SAdrian Hunter def Invalidate(self): 18461c3ca1b3SAdrian Hunter self.validated = False 18471c3ca1b3SAdrian Hunter 18481c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18491c3ca1b3SAdrian Hunter self.value = input_string.strip() 18501c3ca1b3SAdrian Hunter 18511c3ca1b3SAdrian Hunter def Validate(self): 18521c3ca1b3SAdrian Hunter self.validated = True 18531c3ca1b3SAdrian Hunter self.error = "" 18541c3ca1b3SAdrian Hunter self.TurnTextNormal() 18551c3ca1b3SAdrian Hunter self.parent.ClearMessage() 18561c3ca1b3SAdrian Hunter input_string = self.widget.text() 18571c3ca1b3SAdrian Hunter if not len(input_string.strip()): 18581c3ca1b3SAdrian Hunter self.value = "" 18591c3ca1b3SAdrian Hunter return 18601c3ca1b3SAdrian Hunter self.DoValidate(input_string) 18611c3ca1b3SAdrian Hunter 18621c3ca1b3SAdrian Hunter def IsValid(self): 18631c3ca1b3SAdrian Hunter if not self.validated: 18641c3ca1b3SAdrian Hunter self.Validate() 18651c3ca1b3SAdrian Hunter if len(self.error): 18661c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 18671c3ca1b3SAdrian Hunter return False 18681c3ca1b3SAdrian Hunter return True 18691c3ca1b3SAdrian Hunter 18701c3ca1b3SAdrian Hunter def IsNumber(self, value): 18711c3ca1b3SAdrian Hunter try: 18721c3ca1b3SAdrian Hunter x = int(value) 18731c3ca1b3SAdrian Hunter except: 18741c3ca1b3SAdrian Hunter x = 0 18751c3ca1b3SAdrian Hunter return str(x) == value 18761c3ca1b3SAdrian Hunter 18771c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 18781c3ca1b3SAdrian Hunter 18791c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 18801c3ca1b3SAdrian Hunter 18811c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18821c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 18831c3ca1b3SAdrian Hunter 18841c3ca1b3SAdrian Hunter self.column_name = column_name 18851c3ca1b3SAdrian Hunter 18861c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18871c3ca1b3SAdrian Hunter singles = [] 18881c3ca1b3SAdrian Hunter ranges = [] 18891c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18901c3ca1b3SAdrian Hunter if "-" in value: 18911c3ca1b3SAdrian Hunter vrange = value.split("-") 18921c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 18931c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18941c3ca1b3SAdrian Hunter ranges.append(vrange) 18951c3ca1b3SAdrian Hunter else: 18961c3ca1b3SAdrian Hunter if not self.IsNumber(value): 18971c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18981c3ca1b3SAdrian Hunter singles.append(value) 18991c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 19001c3ca1b3SAdrian Hunter if len(singles): 19011c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 19021c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 19031c3ca1b3SAdrian Hunter 1904cd358012SAdrian Hunter# Positive integer dialog data item 1905cd358012SAdrian Hunter 1906cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1907cd358012SAdrian Hunter 1908cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1909cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1910cd358012SAdrian Hunter 1911cd358012SAdrian Hunter def DoValidate(self, input_string): 1912cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1913cd358012SAdrian Hunter return self.InvalidValue(input_string) 1914cd358012SAdrian Hunter value = int(input_string.strip()) 1915cd358012SAdrian Hunter if value <= 0: 1916cd358012SAdrian Hunter return self.InvalidValue(input_string) 1917cd358012SAdrian Hunter self.value = str(value) 1918cd358012SAdrian Hunter 19191c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 19201c3ca1b3SAdrian Hunter 19211c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 19221c3ca1b3SAdrian Hunter 19231c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 19241c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 19251c3ca1b3SAdrian Hunter 19261c3ca1b3SAdrian Hunter self.table_name = table_name 19271c3ca1b3SAdrian Hunter self.match_column = match_column 19281c3ca1b3SAdrian Hunter self.column_name1 = column_name1 19291c3ca1b3SAdrian Hunter self.column_name2 = column_name2 19301c3ca1b3SAdrian Hunter 19311c3ca1b3SAdrian Hunter def ValueToIds(self, value): 19321c3ca1b3SAdrian Hunter ids = [] 19331c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 19341c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 19351c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 19361c3ca1b3SAdrian Hunter if ret: 19371c3ca1b3SAdrian Hunter while query.next(): 19381c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 19391c3ca1b3SAdrian Hunter return ids 19401c3ca1b3SAdrian Hunter 19411c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19421c3ca1b3SAdrian Hunter all_ids = [] 19431c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19441c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 19451c3ca1b3SAdrian Hunter if len(ids): 19461c3ca1b3SAdrian Hunter all_ids.extend(ids) 19471c3ca1b3SAdrian Hunter else: 19481c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19491c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 19501c3ca1b3SAdrian Hunter if self.column_name2: 19511c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 19521c3ca1b3SAdrian Hunter 19531c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 19541c3ca1b3SAdrian Hunter 19551c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 19561c3ca1b3SAdrian Hunter 19571c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 19581c3ca1b3SAdrian Hunter self.column_name = column_name 19591c3ca1b3SAdrian Hunter 19601c3ca1b3SAdrian Hunter self.last_id = 0 19611c3ca1b3SAdrian Hunter self.first_time = 0 19621c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 19631c3ca1b3SAdrian Hunter 19641c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 19651c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 19661c3ca1b3SAdrian Hunter if query.next(): 19671c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 19681c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 19691c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 19701c3ca1b3SAdrian Hunter if query.next(): 19711c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 19721c3ca1b3SAdrian Hunter if placeholder_text: 19731c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 19741c3ca1b3SAdrian Hunter 19751c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 19761c3ca1b3SAdrian Hunter 19771c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 19781c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 19791c3ca1b3SAdrian Hunter if query.next(): 19801c3ca1b3SAdrian Hunter return True, int(query.value(0)) 19811c3ca1b3SAdrian Hunter else: 19821c3ca1b3SAdrian Hunter return False, 0 19831c3ca1b3SAdrian Hunter 19841c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 19851c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 19861c3ca1b3SAdrian Hunter while True: 19871c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 19881c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19891c3ca1b3SAdrian Hunter if not query.next(): 19901c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 19911c3ca1b3SAdrian Hunter if not ok: 19921c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 19931c3ca1b3SAdrian Hunter if not ok: 19941c3ca1b3SAdrian Hunter return str(higher_id) 19951c3ca1b3SAdrian Hunter next_id = dbid 19961c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19971c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 19981c3ca1b3SAdrian Hunter if get_floor: 19991c3ca1b3SAdrian Hunter if target_time > next_time: 20001c3ca1b3SAdrian Hunter lower_id = next_id 20011c3ca1b3SAdrian Hunter else: 20021c3ca1b3SAdrian Hunter higher_id = next_id 20031c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 20041c3ca1b3SAdrian Hunter return str(higher_id) 20051c3ca1b3SAdrian Hunter else: 20061c3ca1b3SAdrian Hunter if target_time >= next_time: 20071c3ca1b3SAdrian Hunter lower_id = next_id 20081c3ca1b3SAdrian Hunter else: 20091c3ca1b3SAdrian Hunter higher_id = next_id 20101c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 20111c3ca1b3SAdrian Hunter return str(lower_id) 20121c3ca1b3SAdrian Hunter 20131c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 20141c3ca1b3SAdrian Hunter mult = 1 20151c3ca1b3SAdrian Hunter suffix = val[-2:] 20161c3ca1b3SAdrian Hunter if suffix == "ms": 20171c3ca1b3SAdrian Hunter mult = 1000000 20181c3ca1b3SAdrian Hunter elif suffix == "us": 20191c3ca1b3SAdrian Hunter mult = 1000 20201c3ca1b3SAdrian Hunter elif suffix == "ns": 20211c3ca1b3SAdrian Hunter mult = 1 20221c3ca1b3SAdrian Hunter else: 20231c3ca1b3SAdrian Hunter return val 20241c3ca1b3SAdrian Hunter val = val[:-2].strip() 20251c3ca1b3SAdrian Hunter if not self.IsNumber(val): 20261c3ca1b3SAdrian Hunter return val 20271c3ca1b3SAdrian Hunter val = int(val) * mult 20281c3ca1b3SAdrian Hunter if val >= 0: 20291c3ca1b3SAdrian Hunter val += self.first_time 20301c3ca1b3SAdrian Hunter else: 20311c3ca1b3SAdrian Hunter val += self.last_time 20321c3ca1b3SAdrian Hunter return str(val) 20331c3ca1b3SAdrian Hunter 20341c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 20351c3ca1b3SAdrian Hunter if vrange[0] == "": 20361c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 20371c3ca1b3SAdrian Hunter if vrange[1] == "": 20381c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 20391c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 20401c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 20411c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 20421c3ca1b3SAdrian Hunter return False 20431c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 20441c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 20451c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 20461c3ca1b3SAdrian Hunter return False 20471c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 20481c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 20491c3ca1b3SAdrian Hunter return True 20501c3ca1b3SAdrian Hunter 20511c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 20521c3ca1b3SAdrian Hunter n = value.count("-") 20531c3ca1b3SAdrian Hunter if n == 1: 20541c3ca1b3SAdrian Hunter pass 20551c3ca1b3SAdrian Hunter elif n == 2: 20561c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 20571c3ca1b3SAdrian Hunter n = 1 20581c3ca1b3SAdrian Hunter elif n == 3: 20591c3ca1b3SAdrian Hunter n = 2 20601c3ca1b3SAdrian Hunter else: 20611c3ca1b3SAdrian Hunter return False 20621c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 20631c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 20641c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 20651c3ca1b3SAdrian Hunter ranges.append(vrange) 20661c3ca1b3SAdrian Hunter return True 20671c3ca1b3SAdrian Hunter return False 20681c3ca1b3SAdrian Hunter 20691c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 20701c3ca1b3SAdrian Hunter ranges = [] 20711c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 20721c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 20731c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20741c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 20751c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 20761c3ca1b3SAdrian Hunter 20770924cd68SAdrian Hunter# Report Dialog Base 2078210cf1f9SAdrian Hunter 20790924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 2080210cf1f9SAdrian Hunter 20810924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 20820924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2083210cf1f9SAdrian Hunter 2084210cf1f9SAdrian Hunter self.glb = glb 2085210cf1f9SAdrian Hunter 20860bf0947aSAdrian Hunter self.report_vars = ReportVars() 2087210cf1f9SAdrian Hunter 20880924cd68SAdrian Hunter self.setWindowTitle(title) 2089210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2090210cf1f9SAdrian Hunter 20911c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2092210cf1f9SAdrian Hunter 20930924cd68SAdrian Hunter self.partial = partial 20940924cd68SAdrian Hunter 2095210cf1f9SAdrian Hunter self.grid = QGridLayout() 2096210cf1f9SAdrian Hunter 2097210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2098210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2099210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2100210cf1f9SAdrian Hunter 2101210cf1f9SAdrian Hunter self.status = QLabel() 2102210cf1f9SAdrian Hunter 2103210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2104210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2105210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2106210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2107210cf1f9SAdrian Hunter 2108210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2109210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2110210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2111210cf1f9SAdrian Hunter 2112210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2113210cf1f9SAdrian Hunter #self.hbox.addStretch() 2114210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2115210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2116210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2117210cf1f9SAdrian Hunter 2118210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2119210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2120210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2121210cf1f9SAdrian Hunter 2122210cf1f9SAdrian Hunter self.setLayout(self.vbox); 2123210cf1f9SAdrian Hunter 2124210cf1f9SAdrian Hunter def Ok(self): 21250bf0947aSAdrian Hunter vars = self.report_vars 21261c3ca1b3SAdrian Hunter for d in self.data_items: 21271c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 21281c3ca1b3SAdrian Hunter vars.name = d.value 2129947cc38dSAdrian Hunter if not vars.name: 2130210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2131210cf1f9SAdrian Hunter return 2132210cf1f9SAdrian Hunter for d in self.data_items: 2133210cf1f9SAdrian Hunter if not d.IsValid(): 2134210cf1f9SAdrian Hunter return 2135210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2136cd358012SAdrian Hunter if d.id == "LIMIT": 2137cd358012SAdrian Hunter vars.limit = d.value 2138cd358012SAdrian Hunter elif len(d.value): 21390bf0947aSAdrian Hunter if len(vars.where_clause): 21400bf0947aSAdrian Hunter vars.where_clause += " AND " 21410bf0947aSAdrian Hunter vars.where_clause += d.value 21420bf0947aSAdrian Hunter if len(vars.where_clause): 21430924cd68SAdrian Hunter if self.partial: 21440bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2145210cf1f9SAdrian Hunter else: 21460bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2147210cf1f9SAdrian Hunter self.accept() 2148210cf1f9SAdrian Hunter 2149210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2150210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2151210cf1f9SAdrian Hunter 2152210cf1f9SAdrian Hunter def ClearMessage(self): 2153210cf1f9SAdrian Hunter self.status.setText("") 2154210cf1f9SAdrian Hunter 21550924cd68SAdrian Hunter# Selected branch report creation dialog 21560924cd68SAdrian Hunter 21570924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 21580924cd68SAdrian Hunter 21590924cd68SAdrian Hunter def __init__(self, glb, parent=None): 21600924cd68SAdrian Hunter title = "Selected Branches" 21611c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 21621c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 21631c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 21641c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 21651c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 21661c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 21671c3ca1b3SAdrian 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), 21681c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 21691c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 21700924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 21710924cd68SAdrian Hunter 217276099f98SAdrian Hunter# Event list 217376099f98SAdrian Hunter 217476099f98SAdrian Hunterdef GetEventList(db): 217576099f98SAdrian Hunter events = [] 217676099f98SAdrian Hunter query = QSqlQuery(db) 217776099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 217876099f98SAdrian Hunter while query.next(): 217976099f98SAdrian Hunter events.append(query.value(0)) 218076099f98SAdrian Hunter return events 218176099f98SAdrian Hunter 2182655cb952SAdrian Hunter# Is a table selectable 2183655cb952SAdrian Hunter 2184530e22fdSAdrian Hunterdef IsSelectable(db, table, sql = "", columns = "*"): 2185655cb952SAdrian Hunter query = QSqlQuery(db) 2186655cb952SAdrian Hunter try: 2187530e22fdSAdrian Hunter QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1") 2188655cb952SAdrian Hunter except: 2189655cb952SAdrian Hunter return False 2190655cb952SAdrian Hunter return True 2191655cb952SAdrian Hunter 21928392b74bSAdrian Hunter# SQL table data model item 21938392b74bSAdrian Hunter 21948392b74bSAdrian Hunterclass SQLTableItem(): 21958392b74bSAdrian Hunter 21968392b74bSAdrian Hunter def __init__(self, row, data): 21978392b74bSAdrian Hunter self.row = row 21988392b74bSAdrian Hunter self.data = data 21998392b74bSAdrian Hunter 22008392b74bSAdrian Hunter def getData(self, column): 22018392b74bSAdrian Hunter return self.data[column] 22028392b74bSAdrian Hunter 22038392b74bSAdrian Hunter# SQL table data model 22048392b74bSAdrian Hunter 22058392b74bSAdrian Hunterclass SQLTableModel(TableModel): 22068392b74bSAdrian Hunter 22078392b74bSAdrian Hunter progress = Signal(object) 22088392b74bSAdrian Hunter 22098c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 22108392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 22118392b74bSAdrian Hunter self.glb = glb 22128392b74bSAdrian Hunter self.more = True 22138392b74bSAdrian Hunter self.populated = 0 22148c90fef9SAdrian Hunter self.column_headers = column_headers 22158453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 22168392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 22178392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 22188392b74bSAdrian Hunter 22198392b74bSAdrian Hunter def DisplayData(self, item, index): 22208392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 22218392b74bSAdrian Hunter return item.getData(index.column()) 22228392b74bSAdrian Hunter 22238392b74bSAdrian Hunter def AddSample(self, data): 22248392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 22258392b74bSAdrian Hunter self.child_items.append(child) 22268392b74bSAdrian Hunter self.populated += 1 22278392b74bSAdrian Hunter 22288392b74bSAdrian Hunter def Update(self, fetched): 22298392b74bSAdrian Hunter if not fetched: 22308392b74bSAdrian Hunter self.more = False 22318392b74bSAdrian Hunter self.progress.emit(0) 22328392b74bSAdrian Hunter child_count = self.child_count 22338392b74bSAdrian Hunter count = self.populated - child_count 22348392b74bSAdrian Hunter if count > 0: 22358392b74bSAdrian Hunter parent = QModelIndex() 22368392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 22378392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 22388392b74bSAdrian Hunter self.child_count += count 22398392b74bSAdrian Hunter self.endInsertRows() 22408392b74bSAdrian Hunter self.progress.emit(self.child_count) 22418392b74bSAdrian Hunter 22428392b74bSAdrian Hunter def FetchMoreRecords(self, count): 22438392b74bSAdrian Hunter current = self.child_count 22448392b74bSAdrian Hunter if self.more: 22458392b74bSAdrian Hunter self.fetcher.Fetch(count) 22468392b74bSAdrian Hunter else: 22478392b74bSAdrian Hunter self.progress.emit(0) 22488392b74bSAdrian Hunter return current 22498392b74bSAdrian Hunter 22508392b74bSAdrian Hunter def HasMoreRecords(self): 22518392b74bSAdrian Hunter return self.more 22528392b74bSAdrian Hunter 22538c90fef9SAdrian Hunter def columnCount(self, parent=None): 22548c90fef9SAdrian Hunter return len(self.column_headers) 22558c90fef9SAdrian Hunter 22568c90fef9SAdrian Hunter def columnHeader(self, column): 22578c90fef9SAdrian Hunter return self.column_headers[column] 22588c90fef9SAdrian Hunter 22598453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 22608453c936SAdrian Hunter data = [] 22618453c936SAdrian Hunter for i in xrange(count): 22628453c936SAdrian Hunter data.append(query.value(i)) 22638453c936SAdrian Hunter return data 22648453c936SAdrian Hunter 22658392b74bSAdrian Hunter# SQL automatic table data model 22668392b74bSAdrian Hunter 22678392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 22688392b74bSAdrian Hunter 22698392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 22708392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 22718392b74bSAdrian Hunter if table_name == "comm_threads_view": 22728392b74bSAdrian Hunter # For now, comm_threads_view has no id column 22738392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 22748c90fef9SAdrian Hunter column_headers = [] 22758392b74bSAdrian Hunter query = QSqlQuery(glb.db) 22768392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 22778392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 22788392b74bSAdrian Hunter while query.next(): 22798c90fef9SAdrian Hunter column_headers.append(query.value(1)) 22808392b74bSAdrian Hunter if table_name == "sqlite_master": 22818392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22828392b74bSAdrian Hunter else: 22838392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 22848392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22858392b74bSAdrian Hunter select_table_name = table_name[19:] 22868392b74bSAdrian Hunter schema = "information_schema" 22878392b74bSAdrian Hunter else: 22888392b74bSAdrian Hunter select_table_name = table_name 22898392b74bSAdrian Hunter schema = "public" 22908392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 22918392b74bSAdrian Hunter while query.next(): 22928c90fef9SAdrian Hunter column_headers.append(query.value(0)) 22938453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 22948453c936SAdrian Hunter if table_name == "samples_view": 22958453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 22968453c936SAdrian Hunter if table_name == "samples": 22978453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 22988c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 22998392b74bSAdrian Hunter 23008453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 23018453c936SAdrian Hunter data = [] 23028453c936SAdrian Hunter data.append(query.value(0)) 23038453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 23048453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 23058453c936SAdrian Hunter for i in xrange(2, count): 23068453c936SAdrian Hunter data.append(query.value(i)) 23078453c936SAdrian Hunter return data 23088453c936SAdrian Hunter 23098453c936SAdrian Hunter def samples_DataPrep(self, query, count): 23108453c936SAdrian Hunter data = [] 23118453c936SAdrian Hunter for i in xrange(9): 23128453c936SAdrian Hunter data.append(query.value(i)) 23138453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 23148453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 23158453c936SAdrian Hunter for i in xrange(10, count): 23168453c936SAdrian Hunter data.append(query.value(i)) 23178453c936SAdrian Hunter return data 23188453c936SAdrian Hunter 23198392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 23208392b74bSAdrian Hunter 23218392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 23228392b74bSAdrian Hunter 23238392b74bSAdrian Hunter def __init__(self, parent=None): 23248392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 23258392b74bSAdrian Hunter 23268392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 23278392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 23288392b74bSAdrian Hunter # so implement a crude alternative 23298392b74bSAdrian Hunter font = self.view.font() 23308392b74bSAdrian Hunter metrics = QFontMetrics(font) 23318392b74bSAdrian Hunter max = 0 23328392b74bSAdrian Hunter for row in xrange(n): 23338392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 23348392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 23358392b74bSAdrian Hunter max = len if len > max else max 23368392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 23378392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 23388392b74bSAdrian Hunter max = len if len > max else max 23398392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 23408392b74bSAdrian Hunter 23418392b74bSAdrian Hunter def ResizeColumnsToContents(self): 23428392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 23438392b74bSAdrian Hunter if n < 1: 23448392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 23458392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 23468392b74bSAdrian Hunter return 23478392b74bSAdrian Hunter columns = self.data_model.columnCount() 23488392b74bSAdrian Hunter for i in xrange(columns): 23498392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 23508392b74bSAdrian Hunter 23518392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 23528392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 23538392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 23548392b74bSAdrian Hunter self.ResizeColumnsToContents() 23558392b74bSAdrian Hunter 235696c43b9aSAdrian Hunter# Convert value to CSV 235796c43b9aSAdrian Hunter 235896c43b9aSAdrian Hunterdef ToCSValue(val): 235996c43b9aSAdrian Hunter if '"' in val: 236096c43b9aSAdrian Hunter val = val.replace('"', '""') 236196c43b9aSAdrian Hunter if "," in val or '"' in val: 236296c43b9aSAdrian Hunter val = '"' + val + '"' 236396c43b9aSAdrian Hunter return val 236496c43b9aSAdrian Hunter 236596c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 236696c43b9aSAdrian Hunter 236796c43b9aSAdrian Hunterglb_max_cols = 1000 236896c43b9aSAdrian Hunter 236996c43b9aSAdrian Hunterdef RowColumnKey(a): 237096c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 237196c43b9aSAdrian Hunter 237296c43b9aSAdrian Hunter# Copy selected table cells to clipboard 237396c43b9aSAdrian Hunter 237496c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 237596c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 237696c43b9aSAdrian Hunter idx_cnt = len(indexes) 237796c43b9aSAdrian Hunter if not idx_cnt: 237896c43b9aSAdrian Hunter return 237996c43b9aSAdrian Hunter if idx_cnt == 1: 238096c43b9aSAdrian Hunter with_hdr=False 238196c43b9aSAdrian Hunter min_row = indexes[0].row() 238296c43b9aSAdrian Hunter max_row = indexes[0].row() 238396c43b9aSAdrian Hunter min_col = indexes[0].column() 238496c43b9aSAdrian Hunter max_col = indexes[0].column() 238596c43b9aSAdrian Hunter for i in indexes: 238696c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 238796c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 238896c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 238996c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 239096c43b9aSAdrian Hunter if max_col > glb_max_cols: 239196c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 239296c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 239396c43b9aSAdrian Hunter for i in indexes: 239496c43b9aSAdrian Hunter c = i.column() - min_col 239596c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 239696c43b9aSAdrian Hunter text = "" 239796c43b9aSAdrian Hunter pad = "" 239896c43b9aSAdrian Hunter sep = "" 239996c43b9aSAdrian Hunter if with_hdr: 240096c43b9aSAdrian Hunter model = indexes[0].model() 240196c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 240296c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 240396c43b9aSAdrian Hunter if as_csv: 240496c43b9aSAdrian Hunter text += sep + ToCSValue(val) 240596c43b9aSAdrian Hunter sep = "," 240696c43b9aSAdrian Hunter else: 240796c43b9aSAdrian Hunter c = col - min_col 240896c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 240996c43b9aSAdrian Hunter width = max_width[c] 241096c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 241196c43b9aSAdrian Hunter if align & Qt.AlignRight: 241296c43b9aSAdrian Hunter val = val.rjust(width) 241396c43b9aSAdrian Hunter text += pad + sep + val 241496c43b9aSAdrian Hunter pad = " " * (width - len(val)) 241596c43b9aSAdrian Hunter sep = " " 241696c43b9aSAdrian Hunter text += "\n" 241796c43b9aSAdrian Hunter pad = "" 241896c43b9aSAdrian Hunter sep = "" 241996c43b9aSAdrian Hunter last_row = min_row 242096c43b9aSAdrian Hunter for i in indexes: 242196c43b9aSAdrian Hunter if i.row() > last_row: 242296c43b9aSAdrian Hunter last_row = i.row() 242396c43b9aSAdrian Hunter text += "\n" 242496c43b9aSAdrian Hunter pad = "" 242596c43b9aSAdrian Hunter sep = "" 242696c43b9aSAdrian Hunter if as_csv: 242796c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 242896c43b9aSAdrian Hunter sep = "," 242996c43b9aSAdrian Hunter else: 243096c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 243196c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 243296c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 243396c43b9aSAdrian Hunter else: 243496c43b9aSAdrian Hunter val = str(i.data()) 243596c43b9aSAdrian Hunter text += pad + sep + val 243696c43b9aSAdrian Hunter pad = " " * (width - len(val)) 243796c43b9aSAdrian Hunter sep = " " 243896c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 243996c43b9aSAdrian Hunter 244096c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 244196c43b9aSAdrian Hunter indexes = view.selectedIndexes() 244296c43b9aSAdrian Hunter if not len(indexes): 244396c43b9aSAdrian Hunter return 244496c43b9aSAdrian Hunter 244596c43b9aSAdrian Hunter selection = view.selectionModel() 244696c43b9aSAdrian Hunter 244796c43b9aSAdrian Hunter first = None 244896c43b9aSAdrian Hunter for i in indexes: 244996c43b9aSAdrian Hunter above = view.indexAbove(i) 245096c43b9aSAdrian Hunter if not selection.isSelected(above): 245196c43b9aSAdrian Hunter first = i 245296c43b9aSAdrian Hunter break 245396c43b9aSAdrian Hunter 245496c43b9aSAdrian Hunter if first is None: 245596c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 245696c43b9aSAdrian Hunter 245796c43b9aSAdrian Hunter model = first.model() 245896c43b9aSAdrian Hunter row_cnt = 0 245996c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 246096c43b9aSAdrian Hunter max_width = [0] * col_cnt 246196c43b9aSAdrian Hunter 246296c43b9aSAdrian Hunter indent_sz = 2 246396c43b9aSAdrian Hunter indent_str = " " * indent_sz 246496c43b9aSAdrian Hunter 246596c43b9aSAdrian Hunter expanded_mark_sz = 2 246696c43b9aSAdrian Hunter if sys.version_info[0] == 3: 246796c43b9aSAdrian Hunter expanded_mark = "\u25BC " 246896c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 246996c43b9aSAdrian Hunter else: 247096c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 247196c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 247296c43b9aSAdrian Hunter leaf_mark = " " 247396c43b9aSAdrian Hunter 247496c43b9aSAdrian Hunter if not as_csv: 247596c43b9aSAdrian Hunter pos = first 247696c43b9aSAdrian Hunter while True: 247796c43b9aSAdrian Hunter row_cnt += 1 247896c43b9aSAdrian Hunter row = pos.row() 247996c43b9aSAdrian Hunter for c in range(col_cnt): 248096c43b9aSAdrian Hunter i = pos.sibling(row, c) 248196c43b9aSAdrian Hunter if c: 248296c43b9aSAdrian Hunter n = len(str(i.data())) 248396c43b9aSAdrian Hunter else: 248496c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 248596c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 248696c43b9aSAdrian Hunter n += expanded_mark_sz 248796c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 248896c43b9aSAdrian Hunter pos = view.indexBelow(pos) 248996c43b9aSAdrian Hunter if not selection.isSelected(pos): 249096c43b9aSAdrian Hunter break 249196c43b9aSAdrian Hunter 249296c43b9aSAdrian Hunter text = "" 249396c43b9aSAdrian Hunter pad = "" 249496c43b9aSAdrian Hunter sep = "" 249596c43b9aSAdrian Hunter if with_hdr: 249696c43b9aSAdrian Hunter for c in range(col_cnt): 249796c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 249896c43b9aSAdrian Hunter if as_csv: 249996c43b9aSAdrian Hunter text += sep + ToCSValue(val) 250096c43b9aSAdrian Hunter sep = "," 250196c43b9aSAdrian Hunter else: 250296c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 250396c43b9aSAdrian Hunter width = max_width[c] 250496c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 250596c43b9aSAdrian Hunter if align & Qt.AlignRight: 250696c43b9aSAdrian Hunter val = val.rjust(width) 250796c43b9aSAdrian Hunter text += pad + sep + val 250896c43b9aSAdrian Hunter pad = " " * (width - len(val)) 250996c43b9aSAdrian Hunter sep = " " 251096c43b9aSAdrian Hunter text += "\n" 251196c43b9aSAdrian Hunter pad = "" 251296c43b9aSAdrian Hunter sep = "" 251396c43b9aSAdrian Hunter 251496c43b9aSAdrian Hunter pos = first 251596c43b9aSAdrian Hunter while True: 251696c43b9aSAdrian Hunter row = pos.row() 251796c43b9aSAdrian Hunter for c in range(col_cnt): 251896c43b9aSAdrian Hunter i = pos.sibling(row, c) 251996c43b9aSAdrian Hunter val = str(i.data()) 252096c43b9aSAdrian Hunter if not c: 252196c43b9aSAdrian Hunter if model.hasChildren(i): 252296c43b9aSAdrian Hunter if view.isExpanded(i): 252396c43b9aSAdrian Hunter mark = expanded_mark 252496c43b9aSAdrian Hunter else: 252596c43b9aSAdrian Hunter mark = not_expanded_mark 252696c43b9aSAdrian Hunter else: 252796c43b9aSAdrian Hunter mark = leaf_mark 252896c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 252996c43b9aSAdrian Hunter if as_csv: 253096c43b9aSAdrian Hunter text += sep + ToCSValue(val) 253196c43b9aSAdrian Hunter sep = "," 253296c43b9aSAdrian Hunter else: 253396c43b9aSAdrian Hunter width = max_width[c] 253496c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 253596c43b9aSAdrian Hunter val = val.rjust(width) 253696c43b9aSAdrian Hunter text += pad + sep + val 253796c43b9aSAdrian Hunter pad = " " * (width - len(val)) 253896c43b9aSAdrian Hunter sep = " " 253996c43b9aSAdrian Hunter pos = view.indexBelow(pos) 254096c43b9aSAdrian Hunter if not selection.isSelected(pos): 254196c43b9aSAdrian Hunter break 254296c43b9aSAdrian Hunter text = text.rstrip() + "\n" 254396c43b9aSAdrian Hunter pad = "" 254496c43b9aSAdrian Hunter sep = "" 254596c43b9aSAdrian Hunter 254696c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 254796c43b9aSAdrian Hunter 254896c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 254996c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 255096c43b9aSAdrian Hunter 255196c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 255296c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 255396c43b9aSAdrian Hunter 255496c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 255596c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 255696c43b9aSAdrian Hunter 25579bc4e4bfSAdrian Hunter# Context menu 25589bc4e4bfSAdrian Hunter 25599bc4e4bfSAdrian Hunterclass ContextMenu(object): 25609bc4e4bfSAdrian Hunter 25619bc4e4bfSAdrian Hunter def __init__(self, view): 25629bc4e4bfSAdrian Hunter self.view = view 25639bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 25649bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 25659bc4e4bfSAdrian Hunter 25669bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 25679bc4e4bfSAdrian Hunter menu = QMenu(self.view) 25689bc4e4bfSAdrian Hunter self.AddActions(menu) 25699bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 25709bc4e4bfSAdrian Hunter 25719bc4e4bfSAdrian Hunter def AddCopy(self, menu): 25729bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 25739bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 25749bc4e4bfSAdrian Hunter 25759bc4e4bfSAdrian Hunter def AddActions(self, menu): 25769bc4e4bfSAdrian Hunter self.AddCopy(menu) 25779bc4e4bfSAdrian Hunter 25789bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 25799bc4e4bfSAdrian Hunter 25809bc4e4bfSAdrian Hunter def __init__(self, view): 25819bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 25829bc4e4bfSAdrian Hunter 25839bc4e4bfSAdrian Hunter def AddActions(self, menu): 25849bc4e4bfSAdrian Hunter i = self.view.currentIndex() 25859bc4e4bfSAdrian Hunter text = str(i.data()).strip() 25869bc4e4bfSAdrian Hunter if len(text): 25879bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 25889bc4e4bfSAdrian Hunter self.AddCopy(menu) 25899bc4e4bfSAdrian Hunter 25908392b74bSAdrian Hunter# Table window 25918392b74bSAdrian Hunter 25928392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 25938392b74bSAdrian Hunter 25948392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 25958392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 25968392b74bSAdrian Hunter 25978392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 25988392b74bSAdrian Hunter 25998392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 26008392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 26018392b74bSAdrian Hunter 26028392b74bSAdrian Hunter self.view = QTableView() 26038392b74bSAdrian Hunter self.view.setModel(self.model) 26048392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 26058392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 26068392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 26078392b74bSAdrian Hunter self.view.setSortingEnabled(True) 260896c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 260996c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 26108392b74bSAdrian Hunter 26118392b74bSAdrian Hunter self.ResizeColumnsToContents() 26128392b74bSAdrian Hunter 26139bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 26149bc4e4bfSAdrian Hunter 26158392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 26168392b74bSAdrian Hunter 26178392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 26188392b74bSAdrian Hunter 26198392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 26208392b74bSAdrian Hunter 26218392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 26228392b74bSAdrian Hunter 26238392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 26248392b74bSAdrian Hunter 26258392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 26268392b74bSAdrian Hunter 26278392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 26288392b74bSAdrian Hunter self.view.setFocus() 26298392b74bSAdrian Hunter self.find_bar.Busy() 26308392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 26318392b74bSAdrian Hunter 26328392b74bSAdrian Hunter def FindDone(self, row): 26338392b74bSAdrian Hunter self.find_bar.Idle() 26348392b74bSAdrian Hunter if row >= 0: 263535fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 26368392b74bSAdrian Hunter else: 26378392b74bSAdrian Hunter self.find_bar.NotFound() 26388392b74bSAdrian Hunter 26398392b74bSAdrian Hunter# Table list 26408392b74bSAdrian Hunter 26418392b74bSAdrian Hunterdef GetTableList(glb): 26428392b74bSAdrian Hunter tables = [] 26438392b74bSAdrian Hunter query = QSqlQuery(glb.db) 26448392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 26458392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 26468392b74bSAdrian Hunter else: 26478392b74bSAdrian 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") 26488392b74bSAdrian Hunter while query.next(): 26498392b74bSAdrian Hunter tables.append(query.value(0)) 26508392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 26518392b74bSAdrian Hunter tables.append("sqlite_master") 26528392b74bSAdrian Hunter else: 26538392b74bSAdrian Hunter tables.append("information_schema.tables") 26548392b74bSAdrian Hunter tables.append("information_schema.views") 26558392b74bSAdrian Hunter tables.append("information_schema.columns") 26568392b74bSAdrian Hunter return tables 26578392b74bSAdrian Hunter 2658cd358012SAdrian Hunter# Top Calls data model 2659cd358012SAdrian Hunter 2660cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2661cd358012SAdrian Hunter 2662cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2663cd358012SAdrian Hunter text = "" 2664cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2665cd358012SAdrian Hunter text = "::text" 2666cd358012SAdrian Hunter limit = "" 2667cd358012SAdrian Hunter if len(report_vars.limit): 2668cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2669cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2670cd358012SAdrian Hunter " CASE" 2671cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2672cd358012SAdrian Hunter " ELSE short_name" 2673cd358012SAdrian Hunter " END AS dso," 2674cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2675cd358012SAdrian Hunter " CASE" 2676cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2677cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2678cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2679cd358012SAdrian Hunter " ELSE ''" + text + 2680cd358012SAdrian Hunter " END AS flags" 2681cd358012SAdrian Hunter " FROM calls" 2682cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2683cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2684cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2685cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2686cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2687cd358012SAdrian Hunter report_vars.where_clause + 2688cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2689cd358012SAdrian Hunter limit 2690cd358012SAdrian Hunter ) 2691cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2692cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2693cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2694cd358012SAdrian Hunter 2695cd358012SAdrian Hunter def columnAlignment(self, column): 2696cd358012SAdrian Hunter return self.alignment[column] 2697cd358012SAdrian Hunter 2698cd358012SAdrian Hunter# Top Calls report creation dialog 2699cd358012SAdrian Hunter 2700cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2701cd358012SAdrian Hunter 2702cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2703cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2704cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2705cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2706cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2707cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2708cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2709cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2710cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2711cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2712cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2713cd358012SAdrian Hunter 2714cd358012SAdrian Hunter# Top Calls window 2715cd358012SAdrian Hunter 2716cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2717cd358012SAdrian Hunter 2718cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2719cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2720cd358012SAdrian Hunter 2721cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2722cd358012SAdrian Hunter self.model = self.data_model 2723cd358012SAdrian Hunter 2724cd358012SAdrian Hunter self.view = QTableView() 2725cd358012SAdrian Hunter self.view.setModel(self.model) 2726cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2727cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 272896c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 272996c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2730cd358012SAdrian Hunter 27319bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 27329bc4e4bfSAdrian Hunter 2733cd358012SAdrian Hunter self.ResizeColumnsToContents() 2734cd358012SAdrian Hunter 2735cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2736cd358012SAdrian Hunter 2737cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2738cd358012SAdrian Hunter 2739cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2740cd358012SAdrian Hunter 2741cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2742cd358012SAdrian Hunter 2743cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2744cd358012SAdrian Hunter 2745cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2746cd358012SAdrian Hunter 2747cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2748cd358012SAdrian Hunter self.view.setFocus() 2749cd358012SAdrian Hunter self.find_bar.Busy() 2750cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2751cd358012SAdrian Hunter 2752cd358012SAdrian Hunter def FindDone(self, row): 2753cd358012SAdrian Hunter self.find_bar.Idle() 2754cd358012SAdrian Hunter if row >= 0: 2755cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2756cd358012SAdrian Hunter else: 2757cd358012SAdrian Hunter self.find_bar.NotFound() 2758cd358012SAdrian Hunter 27591beb5c7bSAdrian Hunter# Action Definition 27601beb5c7bSAdrian Hunter 27611beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 27621beb5c7bSAdrian Hunter action = QAction(label, parent) 27631beb5c7bSAdrian Hunter if shortcut != None: 27641beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 27651beb5c7bSAdrian Hunter action.setStatusTip(tip) 27661beb5c7bSAdrian Hunter action.triggered.connect(callback) 27671beb5c7bSAdrian Hunter return action 27681beb5c7bSAdrian Hunter 27691beb5c7bSAdrian Hunter# Typical application actions 27701beb5c7bSAdrian Hunter 27711beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 27721beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 27731beb5c7bSAdrian Hunter 27741beb5c7bSAdrian Hunter# Typical MDI actions 27751beb5c7bSAdrian Hunter 27761beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 27771beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 27781beb5c7bSAdrian Hunter 27791beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 27801beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 27811beb5c7bSAdrian Hunter 27821beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 27831beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 27841beb5c7bSAdrian Hunter 27851beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 27861beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 27871beb5c7bSAdrian Hunter 27881beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 27891beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 27901beb5c7bSAdrian Hunter 27911beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 27921beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 27931beb5c7bSAdrian Hunter 27941beb5c7bSAdrian Hunter# Typical MDI window menu 27951beb5c7bSAdrian Hunter 27961beb5c7bSAdrian Hunterclass WindowMenu(): 27971beb5c7bSAdrian Hunter 27981beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 27991beb5c7bSAdrian Hunter self.mdi_area = mdi_area 28001beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 28011beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 28021beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 28031beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 28041beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 28051beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 28061beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 28071beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 28081beb5c7bSAdrian Hunter 28091beb5c7bSAdrian Hunter def Update(self): 28101beb5c7bSAdrian Hunter self.window_menu.clear() 28111beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 28121beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 28131beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 28141beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 28151beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 28161beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 28171beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 28181beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 28191beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 28201beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 28211beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28221beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 28231beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 28241beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28251beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 28261beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 28271beb5c7bSAdrian Hunter if sub_window_count == 0: 28281beb5c7bSAdrian Hunter return 28291beb5c7bSAdrian Hunter self.window_menu.addSeparator() 28301beb5c7bSAdrian Hunter nr = 1 28311beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 28321beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 28331beb5c7bSAdrian Hunter if nr < 10: 28341beb5c7bSAdrian Hunter label = "&" + label 28351beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 28361beb5c7bSAdrian Hunter action.setCheckable(True) 28371beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 2838df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 28391beb5c7bSAdrian Hunter self.window_menu.addAction(action) 28401beb5c7bSAdrian Hunter nr += 1 28411beb5c7bSAdrian Hunter 28421beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 28431beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 28441beb5c7bSAdrian Hunter 284565b24292SAdrian Hunter# Help text 284665b24292SAdrian Hunter 284765b24292SAdrian Hunterglb_help_text = """ 284865b24292SAdrian Hunter<h1>Contents</h1> 284965b24292SAdrian Hunter<style> 285065b24292SAdrian Hunterp.c1 { 285165b24292SAdrian Hunter text-indent: 40px; 285265b24292SAdrian Hunter} 285365b24292SAdrian Hunterp.c2 { 285465b24292SAdrian Hunter text-indent: 80px; 285565b24292SAdrian Hunter} 285665b24292SAdrian Hunter} 285765b24292SAdrian Hunter</style> 285865b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 285965b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2860ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2861ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2862ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2863ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 286465b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 286565b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 286665b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 286765b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 286865b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 286965b24292SAdrian Hunterwidths to suit will display something like: 287065b24292SAdrian Hunter<pre> 287165b24292SAdrian Hunter Call Graph: pt_example 287265b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 287365b24292SAdrian Hunterv- ls 287465b24292SAdrian Hunter v- 2638:2638 287565b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 287665b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 287765b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 287865b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 287965b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 288065b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 288165b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 288265b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 288365b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 288465b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 288565b24292SAdrian Hunter</pre> 288665b24292SAdrian Hunter<h3>Points to note:</h3> 288765b24292SAdrian Hunter<ul> 288865b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 288965b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 289065b24292SAdrian Hunter<li>Subsequent levels are functions</li> 289165b24292SAdrian Hunter<li>'Count' is the number of calls</li> 289265b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 289365b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 289465b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 289565b24292SAdrian Hunter</ul> 289665b24292SAdrian Hunter<h3>Find</h3> 289765b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 289865b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2899ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2900ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2901ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2902ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 290365b24292SAdrian HunterThe All branches report displays all branches in chronological order. 290465b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 290565b24292SAdrian Hunter<h3>Disassembly</h3> 290665b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 290765b24292SAdrian Hunter<ol> 290865b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 290965b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 291065b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 291165b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 291265b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 291365b24292SAdrian Hunter</ol> 291465b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 291565b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 291665b24292SAdrian Hunter<pre> 291765b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 291865b24292SAdrian Huntergit clone https://github.com/intelxed/xed 291965b24292SAdrian Huntercd xed 292065b24292SAdrian Hunter./mfile.py --share 292165b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 292265b24292SAdrian Huntersudo ldconfig 292365b24292SAdrian Hunter</pre> 2924530e22fdSAdrian Hunter<h3>Instructions per Cycle (IPC)</h3> 2925530e22fdSAdrian HunterIf available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. 2926530e22fdSAdrian Hunter<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. 2927530e22fdSAdrian HunterDue to the granularity of timing information, the number of cycles for some code blocks will not be known. 2928530e22fdSAdrian HunterIn that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period 2929530e22fdSAdrian Huntersince the previous displayed 'IPC'. 293065b24292SAdrian Hunter<h3>Find</h3> 293165b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 293265b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 293365b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 2934ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 293565b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 293665b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 2937ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 293865b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 293965b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 294065b24292SAdrian Hunter<pre> 294165b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 294265b24292SAdrian Hunter 100us-200us From 100us to 200us 294365b24292SAdrian Hunter 10ms- From 10ms to the end 294465b24292SAdrian Hunter -100ns The first 100ns 294565b24292SAdrian Hunter -10ms- The last 10ms 294665b24292SAdrian Hunter</pre> 294765b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2948ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 2949cd358012SAdrian 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. 2950cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2951cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 295265b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 295365b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 295465b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 295565b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 295665b24292SAdrian Hunterbut that can be slow for large tables. 295765b24292SAdrian Hunter<p>There are also tables of database meta-information. 295865b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 295965b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 296065b24292SAdrian Hunter<h3>Find</h3> 296165b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 296265b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 296365b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 296435fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 296535fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 296665b24292SAdrian Hunter""" 296765b24292SAdrian Hunter 296865b24292SAdrian Hunter# Help window 296965b24292SAdrian Hunter 297065b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 297165b24292SAdrian Hunter 297265b24292SAdrian Hunter def __init__(self, glb, parent=None): 297365b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 297465b24292SAdrian Hunter 297565b24292SAdrian Hunter self.text = QTextBrowser() 297665b24292SAdrian Hunter self.text.setHtml(glb_help_text) 297765b24292SAdrian Hunter self.text.setReadOnly(True) 297865b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 297965b24292SAdrian Hunter 298065b24292SAdrian Hunter self.setWidget(self.text) 298165b24292SAdrian Hunter 298265b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 298365b24292SAdrian Hunter 298465b24292SAdrian Hunter# Main window that only displays the help text 298565b24292SAdrian Hunter 298665b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 298765b24292SAdrian Hunter 298865b24292SAdrian Hunter def __init__(self, parent=None): 298965b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 299065b24292SAdrian Hunter 299165b24292SAdrian Hunter self.setMinimumSize(200, 100) 299265b24292SAdrian Hunter self.resize(800, 600) 299365b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 299465b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 299565b24292SAdrian Hunter 299665b24292SAdrian Hunter self.text = QTextBrowser() 299765b24292SAdrian Hunter self.text.setHtml(glb_help_text) 299865b24292SAdrian Hunter self.text.setReadOnly(True) 299965b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 300065b24292SAdrian Hunter 300165b24292SAdrian Hunter self.setCentralWidget(self.text) 300265b24292SAdrian Hunter 3003b62d18abSAdrian Hunter# PostqreSQL server version 3004b62d18abSAdrian Hunter 3005b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 3006b62d18abSAdrian Hunter query = QSqlQuery(db) 3007b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 3008b62d18abSAdrian Hunter if query.next(): 3009b62d18abSAdrian Hunter v_str = query.value(0) 3010b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 3011b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 3012b62d18abSAdrian Hunter return v_list[1] 3013b62d18abSAdrian Hunter return v_str 3014b62d18abSAdrian Hunter return "Unknown" 3015b62d18abSAdrian Hunter 3016b62d18abSAdrian Hunter# SQLite version 3017b62d18abSAdrian Hunter 3018b62d18abSAdrian Hunterdef SQLiteVersion(db): 3019b62d18abSAdrian Hunter query = QSqlQuery(db) 3020b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 3021b62d18abSAdrian Hunter if query.next(): 3022b62d18abSAdrian Hunter return query.value(0) 3023b62d18abSAdrian Hunter return "Unknown" 3024b62d18abSAdrian Hunter 3025b62d18abSAdrian Hunter# About dialog 3026b62d18abSAdrian Hunter 3027b62d18abSAdrian Hunterclass AboutDialog(QDialog): 3028b62d18abSAdrian Hunter 3029b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 3030b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 3031b62d18abSAdrian Hunter 3032b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 3033b62d18abSAdrian Hunter self.setMinimumWidth(300) 3034b62d18abSAdrian Hunter 3035b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 3036b62d18abSAdrian Hunter 3037b62d18abSAdrian Hunter text = "<pre>" 3038b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 3039b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 3040b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 3041b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 3042b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 3043b62d18abSAdrian Hunter else: 3044b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 3045b62d18abSAdrian Hunter text += "</pre>" 3046b62d18abSAdrian Hunter 3047b62d18abSAdrian Hunter self.text = QTextBrowser() 3048b62d18abSAdrian Hunter self.text.setHtml(text) 3049b62d18abSAdrian Hunter self.text.setReadOnly(True) 3050b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 3051b62d18abSAdrian Hunter 3052b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 3053b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 3054b62d18abSAdrian Hunter 3055b62d18abSAdrian Hunter self.setLayout(self.vbox); 3056b62d18abSAdrian Hunter 305782f68e28SAdrian Hunter# Font resize 305882f68e28SAdrian Hunter 305982f68e28SAdrian Hunterdef ResizeFont(widget, diff): 306082f68e28SAdrian Hunter font = widget.font() 306182f68e28SAdrian Hunter sz = font.pointSize() 306282f68e28SAdrian Hunter font.setPointSize(sz + diff) 306382f68e28SAdrian Hunter widget.setFont(font) 306482f68e28SAdrian Hunter 306582f68e28SAdrian Hunterdef ShrinkFont(widget): 306682f68e28SAdrian Hunter ResizeFont(widget, -1) 306782f68e28SAdrian Hunter 306882f68e28SAdrian Hunterdef EnlargeFont(widget): 306982f68e28SAdrian Hunter ResizeFont(widget, 1) 307082f68e28SAdrian Hunter 30711beb5c7bSAdrian Hunter# Unique name for sub-windows 30721beb5c7bSAdrian Hunter 30731beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 30741beb5c7bSAdrian Hunter if nr > 1: 30751beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 30761beb5c7bSAdrian Hunter return name 30771beb5c7bSAdrian Hunter 30781beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 30791beb5c7bSAdrian Hunter nr = 1 30801beb5c7bSAdrian Hunter while True: 30811beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 30821beb5c7bSAdrian Hunter ok = True 30831beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 30841beb5c7bSAdrian Hunter if sub_window.name == unique_name: 30851beb5c7bSAdrian Hunter ok = False 30861beb5c7bSAdrian Hunter break 30871beb5c7bSAdrian Hunter if ok: 30881beb5c7bSAdrian Hunter return unique_name 30891beb5c7bSAdrian Hunter nr += 1 30901beb5c7bSAdrian Hunter 30911beb5c7bSAdrian Hunter# Add a sub-window 30921beb5c7bSAdrian Hunter 30931beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 30941beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 30951beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 30961beb5c7bSAdrian Hunter sub_window.resize(800, 600) 30971beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 30981beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 30991beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 31001beb5c7bSAdrian Hunter sub_window.name = unique_name 31011beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 31021beb5c7bSAdrian Hunter sub_window.show() 31031beb5c7bSAdrian Hunter 3104031c2a00SAdrian Hunter# Main window 3105031c2a00SAdrian Hunter 3106031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 3107031c2a00SAdrian Hunter 3108031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 3109031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 3110031c2a00SAdrian Hunter 3111031c2a00SAdrian Hunter self.glb = glb 3112031c2a00SAdrian Hunter 31131beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 3114031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 3115031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 3116031c2a00SAdrian Hunter 31171beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 31181beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 31191beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 3120031c2a00SAdrian Hunter 31211beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 3122031c2a00SAdrian Hunter 31231beb5c7bSAdrian Hunter menu = self.menuBar() 3124031c2a00SAdrian Hunter 31251beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 31261beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 31271beb5c7bSAdrian Hunter 3128ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 312996c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 313096c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 3131ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 31328392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 313382f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 313482f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 3135ebd70c7dSAdrian Hunter 31361beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 3137655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 31381beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 31391beb5c7bSAdrian Hunter 3140ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3141ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3142ae8b887cSAdrian Hunter 314376099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 314476099f98SAdrian Hunter 3145cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3146cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3147cd358012SAdrian Hunter 31488392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 31498392b74bSAdrian Hunter 31501beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 31511beb5c7bSAdrian Hunter 315265b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 315365b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 3154b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 315565b24292SAdrian Hunter 31564b208453SAdrian Hunter def Try(self, fn): 31574b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 31584b208453SAdrian Hunter if win: 31594b208453SAdrian Hunter try: 31604b208453SAdrian Hunter fn(win.view) 31614b208453SAdrian Hunter except: 31624b208453SAdrian Hunter pass 31634b208453SAdrian Hunter 316496c43b9aSAdrian Hunter def CopyToClipboard(self): 316596c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 316696c43b9aSAdrian Hunter 316796c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 316896c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 316996c43b9aSAdrian Hunter 3170ebd70c7dSAdrian Hunter def Find(self): 3171ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3172ebd70c7dSAdrian Hunter if win: 3173ebd70c7dSAdrian Hunter try: 3174ebd70c7dSAdrian Hunter win.find_bar.Activate() 3175ebd70c7dSAdrian Hunter except: 3176ebd70c7dSAdrian Hunter pass 3177ebd70c7dSAdrian Hunter 31788392b74bSAdrian Hunter def FetchMoreRecords(self): 31798392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 31808392b74bSAdrian Hunter if win: 31818392b74bSAdrian Hunter try: 31828392b74bSAdrian Hunter win.fetch_bar.Activate() 31838392b74bSAdrian Hunter except: 31848392b74bSAdrian Hunter pass 31858392b74bSAdrian Hunter 318682f68e28SAdrian Hunter def ShrinkFont(self): 31874b208453SAdrian Hunter self.Try(ShrinkFont) 318882f68e28SAdrian Hunter 318982f68e28SAdrian Hunter def EnlargeFont(self): 31904b208453SAdrian Hunter self.Try(EnlargeFont) 319182f68e28SAdrian Hunter 319276099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 319376099f98SAdrian Hunter branches_events = 0 319476099f98SAdrian Hunter for event in events: 319576099f98SAdrian Hunter event = event.split(":")[0] 319676099f98SAdrian Hunter if event == "branches": 319776099f98SAdrian Hunter branches_events += 1 319876099f98SAdrian Hunter dbid = 0 319976099f98SAdrian Hunter for event in events: 320076099f98SAdrian Hunter dbid += 1 320176099f98SAdrian Hunter event = event.split(":")[0] 320276099f98SAdrian Hunter if event == "branches": 320376099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 3204df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 3205210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3206df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 320776099f98SAdrian Hunter 32088392b74bSAdrian Hunter def TableMenu(self, tables, menu): 32098392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 32108392b74bSAdrian Hunter for table in tables: 3211df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 32128392b74bSAdrian Hunter 32131beb5c7bSAdrian Hunter def NewCallGraph(self): 32141beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3215031c2a00SAdrian Hunter 3216ae8b887cSAdrian Hunter def NewCallTree(self): 3217ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3218ae8b887cSAdrian Hunter 3219cd358012SAdrian Hunter def NewTopCalls(self): 3220cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3221cd358012SAdrian Hunter ret = dialog.exec_() 3222cd358012SAdrian Hunter if ret: 3223cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3224cd358012SAdrian Hunter 322576099f98SAdrian Hunter def NewBranchView(self, event_id): 3226947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 322776099f98SAdrian Hunter 3228210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3229210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3230210cf1f9SAdrian Hunter ret = dialog.exec_() 3231210cf1f9SAdrian Hunter if ret: 3232947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3233210cf1f9SAdrian Hunter 32348392b74bSAdrian Hunter def NewTableView(self, table_name): 32358392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 32368392b74bSAdrian Hunter 323765b24292SAdrian Hunter def Help(self): 323865b24292SAdrian Hunter HelpWindow(self.glb, self) 323965b24292SAdrian Hunter 3240b62d18abSAdrian Hunter def About(self): 3241b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 3242b62d18abSAdrian Hunter dialog.exec_() 3243b62d18abSAdrian Hunter 324476099f98SAdrian Hunter# XED Disassembler 324576099f98SAdrian Hunter 324676099f98SAdrian Hunterclass xed_state_t(Structure): 324776099f98SAdrian Hunter 324876099f98SAdrian Hunter _fields_ = [ 324976099f98SAdrian Hunter ("mode", c_int), 325076099f98SAdrian Hunter ("width", c_int) 325176099f98SAdrian Hunter ] 325276099f98SAdrian Hunter 325376099f98SAdrian Hunterclass XEDInstruction(): 325476099f98SAdrian Hunter 325576099f98SAdrian Hunter def __init__(self, libxed): 325676099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 325776099f98SAdrian Hunter xedd_t = c_byte * 512 325876099f98SAdrian Hunter self.xedd = xedd_t() 325976099f98SAdrian Hunter self.xedp = addressof(self.xedd) 326076099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 326176099f98SAdrian Hunter self.state = xed_state_t() 326276099f98SAdrian Hunter self.statep = addressof(self.state) 326376099f98SAdrian Hunter # Buffer for disassembled instruction text 326476099f98SAdrian Hunter self.buffer = create_string_buffer(256) 326576099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 326676099f98SAdrian Hunter 326776099f98SAdrian Hunterclass LibXED(): 326876099f98SAdrian Hunter 326976099f98SAdrian Hunter def __init__(self): 32705ed4419dSAdrian Hunter try: 327176099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 32725ed4419dSAdrian Hunter except: 32735ed4419dSAdrian Hunter self.libxed = None 32745ed4419dSAdrian Hunter if not self.libxed: 32755ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 327676099f98SAdrian Hunter 327776099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 327876099f98SAdrian Hunter self.xed_tables_init.restype = None 327976099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 328076099f98SAdrian Hunter 328176099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 328276099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 328376099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 328476099f98SAdrian Hunter 328576099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 328676099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 328776099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 328876099f98SAdrian Hunter 328976099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 329076099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 329176099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 329276099f98SAdrian Hunter 329376099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 329476099f98SAdrian Hunter self.xed_decode.restype = c_int 329576099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 329676099f98SAdrian Hunter 329776099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 329876099f98SAdrian Hunter self.xed_format_context.restype = c_uint 329976099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 330076099f98SAdrian Hunter 330176099f98SAdrian Hunter self.xed_tables_init() 330276099f98SAdrian Hunter 330376099f98SAdrian Hunter def Instruction(self): 330476099f98SAdrian Hunter return XEDInstruction(self) 330576099f98SAdrian Hunter 330676099f98SAdrian Hunter def SetMode(self, inst, mode): 330776099f98SAdrian Hunter if mode: 330876099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 330976099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 331076099f98SAdrian Hunter else: 331176099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 331276099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 331376099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 331476099f98SAdrian Hunter 331576099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 331676099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 331776099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 331876099f98SAdrian Hunter if err: 331976099f98SAdrian Hunter return 0, "" 332076099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 332176099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 332276099f98SAdrian Hunter if not ok: 332376099f98SAdrian Hunter return 0, "" 3324606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3325606bd60aSAdrian Hunter result = inst.buffer.value 3326606bd60aSAdrian Hunter else: 3327606bd60aSAdrian Hunter result = inst.buffer.value.decode() 332876099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 332976099f98SAdrian Hunter # For now, assume the length is in byte 166 3330606bd60aSAdrian Hunter return inst.xedd[166], result 333176099f98SAdrian Hunter 333276099f98SAdrian Hunterdef TryOpen(file_name): 333376099f98SAdrian Hunter try: 333476099f98SAdrian Hunter return open(file_name, "rb") 333576099f98SAdrian Hunter except: 333676099f98SAdrian Hunter return None 333776099f98SAdrian Hunter 333876099f98SAdrian Hunterdef Is64Bit(f): 333976099f98SAdrian Hunter result = sizeof(c_void_p) 334076099f98SAdrian Hunter # ELF support only 334176099f98SAdrian Hunter pos = f.tell() 334276099f98SAdrian Hunter f.seek(0) 334376099f98SAdrian Hunter header = f.read(7) 334476099f98SAdrian Hunter f.seek(pos) 334576099f98SAdrian Hunter magic = header[0:4] 3346606bd60aSAdrian Hunter if sys.version_info[0] == 2: 334776099f98SAdrian Hunter eclass = ord(header[4]) 334876099f98SAdrian Hunter encoding = ord(header[5]) 334976099f98SAdrian Hunter version = ord(header[6]) 3350606bd60aSAdrian Hunter else: 3351606bd60aSAdrian Hunter eclass = header[4] 3352606bd60aSAdrian Hunter encoding = header[5] 3353606bd60aSAdrian Hunter version = header[6] 335476099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 335576099f98SAdrian Hunter result = True if eclass == 2 else False 335676099f98SAdrian Hunter return result 335776099f98SAdrian Hunter 3358031c2a00SAdrian Hunter# Global data 3359031c2a00SAdrian Hunter 3360031c2a00SAdrian Hunterclass Glb(): 3361031c2a00SAdrian Hunter 3362031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3363031c2a00SAdrian Hunter self.dbref = dbref 3364031c2a00SAdrian Hunter self.db = db 3365031c2a00SAdrian Hunter self.dbname = dbname 336676099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 336776099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 336876099f98SAdrian Hunter if self.buildid_dir: 336976099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 337076099f98SAdrian Hunter else: 337176099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3372031c2a00SAdrian Hunter self.app = None 3373031c2a00SAdrian Hunter self.mainwindow = None 33748392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 337576099f98SAdrian Hunter try: 337676099f98SAdrian Hunter self.disassembler = LibXED() 337776099f98SAdrian Hunter self.have_disassembler = True 337876099f98SAdrian Hunter except: 337976099f98SAdrian Hunter self.have_disassembler = False 338076099f98SAdrian Hunter 338176099f98SAdrian Hunter def FileFromBuildId(self, build_id): 338276099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 338376099f98SAdrian Hunter return TryOpen(file_name) 338476099f98SAdrian Hunter 338576099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 338676099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 338776099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 338876099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 338976099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 339076099f98SAdrian Hunter if f: 339176099f98SAdrian Hunter return f 339276099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 339376099f98SAdrian Hunter f = TryOpen(long_name) 339476099f98SAdrian Hunter if f: 339576099f98SAdrian Hunter return f 339676099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 339776099f98SAdrian Hunter if f: 339876099f98SAdrian Hunter return f 339976099f98SAdrian Hunter return None 34008392b74bSAdrian Hunter 34018392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 34028392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 34038392b74bSAdrian Hunter 34048392b74bSAdrian Hunter # Shutdown any background processes or threads 34058392b74bSAdrian Hunter def ShutdownInstances(self): 34068392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 34078392b74bSAdrian Hunter try: 34088392b74bSAdrian Hunter x.Shutdown() 34098392b74bSAdrian Hunter except: 34108392b74bSAdrian Hunter pass 3411031c2a00SAdrian Hunter 3412031c2a00SAdrian Hunter# Database reference 3413031c2a00SAdrian Hunter 3414031c2a00SAdrian Hunterclass DBRef(): 3415031c2a00SAdrian Hunter 3416031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3417031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3418031c2a00SAdrian Hunter self.dbname = dbname 3419031c2a00SAdrian Hunter 3420031c2a00SAdrian Hunter def Open(self, connection_name): 3421031c2a00SAdrian Hunter dbname = self.dbname 3422031c2a00SAdrian Hunter if self.is_sqlite3: 3423031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3424031c2a00SAdrian Hunter else: 3425031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3426031c2a00SAdrian Hunter opts = dbname.split() 3427031c2a00SAdrian Hunter for opt in opts: 3428031c2a00SAdrian Hunter if "=" in opt: 3429031c2a00SAdrian Hunter opt = opt.split("=") 3430031c2a00SAdrian Hunter if opt[0] == "hostname": 3431031c2a00SAdrian Hunter db.setHostName(opt[1]) 3432031c2a00SAdrian Hunter elif opt[0] == "port": 3433031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3434031c2a00SAdrian Hunter elif opt[0] == "username": 3435031c2a00SAdrian Hunter db.setUserName(opt[1]) 3436031c2a00SAdrian Hunter elif opt[0] == "password": 3437031c2a00SAdrian Hunter db.setPassword(opt[1]) 3438031c2a00SAdrian Hunter elif opt[0] == "dbname": 3439031c2a00SAdrian Hunter dbname = opt[1] 3440031c2a00SAdrian Hunter else: 3441031c2a00SAdrian Hunter dbname = opt 3442031c2a00SAdrian Hunter 3443031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3444031c2a00SAdrian Hunter if not db.open(): 3445031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3446031c2a00SAdrian Hunter return db, dbname 3447031c2a00SAdrian Hunter 3448031c2a00SAdrian Hunter# Main 3449031c2a00SAdrian Hunter 3450031c2a00SAdrian Hunterdef Main(): 34511ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 34521ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 34531ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 3454df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 34551ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 34561ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 34571ed7f47fSAdrian Hunter args = ap.parse_args() 3458031c2a00SAdrian Hunter 34591ed7f47fSAdrian Hunter if args.help_only: 346065b24292SAdrian Hunter app = QApplication(sys.argv) 346165b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 346265b24292SAdrian Hunter mainwindow.show() 346365b24292SAdrian Hunter err = app.exec_() 346465b24292SAdrian Hunter sys.exit(err) 3465031c2a00SAdrian Hunter 34661ed7f47fSAdrian Hunter dbname = args.dbname 34671ed7f47fSAdrian Hunter if dbname is None: 34681ed7f47fSAdrian Hunter ap.print_usage() 34691ed7f47fSAdrian Hunter print("Too few arguments") 34701ed7f47fSAdrian Hunter sys.exit(1) 34711ed7f47fSAdrian Hunter 3472031c2a00SAdrian Hunter is_sqlite3 = False 3473031c2a00SAdrian Hunter try: 3474beda0e72STony Jones f = open(dbname, "rb") 3475beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3476031c2a00SAdrian Hunter is_sqlite3 = True 3477031c2a00SAdrian Hunter f.close() 3478031c2a00SAdrian Hunter except: 3479031c2a00SAdrian Hunter pass 3480031c2a00SAdrian Hunter 3481031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3482031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3483031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3484031c2a00SAdrian Hunter app = QApplication(sys.argv) 3485031c2a00SAdrian Hunter glb.app = app 3486031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3487031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3488031c2a00SAdrian Hunter mainwindow.show() 3489031c2a00SAdrian Hunter err = app.exec_() 34908392b74bSAdrian Hunter glb.ShutdownInstances() 3491031c2a00SAdrian Hunter db.close() 3492031c2a00SAdrian Hunter sys.exit(err) 3493031c2a00SAdrian Hunter 3494031c2a00SAdrian Hunterif __name__ == "__main__": 3495031c2a00SAdrian Hunter Main() 3496