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 108*df8ea22aSAdrian Hunter 109*df8ea22aSAdrian Hunterpyside_version_1 = True 110*df8ea22aSAdrian Hunterif not "--pyside-version-1" in sys.argv: 111*df8ea22aSAdrian Hunter try: 112*df8ea22aSAdrian Hunter from PySide2.QtCore import * 113*df8ea22aSAdrian Hunter from PySide2.QtGui import * 114*df8ea22aSAdrian Hunter from PySide2.QtSql import * 115*df8ea22aSAdrian Hunter from PySide2.QtWidgets import * 116*df8ea22aSAdrian Hunter pyside_version_1 = False 117*df8ea22aSAdrian Hunter except: 118*df8ea22aSAdrian Hunter pass 119*df8ea22aSAdrian Hunter 120*df8ea22aSAdrian Hunterif pyside_version_1: 121031c2a00SAdrian Hunter from PySide.QtCore import * 122031c2a00SAdrian Hunter from PySide.QtGui import * 123031c2a00SAdrian Hunter from PySide.QtSql import * 124*df8ea22aSAdrian 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 203a448ba23SAdrian Hunter def __init__(self, glb, parent=None): 204031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 205a448ba23SAdrian Hunter self.glb = glb 206a448ba23SAdrian Hunter self.root = self.GetRoot() 207031c2a00SAdrian Hunter self.last_row_read = 0 208031c2a00SAdrian Hunter 209031c2a00SAdrian Hunter def Item(self, parent): 210031c2a00SAdrian Hunter if parent.isValid(): 211031c2a00SAdrian Hunter return parent.internalPointer() 212031c2a00SAdrian Hunter else: 213031c2a00SAdrian Hunter return self.root 214031c2a00SAdrian Hunter 215031c2a00SAdrian Hunter def rowCount(self, parent): 216031c2a00SAdrian Hunter result = self.Item(parent).childCount() 217031c2a00SAdrian Hunter if result < 0: 218031c2a00SAdrian Hunter result = 0 219031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 220031c2a00SAdrian Hunter return result 221031c2a00SAdrian Hunter 222031c2a00SAdrian Hunter def hasChildren(self, parent): 223031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 224031c2a00SAdrian Hunter 225031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 226031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 227031c2a00SAdrian Hunter return self.columnAlignment(section) 228031c2a00SAdrian Hunter if role != Qt.DisplayRole: 229031c2a00SAdrian Hunter return None 230031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 231031c2a00SAdrian Hunter return None 232031c2a00SAdrian Hunter return self.columnHeader(section) 233031c2a00SAdrian Hunter 234031c2a00SAdrian Hunter def parent(self, child): 235031c2a00SAdrian Hunter child_item = child.internalPointer() 236031c2a00SAdrian Hunter if child_item is self.root: 237031c2a00SAdrian Hunter return QModelIndex() 238031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 239031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 240031c2a00SAdrian Hunter 241031c2a00SAdrian Hunter def index(self, row, column, parent): 242031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 243031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 244031c2a00SAdrian Hunter 245031c2a00SAdrian Hunter def DisplayData(self, item, index): 246031c2a00SAdrian Hunter return item.getData(index.column()) 247031c2a00SAdrian Hunter 2488392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2498392b74bSAdrian Hunter if row > self.last_row_read: 2508392b74bSAdrian Hunter self.last_row_read = row 2518392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2528392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2538392b74bSAdrian Hunter 2548392b74bSAdrian Hunter def columnAlignment(self, column): 2558392b74bSAdrian Hunter return Qt.AlignLeft 2568392b74bSAdrian Hunter 2578392b74bSAdrian Hunter def columnFont(self, column): 2588392b74bSAdrian Hunter return None 2598392b74bSAdrian Hunter 2608392b74bSAdrian Hunter def data(self, index, role): 2618392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2628392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2638392b74bSAdrian Hunter if role == Qt.FontRole: 2648392b74bSAdrian Hunter return self.columnFont(index.column()) 2658392b74bSAdrian Hunter if role != Qt.DisplayRole: 2668392b74bSAdrian Hunter return None 2678392b74bSAdrian Hunter item = index.internalPointer() 2688392b74bSAdrian Hunter return self.DisplayData(item, index) 2698392b74bSAdrian Hunter 2708392b74bSAdrian Hunter# Table data model 2718392b74bSAdrian Hunter 2728392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2738392b74bSAdrian Hunter 2748392b74bSAdrian Hunter def __init__(self, parent=None): 2758392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2768392b74bSAdrian Hunter self.child_count = 0 2778392b74bSAdrian Hunter self.child_items = [] 2788392b74bSAdrian Hunter self.last_row_read = 0 2798392b74bSAdrian Hunter 2808392b74bSAdrian Hunter def Item(self, parent): 2818392b74bSAdrian Hunter if parent.isValid(): 2828392b74bSAdrian Hunter return parent.internalPointer() 2838392b74bSAdrian Hunter else: 2848392b74bSAdrian Hunter return self 2858392b74bSAdrian Hunter 2868392b74bSAdrian Hunter def rowCount(self, parent): 2878392b74bSAdrian Hunter return self.child_count 2888392b74bSAdrian Hunter 2898392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2908392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2918392b74bSAdrian Hunter return self.columnAlignment(section) 2928392b74bSAdrian Hunter if role != Qt.DisplayRole: 2938392b74bSAdrian Hunter return None 2948392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2958392b74bSAdrian Hunter return None 2968392b74bSAdrian Hunter return self.columnHeader(section) 2978392b74bSAdrian Hunter 2988392b74bSAdrian Hunter def index(self, row, column, parent): 2998392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 3008392b74bSAdrian Hunter 3018392b74bSAdrian Hunter def DisplayData(self, item, index): 3028392b74bSAdrian Hunter return item.getData(index.column()) 3038392b74bSAdrian Hunter 3048392b74bSAdrian Hunter def FetchIfNeeded(self, row): 3058392b74bSAdrian Hunter if row > self.last_row_read: 3068392b74bSAdrian Hunter self.last_row_read = row 3078392b74bSAdrian Hunter if row + 10 >= self.child_count: 3088392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 3098392b74bSAdrian Hunter 310031c2a00SAdrian Hunter def columnAlignment(self, column): 311031c2a00SAdrian Hunter return Qt.AlignLeft 312031c2a00SAdrian Hunter 313031c2a00SAdrian Hunter def columnFont(self, column): 314031c2a00SAdrian Hunter return None 315031c2a00SAdrian Hunter 316031c2a00SAdrian Hunter def data(self, index, role): 317031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 318031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 319031c2a00SAdrian Hunter if role == Qt.FontRole: 320031c2a00SAdrian Hunter return self.columnFont(index.column()) 321031c2a00SAdrian Hunter if role != Qt.DisplayRole: 322031c2a00SAdrian Hunter return None 323031c2a00SAdrian Hunter item = index.internalPointer() 324031c2a00SAdrian Hunter return self.DisplayData(item, index) 325031c2a00SAdrian Hunter 3261beb5c7bSAdrian Hunter# Model cache 3271beb5c7bSAdrian Hunter 3281beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 3291beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 3301beb5c7bSAdrian Hunter 3311beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 3321beb5c7bSAdrian Hunter model_cache_lock.acquire() 3331beb5c7bSAdrian Hunter try: 3341beb5c7bSAdrian Hunter model = model_cache[model_name] 3351beb5c7bSAdrian Hunter except: 3361beb5c7bSAdrian Hunter model = None 3371beb5c7bSAdrian Hunter if model is None: 3381beb5c7bSAdrian Hunter model = create_fn() 3391beb5c7bSAdrian Hunter model_cache[model_name] = model 3401beb5c7bSAdrian Hunter model_cache_lock.release() 3411beb5c7bSAdrian Hunter return model 3421beb5c7bSAdrian Hunter 343ebd70c7dSAdrian Hunter# Find bar 344ebd70c7dSAdrian Hunter 345ebd70c7dSAdrian Hunterclass FindBar(): 346ebd70c7dSAdrian Hunter 347ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 348ebd70c7dSAdrian Hunter self.finder = finder 349ebd70c7dSAdrian Hunter self.context = [] 350ebd70c7dSAdrian Hunter self.last_value = None 351ebd70c7dSAdrian Hunter self.last_pattern = None 352ebd70c7dSAdrian Hunter 353ebd70c7dSAdrian Hunter label = QLabel("Find:") 354ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 355ebd70c7dSAdrian Hunter 356ebd70c7dSAdrian Hunter self.textbox = QComboBox() 357ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 358ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 359ebd70c7dSAdrian Hunter 360ebd70c7dSAdrian Hunter self.progress = QProgressBar() 361ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 362ebd70c7dSAdrian Hunter self.progress.hide() 363ebd70c7dSAdrian Hunter 364ebd70c7dSAdrian Hunter if is_reg_expr: 365ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 366ebd70c7dSAdrian Hunter else: 367ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 368ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 369ebd70c7dSAdrian Hunter 370ebd70c7dSAdrian Hunter self.next_button = QToolButton() 371ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 372ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 373ebd70c7dSAdrian Hunter 374ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 375ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 376ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 377ebd70c7dSAdrian Hunter 378ebd70c7dSAdrian Hunter self.close_button = QToolButton() 379ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 380ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 381ebd70c7dSAdrian Hunter 382ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 383ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 384ebd70c7dSAdrian Hunter 385ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 386ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 387ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 388ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 389ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 390ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 391ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 392ebd70c7dSAdrian Hunter 393ebd70c7dSAdrian Hunter self.bar = QWidget() 394ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 395ebd70c7dSAdrian Hunter self.bar.hide() 396ebd70c7dSAdrian Hunter 397ebd70c7dSAdrian Hunter def Widget(self): 398ebd70c7dSAdrian Hunter return self.bar 399ebd70c7dSAdrian Hunter 400ebd70c7dSAdrian Hunter def Activate(self): 401ebd70c7dSAdrian Hunter self.bar.show() 402ebd70c7dSAdrian Hunter self.textbox.setFocus() 403ebd70c7dSAdrian Hunter 404ebd70c7dSAdrian Hunter def Deactivate(self): 405ebd70c7dSAdrian Hunter self.bar.hide() 406ebd70c7dSAdrian Hunter 407ebd70c7dSAdrian Hunter def Busy(self): 408ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 409ebd70c7dSAdrian Hunter self.pattern.hide() 410ebd70c7dSAdrian Hunter self.next_button.hide() 411ebd70c7dSAdrian Hunter self.prev_button.hide() 412ebd70c7dSAdrian Hunter self.progress.show() 413ebd70c7dSAdrian Hunter 414ebd70c7dSAdrian Hunter def Idle(self): 415ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 416ebd70c7dSAdrian Hunter self.progress.hide() 417ebd70c7dSAdrian Hunter self.pattern.show() 418ebd70c7dSAdrian Hunter self.next_button.show() 419ebd70c7dSAdrian Hunter self.prev_button.show() 420ebd70c7dSAdrian Hunter 421ebd70c7dSAdrian Hunter def Find(self, direction): 422ebd70c7dSAdrian Hunter value = self.textbox.currentText() 423ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 424ebd70c7dSAdrian Hunter self.last_value = value 425ebd70c7dSAdrian Hunter self.last_pattern = pattern 426ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 427ebd70c7dSAdrian Hunter 428ebd70c7dSAdrian Hunter def ValueChanged(self): 429ebd70c7dSAdrian Hunter value = self.textbox.currentText() 430ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 431ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 432ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 433ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 434ebd70c7dSAdrian Hunter if data == None: 435ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 436ebd70c7dSAdrian Hunter else: 437ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 438ebd70c7dSAdrian Hunter self.Find(0) 439ebd70c7dSAdrian Hunter 440ebd70c7dSAdrian Hunter def NextPrev(self, direction): 441ebd70c7dSAdrian Hunter value = self.textbox.currentText() 442ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 443ebd70c7dSAdrian Hunter if value != self.last_value: 444ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 445ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 446ebd70c7dSAdrian Hunter if index < 0: 447ebd70c7dSAdrian Hunter index = self.textbox.count() 448ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 449ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 450ebd70c7dSAdrian Hunter return 451ebd70c7dSAdrian Hunter else: 452ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 453ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 454ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 455ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 456ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 457ebd70c7dSAdrian Hunter self.Find(direction) 458ebd70c7dSAdrian Hunter 459ebd70c7dSAdrian Hunter def NotFound(self): 460ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 461ebd70c7dSAdrian Hunter 462031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 463031c2a00SAdrian Hunter 464031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 465031c2a00SAdrian Hunter 466031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 467031c2a00SAdrian Hunter self.glb = glb 468031c2a00SAdrian Hunter self.row = row 469031c2a00SAdrian Hunter self.parent_item = parent_item 470031c2a00SAdrian Hunter self.query_done = False; 471031c2a00SAdrian Hunter self.child_count = 0 472031c2a00SAdrian Hunter self.child_items = [] 4733ac641f4SAdrian Hunter if parent_item: 4743ac641f4SAdrian Hunter self.level = parent_item.level + 1 4753ac641f4SAdrian Hunter else: 4763ac641f4SAdrian Hunter self.level = 0 477031c2a00SAdrian Hunter 478031c2a00SAdrian Hunter def getChildItem(self, row): 479031c2a00SAdrian Hunter return self.child_items[row] 480031c2a00SAdrian Hunter 481031c2a00SAdrian Hunter def getParentItem(self): 482031c2a00SAdrian Hunter return self.parent_item 483031c2a00SAdrian Hunter 484031c2a00SAdrian Hunter def getRow(self): 485031c2a00SAdrian Hunter return self.row 486031c2a00SAdrian Hunter 487031c2a00SAdrian Hunter def childCount(self): 488031c2a00SAdrian Hunter if not self.query_done: 489031c2a00SAdrian Hunter self.Select() 490031c2a00SAdrian Hunter if not self.child_count: 491031c2a00SAdrian Hunter return -1 492031c2a00SAdrian Hunter return self.child_count 493031c2a00SAdrian Hunter 494031c2a00SAdrian Hunter def hasChildren(self): 495031c2a00SAdrian Hunter if not self.query_done: 496031c2a00SAdrian Hunter return True 497031c2a00SAdrian Hunter return self.child_count > 0 498031c2a00SAdrian Hunter 499031c2a00SAdrian Hunter def getData(self, column): 500031c2a00SAdrian Hunter return self.data[column] 501031c2a00SAdrian Hunter 502031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 503031c2a00SAdrian Hunter 504031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 505031c2a00SAdrian Hunter 506031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 507031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 508031c2a00SAdrian Hunter self.comm_id = comm_id 509031c2a00SAdrian Hunter self.thread_id = thread_id 510031c2a00SAdrian Hunter self.call_path_id = call_path_id 511031c2a00SAdrian Hunter self.branch_count = branch_count 512031c2a00SAdrian Hunter self.time = time 513031c2a00SAdrian Hunter 514031c2a00SAdrian Hunter def Select(self): 515031c2a00SAdrian Hunter self.query_done = True; 516031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 517031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 518031c2a00SAdrian Hunter " FROM calls" 519031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 520031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 521031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 522031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 523031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 524031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 525031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 526031c2a00SAdrian Hunter " ORDER BY call_path_id") 527031c2a00SAdrian Hunter while query.next(): 528031c2a00SAdrian Hunter child_item = CallGraphLevelThreeItem(self.glb, 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) 529031c2a00SAdrian Hunter self.child_items.append(child_item) 530031c2a00SAdrian Hunter self.child_count += 1 531031c2a00SAdrian Hunter 532031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 533031c2a00SAdrian Hunter 534031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 535031c2a00SAdrian Hunter 536031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 537031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 538031c2a00SAdrian Hunter dso = dsoname(dso) 539031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 540031c2a00SAdrian Hunter self.dbid = call_path_id 541031c2a00SAdrian Hunter 542031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 543031c2a00SAdrian Hunter 544031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 547031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 548031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 549031c2a00SAdrian Hunter self.dbid = thread_id 550031c2a00SAdrian Hunter 551031c2a00SAdrian Hunter def Select(self): 552031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 553031c2a00SAdrian Hunter for child_item in self.child_items: 554031c2a00SAdrian Hunter self.time += child_item.time 555031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 556031c2a00SAdrian Hunter for child_item in self.child_items: 557031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 558031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 559031c2a00SAdrian Hunter 560031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 561031c2a00SAdrian Hunter 562031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 563031c2a00SAdrian Hunter 564031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 565031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 566031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 567031c2a00SAdrian Hunter self.dbid = comm_id 568031c2a00SAdrian Hunter 569031c2a00SAdrian Hunter def Select(self): 570031c2a00SAdrian Hunter self.query_done = True; 571031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 572031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 573031c2a00SAdrian Hunter " FROM comm_threads" 574031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 575031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 576031c2a00SAdrian Hunter while query.next(): 577031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 578031c2a00SAdrian Hunter self.child_items.append(child_item) 579031c2a00SAdrian Hunter self.child_count += 1 580031c2a00SAdrian Hunter 581031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 582031c2a00SAdrian Hunter 583031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 584031c2a00SAdrian Hunter 585031c2a00SAdrian Hunter def __init__(self, glb): 586031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 587031c2a00SAdrian Hunter self.dbid = 0 588031c2a00SAdrian Hunter self.query_done = True; 589031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 590031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 591031c2a00SAdrian Hunter while query.next(): 592031c2a00SAdrian Hunter if not query.value(0): 593031c2a00SAdrian Hunter continue 594031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 595031c2a00SAdrian Hunter self.child_items.append(child_item) 596031c2a00SAdrian Hunter self.child_count += 1 597031c2a00SAdrian Hunter 598254c0d82SAdrian Hunter# Context-sensitive call graph data model base 599031c2a00SAdrian Hunter 600254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 601031c2a00SAdrian Hunter 602031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 603254c0d82SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, parent) 604031c2a00SAdrian Hunter 605ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 606ebd70c7dSAdrian Hunter if pattern: 607ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 608ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 609ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 610ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 611ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 612ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 613ebd70c7dSAdrian Hunter # Escape % and _ 614ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 615ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 616ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 617ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 618ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 619ebd70c7dSAdrian Hunter else: 620ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 621ebd70c7dSAdrian Hunter else: 622ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 623254c0d82SAdrian Hunter self.DoFindSelect(query, match) 624ebd70c7dSAdrian Hunter 625ebd70c7dSAdrian Hunter def Found(self, query, found): 626ebd70c7dSAdrian Hunter if found: 627ebd70c7dSAdrian Hunter return self.FindPath(query) 628ebd70c7dSAdrian Hunter return [] 629ebd70c7dSAdrian Hunter 630ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 631ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 632ebd70c7dSAdrian Hunter found = query.first() 633ebd70c7dSAdrian Hunter else: 634ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 635ebd70c7dSAdrian Hunter found = query.next() 636ebd70c7dSAdrian Hunter return self.Found(query, found) 637ebd70c7dSAdrian Hunter 638ebd70c7dSAdrian Hunter def FindNext(self, query): 639ebd70c7dSAdrian Hunter found = query.next() 640ebd70c7dSAdrian Hunter if not found: 641ebd70c7dSAdrian Hunter found = query.first() 642ebd70c7dSAdrian Hunter return self.Found(query, found) 643ebd70c7dSAdrian Hunter 644ebd70c7dSAdrian Hunter def FindPrev(self, query): 645ebd70c7dSAdrian Hunter found = query.previous() 646ebd70c7dSAdrian Hunter if not found: 647ebd70c7dSAdrian Hunter found = query.last() 648ebd70c7dSAdrian Hunter return self.Found(query, found) 649ebd70c7dSAdrian Hunter 650ebd70c7dSAdrian Hunter def FindThread(self, c): 651ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 652ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 653ebd70c7dSAdrian Hunter elif c.direction > 0: 654ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 655ebd70c7dSAdrian Hunter else: 656ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 657ebd70c7dSAdrian Hunter return (True, ids) 658ebd70c7dSAdrian Hunter 659ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 660ebd70c7dSAdrian Hunter class Context(): 661ebd70c7dSAdrian Hunter def __init__(self, *x): 662ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 663ebd70c7dSAdrian Hunter def Update(self, *x): 664ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 665ebd70c7dSAdrian Hunter if len(context): 666ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 667ebd70c7dSAdrian Hunter else: 668ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 669ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 670ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 671ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 672ebd70c7dSAdrian Hunter thread.start() 673ebd70c7dSAdrian Hunter 674ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 675ebd70c7dSAdrian Hunter callback(ids) 676ebd70c7dSAdrian Hunter 677254c0d82SAdrian Hunter# Context-sensitive call graph data model 678254c0d82SAdrian Hunter 679254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 680254c0d82SAdrian Hunter 681254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 682254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 683254c0d82SAdrian Hunter 684254c0d82SAdrian Hunter def GetRoot(self): 685254c0d82SAdrian Hunter return CallGraphRootItem(self.glb) 686254c0d82SAdrian Hunter 687254c0d82SAdrian Hunter def columnCount(self, parent=None): 688254c0d82SAdrian Hunter return 7 689254c0d82SAdrian Hunter 690254c0d82SAdrian Hunter def columnHeader(self, column): 691254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 692254c0d82SAdrian Hunter return headers[column] 693254c0d82SAdrian Hunter 694254c0d82SAdrian Hunter def columnAlignment(self, column): 695254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 696254c0d82SAdrian Hunter return alignment[column] 697254c0d82SAdrian Hunter 698254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 699254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 700254c0d82SAdrian Hunter " FROM calls" 701254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 702254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 703254c0d82SAdrian Hunter " WHERE symbols.name" + match + 704254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 705254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 706254c0d82SAdrian Hunter 707254c0d82SAdrian Hunter def FindPath(self, query): 708254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 709254c0d82SAdrian Hunter # to open the tree at the right place. 710254c0d82SAdrian Hunter ids = [] 711254c0d82SAdrian Hunter parent_id = query.value(0) 712254c0d82SAdrian Hunter while parent_id: 713254c0d82SAdrian Hunter ids.insert(0, parent_id) 714254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 715254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 716254c0d82SAdrian Hunter " FROM call_paths" 717254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 718254c0d82SAdrian Hunter if not q2.next(): 719254c0d82SAdrian Hunter break 720254c0d82SAdrian Hunter parent_id = q2.value(0) 721254c0d82SAdrian Hunter # The call path root is not used 722254c0d82SAdrian Hunter if ids[0] == 1: 723254c0d82SAdrian Hunter del ids[0] 724254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 725254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 726254c0d82SAdrian Hunter return ids 727254c0d82SAdrian Hunter 728ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 729ae8b887cSAdrian Hunter 730ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 731ae8b887cSAdrian Hunter 732ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): 733ae8b887cSAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 734ae8b887cSAdrian Hunter self.comm_id = comm_id 735ae8b887cSAdrian Hunter self.thread_id = thread_id 736ae8b887cSAdrian Hunter self.calls_id = calls_id 737ae8b887cSAdrian Hunter self.branch_count = branch_count 738ae8b887cSAdrian Hunter self.time = time 739ae8b887cSAdrian Hunter 740ae8b887cSAdrian Hunter def Select(self): 741ae8b887cSAdrian Hunter self.query_done = True; 742ae8b887cSAdrian Hunter if self.calls_id == 0: 743ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 744ae8b887cSAdrian Hunter else: 745ae8b887cSAdrian Hunter comm_thread = "" 746ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 747ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" 748ae8b887cSAdrian Hunter " FROM calls" 749ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 750ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 751ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 752ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 753ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 754ae8b887cSAdrian Hunter while query.next(): 755ae8b887cSAdrian Hunter child_item = CallTreeLevelThreeItem(self.glb, 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) 756ae8b887cSAdrian Hunter self.child_items.append(child_item) 757ae8b887cSAdrian Hunter self.child_count += 1 758ae8b887cSAdrian Hunter 759ae8b887cSAdrian Hunter# Call tree data model level three item 760ae8b887cSAdrian Hunter 761ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 762ae8b887cSAdrian Hunter 763ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): 764ae8b887cSAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) 765ae8b887cSAdrian Hunter dso = dsoname(dso) 766ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 767ae8b887cSAdrian Hunter self.dbid = calls_id 768ae8b887cSAdrian Hunter 769ae8b887cSAdrian Hunter# Call tree data model level two item 770ae8b887cSAdrian Hunter 771ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 772ae8b887cSAdrian Hunter 773ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 774ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) 775ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 776ae8b887cSAdrian Hunter self.dbid = thread_id 777ae8b887cSAdrian Hunter 778ae8b887cSAdrian Hunter def Select(self): 779ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 780ae8b887cSAdrian Hunter for child_item in self.child_items: 781ae8b887cSAdrian Hunter self.time += child_item.time 782ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 783ae8b887cSAdrian Hunter for child_item in self.child_items: 784ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 785ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 786ae8b887cSAdrian Hunter 787ae8b887cSAdrian Hunter# Call tree data model level one item 788ae8b887cSAdrian Hunter 789ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 790ae8b887cSAdrian Hunter 791ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 792ae8b887cSAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) 793ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 794ae8b887cSAdrian Hunter self.dbid = comm_id 795ae8b887cSAdrian Hunter 796ae8b887cSAdrian Hunter def Select(self): 797ae8b887cSAdrian Hunter self.query_done = True; 798ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 799ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 800ae8b887cSAdrian Hunter " FROM comm_threads" 801ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 802ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 803ae8b887cSAdrian Hunter while query.next(): 804ae8b887cSAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 805ae8b887cSAdrian Hunter self.child_items.append(child_item) 806ae8b887cSAdrian Hunter self.child_count += 1 807ae8b887cSAdrian Hunter 808ae8b887cSAdrian Hunter# Call tree data model root item 809ae8b887cSAdrian Hunter 810ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 811ae8b887cSAdrian Hunter 812ae8b887cSAdrian Hunter def __init__(self, glb): 813ae8b887cSAdrian Hunter super(CallTreeRootItem, self).__init__(glb, 0, None) 814ae8b887cSAdrian Hunter self.dbid = 0 815ae8b887cSAdrian Hunter self.query_done = True; 816ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 817ae8b887cSAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 818ae8b887cSAdrian Hunter while query.next(): 819ae8b887cSAdrian Hunter if not query.value(0): 820ae8b887cSAdrian Hunter continue 821ae8b887cSAdrian Hunter child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 822ae8b887cSAdrian Hunter self.child_items.append(child_item) 823ae8b887cSAdrian Hunter self.child_count += 1 824ae8b887cSAdrian Hunter 825ae8b887cSAdrian Hunter# Call Tree data model 826ae8b887cSAdrian Hunter 827ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 828ae8b887cSAdrian Hunter 829ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 830ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 831ae8b887cSAdrian Hunter 832ae8b887cSAdrian Hunter def GetRoot(self): 833ae8b887cSAdrian Hunter return CallTreeRootItem(self.glb) 834ae8b887cSAdrian Hunter 835ae8b887cSAdrian Hunter def columnCount(self, parent=None): 836ae8b887cSAdrian Hunter return 7 837ae8b887cSAdrian Hunter 838ae8b887cSAdrian Hunter def columnHeader(self, column): 839ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 840ae8b887cSAdrian Hunter return headers[column] 841ae8b887cSAdrian Hunter 842ae8b887cSAdrian Hunter def columnAlignment(self, column): 843ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 844ae8b887cSAdrian Hunter return alignment[column] 845ae8b887cSAdrian Hunter 846ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 847ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 848ae8b887cSAdrian Hunter " FROM calls" 849ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 850ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 851ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 852ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 853ae8b887cSAdrian Hunter 854ae8b887cSAdrian Hunter def FindPath(self, query): 855ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 856ae8b887cSAdrian Hunter # to open the tree at the right place. 857ae8b887cSAdrian Hunter ids = [] 858ae8b887cSAdrian Hunter parent_id = query.value(0) 859ae8b887cSAdrian Hunter while parent_id: 860ae8b887cSAdrian Hunter ids.insert(0, parent_id) 861ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 862ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 863ae8b887cSAdrian Hunter " FROM calls" 864ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 865ae8b887cSAdrian Hunter if not q2.next(): 866ae8b887cSAdrian Hunter break 867ae8b887cSAdrian Hunter parent_id = q2.value(0) 868ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 869ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 870ae8b887cSAdrian Hunter return ids 871ae8b887cSAdrian Hunter 872ebd70c7dSAdrian Hunter# Vertical widget layout 873ebd70c7dSAdrian Hunter 874ebd70c7dSAdrian Hunterclass VBox(): 875ebd70c7dSAdrian Hunter 876ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 877ebd70c7dSAdrian Hunter self.vbox = QWidget() 878ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 879ebd70c7dSAdrian Hunter 880ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 881ebd70c7dSAdrian Hunter 882ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 883ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 884ebd70c7dSAdrian Hunter if w3: 885ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 886ebd70c7dSAdrian Hunter 887ebd70c7dSAdrian Hunter def Widget(self): 888ebd70c7dSAdrian Hunter return self.vbox 889ebd70c7dSAdrian Hunter 890a731cc4cSAdrian Hunter# Tree window base 8911beb5c7bSAdrian Hunter 892a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 8931beb5c7bSAdrian Hunter 894a731cc4cSAdrian Hunter def __init__(self, parent=None): 895a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 8961beb5c7bSAdrian Hunter 897a731cc4cSAdrian Hunter self.model = None 898a731cc4cSAdrian Hunter self.find_bar = None 8991beb5c7bSAdrian Hunter 900be6e7471SAdrian Hunter self.view = QTreeView() 90196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 90296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 903be6e7471SAdrian Hunter 9049bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 9059bc4e4bfSAdrian Hunter 906ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 907ebd70c7dSAdrian Hunter if not len(ids): 908ebd70c7dSAdrian Hunter return False 909ebd70c7dSAdrian Hunter parent = QModelIndex() 910ebd70c7dSAdrian Hunter for dbid in ids: 911ebd70c7dSAdrian Hunter found = False 912ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 913ebd70c7dSAdrian Hunter for row in xrange(n): 914ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 915ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 916ebd70c7dSAdrian Hunter found = True 917ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 918ebd70c7dSAdrian Hunter parent = child 919ebd70c7dSAdrian Hunter break 920ebd70c7dSAdrian Hunter if not found: 921ebd70c7dSAdrian Hunter break 922ebd70c7dSAdrian Hunter return found 923ebd70c7dSAdrian Hunter 924ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 925ebd70c7dSAdrian Hunter self.view.setFocus() 926ebd70c7dSAdrian Hunter self.find_bar.Busy() 927ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 928ebd70c7dSAdrian Hunter 929ebd70c7dSAdrian Hunter def FindDone(self, ids): 930ebd70c7dSAdrian Hunter found = True 931ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 932ebd70c7dSAdrian Hunter found = False 933ebd70c7dSAdrian Hunter self.find_bar.Idle() 934ebd70c7dSAdrian Hunter if not found: 935ebd70c7dSAdrian Hunter self.find_bar.NotFound() 936ebd70c7dSAdrian Hunter 937a731cc4cSAdrian Hunter 938a731cc4cSAdrian Hunter# Context-sensitive call graph window 939a731cc4cSAdrian Hunter 940a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 941a731cc4cSAdrian Hunter 942a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 943a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 944a731cc4cSAdrian Hunter 945a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 946a731cc4cSAdrian Hunter 947a731cc4cSAdrian Hunter self.view.setModel(self.model) 948a731cc4cSAdrian Hunter 949a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 950a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 951a731cc4cSAdrian Hunter 952a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 953a731cc4cSAdrian Hunter 954a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 955a731cc4cSAdrian Hunter 956a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 957a731cc4cSAdrian Hunter 958a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 959a731cc4cSAdrian Hunter 960ae8b887cSAdrian Hunter# Call tree window 961ae8b887cSAdrian Hunter 962ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 963ae8b887cSAdrian Hunter 964ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 965ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 966ae8b887cSAdrian Hunter 967ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 968ae8b887cSAdrian Hunter 969ae8b887cSAdrian Hunter self.view.setModel(self.model) 970ae8b887cSAdrian Hunter 971ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 972ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 973ae8b887cSAdrian Hunter 974ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 975ae8b887cSAdrian Hunter 976ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 977ae8b887cSAdrian Hunter 978ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 979ae8b887cSAdrian Hunter 980ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 981ae8b887cSAdrian Hunter 9828392b74bSAdrian Hunter# Child data item finder 9838392b74bSAdrian Hunter 9848392b74bSAdrian Hunterclass ChildDataItemFinder(): 9858392b74bSAdrian Hunter 9868392b74bSAdrian Hunter def __init__(self, root): 9878392b74bSAdrian Hunter self.root = root 9888392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 9898392b74bSAdrian Hunter self.rows = [] 9908392b74bSAdrian Hunter self.pos = 0 9918392b74bSAdrian Hunter 9928392b74bSAdrian Hunter def FindSelect(self): 9938392b74bSAdrian Hunter self.rows = [] 9948392b74bSAdrian Hunter if self.pattern: 9958392b74bSAdrian Hunter pattern = re.compile(self.value) 9968392b74bSAdrian Hunter for child in self.root.child_items: 9978392b74bSAdrian Hunter for column_data in child.data: 9988392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 9998392b74bSAdrian Hunter self.rows.append(child.row) 10008392b74bSAdrian Hunter break 10018392b74bSAdrian Hunter else: 10028392b74bSAdrian Hunter for child in self.root.child_items: 10038392b74bSAdrian Hunter for column_data in child.data: 10048392b74bSAdrian Hunter if self.value in str(column_data): 10058392b74bSAdrian Hunter self.rows.append(child.row) 10068392b74bSAdrian Hunter break 10078392b74bSAdrian Hunter 10088392b74bSAdrian Hunter def FindValue(self): 10098392b74bSAdrian Hunter self.pos = 0 10108392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 10118392b74bSAdrian Hunter self.FindSelect() 10128392b74bSAdrian Hunter if not len(self.rows): 10138392b74bSAdrian Hunter return -1 10148392b74bSAdrian Hunter return self.rows[self.pos] 10158392b74bSAdrian Hunter 10168392b74bSAdrian Hunter def FindThread(self): 10178392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 10188392b74bSAdrian Hunter row = self.FindValue() 10198392b74bSAdrian Hunter elif len(self.rows): 10208392b74bSAdrian Hunter if self.direction > 0: 10218392b74bSAdrian Hunter self.pos += 1 10228392b74bSAdrian Hunter if self.pos >= len(self.rows): 10238392b74bSAdrian Hunter self.pos = 0 10248392b74bSAdrian Hunter else: 10258392b74bSAdrian Hunter self.pos -= 1 10268392b74bSAdrian Hunter if self.pos < 0: 10278392b74bSAdrian Hunter self.pos = len(self.rows) - 1 10288392b74bSAdrian Hunter row = self.rows[self.pos] 10298392b74bSAdrian Hunter else: 10308392b74bSAdrian Hunter row = -1 10318392b74bSAdrian Hunter return (True, row) 10328392b74bSAdrian Hunter 10338392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 10348392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 10358392b74bSAdrian Hunter # Use a thread so the UI is not blocked 10368392b74bSAdrian Hunter thread = Thread(self.FindThread) 10378392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 10388392b74bSAdrian Hunter thread.start() 10398392b74bSAdrian Hunter 10408392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 10418392b74bSAdrian Hunter callback(row) 10428392b74bSAdrian Hunter 10438392b74bSAdrian Hunter# Number of database records to fetch in one go 10448392b74bSAdrian Hunter 10458392b74bSAdrian Hunterglb_chunk_sz = 10000 10468392b74bSAdrian Hunter 10478392b74bSAdrian Hunter# Background process for SQL data fetcher 10488392b74bSAdrian Hunter 10498392b74bSAdrian Hunterclass SQLFetcherProcess(): 10508392b74bSAdrian Hunter 10518392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 10528392b74bSAdrian Hunter # Need a unique connection name 10538392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 10548392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 10558392b74bSAdrian Hunter self.sql = sql 10568392b74bSAdrian Hunter self.buffer = buffer 10578392b74bSAdrian Hunter self.head = head 10588392b74bSAdrian Hunter self.tail = tail 10598392b74bSAdrian Hunter self.fetch_count = fetch_count 10608392b74bSAdrian Hunter self.fetching_done = fetching_done 10618392b74bSAdrian Hunter self.process_target = process_target 10628392b74bSAdrian Hunter self.wait_event = wait_event 10638392b74bSAdrian Hunter self.fetched_event = fetched_event 10648392b74bSAdrian Hunter self.prep = prep 10658392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 10668392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 10678392b74bSAdrian Hunter self.last_id = -1 10688392b74bSAdrian Hunter self.fetched = 0 10698392b74bSAdrian Hunter self.more = True 10708392b74bSAdrian Hunter self.local_head = self.head.value 10718392b74bSAdrian Hunter self.local_tail = self.tail.value 10728392b74bSAdrian Hunter 10738392b74bSAdrian Hunter def Select(self): 10748392b74bSAdrian Hunter if self.query_limit: 10758392b74bSAdrian Hunter if self.query_limit == 1: 10768392b74bSAdrian Hunter return 10778392b74bSAdrian Hunter self.query_limit -= 1 10788392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 10798392b74bSAdrian Hunter QueryExec(self.query, stmt) 10808392b74bSAdrian Hunter 10818392b74bSAdrian Hunter def Next(self): 10828392b74bSAdrian Hunter if not self.query.next(): 10838392b74bSAdrian Hunter self.Select() 10848392b74bSAdrian Hunter if not self.query.next(): 10858392b74bSAdrian Hunter return None 10868392b74bSAdrian Hunter self.last_id = self.query.value(0) 10878392b74bSAdrian Hunter return self.prep(self.query) 10888392b74bSAdrian Hunter 10898392b74bSAdrian Hunter def WaitForTarget(self): 10908392b74bSAdrian Hunter while True: 10918392b74bSAdrian Hunter self.wait_event.clear() 10928392b74bSAdrian Hunter target = self.process_target.value 10938392b74bSAdrian Hunter if target > self.fetched or target < 0: 10948392b74bSAdrian Hunter break 10958392b74bSAdrian Hunter self.wait_event.wait() 10968392b74bSAdrian Hunter return target 10978392b74bSAdrian Hunter 10988392b74bSAdrian Hunter def HasSpace(self, sz): 10998392b74bSAdrian Hunter if self.local_tail <= self.local_head: 11008392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 11018392b74bSAdrian Hunter if space > sz: 11028392b74bSAdrian Hunter return True 11038392b74bSAdrian Hunter if space >= glb_nsz: 11048392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1105beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 11068392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 11078392b74bSAdrian Hunter self.local_head = 0 11088392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 11098392b74bSAdrian Hunter return True 11108392b74bSAdrian Hunter return False 11118392b74bSAdrian Hunter 11128392b74bSAdrian Hunter def WaitForSpace(self, sz): 11138392b74bSAdrian Hunter if self.HasSpace(sz): 11148392b74bSAdrian Hunter return 11158392b74bSAdrian Hunter while True: 11168392b74bSAdrian Hunter self.wait_event.clear() 11178392b74bSAdrian Hunter self.local_tail = self.tail.value 11188392b74bSAdrian Hunter if self.HasSpace(sz): 11198392b74bSAdrian Hunter return 11208392b74bSAdrian Hunter self.wait_event.wait() 11218392b74bSAdrian Hunter 11228392b74bSAdrian Hunter def AddToBuffer(self, obj): 1123beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 11248392b74bSAdrian Hunter n = len(d) 1125beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 11268392b74bSAdrian Hunter sz = n + glb_nsz 11278392b74bSAdrian Hunter self.WaitForSpace(sz) 11288392b74bSAdrian Hunter pos = self.local_head 11298392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 11308392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 11318392b74bSAdrian Hunter self.local_head += sz 11328392b74bSAdrian Hunter 11338392b74bSAdrian Hunter def FetchBatch(self, batch_size): 11348392b74bSAdrian Hunter fetched = 0 11358392b74bSAdrian Hunter while batch_size > fetched: 11368392b74bSAdrian Hunter obj = self.Next() 11378392b74bSAdrian Hunter if obj is None: 11388392b74bSAdrian Hunter self.more = False 11398392b74bSAdrian Hunter break 11408392b74bSAdrian Hunter self.AddToBuffer(obj) 11418392b74bSAdrian Hunter fetched += 1 11428392b74bSAdrian Hunter if fetched: 11438392b74bSAdrian Hunter self.fetched += fetched 11448392b74bSAdrian Hunter with self.fetch_count.get_lock(): 11458392b74bSAdrian Hunter self.fetch_count.value += fetched 11468392b74bSAdrian Hunter self.head.value = self.local_head 11478392b74bSAdrian Hunter self.fetched_event.set() 11488392b74bSAdrian Hunter 11498392b74bSAdrian Hunter def Run(self): 11508392b74bSAdrian Hunter while self.more: 11518392b74bSAdrian Hunter target = self.WaitForTarget() 11528392b74bSAdrian Hunter if target < 0: 11538392b74bSAdrian Hunter break 11548392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 11558392b74bSAdrian Hunter self.FetchBatch(batch_size) 11568392b74bSAdrian Hunter self.fetching_done.value = True 11578392b74bSAdrian Hunter self.fetched_event.set() 11588392b74bSAdrian Hunter 11598392b74bSAdrian Hunterdef SQLFetcherFn(*x): 11608392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 11618392b74bSAdrian Hunter process.Run() 11628392b74bSAdrian Hunter 11638392b74bSAdrian Hunter# SQL data fetcher 11648392b74bSAdrian Hunter 11658392b74bSAdrian Hunterclass SQLFetcher(QObject): 11668392b74bSAdrian Hunter 11678392b74bSAdrian Hunter done = Signal(object) 11688392b74bSAdrian Hunter 11698392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 11708392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 11718392b74bSAdrian Hunter self.process_data = process_data 11728392b74bSAdrian Hunter self.more = True 11738392b74bSAdrian Hunter self.target = 0 11748392b74bSAdrian Hunter self.last_target = 0 11758392b74bSAdrian Hunter self.fetched = 0 11768392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 11778392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 11788392b74bSAdrian Hunter self.head = Value(c_longlong) 11798392b74bSAdrian Hunter self.tail = Value(c_longlong) 11808392b74bSAdrian Hunter self.local_tail = 0 11818392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 11828392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 11838392b74bSAdrian Hunter self.last_count = 0 11848392b74bSAdrian Hunter self.process_target = Value(c_longlong) 11858392b74bSAdrian Hunter self.wait_event = Event() 11868392b74bSAdrian Hunter self.fetched_event = Event() 11878392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 11888392b74bSAdrian 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)) 11898392b74bSAdrian Hunter self.process.start() 11908392b74bSAdrian Hunter self.thread = Thread(self.Thread) 11918392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 11928392b74bSAdrian Hunter self.thread.start() 11938392b74bSAdrian Hunter 11948392b74bSAdrian Hunter def Shutdown(self): 11958392b74bSAdrian Hunter # Tell the thread and process to exit 11968392b74bSAdrian Hunter self.process_target.value = -1 11978392b74bSAdrian Hunter self.wait_event.set() 11988392b74bSAdrian Hunter self.more = False 11998392b74bSAdrian Hunter self.fetching_done.value = True 12008392b74bSAdrian Hunter self.fetched_event.set() 12018392b74bSAdrian Hunter 12028392b74bSAdrian Hunter def Thread(self): 12038392b74bSAdrian Hunter if not self.more: 12048392b74bSAdrian Hunter return True, 0 12058392b74bSAdrian Hunter while True: 12068392b74bSAdrian Hunter self.fetched_event.clear() 12078392b74bSAdrian Hunter fetch_count = self.fetch_count.value 12088392b74bSAdrian Hunter if fetch_count != self.last_count: 12098392b74bSAdrian Hunter break 12108392b74bSAdrian Hunter if self.fetching_done.value: 12118392b74bSAdrian Hunter self.more = False 12128392b74bSAdrian Hunter return True, 0 12138392b74bSAdrian Hunter self.fetched_event.wait() 12148392b74bSAdrian Hunter count = fetch_count - self.last_count 12158392b74bSAdrian Hunter self.last_count = fetch_count 12168392b74bSAdrian Hunter self.fetched += count 12178392b74bSAdrian Hunter return False, count 12188392b74bSAdrian Hunter 12198392b74bSAdrian Hunter def Fetch(self, nr): 12208392b74bSAdrian Hunter if not self.more: 12218392b74bSAdrian Hunter # -1 inidcates there are no more 12228392b74bSAdrian Hunter return -1 12238392b74bSAdrian Hunter result = self.fetched 12248392b74bSAdrian Hunter extra = result + nr - self.target 12258392b74bSAdrian Hunter if extra > 0: 12268392b74bSAdrian Hunter self.target += extra 12278392b74bSAdrian Hunter # process_target < 0 indicates shutting down 12288392b74bSAdrian Hunter if self.process_target.value >= 0: 12298392b74bSAdrian Hunter self.process_target.value = self.target 12308392b74bSAdrian Hunter self.wait_event.set() 12318392b74bSAdrian Hunter return result 12328392b74bSAdrian Hunter 12338392b74bSAdrian Hunter def RemoveFromBuffer(self): 12348392b74bSAdrian Hunter pos = self.local_tail 12358392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 12368392b74bSAdrian Hunter pos = 0 1237beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 12388392b74bSAdrian Hunter if n == 0: 12398392b74bSAdrian Hunter pos = 0 1240beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 12418392b74bSAdrian Hunter pos += glb_nsz 1242beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 12438392b74bSAdrian Hunter self.local_tail = pos + n 12448392b74bSAdrian Hunter return obj 12458392b74bSAdrian Hunter 12468392b74bSAdrian Hunter def ProcessData(self, count): 12478392b74bSAdrian Hunter for i in xrange(count): 12488392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 12498392b74bSAdrian Hunter self.process_data(obj) 12508392b74bSAdrian Hunter self.tail.value = self.local_tail 12518392b74bSAdrian Hunter self.wait_event.set() 12528392b74bSAdrian Hunter self.done.emit(count) 12538392b74bSAdrian Hunter 12548392b74bSAdrian Hunter# Fetch more records bar 12558392b74bSAdrian Hunter 12568392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 12578392b74bSAdrian Hunter 12588392b74bSAdrian Hunter def __init__(self, model, parent): 12598392b74bSAdrian Hunter self.model = model 12608392b74bSAdrian Hunter 12618392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 12628392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12638392b74bSAdrian Hunter 12648392b74bSAdrian Hunter self.fetch_count = QSpinBox() 12658392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 12668392b74bSAdrian Hunter self.fetch_count.setValue(10) 12678392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12688392b74bSAdrian Hunter 12698392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 12708392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12718392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 12728392b74bSAdrian Hunter 12738392b74bSAdrian Hunter self.progress = QProgressBar() 12748392b74bSAdrian Hunter self.progress.setRange(0, 100) 12758392b74bSAdrian Hunter self.progress.hide() 12768392b74bSAdrian Hunter 12778392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 12788392b74bSAdrian Hunter self.done_label.hide() 12798392b74bSAdrian Hunter 12808392b74bSAdrian Hunter self.spacer = QLabel("") 12818392b74bSAdrian Hunter 12828392b74bSAdrian Hunter self.close_button = QToolButton() 12838392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 12848392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 12858392b74bSAdrian Hunter 12868392b74bSAdrian Hunter self.hbox = QHBoxLayout() 12878392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 12888392b74bSAdrian Hunter 12898392b74bSAdrian Hunter self.hbox.addWidget(self.label) 12908392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 12918392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 12928392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 12938392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 12948392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 12958392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 12968392b74bSAdrian Hunter 12978392b74bSAdrian Hunter self.bar = QWidget() 12988392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 12998392b74bSAdrian Hunter self.bar.show() 13008392b74bSAdrian Hunter 13018392b74bSAdrian Hunter self.in_progress = False 13028392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 13038392b74bSAdrian Hunter 13048392b74bSAdrian Hunter self.done = False 13058392b74bSAdrian Hunter 13068392b74bSAdrian Hunter if not model.HasMoreRecords(): 13078392b74bSAdrian Hunter self.Done() 13088392b74bSAdrian Hunter 13098392b74bSAdrian Hunter def Widget(self): 13108392b74bSAdrian Hunter return self.bar 13118392b74bSAdrian Hunter 13128392b74bSAdrian Hunter def Activate(self): 13138392b74bSAdrian Hunter self.bar.show() 13148392b74bSAdrian Hunter self.fetch.setFocus() 13158392b74bSAdrian Hunter 13168392b74bSAdrian Hunter def Deactivate(self): 13178392b74bSAdrian Hunter self.bar.hide() 13188392b74bSAdrian Hunter 13198392b74bSAdrian Hunter def Enable(self, enable): 13208392b74bSAdrian Hunter self.fetch.setEnabled(enable) 13218392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 13228392b74bSAdrian Hunter 13238392b74bSAdrian Hunter def Busy(self): 13248392b74bSAdrian Hunter self.Enable(False) 13258392b74bSAdrian Hunter self.fetch.hide() 13268392b74bSAdrian Hunter self.spacer.hide() 13278392b74bSAdrian Hunter self.progress.show() 13288392b74bSAdrian Hunter 13298392b74bSAdrian Hunter def Idle(self): 13308392b74bSAdrian Hunter self.in_progress = False 13318392b74bSAdrian Hunter self.Enable(True) 13328392b74bSAdrian Hunter self.progress.hide() 13338392b74bSAdrian Hunter self.fetch.show() 13348392b74bSAdrian Hunter self.spacer.show() 13358392b74bSAdrian Hunter 13368392b74bSAdrian Hunter def Target(self): 13378392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 13388392b74bSAdrian Hunter 13398392b74bSAdrian Hunter def Done(self): 13408392b74bSAdrian Hunter self.done = True 13418392b74bSAdrian Hunter self.Idle() 13428392b74bSAdrian Hunter self.label.hide() 13438392b74bSAdrian Hunter self.fetch_count.hide() 13448392b74bSAdrian Hunter self.fetch.hide() 13458392b74bSAdrian Hunter self.spacer.hide() 13468392b74bSAdrian Hunter self.done_label.show() 13478392b74bSAdrian Hunter 13488392b74bSAdrian Hunter def Progress(self, count): 13498392b74bSAdrian Hunter if self.in_progress: 13508392b74bSAdrian Hunter if count: 13518392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 13528392b74bSAdrian Hunter if percent >= 100: 13538392b74bSAdrian Hunter self.Idle() 13548392b74bSAdrian Hunter else: 13558392b74bSAdrian Hunter self.progress.setValue(percent) 13568392b74bSAdrian Hunter if not count: 13578392b74bSAdrian Hunter # Count value of zero means no more records 13588392b74bSAdrian Hunter self.Done() 13598392b74bSAdrian Hunter 13608392b74bSAdrian Hunter def FetchMoreRecords(self): 13618392b74bSAdrian Hunter if self.done: 13628392b74bSAdrian Hunter return 13638392b74bSAdrian Hunter self.progress.setValue(0) 13648392b74bSAdrian Hunter self.Busy() 13658392b74bSAdrian Hunter self.in_progress = True 13668392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 13678392b74bSAdrian Hunter 136876099f98SAdrian Hunter# Brance data model level two item 136976099f98SAdrian Hunter 137076099f98SAdrian Hunterclass BranchLevelTwoItem(): 137176099f98SAdrian Hunter 137276099f98SAdrian Hunter def __init__(self, row, text, parent_item): 137376099f98SAdrian Hunter self.row = row 137476099f98SAdrian Hunter self.parent_item = parent_item 137576099f98SAdrian Hunter self.data = [""] * 8 137676099f98SAdrian Hunter self.data[7] = text 137776099f98SAdrian Hunter self.level = 2 137876099f98SAdrian Hunter 137976099f98SAdrian Hunter def getParentItem(self): 138076099f98SAdrian Hunter return self.parent_item 138176099f98SAdrian Hunter 138276099f98SAdrian Hunter def getRow(self): 138376099f98SAdrian Hunter return self.row 138476099f98SAdrian Hunter 138576099f98SAdrian Hunter def childCount(self): 138676099f98SAdrian Hunter return 0 138776099f98SAdrian Hunter 138876099f98SAdrian Hunter def hasChildren(self): 138976099f98SAdrian Hunter return False 139076099f98SAdrian Hunter 139176099f98SAdrian Hunter def getData(self, column): 139276099f98SAdrian Hunter return self.data[column] 139376099f98SAdrian Hunter 139476099f98SAdrian Hunter# Brance data model level one item 139576099f98SAdrian Hunter 139676099f98SAdrian Hunterclass BranchLevelOneItem(): 139776099f98SAdrian Hunter 139876099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 139976099f98SAdrian Hunter self.glb = glb 140076099f98SAdrian Hunter self.row = row 140176099f98SAdrian Hunter self.parent_item = parent_item 140276099f98SAdrian Hunter self.child_count = 0 140376099f98SAdrian Hunter self.child_items = [] 140476099f98SAdrian Hunter self.data = data[1:] 140576099f98SAdrian Hunter self.dbid = data[0] 140676099f98SAdrian Hunter self.level = 1 140776099f98SAdrian Hunter self.query_done = False 140876099f98SAdrian Hunter 140976099f98SAdrian Hunter def getChildItem(self, row): 141076099f98SAdrian Hunter return self.child_items[row] 141176099f98SAdrian Hunter 141276099f98SAdrian Hunter def getParentItem(self): 141376099f98SAdrian Hunter return self.parent_item 141476099f98SAdrian Hunter 141576099f98SAdrian Hunter def getRow(self): 141676099f98SAdrian Hunter return self.row 141776099f98SAdrian Hunter 141876099f98SAdrian Hunter def Select(self): 141976099f98SAdrian Hunter self.query_done = True 142076099f98SAdrian Hunter 142176099f98SAdrian Hunter if not self.glb.have_disassembler: 142276099f98SAdrian Hunter return 142376099f98SAdrian Hunter 142476099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 142576099f98SAdrian Hunter 142676099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 142776099f98SAdrian Hunter " FROM samples" 142876099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 142976099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 143076099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 143176099f98SAdrian Hunter if not query.next(): 143276099f98SAdrian Hunter return 143376099f98SAdrian Hunter cpu = query.value(0) 143476099f98SAdrian Hunter dso = query.value(1) 143576099f98SAdrian Hunter sym = query.value(2) 143676099f98SAdrian Hunter if dso == 0 or sym == 0: 143776099f98SAdrian Hunter return 143876099f98SAdrian Hunter off = query.value(3) 143976099f98SAdrian Hunter short_name = query.value(4) 144076099f98SAdrian Hunter long_name = query.value(5) 144176099f98SAdrian Hunter build_id = query.value(6) 144276099f98SAdrian Hunter sym_start = query.value(7) 144376099f98SAdrian Hunter ip = query.value(8) 144476099f98SAdrian Hunter 144576099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 144676099f98SAdrian Hunter " FROM samples" 144776099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 144876099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 144976099f98SAdrian Hunter " ORDER BY samples.id" 145076099f98SAdrian Hunter " LIMIT 1") 145176099f98SAdrian Hunter if not query.next(): 145276099f98SAdrian Hunter return 145376099f98SAdrian Hunter if query.value(0) != dso: 145476099f98SAdrian Hunter # Cannot disassemble from one dso to another 145576099f98SAdrian Hunter return 145676099f98SAdrian Hunter bsym = query.value(1) 145776099f98SAdrian Hunter boff = query.value(2) 145876099f98SAdrian Hunter bsym_start = query.value(3) 145976099f98SAdrian Hunter if bsym == 0: 146076099f98SAdrian Hunter return 146176099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 146276099f98SAdrian Hunter if tot <= 0 or tot > 16384: 146376099f98SAdrian Hunter return 146476099f98SAdrian Hunter 146576099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 146676099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 146776099f98SAdrian Hunter if not f: 146876099f98SAdrian Hunter return 146976099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 147076099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 147176099f98SAdrian Hunter 147276099f98SAdrian Hunter buf_sz = tot + 16 147376099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 147476099f98SAdrian Hunter f.seek(sym_start + off) 147576099f98SAdrian Hunter buf.value = f.read(buf_sz) 147676099f98SAdrian Hunter buf_ptr = addressof(buf) 147776099f98SAdrian Hunter i = 0 147876099f98SAdrian Hunter while tot > 0: 147976099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 148076099f98SAdrian Hunter if cnt: 148176099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 148276099f98SAdrian Hunter for k in xrange(cnt): 148376099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 148476099f98SAdrian Hunter i += 1 148576099f98SAdrian Hunter while k < 15: 148676099f98SAdrian Hunter byte_str += " " 148776099f98SAdrian Hunter k += 1 148876099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 148976099f98SAdrian Hunter self.child_count += 1 149076099f98SAdrian Hunter else: 149176099f98SAdrian Hunter return 149276099f98SAdrian Hunter buf_ptr += cnt 149376099f98SAdrian Hunter tot -= cnt 149476099f98SAdrian Hunter buf_sz -= cnt 149576099f98SAdrian Hunter ip += cnt 149676099f98SAdrian Hunter 149776099f98SAdrian Hunter def childCount(self): 149876099f98SAdrian Hunter if not self.query_done: 149976099f98SAdrian Hunter self.Select() 150076099f98SAdrian Hunter if not self.child_count: 150176099f98SAdrian Hunter return -1 150276099f98SAdrian Hunter return self.child_count 150376099f98SAdrian Hunter 150476099f98SAdrian Hunter def hasChildren(self): 150576099f98SAdrian Hunter if not self.query_done: 150676099f98SAdrian Hunter return True 150776099f98SAdrian Hunter return self.child_count > 0 150876099f98SAdrian Hunter 150976099f98SAdrian Hunter def getData(self, column): 151076099f98SAdrian Hunter return self.data[column] 151176099f98SAdrian Hunter 151276099f98SAdrian Hunter# Brance data model root item 151376099f98SAdrian Hunter 151476099f98SAdrian Hunterclass BranchRootItem(): 151576099f98SAdrian Hunter 151676099f98SAdrian Hunter def __init__(self): 151776099f98SAdrian Hunter self.child_count = 0 151876099f98SAdrian Hunter self.child_items = [] 151976099f98SAdrian Hunter self.level = 0 152076099f98SAdrian Hunter 152176099f98SAdrian Hunter def getChildItem(self, row): 152276099f98SAdrian Hunter return self.child_items[row] 152376099f98SAdrian Hunter 152476099f98SAdrian Hunter def getParentItem(self): 152576099f98SAdrian Hunter return None 152676099f98SAdrian Hunter 152776099f98SAdrian Hunter def getRow(self): 152876099f98SAdrian Hunter return 0 152976099f98SAdrian Hunter 153076099f98SAdrian Hunter def childCount(self): 153176099f98SAdrian Hunter return self.child_count 153276099f98SAdrian Hunter 153376099f98SAdrian Hunter def hasChildren(self): 153476099f98SAdrian Hunter return self.child_count > 0 153576099f98SAdrian Hunter 153676099f98SAdrian Hunter def getData(self, column): 153776099f98SAdrian Hunter return "" 153876099f98SAdrian Hunter 153976099f98SAdrian Hunter# Branch data preparation 154076099f98SAdrian Hunter 154176099f98SAdrian Hunterdef BranchDataPrep(query): 154276099f98SAdrian Hunter data = [] 154376099f98SAdrian Hunter for i in xrange(0, 8): 154476099f98SAdrian Hunter data.append(query.value(i)) 154576099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 154676099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 154776099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 154876099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 154976099f98SAdrian Hunter return data 155076099f98SAdrian Hunter 15518453c936SAdrian Hunterdef BranchDataPrepWA(query): 15528453c936SAdrian Hunter data = [] 15538453c936SAdrian Hunter data.append(query.value(0)) 15548453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 15558453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 15568453c936SAdrian Hunter for i in xrange(2, 8): 15578453c936SAdrian Hunter data.append(query.value(i)) 15588453c936SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 15598453c936SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 15608453c936SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 15618453c936SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 15628453c936SAdrian Hunter return data 15638453c936SAdrian Hunter 156476099f98SAdrian Hunter# Branch data model 156576099f98SAdrian Hunter 156676099f98SAdrian Hunterclass BranchModel(TreeModel): 156776099f98SAdrian Hunter 156876099f98SAdrian Hunter progress = Signal(object) 156976099f98SAdrian Hunter 157076099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 1571a448ba23SAdrian Hunter super(BranchModel, self).__init__(glb, parent) 157276099f98SAdrian Hunter self.event_id = event_id 157376099f98SAdrian Hunter self.more = True 157476099f98SAdrian Hunter self.populated = 0 157576099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 157676099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 157776099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 157876099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 157976099f98SAdrian Hunter " FROM samples" 158076099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 158176099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 158276099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 158376099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 158476099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 158576099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 158676099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 158776099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 158876099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 158976099f98SAdrian Hunter " ORDER BY samples.id" 159076099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 15918453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 15928453c936SAdrian Hunter prep = BranchDataPrepWA 15938453c936SAdrian Hunter else: 15948453c936SAdrian Hunter prep = BranchDataPrep 15958453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 159676099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 159776099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 159876099f98SAdrian Hunter 1599a448ba23SAdrian Hunter def GetRoot(self): 1600a448ba23SAdrian Hunter return BranchRootItem() 1601a448ba23SAdrian Hunter 160276099f98SAdrian Hunter def columnCount(self, parent=None): 160376099f98SAdrian Hunter return 8 160476099f98SAdrian Hunter 160576099f98SAdrian Hunter def columnHeader(self, column): 160676099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 160776099f98SAdrian Hunter 160876099f98SAdrian Hunter def columnFont(self, column): 160976099f98SAdrian Hunter if column != 7: 161076099f98SAdrian Hunter return None 161176099f98SAdrian Hunter return QFont("Monospace") 161276099f98SAdrian Hunter 161376099f98SAdrian Hunter def DisplayData(self, item, index): 161476099f98SAdrian Hunter if item.level == 1: 161576099f98SAdrian Hunter self.FetchIfNeeded(item.row) 161676099f98SAdrian Hunter return item.getData(index.column()) 161776099f98SAdrian Hunter 161876099f98SAdrian Hunter def AddSample(self, data): 161976099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 162076099f98SAdrian Hunter self.root.child_items.append(child) 162176099f98SAdrian Hunter self.populated += 1 162276099f98SAdrian Hunter 162376099f98SAdrian Hunter def Update(self, fetched): 162476099f98SAdrian Hunter if not fetched: 162576099f98SAdrian Hunter self.more = False 162676099f98SAdrian Hunter self.progress.emit(0) 162776099f98SAdrian Hunter child_count = self.root.child_count 162876099f98SAdrian Hunter count = self.populated - child_count 162976099f98SAdrian Hunter if count > 0: 163076099f98SAdrian Hunter parent = QModelIndex() 163176099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 163276099f98SAdrian Hunter self.insertRows(child_count, count, parent) 163376099f98SAdrian Hunter self.root.child_count += count 163476099f98SAdrian Hunter self.endInsertRows() 163576099f98SAdrian Hunter self.progress.emit(self.root.child_count) 163676099f98SAdrian Hunter 163776099f98SAdrian Hunter def FetchMoreRecords(self, count): 163876099f98SAdrian Hunter current = self.root.child_count 163976099f98SAdrian Hunter if self.more: 164076099f98SAdrian Hunter self.fetcher.Fetch(count) 164176099f98SAdrian Hunter else: 164276099f98SAdrian Hunter self.progress.emit(0) 164376099f98SAdrian Hunter return current 164476099f98SAdrian Hunter 164576099f98SAdrian Hunter def HasMoreRecords(self): 164676099f98SAdrian Hunter return self.more 164776099f98SAdrian Hunter 16480bf0947aSAdrian Hunter# Report Variables 16490bf0947aSAdrian Hunter 16500bf0947aSAdrian Hunterclass ReportVars(): 16510bf0947aSAdrian Hunter 1652cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1653947cc38dSAdrian Hunter self.name = name 16540bf0947aSAdrian Hunter self.where_clause = where_clause 1655cd358012SAdrian Hunter self.limit = limit 16560bf0947aSAdrian Hunter 16570bf0947aSAdrian Hunter def UniqueId(self): 1658cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 16590bf0947aSAdrian Hunter 166076099f98SAdrian Hunter# Branch window 166176099f98SAdrian Hunter 166276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 166376099f98SAdrian Hunter 1664947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 166576099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 166676099f98SAdrian Hunter 16670bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 166876099f98SAdrian Hunter 16690bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 167076099f98SAdrian Hunter 167176099f98SAdrian Hunter self.view = QTreeView() 167276099f98SAdrian Hunter self.view.setUniformRowHeights(True) 167396c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 167496c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 167576099f98SAdrian Hunter self.view.setModel(self.model) 167676099f98SAdrian Hunter 167776099f98SAdrian Hunter self.ResizeColumnsToContents() 167876099f98SAdrian Hunter 16799bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 16809bc4e4bfSAdrian Hunter 168176099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 168276099f98SAdrian Hunter 168376099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 168476099f98SAdrian Hunter 168576099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 168676099f98SAdrian Hunter 168776099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 168876099f98SAdrian Hunter 168976099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 169076099f98SAdrian Hunter 1691947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 169276099f98SAdrian Hunter 169376099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 169476099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 169576099f98SAdrian Hunter # so implement a crude alternative 169676099f98SAdrian Hunter mm = "MM" if column else "MMMM" 169776099f98SAdrian Hunter font = self.view.font() 169876099f98SAdrian Hunter metrics = QFontMetrics(font) 169976099f98SAdrian Hunter max = 0 170076099f98SAdrian Hunter for row in xrange(n): 170176099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 170276099f98SAdrian Hunter len = metrics.width(str(val) + mm) 170376099f98SAdrian Hunter max = len if len > max else max 170476099f98SAdrian Hunter val = self.model.columnHeader(column) 170576099f98SAdrian Hunter len = metrics.width(str(val) + mm) 170676099f98SAdrian Hunter max = len if len > max else max 170776099f98SAdrian Hunter self.view.setColumnWidth(column, max) 170876099f98SAdrian Hunter 170976099f98SAdrian Hunter def ResizeColumnsToContents(self): 171076099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 171176099f98SAdrian Hunter if n < 1: 171276099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 171376099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 171476099f98SAdrian Hunter return 171576099f98SAdrian Hunter columns = self.model.columnCount() 171676099f98SAdrian Hunter for i in xrange(columns): 171776099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 171876099f98SAdrian Hunter 171976099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 172076099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 172176099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 172276099f98SAdrian Hunter self.ResizeColumnsToContents() 172376099f98SAdrian Hunter 172476099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 172576099f98SAdrian Hunter self.view.setFocus() 172676099f98SAdrian Hunter self.find_bar.Busy() 172776099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 172876099f98SAdrian Hunter 172976099f98SAdrian Hunter def FindDone(self, row): 173076099f98SAdrian Hunter self.find_bar.Idle() 173176099f98SAdrian Hunter if row >= 0: 173276099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 173376099f98SAdrian Hunter else: 173476099f98SAdrian Hunter self.find_bar.NotFound() 173576099f98SAdrian Hunter 17361c3ca1b3SAdrian Hunter# Line edit data item 17371c3ca1b3SAdrian Hunter 17381c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 17391c3ca1b3SAdrian Hunter 1740cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 17411c3ca1b3SAdrian Hunter self.glb = glb 17421c3ca1b3SAdrian Hunter self.label = label 17431c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 17441c3ca1b3SAdrian Hunter self.parent = parent 17451c3ca1b3SAdrian Hunter self.id = id 17461c3ca1b3SAdrian Hunter 1747cd358012SAdrian Hunter self.value = default 17481c3ca1b3SAdrian Hunter 1749cd358012SAdrian Hunter self.widget = QLineEdit(default) 17501c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 17511c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 17521c3ca1b3SAdrian Hunter self.red = False 17531c3ca1b3SAdrian Hunter self.error = "" 17541c3ca1b3SAdrian Hunter self.validated = True 17551c3ca1b3SAdrian Hunter 17561c3ca1b3SAdrian Hunter if placeholder_text: 17571c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 17581c3ca1b3SAdrian Hunter 17591c3ca1b3SAdrian Hunter def TurnTextRed(self): 17601c3ca1b3SAdrian Hunter if not self.red: 17611c3ca1b3SAdrian Hunter palette = QPalette() 17621c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 17631c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17641c3ca1b3SAdrian Hunter self.red = True 17651c3ca1b3SAdrian Hunter 17661c3ca1b3SAdrian Hunter def TurnTextNormal(self): 17671c3ca1b3SAdrian Hunter if self.red: 17681c3ca1b3SAdrian Hunter palette = QPalette() 17691c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17701c3ca1b3SAdrian Hunter self.red = False 17711c3ca1b3SAdrian Hunter 17721c3ca1b3SAdrian Hunter def InvalidValue(self, value): 17731c3ca1b3SAdrian Hunter self.value = "" 17741c3ca1b3SAdrian Hunter self.TurnTextRed() 17751c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 17761c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 17771c3ca1b3SAdrian Hunter 17781c3ca1b3SAdrian Hunter def Invalidate(self): 17791c3ca1b3SAdrian Hunter self.validated = False 17801c3ca1b3SAdrian Hunter 17811c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 17821c3ca1b3SAdrian Hunter self.value = input_string.strip() 17831c3ca1b3SAdrian Hunter 17841c3ca1b3SAdrian Hunter def Validate(self): 17851c3ca1b3SAdrian Hunter self.validated = True 17861c3ca1b3SAdrian Hunter self.error = "" 17871c3ca1b3SAdrian Hunter self.TurnTextNormal() 17881c3ca1b3SAdrian Hunter self.parent.ClearMessage() 17891c3ca1b3SAdrian Hunter input_string = self.widget.text() 17901c3ca1b3SAdrian Hunter if not len(input_string.strip()): 17911c3ca1b3SAdrian Hunter self.value = "" 17921c3ca1b3SAdrian Hunter return 17931c3ca1b3SAdrian Hunter self.DoValidate(input_string) 17941c3ca1b3SAdrian Hunter 17951c3ca1b3SAdrian Hunter def IsValid(self): 17961c3ca1b3SAdrian Hunter if not self.validated: 17971c3ca1b3SAdrian Hunter self.Validate() 17981c3ca1b3SAdrian Hunter if len(self.error): 17991c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 18001c3ca1b3SAdrian Hunter return False 18011c3ca1b3SAdrian Hunter return True 18021c3ca1b3SAdrian Hunter 18031c3ca1b3SAdrian Hunter def IsNumber(self, value): 18041c3ca1b3SAdrian Hunter try: 18051c3ca1b3SAdrian Hunter x = int(value) 18061c3ca1b3SAdrian Hunter except: 18071c3ca1b3SAdrian Hunter x = 0 18081c3ca1b3SAdrian Hunter return str(x) == value 18091c3ca1b3SAdrian Hunter 18101c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 18111c3ca1b3SAdrian Hunter 18121c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 18131c3ca1b3SAdrian Hunter 18141c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18151c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 18161c3ca1b3SAdrian Hunter 18171c3ca1b3SAdrian Hunter self.column_name = column_name 18181c3ca1b3SAdrian Hunter 18191c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18201c3ca1b3SAdrian Hunter singles = [] 18211c3ca1b3SAdrian Hunter ranges = [] 18221c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18231c3ca1b3SAdrian Hunter if "-" in value: 18241c3ca1b3SAdrian Hunter vrange = value.split("-") 18251c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 18261c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18271c3ca1b3SAdrian Hunter ranges.append(vrange) 18281c3ca1b3SAdrian Hunter else: 18291c3ca1b3SAdrian Hunter if not self.IsNumber(value): 18301c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18311c3ca1b3SAdrian Hunter singles.append(value) 18321c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 18331c3ca1b3SAdrian Hunter if len(singles): 18341c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 18351c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 18361c3ca1b3SAdrian Hunter 1837cd358012SAdrian Hunter# Positive integer dialog data item 1838cd358012SAdrian Hunter 1839cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1840cd358012SAdrian Hunter 1841cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1842cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1843cd358012SAdrian Hunter 1844cd358012SAdrian Hunter def DoValidate(self, input_string): 1845cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1846cd358012SAdrian Hunter return self.InvalidValue(input_string) 1847cd358012SAdrian Hunter value = int(input_string.strip()) 1848cd358012SAdrian Hunter if value <= 0: 1849cd358012SAdrian Hunter return self.InvalidValue(input_string) 1850cd358012SAdrian Hunter self.value = str(value) 1851cd358012SAdrian Hunter 18521c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 18531c3ca1b3SAdrian Hunter 18541c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 18551c3ca1b3SAdrian Hunter 18561c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 18571c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 18581c3ca1b3SAdrian Hunter 18591c3ca1b3SAdrian Hunter self.table_name = table_name 18601c3ca1b3SAdrian Hunter self.match_column = match_column 18611c3ca1b3SAdrian Hunter self.column_name1 = column_name1 18621c3ca1b3SAdrian Hunter self.column_name2 = column_name2 18631c3ca1b3SAdrian Hunter 18641c3ca1b3SAdrian Hunter def ValueToIds(self, value): 18651c3ca1b3SAdrian Hunter ids = [] 18661c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 18671c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 18681c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 18691c3ca1b3SAdrian Hunter if ret: 18701c3ca1b3SAdrian Hunter while query.next(): 18711c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 18721c3ca1b3SAdrian Hunter return ids 18731c3ca1b3SAdrian Hunter 18741c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18751c3ca1b3SAdrian Hunter all_ids = [] 18761c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18771c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 18781c3ca1b3SAdrian Hunter if len(ids): 18791c3ca1b3SAdrian Hunter all_ids.extend(ids) 18801c3ca1b3SAdrian Hunter else: 18811c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18821c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 18831c3ca1b3SAdrian Hunter if self.column_name2: 18841c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 18851c3ca1b3SAdrian Hunter 18861c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 18871c3ca1b3SAdrian Hunter 18881c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 18891c3ca1b3SAdrian Hunter 18901c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18911c3ca1b3SAdrian Hunter self.column_name = column_name 18921c3ca1b3SAdrian Hunter 18931c3ca1b3SAdrian Hunter self.last_id = 0 18941c3ca1b3SAdrian Hunter self.first_time = 0 18951c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 18961c3ca1b3SAdrian Hunter 18971c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 18981c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 18991c3ca1b3SAdrian Hunter if query.next(): 19001c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 19011c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 19021c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 19031c3ca1b3SAdrian Hunter if query.next(): 19041c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 19051c3ca1b3SAdrian Hunter if placeholder_text: 19061c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 19071c3ca1b3SAdrian Hunter 19081c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 19091c3ca1b3SAdrian Hunter 19101c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 19111c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 19121c3ca1b3SAdrian Hunter if query.next(): 19131c3ca1b3SAdrian Hunter return True, int(query.value(0)) 19141c3ca1b3SAdrian Hunter else: 19151c3ca1b3SAdrian Hunter return False, 0 19161c3ca1b3SAdrian Hunter 19171c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 19181c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 19191c3ca1b3SAdrian Hunter while True: 19201c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 19211c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19221c3ca1b3SAdrian Hunter if not query.next(): 19231c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 19241c3ca1b3SAdrian Hunter if not ok: 19251c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 19261c3ca1b3SAdrian Hunter if not ok: 19271c3ca1b3SAdrian Hunter return str(higher_id) 19281c3ca1b3SAdrian Hunter next_id = dbid 19291c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19301c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 19311c3ca1b3SAdrian Hunter if get_floor: 19321c3ca1b3SAdrian Hunter if target_time > next_time: 19331c3ca1b3SAdrian Hunter lower_id = next_id 19341c3ca1b3SAdrian Hunter else: 19351c3ca1b3SAdrian Hunter higher_id = next_id 19361c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19371c3ca1b3SAdrian Hunter return str(higher_id) 19381c3ca1b3SAdrian Hunter else: 19391c3ca1b3SAdrian Hunter if target_time >= next_time: 19401c3ca1b3SAdrian Hunter lower_id = next_id 19411c3ca1b3SAdrian Hunter else: 19421c3ca1b3SAdrian Hunter higher_id = next_id 19431c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19441c3ca1b3SAdrian Hunter return str(lower_id) 19451c3ca1b3SAdrian Hunter 19461c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 19471c3ca1b3SAdrian Hunter mult = 1 19481c3ca1b3SAdrian Hunter suffix = val[-2:] 19491c3ca1b3SAdrian Hunter if suffix == "ms": 19501c3ca1b3SAdrian Hunter mult = 1000000 19511c3ca1b3SAdrian Hunter elif suffix == "us": 19521c3ca1b3SAdrian Hunter mult = 1000 19531c3ca1b3SAdrian Hunter elif suffix == "ns": 19541c3ca1b3SAdrian Hunter mult = 1 19551c3ca1b3SAdrian Hunter else: 19561c3ca1b3SAdrian Hunter return val 19571c3ca1b3SAdrian Hunter val = val[:-2].strip() 19581c3ca1b3SAdrian Hunter if not self.IsNumber(val): 19591c3ca1b3SAdrian Hunter return val 19601c3ca1b3SAdrian Hunter val = int(val) * mult 19611c3ca1b3SAdrian Hunter if val >= 0: 19621c3ca1b3SAdrian Hunter val += self.first_time 19631c3ca1b3SAdrian Hunter else: 19641c3ca1b3SAdrian Hunter val += self.last_time 19651c3ca1b3SAdrian Hunter return str(val) 19661c3ca1b3SAdrian Hunter 19671c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 19681c3ca1b3SAdrian Hunter if vrange[0] == "": 19691c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 19701c3ca1b3SAdrian Hunter if vrange[1] == "": 19711c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 19721c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 19731c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 19741c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 19751c3ca1b3SAdrian Hunter return False 19761c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 19771c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 19781c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 19791c3ca1b3SAdrian Hunter return False 19801c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 19811c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 19821c3ca1b3SAdrian Hunter return True 19831c3ca1b3SAdrian Hunter 19841c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 19851c3ca1b3SAdrian Hunter n = value.count("-") 19861c3ca1b3SAdrian Hunter if n == 1: 19871c3ca1b3SAdrian Hunter pass 19881c3ca1b3SAdrian Hunter elif n == 2: 19891c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 19901c3ca1b3SAdrian Hunter n = 1 19911c3ca1b3SAdrian Hunter elif n == 3: 19921c3ca1b3SAdrian Hunter n = 2 19931c3ca1b3SAdrian Hunter else: 19941c3ca1b3SAdrian Hunter return False 19951c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 19961c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 19971c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 19981c3ca1b3SAdrian Hunter ranges.append(vrange) 19991c3ca1b3SAdrian Hunter return True 20001c3ca1b3SAdrian Hunter return False 20011c3ca1b3SAdrian Hunter 20021c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 20031c3ca1b3SAdrian Hunter ranges = [] 20041c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 20051c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 20061c3ca1b3SAdrian Hunter return self.InvalidValue(value) 20071c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 20081c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 20091c3ca1b3SAdrian Hunter 20100924cd68SAdrian Hunter# Report Dialog Base 2011210cf1f9SAdrian Hunter 20120924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 2013210cf1f9SAdrian Hunter 20140924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 20150924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2016210cf1f9SAdrian Hunter 2017210cf1f9SAdrian Hunter self.glb = glb 2018210cf1f9SAdrian Hunter 20190bf0947aSAdrian Hunter self.report_vars = ReportVars() 2020210cf1f9SAdrian Hunter 20210924cd68SAdrian Hunter self.setWindowTitle(title) 2022210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2023210cf1f9SAdrian Hunter 20241c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2025210cf1f9SAdrian Hunter 20260924cd68SAdrian Hunter self.partial = partial 20270924cd68SAdrian Hunter 2028210cf1f9SAdrian Hunter self.grid = QGridLayout() 2029210cf1f9SAdrian Hunter 2030210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2031210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2032210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2033210cf1f9SAdrian Hunter 2034210cf1f9SAdrian Hunter self.status = QLabel() 2035210cf1f9SAdrian Hunter 2036210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2037210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2038210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2039210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2040210cf1f9SAdrian Hunter 2041210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2042210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2043210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2044210cf1f9SAdrian Hunter 2045210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2046210cf1f9SAdrian Hunter #self.hbox.addStretch() 2047210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2048210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2049210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2050210cf1f9SAdrian Hunter 2051210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2052210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2053210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2054210cf1f9SAdrian Hunter 2055210cf1f9SAdrian Hunter self.setLayout(self.vbox); 2056210cf1f9SAdrian Hunter 2057210cf1f9SAdrian Hunter def Ok(self): 20580bf0947aSAdrian Hunter vars = self.report_vars 20591c3ca1b3SAdrian Hunter for d in self.data_items: 20601c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 20611c3ca1b3SAdrian Hunter vars.name = d.value 2062947cc38dSAdrian Hunter if not vars.name: 2063210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2064210cf1f9SAdrian Hunter return 2065210cf1f9SAdrian Hunter for d in self.data_items: 2066210cf1f9SAdrian Hunter if not d.IsValid(): 2067210cf1f9SAdrian Hunter return 2068210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2069cd358012SAdrian Hunter if d.id == "LIMIT": 2070cd358012SAdrian Hunter vars.limit = d.value 2071cd358012SAdrian Hunter elif len(d.value): 20720bf0947aSAdrian Hunter if len(vars.where_clause): 20730bf0947aSAdrian Hunter vars.where_clause += " AND " 20740bf0947aSAdrian Hunter vars.where_clause += d.value 20750bf0947aSAdrian Hunter if len(vars.where_clause): 20760924cd68SAdrian Hunter if self.partial: 20770bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2078210cf1f9SAdrian Hunter else: 20790bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2080210cf1f9SAdrian Hunter self.accept() 2081210cf1f9SAdrian Hunter 2082210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2083210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2084210cf1f9SAdrian Hunter 2085210cf1f9SAdrian Hunter def ClearMessage(self): 2086210cf1f9SAdrian Hunter self.status.setText("") 2087210cf1f9SAdrian Hunter 20880924cd68SAdrian Hunter# Selected branch report creation dialog 20890924cd68SAdrian Hunter 20900924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 20910924cd68SAdrian Hunter 20920924cd68SAdrian Hunter def __init__(self, glb, parent=None): 20930924cd68SAdrian Hunter title = "Selected Branches" 20941c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 20951c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 20961c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 20971c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 20981c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 20991c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 21001c3ca1b3SAdrian 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), 21011c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 21021c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 21030924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 21040924cd68SAdrian Hunter 210576099f98SAdrian Hunter# Event list 210676099f98SAdrian Hunter 210776099f98SAdrian Hunterdef GetEventList(db): 210876099f98SAdrian Hunter events = [] 210976099f98SAdrian Hunter query = QSqlQuery(db) 211076099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 211176099f98SAdrian Hunter while query.next(): 211276099f98SAdrian Hunter events.append(query.value(0)) 211376099f98SAdrian Hunter return events 211476099f98SAdrian Hunter 2115655cb952SAdrian Hunter# Is a table selectable 2116655cb952SAdrian Hunter 2117ae8b887cSAdrian Hunterdef IsSelectable(db, table, sql = ""): 2118655cb952SAdrian Hunter query = QSqlQuery(db) 2119655cb952SAdrian Hunter try: 2120ae8b887cSAdrian Hunter QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") 2121655cb952SAdrian Hunter except: 2122655cb952SAdrian Hunter return False 2123655cb952SAdrian Hunter return True 2124655cb952SAdrian Hunter 21258392b74bSAdrian Hunter# SQL table data model item 21268392b74bSAdrian Hunter 21278392b74bSAdrian Hunterclass SQLTableItem(): 21288392b74bSAdrian Hunter 21298392b74bSAdrian Hunter def __init__(self, row, data): 21308392b74bSAdrian Hunter self.row = row 21318392b74bSAdrian Hunter self.data = data 21328392b74bSAdrian Hunter 21338392b74bSAdrian Hunter def getData(self, column): 21348392b74bSAdrian Hunter return self.data[column] 21358392b74bSAdrian Hunter 21368392b74bSAdrian Hunter# SQL table data model 21378392b74bSAdrian Hunter 21388392b74bSAdrian Hunterclass SQLTableModel(TableModel): 21398392b74bSAdrian Hunter 21408392b74bSAdrian Hunter progress = Signal(object) 21418392b74bSAdrian Hunter 21428c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 21438392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 21448392b74bSAdrian Hunter self.glb = glb 21458392b74bSAdrian Hunter self.more = True 21468392b74bSAdrian Hunter self.populated = 0 21478c90fef9SAdrian Hunter self.column_headers = column_headers 21488453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 21498392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 21508392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 21518392b74bSAdrian Hunter 21528392b74bSAdrian Hunter def DisplayData(self, item, index): 21538392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 21548392b74bSAdrian Hunter return item.getData(index.column()) 21558392b74bSAdrian Hunter 21568392b74bSAdrian Hunter def AddSample(self, data): 21578392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 21588392b74bSAdrian Hunter self.child_items.append(child) 21598392b74bSAdrian Hunter self.populated += 1 21608392b74bSAdrian Hunter 21618392b74bSAdrian Hunter def Update(self, fetched): 21628392b74bSAdrian Hunter if not fetched: 21638392b74bSAdrian Hunter self.more = False 21648392b74bSAdrian Hunter self.progress.emit(0) 21658392b74bSAdrian Hunter child_count = self.child_count 21668392b74bSAdrian Hunter count = self.populated - child_count 21678392b74bSAdrian Hunter if count > 0: 21688392b74bSAdrian Hunter parent = QModelIndex() 21698392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 21708392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 21718392b74bSAdrian Hunter self.child_count += count 21728392b74bSAdrian Hunter self.endInsertRows() 21738392b74bSAdrian Hunter self.progress.emit(self.child_count) 21748392b74bSAdrian Hunter 21758392b74bSAdrian Hunter def FetchMoreRecords(self, count): 21768392b74bSAdrian Hunter current = self.child_count 21778392b74bSAdrian Hunter if self.more: 21788392b74bSAdrian Hunter self.fetcher.Fetch(count) 21798392b74bSAdrian Hunter else: 21808392b74bSAdrian Hunter self.progress.emit(0) 21818392b74bSAdrian Hunter return current 21828392b74bSAdrian Hunter 21838392b74bSAdrian Hunter def HasMoreRecords(self): 21848392b74bSAdrian Hunter return self.more 21858392b74bSAdrian Hunter 21868c90fef9SAdrian Hunter def columnCount(self, parent=None): 21878c90fef9SAdrian Hunter return len(self.column_headers) 21888c90fef9SAdrian Hunter 21898c90fef9SAdrian Hunter def columnHeader(self, column): 21908c90fef9SAdrian Hunter return self.column_headers[column] 21918c90fef9SAdrian Hunter 21928453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 21938453c936SAdrian Hunter data = [] 21948453c936SAdrian Hunter for i in xrange(count): 21958453c936SAdrian Hunter data.append(query.value(i)) 21968453c936SAdrian Hunter return data 21978453c936SAdrian Hunter 21988392b74bSAdrian Hunter# SQL automatic table data model 21998392b74bSAdrian Hunter 22008392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 22018392b74bSAdrian Hunter 22028392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 22038392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 22048392b74bSAdrian Hunter if table_name == "comm_threads_view": 22058392b74bSAdrian Hunter # For now, comm_threads_view has no id column 22068392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 22078c90fef9SAdrian Hunter column_headers = [] 22088392b74bSAdrian Hunter query = QSqlQuery(glb.db) 22098392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 22108392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 22118392b74bSAdrian Hunter while query.next(): 22128c90fef9SAdrian Hunter column_headers.append(query.value(1)) 22138392b74bSAdrian Hunter if table_name == "sqlite_master": 22148392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22158392b74bSAdrian Hunter else: 22168392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 22178392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22188392b74bSAdrian Hunter select_table_name = table_name[19:] 22198392b74bSAdrian Hunter schema = "information_schema" 22208392b74bSAdrian Hunter else: 22218392b74bSAdrian Hunter select_table_name = table_name 22228392b74bSAdrian Hunter schema = "public" 22238392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 22248392b74bSAdrian Hunter while query.next(): 22258c90fef9SAdrian Hunter column_headers.append(query.value(0)) 22268453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 22278453c936SAdrian Hunter if table_name == "samples_view": 22288453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 22298453c936SAdrian Hunter if table_name == "samples": 22308453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 22318c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 22328392b74bSAdrian Hunter 22338453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 22348453c936SAdrian Hunter data = [] 22358453c936SAdrian Hunter data.append(query.value(0)) 22368453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22378453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 22388453c936SAdrian Hunter for i in xrange(2, count): 22398453c936SAdrian Hunter data.append(query.value(i)) 22408453c936SAdrian Hunter return data 22418453c936SAdrian Hunter 22428453c936SAdrian Hunter def samples_DataPrep(self, query, count): 22438453c936SAdrian Hunter data = [] 22448453c936SAdrian Hunter for i in xrange(9): 22458453c936SAdrian Hunter data.append(query.value(i)) 22468453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22478453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 22488453c936SAdrian Hunter for i in xrange(10, count): 22498453c936SAdrian Hunter data.append(query.value(i)) 22508453c936SAdrian Hunter return data 22518453c936SAdrian Hunter 22528392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 22538392b74bSAdrian Hunter 22548392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 22558392b74bSAdrian Hunter 22568392b74bSAdrian Hunter def __init__(self, parent=None): 22578392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 22588392b74bSAdrian Hunter 22598392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 22608392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 22618392b74bSAdrian Hunter # so implement a crude alternative 22628392b74bSAdrian Hunter font = self.view.font() 22638392b74bSAdrian Hunter metrics = QFontMetrics(font) 22648392b74bSAdrian Hunter max = 0 22658392b74bSAdrian Hunter for row in xrange(n): 22668392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 22678392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22688392b74bSAdrian Hunter max = len if len > max else max 22698392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 22708392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22718392b74bSAdrian Hunter max = len if len > max else max 22728392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 22738392b74bSAdrian Hunter 22748392b74bSAdrian Hunter def ResizeColumnsToContents(self): 22758392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 22768392b74bSAdrian Hunter if n < 1: 22778392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 22788392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 22798392b74bSAdrian Hunter return 22808392b74bSAdrian Hunter columns = self.data_model.columnCount() 22818392b74bSAdrian Hunter for i in xrange(columns): 22828392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 22838392b74bSAdrian Hunter 22848392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 22858392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 22868392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 22878392b74bSAdrian Hunter self.ResizeColumnsToContents() 22888392b74bSAdrian Hunter 228996c43b9aSAdrian Hunter# Convert value to CSV 229096c43b9aSAdrian Hunter 229196c43b9aSAdrian Hunterdef ToCSValue(val): 229296c43b9aSAdrian Hunter if '"' in val: 229396c43b9aSAdrian Hunter val = val.replace('"', '""') 229496c43b9aSAdrian Hunter if "," in val or '"' in val: 229596c43b9aSAdrian Hunter val = '"' + val + '"' 229696c43b9aSAdrian Hunter return val 229796c43b9aSAdrian Hunter 229896c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 229996c43b9aSAdrian Hunter 230096c43b9aSAdrian Hunterglb_max_cols = 1000 230196c43b9aSAdrian Hunter 230296c43b9aSAdrian Hunterdef RowColumnKey(a): 230396c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 230496c43b9aSAdrian Hunter 230596c43b9aSAdrian Hunter# Copy selected table cells to clipboard 230696c43b9aSAdrian Hunter 230796c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 230896c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 230996c43b9aSAdrian Hunter idx_cnt = len(indexes) 231096c43b9aSAdrian Hunter if not idx_cnt: 231196c43b9aSAdrian Hunter return 231296c43b9aSAdrian Hunter if idx_cnt == 1: 231396c43b9aSAdrian Hunter with_hdr=False 231496c43b9aSAdrian Hunter min_row = indexes[0].row() 231596c43b9aSAdrian Hunter max_row = indexes[0].row() 231696c43b9aSAdrian Hunter min_col = indexes[0].column() 231796c43b9aSAdrian Hunter max_col = indexes[0].column() 231896c43b9aSAdrian Hunter for i in indexes: 231996c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 232096c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 232196c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 232296c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 232396c43b9aSAdrian Hunter if max_col > glb_max_cols: 232496c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 232596c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 232696c43b9aSAdrian Hunter for i in indexes: 232796c43b9aSAdrian Hunter c = i.column() - min_col 232896c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 232996c43b9aSAdrian Hunter text = "" 233096c43b9aSAdrian Hunter pad = "" 233196c43b9aSAdrian Hunter sep = "" 233296c43b9aSAdrian Hunter if with_hdr: 233396c43b9aSAdrian Hunter model = indexes[0].model() 233496c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 233596c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 233696c43b9aSAdrian Hunter if as_csv: 233796c43b9aSAdrian Hunter text += sep + ToCSValue(val) 233896c43b9aSAdrian Hunter sep = "," 233996c43b9aSAdrian Hunter else: 234096c43b9aSAdrian Hunter c = col - min_col 234196c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 234296c43b9aSAdrian Hunter width = max_width[c] 234396c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 234496c43b9aSAdrian Hunter if align & Qt.AlignRight: 234596c43b9aSAdrian Hunter val = val.rjust(width) 234696c43b9aSAdrian Hunter text += pad + sep + val 234796c43b9aSAdrian Hunter pad = " " * (width - len(val)) 234896c43b9aSAdrian Hunter sep = " " 234996c43b9aSAdrian Hunter text += "\n" 235096c43b9aSAdrian Hunter pad = "" 235196c43b9aSAdrian Hunter sep = "" 235296c43b9aSAdrian Hunter last_row = min_row 235396c43b9aSAdrian Hunter for i in indexes: 235496c43b9aSAdrian Hunter if i.row() > last_row: 235596c43b9aSAdrian Hunter last_row = i.row() 235696c43b9aSAdrian Hunter text += "\n" 235796c43b9aSAdrian Hunter pad = "" 235896c43b9aSAdrian Hunter sep = "" 235996c43b9aSAdrian Hunter if as_csv: 236096c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 236196c43b9aSAdrian Hunter sep = "," 236296c43b9aSAdrian Hunter else: 236396c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 236496c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 236596c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 236696c43b9aSAdrian Hunter else: 236796c43b9aSAdrian Hunter val = str(i.data()) 236896c43b9aSAdrian Hunter text += pad + sep + val 236996c43b9aSAdrian Hunter pad = " " * (width - len(val)) 237096c43b9aSAdrian Hunter sep = " " 237196c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 237296c43b9aSAdrian Hunter 237396c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 237496c43b9aSAdrian Hunter indexes = view.selectedIndexes() 237596c43b9aSAdrian Hunter if not len(indexes): 237696c43b9aSAdrian Hunter return 237796c43b9aSAdrian Hunter 237896c43b9aSAdrian Hunter selection = view.selectionModel() 237996c43b9aSAdrian Hunter 238096c43b9aSAdrian Hunter first = None 238196c43b9aSAdrian Hunter for i in indexes: 238296c43b9aSAdrian Hunter above = view.indexAbove(i) 238396c43b9aSAdrian Hunter if not selection.isSelected(above): 238496c43b9aSAdrian Hunter first = i 238596c43b9aSAdrian Hunter break 238696c43b9aSAdrian Hunter 238796c43b9aSAdrian Hunter if first is None: 238896c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 238996c43b9aSAdrian Hunter 239096c43b9aSAdrian Hunter model = first.model() 239196c43b9aSAdrian Hunter row_cnt = 0 239296c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 239396c43b9aSAdrian Hunter max_width = [0] * col_cnt 239496c43b9aSAdrian Hunter 239596c43b9aSAdrian Hunter indent_sz = 2 239696c43b9aSAdrian Hunter indent_str = " " * indent_sz 239796c43b9aSAdrian Hunter 239896c43b9aSAdrian Hunter expanded_mark_sz = 2 239996c43b9aSAdrian Hunter if sys.version_info[0] == 3: 240096c43b9aSAdrian Hunter expanded_mark = "\u25BC " 240196c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 240296c43b9aSAdrian Hunter else: 240396c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 240496c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 240596c43b9aSAdrian Hunter leaf_mark = " " 240696c43b9aSAdrian Hunter 240796c43b9aSAdrian Hunter if not as_csv: 240896c43b9aSAdrian Hunter pos = first 240996c43b9aSAdrian Hunter while True: 241096c43b9aSAdrian Hunter row_cnt += 1 241196c43b9aSAdrian Hunter row = pos.row() 241296c43b9aSAdrian Hunter for c in range(col_cnt): 241396c43b9aSAdrian Hunter i = pos.sibling(row, c) 241496c43b9aSAdrian Hunter if c: 241596c43b9aSAdrian Hunter n = len(str(i.data())) 241696c43b9aSAdrian Hunter else: 241796c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 241896c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 241996c43b9aSAdrian Hunter n += expanded_mark_sz 242096c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 242196c43b9aSAdrian Hunter pos = view.indexBelow(pos) 242296c43b9aSAdrian Hunter if not selection.isSelected(pos): 242396c43b9aSAdrian Hunter break 242496c43b9aSAdrian Hunter 242596c43b9aSAdrian Hunter text = "" 242696c43b9aSAdrian Hunter pad = "" 242796c43b9aSAdrian Hunter sep = "" 242896c43b9aSAdrian Hunter if with_hdr: 242996c43b9aSAdrian Hunter for c in range(col_cnt): 243096c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 243196c43b9aSAdrian Hunter if as_csv: 243296c43b9aSAdrian Hunter text += sep + ToCSValue(val) 243396c43b9aSAdrian Hunter sep = "," 243496c43b9aSAdrian Hunter else: 243596c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 243696c43b9aSAdrian Hunter width = max_width[c] 243796c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 243896c43b9aSAdrian Hunter if align & Qt.AlignRight: 243996c43b9aSAdrian Hunter val = val.rjust(width) 244096c43b9aSAdrian Hunter text += pad + sep + val 244196c43b9aSAdrian Hunter pad = " " * (width - len(val)) 244296c43b9aSAdrian Hunter sep = " " 244396c43b9aSAdrian Hunter text += "\n" 244496c43b9aSAdrian Hunter pad = "" 244596c43b9aSAdrian Hunter sep = "" 244696c43b9aSAdrian Hunter 244796c43b9aSAdrian Hunter pos = first 244896c43b9aSAdrian Hunter while True: 244996c43b9aSAdrian Hunter row = pos.row() 245096c43b9aSAdrian Hunter for c in range(col_cnt): 245196c43b9aSAdrian Hunter i = pos.sibling(row, c) 245296c43b9aSAdrian Hunter val = str(i.data()) 245396c43b9aSAdrian Hunter if not c: 245496c43b9aSAdrian Hunter if model.hasChildren(i): 245596c43b9aSAdrian Hunter if view.isExpanded(i): 245696c43b9aSAdrian Hunter mark = expanded_mark 245796c43b9aSAdrian Hunter else: 245896c43b9aSAdrian Hunter mark = not_expanded_mark 245996c43b9aSAdrian Hunter else: 246096c43b9aSAdrian Hunter mark = leaf_mark 246196c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 246296c43b9aSAdrian Hunter if as_csv: 246396c43b9aSAdrian Hunter text += sep + ToCSValue(val) 246496c43b9aSAdrian Hunter sep = "," 246596c43b9aSAdrian Hunter else: 246696c43b9aSAdrian Hunter width = max_width[c] 246796c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 246896c43b9aSAdrian Hunter val = val.rjust(width) 246996c43b9aSAdrian Hunter text += pad + sep + val 247096c43b9aSAdrian Hunter pad = " " * (width - len(val)) 247196c43b9aSAdrian Hunter sep = " " 247296c43b9aSAdrian Hunter pos = view.indexBelow(pos) 247396c43b9aSAdrian Hunter if not selection.isSelected(pos): 247496c43b9aSAdrian Hunter break 247596c43b9aSAdrian Hunter text = text.rstrip() + "\n" 247696c43b9aSAdrian Hunter pad = "" 247796c43b9aSAdrian Hunter sep = "" 247896c43b9aSAdrian Hunter 247996c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 248096c43b9aSAdrian Hunter 248196c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 248296c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 248396c43b9aSAdrian Hunter 248496c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 248596c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 248696c43b9aSAdrian Hunter 248796c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 248896c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 248996c43b9aSAdrian Hunter 24909bc4e4bfSAdrian Hunter# Context menu 24919bc4e4bfSAdrian Hunter 24929bc4e4bfSAdrian Hunterclass ContextMenu(object): 24939bc4e4bfSAdrian Hunter 24949bc4e4bfSAdrian Hunter def __init__(self, view): 24959bc4e4bfSAdrian Hunter self.view = view 24969bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 24979bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 24989bc4e4bfSAdrian Hunter 24999bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 25009bc4e4bfSAdrian Hunter menu = QMenu(self.view) 25019bc4e4bfSAdrian Hunter self.AddActions(menu) 25029bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 25039bc4e4bfSAdrian Hunter 25049bc4e4bfSAdrian Hunter def AddCopy(self, menu): 25059bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 25069bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 25079bc4e4bfSAdrian Hunter 25089bc4e4bfSAdrian Hunter def AddActions(self, menu): 25099bc4e4bfSAdrian Hunter self.AddCopy(menu) 25109bc4e4bfSAdrian Hunter 25119bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 25129bc4e4bfSAdrian Hunter 25139bc4e4bfSAdrian Hunter def __init__(self, view): 25149bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 25159bc4e4bfSAdrian Hunter 25169bc4e4bfSAdrian Hunter def AddActions(self, menu): 25179bc4e4bfSAdrian Hunter i = self.view.currentIndex() 25189bc4e4bfSAdrian Hunter text = str(i.data()).strip() 25199bc4e4bfSAdrian Hunter if len(text): 25209bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 25219bc4e4bfSAdrian Hunter self.AddCopy(menu) 25229bc4e4bfSAdrian Hunter 25238392b74bSAdrian Hunter# Table window 25248392b74bSAdrian Hunter 25258392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 25268392b74bSAdrian Hunter 25278392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 25288392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 25298392b74bSAdrian Hunter 25308392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 25318392b74bSAdrian Hunter 25328392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 25338392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 25348392b74bSAdrian Hunter 25358392b74bSAdrian Hunter self.view = QTableView() 25368392b74bSAdrian Hunter self.view.setModel(self.model) 25378392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 25388392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 25398392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 25408392b74bSAdrian Hunter self.view.setSortingEnabled(True) 254196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 254296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 25438392b74bSAdrian Hunter 25448392b74bSAdrian Hunter self.ResizeColumnsToContents() 25458392b74bSAdrian Hunter 25469bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 25479bc4e4bfSAdrian Hunter 25488392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 25498392b74bSAdrian Hunter 25508392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 25518392b74bSAdrian Hunter 25528392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 25538392b74bSAdrian Hunter 25548392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 25558392b74bSAdrian Hunter 25568392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 25578392b74bSAdrian Hunter 25588392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 25598392b74bSAdrian Hunter 25608392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 25618392b74bSAdrian Hunter self.view.setFocus() 25628392b74bSAdrian Hunter self.find_bar.Busy() 25638392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 25648392b74bSAdrian Hunter 25658392b74bSAdrian Hunter def FindDone(self, row): 25668392b74bSAdrian Hunter self.find_bar.Idle() 25678392b74bSAdrian Hunter if row >= 0: 256835fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 25698392b74bSAdrian Hunter else: 25708392b74bSAdrian Hunter self.find_bar.NotFound() 25718392b74bSAdrian Hunter 25728392b74bSAdrian Hunter# Table list 25738392b74bSAdrian Hunter 25748392b74bSAdrian Hunterdef GetTableList(glb): 25758392b74bSAdrian Hunter tables = [] 25768392b74bSAdrian Hunter query = QSqlQuery(glb.db) 25778392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 25788392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 25798392b74bSAdrian Hunter else: 25808392b74bSAdrian 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") 25818392b74bSAdrian Hunter while query.next(): 25828392b74bSAdrian Hunter tables.append(query.value(0)) 25838392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 25848392b74bSAdrian Hunter tables.append("sqlite_master") 25858392b74bSAdrian Hunter else: 25868392b74bSAdrian Hunter tables.append("information_schema.tables") 25878392b74bSAdrian Hunter tables.append("information_schema.views") 25888392b74bSAdrian Hunter tables.append("information_schema.columns") 25898392b74bSAdrian Hunter return tables 25908392b74bSAdrian Hunter 2591cd358012SAdrian Hunter# Top Calls data model 2592cd358012SAdrian Hunter 2593cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2594cd358012SAdrian Hunter 2595cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2596cd358012SAdrian Hunter text = "" 2597cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2598cd358012SAdrian Hunter text = "::text" 2599cd358012SAdrian Hunter limit = "" 2600cd358012SAdrian Hunter if len(report_vars.limit): 2601cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2602cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2603cd358012SAdrian Hunter " CASE" 2604cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2605cd358012SAdrian Hunter " ELSE short_name" 2606cd358012SAdrian Hunter " END AS dso," 2607cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2608cd358012SAdrian Hunter " CASE" 2609cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2610cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2611cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2612cd358012SAdrian Hunter " ELSE ''" + text + 2613cd358012SAdrian Hunter " END AS flags" 2614cd358012SAdrian Hunter " FROM calls" 2615cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2616cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2617cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2618cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2619cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2620cd358012SAdrian Hunter report_vars.where_clause + 2621cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2622cd358012SAdrian Hunter limit 2623cd358012SAdrian Hunter ) 2624cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2625cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2626cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2627cd358012SAdrian Hunter 2628cd358012SAdrian Hunter def columnAlignment(self, column): 2629cd358012SAdrian Hunter return self.alignment[column] 2630cd358012SAdrian Hunter 2631cd358012SAdrian Hunter# Top Calls report creation dialog 2632cd358012SAdrian Hunter 2633cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2634cd358012SAdrian Hunter 2635cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2636cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2637cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2638cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2639cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2640cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2641cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2642cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2643cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2644cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2645cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2646cd358012SAdrian Hunter 2647cd358012SAdrian Hunter# Top Calls window 2648cd358012SAdrian Hunter 2649cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2650cd358012SAdrian Hunter 2651cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2652cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2653cd358012SAdrian Hunter 2654cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2655cd358012SAdrian Hunter self.model = self.data_model 2656cd358012SAdrian Hunter 2657cd358012SAdrian Hunter self.view = QTableView() 2658cd358012SAdrian Hunter self.view.setModel(self.model) 2659cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2660cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 266196c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 266296c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2663cd358012SAdrian Hunter 26649bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 26659bc4e4bfSAdrian Hunter 2666cd358012SAdrian Hunter self.ResizeColumnsToContents() 2667cd358012SAdrian Hunter 2668cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2669cd358012SAdrian Hunter 2670cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2671cd358012SAdrian Hunter 2672cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2673cd358012SAdrian Hunter 2674cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2675cd358012SAdrian Hunter 2676cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2677cd358012SAdrian Hunter 2678cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2679cd358012SAdrian Hunter 2680cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2681cd358012SAdrian Hunter self.view.setFocus() 2682cd358012SAdrian Hunter self.find_bar.Busy() 2683cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2684cd358012SAdrian Hunter 2685cd358012SAdrian Hunter def FindDone(self, row): 2686cd358012SAdrian Hunter self.find_bar.Idle() 2687cd358012SAdrian Hunter if row >= 0: 2688cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2689cd358012SAdrian Hunter else: 2690cd358012SAdrian Hunter self.find_bar.NotFound() 2691cd358012SAdrian Hunter 26921beb5c7bSAdrian Hunter# Action Definition 26931beb5c7bSAdrian Hunter 26941beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 26951beb5c7bSAdrian Hunter action = QAction(label, parent) 26961beb5c7bSAdrian Hunter if shortcut != None: 26971beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 26981beb5c7bSAdrian Hunter action.setStatusTip(tip) 26991beb5c7bSAdrian Hunter action.triggered.connect(callback) 27001beb5c7bSAdrian Hunter return action 27011beb5c7bSAdrian Hunter 27021beb5c7bSAdrian Hunter# Typical application actions 27031beb5c7bSAdrian Hunter 27041beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 27051beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 27061beb5c7bSAdrian Hunter 27071beb5c7bSAdrian Hunter# Typical MDI actions 27081beb5c7bSAdrian Hunter 27091beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 27101beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 27111beb5c7bSAdrian Hunter 27121beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 27131beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 27141beb5c7bSAdrian Hunter 27151beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 27161beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 27171beb5c7bSAdrian Hunter 27181beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 27191beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 27201beb5c7bSAdrian Hunter 27211beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 27221beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 27231beb5c7bSAdrian Hunter 27241beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 27251beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 27261beb5c7bSAdrian Hunter 27271beb5c7bSAdrian Hunter# Typical MDI window menu 27281beb5c7bSAdrian Hunter 27291beb5c7bSAdrian Hunterclass WindowMenu(): 27301beb5c7bSAdrian Hunter 27311beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 27321beb5c7bSAdrian Hunter self.mdi_area = mdi_area 27331beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 27341beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 27351beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 27361beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 27371beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 27381beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 27391beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 27401beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 27411beb5c7bSAdrian Hunter 27421beb5c7bSAdrian Hunter def Update(self): 27431beb5c7bSAdrian Hunter self.window_menu.clear() 27441beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 27451beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 27461beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 27471beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 27481beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 27491beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 27501beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 27511beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 27521beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 27531beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 27541beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27551beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 27561beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 27571beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27581beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 27591beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 27601beb5c7bSAdrian Hunter if sub_window_count == 0: 27611beb5c7bSAdrian Hunter return 27621beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27631beb5c7bSAdrian Hunter nr = 1 27641beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 27651beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 27661beb5c7bSAdrian Hunter if nr < 10: 27671beb5c7bSAdrian Hunter label = "&" + label 27681beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 27691beb5c7bSAdrian Hunter action.setCheckable(True) 27701beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 2771*df8ea22aSAdrian Hunter action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x)) 27721beb5c7bSAdrian Hunter self.window_menu.addAction(action) 27731beb5c7bSAdrian Hunter nr += 1 27741beb5c7bSAdrian Hunter 27751beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 27761beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 27771beb5c7bSAdrian Hunter 277865b24292SAdrian Hunter# Help text 277965b24292SAdrian Hunter 278065b24292SAdrian Hunterglb_help_text = """ 278165b24292SAdrian Hunter<h1>Contents</h1> 278265b24292SAdrian Hunter<style> 278365b24292SAdrian Hunterp.c1 { 278465b24292SAdrian Hunter text-indent: 40px; 278565b24292SAdrian Hunter} 278665b24292SAdrian Hunterp.c2 { 278765b24292SAdrian Hunter text-indent: 80px; 278865b24292SAdrian Hunter} 278965b24292SAdrian Hunter} 279065b24292SAdrian Hunter</style> 279165b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 279265b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2793ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2794ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2795ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2796ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 279765b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 279865b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 279965b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 280065b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 280165b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 280265b24292SAdrian Hunterwidths to suit will display something like: 280365b24292SAdrian Hunter<pre> 280465b24292SAdrian Hunter Call Graph: pt_example 280565b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 280665b24292SAdrian Hunterv- ls 280765b24292SAdrian Hunter v- 2638:2638 280865b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 280965b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 281065b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 281165b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 281265b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 281365b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 281465b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 281565b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 281665b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 281765b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 281865b24292SAdrian Hunter</pre> 281965b24292SAdrian Hunter<h3>Points to note:</h3> 282065b24292SAdrian Hunter<ul> 282165b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 282265b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 282365b24292SAdrian Hunter<li>Subsequent levels are functions</li> 282465b24292SAdrian Hunter<li>'Count' is the number of calls</li> 282565b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 282665b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 282765b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 282865b24292SAdrian Hunter</ul> 282965b24292SAdrian Hunter<h3>Find</h3> 283065b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 283165b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2832ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2833ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2834ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2835ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 283665b24292SAdrian HunterThe All branches report displays all branches in chronological order. 283765b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 283865b24292SAdrian Hunter<h3>Disassembly</h3> 283965b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 284065b24292SAdrian Hunter<ol> 284165b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 284265b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 284365b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 284465b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 284565b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 284665b24292SAdrian Hunter</ol> 284765b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 284865b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 284965b24292SAdrian Hunter<pre> 285065b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 285165b24292SAdrian Huntergit clone https://github.com/intelxed/xed 285265b24292SAdrian Huntercd xed 285365b24292SAdrian Hunter./mfile.py --share 285465b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 285565b24292SAdrian Huntersudo ldconfig 285665b24292SAdrian Hunter</pre> 285765b24292SAdrian Hunter<h3>Find</h3> 285865b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 285965b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 286065b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 2861ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 286265b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 286365b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 2864ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 286565b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 286665b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 286765b24292SAdrian Hunter<pre> 286865b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 286965b24292SAdrian Hunter 100us-200us From 100us to 200us 287065b24292SAdrian Hunter 10ms- From 10ms to the end 287165b24292SAdrian Hunter -100ns The first 100ns 287265b24292SAdrian Hunter -10ms- The last 10ms 287365b24292SAdrian Hunter</pre> 287465b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2875ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 2876cd358012SAdrian 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. 2877cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2878cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 287965b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 288065b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 288165b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 288265b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 288365b24292SAdrian Hunterbut that can be slow for large tables. 288465b24292SAdrian Hunter<p>There are also tables of database meta-information. 288565b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 288665b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 288765b24292SAdrian Hunter<h3>Find</h3> 288865b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 288965b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 289065b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 289135fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 289235fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 289365b24292SAdrian Hunter""" 289465b24292SAdrian Hunter 289565b24292SAdrian Hunter# Help window 289665b24292SAdrian Hunter 289765b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 289865b24292SAdrian Hunter 289965b24292SAdrian Hunter def __init__(self, glb, parent=None): 290065b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 290165b24292SAdrian Hunter 290265b24292SAdrian Hunter self.text = QTextBrowser() 290365b24292SAdrian Hunter self.text.setHtml(glb_help_text) 290465b24292SAdrian Hunter self.text.setReadOnly(True) 290565b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 290665b24292SAdrian Hunter 290765b24292SAdrian Hunter self.setWidget(self.text) 290865b24292SAdrian Hunter 290965b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 291065b24292SAdrian Hunter 291165b24292SAdrian Hunter# Main window that only displays the help text 291265b24292SAdrian Hunter 291365b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 291465b24292SAdrian Hunter 291565b24292SAdrian Hunter def __init__(self, parent=None): 291665b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 291765b24292SAdrian Hunter 291865b24292SAdrian Hunter self.setMinimumSize(200, 100) 291965b24292SAdrian Hunter self.resize(800, 600) 292065b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 292165b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 292265b24292SAdrian Hunter 292365b24292SAdrian Hunter self.text = QTextBrowser() 292465b24292SAdrian Hunter self.text.setHtml(glb_help_text) 292565b24292SAdrian Hunter self.text.setReadOnly(True) 292665b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 292765b24292SAdrian Hunter 292865b24292SAdrian Hunter self.setCentralWidget(self.text) 292965b24292SAdrian Hunter 2930b62d18abSAdrian Hunter# PostqreSQL server version 2931b62d18abSAdrian Hunter 2932b62d18abSAdrian Hunterdef PostqreSQLServerVersion(db): 2933b62d18abSAdrian Hunter query = QSqlQuery(db) 2934b62d18abSAdrian Hunter QueryExec(query, "SELECT VERSION()") 2935b62d18abSAdrian Hunter if query.next(): 2936b62d18abSAdrian Hunter v_str = query.value(0) 2937b62d18abSAdrian Hunter v_list = v_str.strip().split(" ") 2938b62d18abSAdrian Hunter if v_list[0] == "PostgreSQL" and v_list[2] == "on": 2939b62d18abSAdrian Hunter return v_list[1] 2940b62d18abSAdrian Hunter return v_str 2941b62d18abSAdrian Hunter return "Unknown" 2942b62d18abSAdrian Hunter 2943b62d18abSAdrian Hunter# SQLite version 2944b62d18abSAdrian Hunter 2945b62d18abSAdrian Hunterdef SQLiteVersion(db): 2946b62d18abSAdrian Hunter query = QSqlQuery(db) 2947b62d18abSAdrian Hunter QueryExec(query, "SELECT sqlite_version()") 2948b62d18abSAdrian Hunter if query.next(): 2949b62d18abSAdrian Hunter return query.value(0) 2950b62d18abSAdrian Hunter return "Unknown" 2951b62d18abSAdrian Hunter 2952b62d18abSAdrian Hunter# About dialog 2953b62d18abSAdrian Hunter 2954b62d18abSAdrian Hunterclass AboutDialog(QDialog): 2955b62d18abSAdrian Hunter 2956b62d18abSAdrian Hunter def __init__(self, glb, parent=None): 2957b62d18abSAdrian Hunter super(AboutDialog, self).__init__(parent) 2958b62d18abSAdrian Hunter 2959b62d18abSAdrian Hunter self.setWindowTitle("About Exported SQL Viewer") 2960b62d18abSAdrian Hunter self.setMinimumWidth(300) 2961b62d18abSAdrian Hunter 2962b62d18abSAdrian Hunter pyside_version = "1" if pyside_version_1 else "2" 2963b62d18abSAdrian Hunter 2964b62d18abSAdrian Hunter text = "<pre>" 2965b62d18abSAdrian Hunter text += "Python version: " + sys.version.split(" ")[0] + "\n" 2966b62d18abSAdrian Hunter text += "PySide version: " + pyside_version + "\n" 2967b62d18abSAdrian Hunter text += "Qt version: " + qVersion() + "\n" 2968b62d18abSAdrian Hunter if glb.dbref.is_sqlite3: 2969b62d18abSAdrian Hunter text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" 2970b62d18abSAdrian Hunter else: 2971b62d18abSAdrian Hunter text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" 2972b62d18abSAdrian Hunter text += "</pre>" 2973b62d18abSAdrian Hunter 2974b62d18abSAdrian Hunter self.text = QTextBrowser() 2975b62d18abSAdrian Hunter self.text.setHtml(text) 2976b62d18abSAdrian Hunter self.text.setReadOnly(True) 2977b62d18abSAdrian Hunter self.text.setOpenExternalLinks(True) 2978b62d18abSAdrian Hunter 2979b62d18abSAdrian Hunter self.vbox = QVBoxLayout() 2980b62d18abSAdrian Hunter self.vbox.addWidget(self.text) 2981b62d18abSAdrian Hunter 2982b62d18abSAdrian Hunter self.setLayout(self.vbox); 2983b62d18abSAdrian Hunter 298482f68e28SAdrian Hunter# Font resize 298582f68e28SAdrian Hunter 298682f68e28SAdrian Hunterdef ResizeFont(widget, diff): 298782f68e28SAdrian Hunter font = widget.font() 298882f68e28SAdrian Hunter sz = font.pointSize() 298982f68e28SAdrian Hunter font.setPointSize(sz + diff) 299082f68e28SAdrian Hunter widget.setFont(font) 299182f68e28SAdrian Hunter 299282f68e28SAdrian Hunterdef ShrinkFont(widget): 299382f68e28SAdrian Hunter ResizeFont(widget, -1) 299482f68e28SAdrian Hunter 299582f68e28SAdrian Hunterdef EnlargeFont(widget): 299682f68e28SAdrian Hunter ResizeFont(widget, 1) 299782f68e28SAdrian Hunter 29981beb5c7bSAdrian Hunter# Unique name for sub-windows 29991beb5c7bSAdrian Hunter 30001beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 30011beb5c7bSAdrian Hunter if nr > 1: 30021beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 30031beb5c7bSAdrian Hunter return name 30041beb5c7bSAdrian Hunter 30051beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 30061beb5c7bSAdrian Hunter nr = 1 30071beb5c7bSAdrian Hunter while True: 30081beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 30091beb5c7bSAdrian Hunter ok = True 30101beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 30111beb5c7bSAdrian Hunter if sub_window.name == unique_name: 30121beb5c7bSAdrian Hunter ok = False 30131beb5c7bSAdrian Hunter break 30141beb5c7bSAdrian Hunter if ok: 30151beb5c7bSAdrian Hunter return unique_name 30161beb5c7bSAdrian Hunter nr += 1 30171beb5c7bSAdrian Hunter 30181beb5c7bSAdrian Hunter# Add a sub-window 30191beb5c7bSAdrian Hunter 30201beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 30211beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 30221beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 30231beb5c7bSAdrian Hunter sub_window.resize(800, 600) 30241beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 30251beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 30261beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 30271beb5c7bSAdrian Hunter sub_window.name = unique_name 30281beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 30291beb5c7bSAdrian Hunter sub_window.show() 30301beb5c7bSAdrian Hunter 3031031c2a00SAdrian Hunter# Main window 3032031c2a00SAdrian Hunter 3033031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 3034031c2a00SAdrian Hunter 3035031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 3036031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 3037031c2a00SAdrian Hunter 3038031c2a00SAdrian Hunter self.glb = glb 3039031c2a00SAdrian Hunter 30401beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 3041031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 3042031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 3043031c2a00SAdrian Hunter 30441beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 30451beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 30461beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 3047031c2a00SAdrian Hunter 30481beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 3049031c2a00SAdrian Hunter 30501beb5c7bSAdrian Hunter menu = self.menuBar() 3051031c2a00SAdrian Hunter 30521beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 30531beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 30541beb5c7bSAdrian Hunter 3055ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 305696c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 305796c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 3058ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 30598392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 306082f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 306182f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 3062ebd70c7dSAdrian Hunter 30631beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 3064655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 30651beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 30661beb5c7bSAdrian Hunter 3067ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3068ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3069ae8b887cSAdrian Hunter 307076099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 307176099f98SAdrian Hunter 3072cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3073cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3074cd358012SAdrian Hunter 30758392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 30768392b74bSAdrian Hunter 30771beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 30781beb5c7bSAdrian Hunter 307965b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 308065b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 3081b62d18abSAdrian Hunter help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) 308265b24292SAdrian Hunter 30834b208453SAdrian Hunter def Try(self, fn): 30844b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 30854b208453SAdrian Hunter if win: 30864b208453SAdrian Hunter try: 30874b208453SAdrian Hunter fn(win.view) 30884b208453SAdrian Hunter except: 30894b208453SAdrian Hunter pass 30904b208453SAdrian Hunter 309196c43b9aSAdrian Hunter def CopyToClipboard(self): 309296c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 309396c43b9aSAdrian Hunter 309496c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 309596c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 309696c43b9aSAdrian Hunter 3097ebd70c7dSAdrian Hunter def Find(self): 3098ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3099ebd70c7dSAdrian Hunter if win: 3100ebd70c7dSAdrian Hunter try: 3101ebd70c7dSAdrian Hunter win.find_bar.Activate() 3102ebd70c7dSAdrian Hunter except: 3103ebd70c7dSAdrian Hunter pass 3104ebd70c7dSAdrian Hunter 31058392b74bSAdrian Hunter def FetchMoreRecords(self): 31068392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 31078392b74bSAdrian Hunter if win: 31088392b74bSAdrian Hunter try: 31098392b74bSAdrian Hunter win.fetch_bar.Activate() 31108392b74bSAdrian Hunter except: 31118392b74bSAdrian Hunter pass 31128392b74bSAdrian Hunter 311382f68e28SAdrian Hunter def ShrinkFont(self): 31144b208453SAdrian Hunter self.Try(ShrinkFont) 311582f68e28SAdrian Hunter 311682f68e28SAdrian Hunter def EnlargeFont(self): 31174b208453SAdrian Hunter self.Try(EnlargeFont) 311882f68e28SAdrian Hunter 311976099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 312076099f98SAdrian Hunter branches_events = 0 312176099f98SAdrian Hunter for event in events: 312276099f98SAdrian Hunter event = event.split(":")[0] 312376099f98SAdrian Hunter if event == "branches": 312476099f98SAdrian Hunter branches_events += 1 312576099f98SAdrian Hunter dbid = 0 312676099f98SAdrian Hunter for event in events: 312776099f98SAdrian Hunter dbid += 1 312876099f98SAdrian Hunter event = event.split(":")[0] 312976099f98SAdrian Hunter if event == "branches": 313076099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 3131*df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self)) 3132210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3133*df8ea22aSAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self)) 313476099f98SAdrian Hunter 31358392b74bSAdrian Hunter def TableMenu(self, tables, menu): 31368392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 31378392b74bSAdrian Hunter for table in tables: 3138*df8ea22aSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self)) 31398392b74bSAdrian Hunter 31401beb5c7bSAdrian Hunter def NewCallGraph(self): 31411beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3142031c2a00SAdrian Hunter 3143ae8b887cSAdrian Hunter def NewCallTree(self): 3144ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3145ae8b887cSAdrian Hunter 3146cd358012SAdrian Hunter def NewTopCalls(self): 3147cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3148cd358012SAdrian Hunter ret = dialog.exec_() 3149cd358012SAdrian Hunter if ret: 3150cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3151cd358012SAdrian Hunter 315276099f98SAdrian Hunter def NewBranchView(self, event_id): 3153947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 315476099f98SAdrian Hunter 3155210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3156210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3157210cf1f9SAdrian Hunter ret = dialog.exec_() 3158210cf1f9SAdrian Hunter if ret: 3159947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3160210cf1f9SAdrian Hunter 31618392b74bSAdrian Hunter def NewTableView(self, table_name): 31628392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 31638392b74bSAdrian Hunter 316465b24292SAdrian Hunter def Help(self): 316565b24292SAdrian Hunter HelpWindow(self.glb, self) 316665b24292SAdrian Hunter 3167b62d18abSAdrian Hunter def About(self): 3168b62d18abSAdrian Hunter dialog = AboutDialog(self.glb, self) 3169b62d18abSAdrian Hunter dialog.exec_() 3170b62d18abSAdrian Hunter 317176099f98SAdrian Hunter# XED Disassembler 317276099f98SAdrian Hunter 317376099f98SAdrian Hunterclass xed_state_t(Structure): 317476099f98SAdrian Hunter 317576099f98SAdrian Hunter _fields_ = [ 317676099f98SAdrian Hunter ("mode", c_int), 317776099f98SAdrian Hunter ("width", c_int) 317876099f98SAdrian Hunter ] 317976099f98SAdrian Hunter 318076099f98SAdrian Hunterclass XEDInstruction(): 318176099f98SAdrian Hunter 318276099f98SAdrian Hunter def __init__(self, libxed): 318376099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 318476099f98SAdrian Hunter xedd_t = c_byte * 512 318576099f98SAdrian Hunter self.xedd = xedd_t() 318676099f98SAdrian Hunter self.xedp = addressof(self.xedd) 318776099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 318876099f98SAdrian Hunter self.state = xed_state_t() 318976099f98SAdrian Hunter self.statep = addressof(self.state) 319076099f98SAdrian Hunter # Buffer for disassembled instruction text 319176099f98SAdrian Hunter self.buffer = create_string_buffer(256) 319276099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 319376099f98SAdrian Hunter 319476099f98SAdrian Hunterclass LibXED(): 319576099f98SAdrian Hunter 319676099f98SAdrian Hunter def __init__(self): 31975ed4419dSAdrian Hunter try: 319876099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 31995ed4419dSAdrian Hunter except: 32005ed4419dSAdrian Hunter self.libxed = None 32015ed4419dSAdrian Hunter if not self.libxed: 32025ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 320376099f98SAdrian Hunter 320476099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 320576099f98SAdrian Hunter self.xed_tables_init.restype = None 320676099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 320776099f98SAdrian Hunter 320876099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 320976099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 321076099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 321176099f98SAdrian Hunter 321276099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 321376099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 321476099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 321576099f98SAdrian Hunter 321676099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 321776099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 321876099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 321976099f98SAdrian Hunter 322076099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 322176099f98SAdrian Hunter self.xed_decode.restype = c_int 322276099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 322376099f98SAdrian Hunter 322476099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 322576099f98SAdrian Hunter self.xed_format_context.restype = c_uint 322676099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 322776099f98SAdrian Hunter 322876099f98SAdrian Hunter self.xed_tables_init() 322976099f98SAdrian Hunter 323076099f98SAdrian Hunter def Instruction(self): 323176099f98SAdrian Hunter return XEDInstruction(self) 323276099f98SAdrian Hunter 323376099f98SAdrian Hunter def SetMode(self, inst, mode): 323476099f98SAdrian Hunter if mode: 323576099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 323676099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 323776099f98SAdrian Hunter else: 323876099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 323976099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 324076099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 324176099f98SAdrian Hunter 324276099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 324376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 324476099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 324576099f98SAdrian Hunter if err: 324676099f98SAdrian Hunter return 0, "" 324776099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 324876099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 324976099f98SAdrian Hunter if not ok: 325076099f98SAdrian Hunter return 0, "" 3251606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3252606bd60aSAdrian Hunter result = inst.buffer.value 3253606bd60aSAdrian Hunter else: 3254606bd60aSAdrian Hunter result = inst.buffer.value.decode() 325576099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 325676099f98SAdrian Hunter # For now, assume the length is in byte 166 3257606bd60aSAdrian Hunter return inst.xedd[166], result 325876099f98SAdrian Hunter 325976099f98SAdrian Hunterdef TryOpen(file_name): 326076099f98SAdrian Hunter try: 326176099f98SAdrian Hunter return open(file_name, "rb") 326276099f98SAdrian Hunter except: 326376099f98SAdrian Hunter return None 326476099f98SAdrian Hunter 326576099f98SAdrian Hunterdef Is64Bit(f): 326676099f98SAdrian Hunter result = sizeof(c_void_p) 326776099f98SAdrian Hunter # ELF support only 326876099f98SAdrian Hunter pos = f.tell() 326976099f98SAdrian Hunter f.seek(0) 327076099f98SAdrian Hunter header = f.read(7) 327176099f98SAdrian Hunter f.seek(pos) 327276099f98SAdrian Hunter magic = header[0:4] 3273606bd60aSAdrian Hunter if sys.version_info[0] == 2: 327476099f98SAdrian Hunter eclass = ord(header[4]) 327576099f98SAdrian Hunter encoding = ord(header[5]) 327676099f98SAdrian Hunter version = ord(header[6]) 3277606bd60aSAdrian Hunter else: 3278606bd60aSAdrian Hunter eclass = header[4] 3279606bd60aSAdrian Hunter encoding = header[5] 3280606bd60aSAdrian Hunter version = header[6] 328176099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 328276099f98SAdrian Hunter result = True if eclass == 2 else False 328376099f98SAdrian Hunter return result 328476099f98SAdrian Hunter 3285031c2a00SAdrian Hunter# Global data 3286031c2a00SAdrian Hunter 3287031c2a00SAdrian Hunterclass Glb(): 3288031c2a00SAdrian Hunter 3289031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3290031c2a00SAdrian Hunter self.dbref = dbref 3291031c2a00SAdrian Hunter self.db = db 3292031c2a00SAdrian Hunter self.dbname = dbname 329376099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 329476099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 329576099f98SAdrian Hunter if self.buildid_dir: 329676099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 329776099f98SAdrian Hunter else: 329876099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3299031c2a00SAdrian Hunter self.app = None 3300031c2a00SAdrian Hunter self.mainwindow = None 33018392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 330276099f98SAdrian Hunter try: 330376099f98SAdrian Hunter self.disassembler = LibXED() 330476099f98SAdrian Hunter self.have_disassembler = True 330576099f98SAdrian Hunter except: 330676099f98SAdrian Hunter self.have_disassembler = False 330776099f98SAdrian Hunter 330876099f98SAdrian Hunter def FileFromBuildId(self, build_id): 330976099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 331076099f98SAdrian Hunter return TryOpen(file_name) 331176099f98SAdrian Hunter 331276099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 331376099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 331476099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 331576099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 331676099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 331776099f98SAdrian Hunter if f: 331876099f98SAdrian Hunter return f 331976099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 332076099f98SAdrian Hunter f = TryOpen(long_name) 332176099f98SAdrian Hunter if f: 332276099f98SAdrian Hunter return f 332376099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 332476099f98SAdrian Hunter if f: 332576099f98SAdrian Hunter return f 332676099f98SAdrian Hunter return None 33278392b74bSAdrian Hunter 33288392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 33298392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 33308392b74bSAdrian Hunter 33318392b74bSAdrian Hunter # Shutdown any background processes or threads 33328392b74bSAdrian Hunter def ShutdownInstances(self): 33338392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 33348392b74bSAdrian Hunter try: 33358392b74bSAdrian Hunter x.Shutdown() 33368392b74bSAdrian Hunter except: 33378392b74bSAdrian Hunter pass 3338031c2a00SAdrian Hunter 3339031c2a00SAdrian Hunter# Database reference 3340031c2a00SAdrian Hunter 3341031c2a00SAdrian Hunterclass DBRef(): 3342031c2a00SAdrian Hunter 3343031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3344031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3345031c2a00SAdrian Hunter self.dbname = dbname 3346031c2a00SAdrian Hunter 3347031c2a00SAdrian Hunter def Open(self, connection_name): 3348031c2a00SAdrian Hunter dbname = self.dbname 3349031c2a00SAdrian Hunter if self.is_sqlite3: 3350031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3351031c2a00SAdrian Hunter else: 3352031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3353031c2a00SAdrian Hunter opts = dbname.split() 3354031c2a00SAdrian Hunter for opt in opts: 3355031c2a00SAdrian Hunter if "=" in opt: 3356031c2a00SAdrian Hunter opt = opt.split("=") 3357031c2a00SAdrian Hunter if opt[0] == "hostname": 3358031c2a00SAdrian Hunter db.setHostName(opt[1]) 3359031c2a00SAdrian Hunter elif opt[0] == "port": 3360031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3361031c2a00SAdrian Hunter elif opt[0] == "username": 3362031c2a00SAdrian Hunter db.setUserName(opt[1]) 3363031c2a00SAdrian Hunter elif opt[0] == "password": 3364031c2a00SAdrian Hunter db.setPassword(opt[1]) 3365031c2a00SAdrian Hunter elif opt[0] == "dbname": 3366031c2a00SAdrian Hunter dbname = opt[1] 3367031c2a00SAdrian Hunter else: 3368031c2a00SAdrian Hunter dbname = opt 3369031c2a00SAdrian Hunter 3370031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3371031c2a00SAdrian Hunter if not db.open(): 3372031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3373031c2a00SAdrian Hunter return db, dbname 3374031c2a00SAdrian Hunter 3375031c2a00SAdrian Hunter# Main 3376031c2a00SAdrian Hunter 3377031c2a00SAdrian Hunterdef Main(): 33781ed7f47fSAdrian Hunter usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ 33791ed7f47fSAdrian Hunter " or: exported-sql-viewer.py --help-only" 33801ed7f47fSAdrian Hunter ap = argparse.ArgumentParser(usage = usage_str, add_help = False) 3381*df8ea22aSAdrian Hunter ap.add_argument("--pyside-version-1", action='store_true') 33821ed7f47fSAdrian Hunter ap.add_argument("dbname", nargs="?") 33831ed7f47fSAdrian Hunter ap.add_argument("--help-only", action='store_true') 33841ed7f47fSAdrian Hunter args = ap.parse_args() 3385031c2a00SAdrian Hunter 33861ed7f47fSAdrian Hunter if args.help_only: 338765b24292SAdrian Hunter app = QApplication(sys.argv) 338865b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 338965b24292SAdrian Hunter mainwindow.show() 339065b24292SAdrian Hunter err = app.exec_() 339165b24292SAdrian Hunter sys.exit(err) 3392031c2a00SAdrian Hunter 33931ed7f47fSAdrian Hunter dbname = args.dbname 33941ed7f47fSAdrian Hunter if dbname is None: 33951ed7f47fSAdrian Hunter ap.print_usage() 33961ed7f47fSAdrian Hunter print("Too few arguments") 33971ed7f47fSAdrian Hunter sys.exit(1) 33981ed7f47fSAdrian Hunter 3399031c2a00SAdrian Hunter is_sqlite3 = False 3400031c2a00SAdrian Hunter try: 3401beda0e72STony Jones f = open(dbname, "rb") 3402beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3403031c2a00SAdrian Hunter is_sqlite3 = True 3404031c2a00SAdrian Hunter f.close() 3405031c2a00SAdrian Hunter except: 3406031c2a00SAdrian Hunter pass 3407031c2a00SAdrian Hunter 3408031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3409031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3410031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3411031c2a00SAdrian Hunter app = QApplication(sys.argv) 3412031c2a00SAdrian Hunter glb.app = app 3413031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3414031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3415031c2a00SAdrian Hunter mainwindow.show() 3416031c2a00SAdrian Hunter err = app.exec_() 34178392b74bSAdrian Hunter glb.ShutdownInstances() 3418031c2a00SAdrian Hunter db.close() 3419031c2a00SAdrian Hunter sys.exit(err) 3420031c2a00SAdrian Hunter 3421031c2a00SAdrian Hunterif __name__ == "__main__": 3422031c2a00SAdrian Hunter Main() 3423