1b3a67546SAdrian Hunter#!/usr/bin/env python2 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 941beb5c7bSAdrian Hunterimport weakref 951beb5c7bSAdrian Hunterimport threading 96ebd70c7dSAdrian Hunterimport string 97beda0e72STony Jonestry: 98beda0e72STony Jones # Python2 99beda0e72STony Jones import cPickle as pickle 100beda0e72STony Jones # size of pickled integer big enough for record size 101beda0e72STony Jones glb_nsz = 8 102beda0e72STony Jonesexcept ImportError: 103beda0e72STony Jones import pickle 104beda0e72STony Jones glb_nsz = 16 1058392b74bSAdrian Hunterimport re 1068392b74bSAdrian Hunterimport os 107031c2a00SAdrian Hunterfrom PySide.QtCore import * 108031c2a00SAdrian Hunterfrom PySide.QtGui import * 109031c2a00SAdrian Hunterfrom PySide.QtSql import * 1108453c936SAdrian Hunterpyside_version_1 = True 111031c2a00SAdrian Hunterfrom decimal import * 1128392b74bSAdrian Hunterfrom ctypes import * 1138392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event 114031c2a00SAdrian Hunter 115beda0e72STony Jones# xrange is range in Python3 116beda0e72STony Jonestry: 117beda0e72STony Jones xrange 118beda0e72STony Jonesexcept NameError: 119beda0e72STony Jones xrange = range 120beda0e72STony Jones 121beda0e72STony Jonesdef printerr(*args, **keyword_args): 122beda0e72STony Jones print(*args, file=sys.stderr, **keyword_args) 123beda0e72STony Jones 124031c2a00SAdrian Hunter# Data formatting helpers 125031c2a00SAdrian Hunter 12676099f98SAdrian Hunterdef tohex(ip): 12776099f98SAdrian Hunter if ip < 0: 12876099f98SAdrian Hunter ip += 1 << 64 12976099f98SAdrian Hunter return "%x" % ip 13076099f98SAdrian Hunter 13176099f98SAdrian Hunterdef offstr(offset): 13276099f98SAdrian Hunter if offset: 13376099f98SAdrian Hunter return "+0x%x" % offset 13476099f98SAdrian Hunter return "" 13576099f98SAdrian Hunter 136031c2a00SAdrian Hunterdef dsoname(name): 137031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 138031c2a00SAdrian Hunter return "[kernel]" 139031c2a00SAdrian Hunter return name 140031c2a00SAdrian Hunter 141210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0): 142210cf1f9SAdrian Hunter pos = s.find(sub) 143210cf1f9SAdrian Hunter if pos < 0: 144210cf1f9SAdrian Hunter return pos 145210cf1f9SAdrian Hunter if n <= 1: 146210cf1f9SAdrian Hunter return offs + pos 147210cf1f9SAdrian Hunter return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) 148210cf1f9SAdrian Hunter 149031c2a00SAdrian Hunter# Percent to one decimal place 150031c2a00SAdrian Hunter 151031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 152031c2a00SAdrian Hunter if not d: 153031c2a00SAdrian Hunter return "0.0" 154031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 155031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 156031c2a00SAdrian Hunter 157031c2a00SAdrian Hunter# Helper for queries that must not fail 158031c2a00SAdrian Hunter 159031c2a00SAdrian Hunterdef QueryExec(query, stmt): 160031c2a00SAdrian Hunter ret = query.exec_(stmt) 161031c2a00SAdrian Hunter if not ret: 162031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 163031c2a00SAdrian Hunter 164ebd70c7dSAdrian Hunter# Background thread 165ebd70c7dSAdrian Hunter 166ebd70c7dSAdrian Hunterclass Thread(QThread): 167ebd70c7dSAdrian Hunter 168ebd70c7dSAdrian Hunter done = Signal(object) 169ebd70c7dSAdrian Hunter 170ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 171ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 172ebd70c7dSAdrian Hunter self.task = task 173ebd70c7dSAdrian Hunter self.param = param 174ebd70c7dSAdrian Hunter 175ebd70c7dSAdrian Hunter def run(self): 176ebd70c7dSAdrian Hunter while True: 177ebd70c7dSAdrian Hunter if self.param is None: 178ebd70c7dSAdrian Hunter done, result = self.task() 179ebd70c7dSAdrian Hunter else: 180ebd70c7dSAdrian Hunter done, result = self.task(self.param) 181ebd70c7dSAdrian Hunter self.done.emit(result) 182ebd70c7dSAdrian Hunter if done: 183ebd70c7dSAdrian Hunter break 184ebd70c7dSAdrian Hunter 185031c2a00SAdrian Hunter# Tree data model 186031c2a00SAdrian Hunter 187031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 188031c2a00SAdrian Hunter 189a448ba23SAdrian Hunter def __init__(self, glb, parent=None): 190031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 191a448ba23SAdrian Hunter self.glb = glb 192a448ba23SAdrian Hunter self.root = self.GetRoot() 193031c2a00SAdrian Hunter self.last_row_read = 0 194031c2a00SAdrian Hunter 195031c2a00SAdrian Hunter def Item(self, parent): 196031c2a00SAdrian Hunter if parent.isValid(): 197031c2a00SAdrian Hunter return parent.internalPointer() 198031c2a00SAdrian Hunter else: 199031c2a00SAdrian Hunter return self.root 200031c2a00SAdrian Hunter 201031c2a00SAdrian Hunter def rowCount(self, parent): 202031c2a00SAdrian Hunter result = self.Item(parent).childCount() 203031c2a00SAdrian Hunter if result < 0: 204031c2a00SAdrian Hunter result = 0 205031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 206031c2a00SAdrian Hunter return result 207031c2a00SAdrian Hunter 208031c2a00SAdrian Hunter def hasChildren(self, parent): 209031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 210031c2a00SAdrian Hunter 211031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 212031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 213031c2a00SAdrian Hunter return self.columnAlignment(section) 214031c2a00SAdrian Hunter if role != Qt.DisplayRole: 215031c2a00SAdrian Hunter return None 216031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 217031c2a00SAdrian Hunter return None 218031c2a00SAdrian Hunter return self.columnHeader(section) 219031c2a00SAdrian Hunter 220031c2a00SAdrian Hunter def parent(self, child): 221031c2a00SAdrian Hunter child_item = child.internalPointer() 222031c2a00SAdrian Hunter if child_item is self.root: 223031c2a00SAdrian Hunter return QModelIndex() 224031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 225031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 226031c2a00SAdrian Hunter 227031c2a00SAdrian Hunter def index(self, row, column, parent): 228031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 229031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 230031c2a00SAdrian Hunter 231031c2a00SAdrian Hunter def DisplayData(self, item, index): 232031c2a00SAdrian Hunter return item.getData(index.column()) 233031c2a00SAdrian Hunter 2348392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2358392b74bSAdrian Hunter if row > self.last_row_read: 2368392b74bSAdrian Hunter self.last_row_read = row 2378392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2388392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2398392b74bSAdrian Hunter 2408392b74bSAdrian Hunter def columnAlignment(self, column): 2418392b74bSAdrian Hunter return Qt.AlignLeft 2428392b74bSAdrian Hunter 2438392b74bSAdrian Hunter def columnFont(self, column): 2448392b74bSAdrian Hunter return None 2458392b74bSAdrian Hunter 2468392b74bSAdrian Hunter def data(self, index, role): 2478392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2488392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2498392b74bSAdrian Hunter if role == Qt.FontRole: 2508392b74bSAdrian Hunter return self.columnFont(index.column()) 2518392b74bSAdrian Hunter if role != Qt.DisplayRole: 2528392b74bSAdrian Hunter return None 2538392b74bSAdrian Hunter item = index.internalPointer() 2548392b74bSAdrian Hunter return self.DisplayData(item, index) 2558392b74bSAdrian Hunter 2568392b74bSAdrian Hunter# Table data model 2578392b74bSAdrian Hunter 2588392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2598392b74bSAdrian Hunter 2608392b74bSAdrian Hunter def __init__(self, parent=None): 2618392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2628392b74bSAdrian Hunter self.child_count = 0 2638392b74bSAdrian Hunter self.child_items = [] 2648392b74bSAdrian Hunter self.last_row_read = 0 2658392b74bSAdrian Hunter 2668392b74bSAdrian Hunter def Item(self, parent): 2678392b74bSAdrian Hunter if parent.isValid(): 2688392b74bSAdrian Hunter return parent.internalPointer() 2698392b74bSAdrian Hunter else: 2708392b74bSAdrian Hunter return self 2718392b74bSAdrian Hunter 2728392b74bSAdrian Hunter def rowCount(self, parent): 2738392b74bSAdrian Hunter return self.child_count 2748392b74bSAdrian Hunter 2758392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2768392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2778392b74bSAdrian Hunter return self.columnAlignment(section) 2788392b74bSAdrian Hunter if role != Qt.DisplayRole: 2798392b74bSAdrian Hunter return None 2808392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2818392b74bSAdrian Hunter return None 2828392b74bSAdrian Hunter return self.columnHeader(section) 2838392b74bSAdrian Hunter 2848392b74bSAdrian Hunter def index(self, row, column, parent): 2858392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 2868392b74bSAdrian Hunter 2878392b74bSAdrian Hunter def DisplayData(self, item, index): 2888392b74bSAdrian Hunter return item.getData(index.column()) 2898392b74bSAdrian Hunter 2908392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2918392b74bSAdrian Hunter if row > self.last_row_read: 2928392b74bSAdrian Hunter self.last_row_read = row 2938392b74bSAdrian Hunter if row + 10 >= self.child_count: 2948392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2958392b74bSAdrian Hunter 296031c2a00SAdrian Hunter def columnAlignment(self, column): 297031c2a00SAdrian Hunter return Qt.AlignLeft 298031c2a00SAdrian Hunter 299031c2a00SAdrian Hunter def columnFont(self, column): 300031c2a00SAdrian Hunter return None 301031c2a00SAdrian Hunter 302031c2a00SAdrian Hunter def data(self, index, role): 303031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 304031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 305031c2a00SAdrian Hunter if role == Qt.FontRole: 306031c2a00SAdrian Hunter return self.columnFont(index.column()) 307031c2a00SAdrian Hunter if role != Qt.DisplayRole: 308031c2a00SAdrian Hunter return None 309031c2a00SAdrian Hunter item = index.internalPointer() 310031c2a00SAdrian Hunter return self.DisplayData(item, index) 311031c2a00SAdrian Hunter 3121beb5c7bSAdrian Hunter# Model cache 3131beb5c7bSAdrian Hunter 3141beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 3151beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 3161beb5c7bSAdrian Hunter 3171beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 3181beb5c7bSAdrian Hunter model_cache_lock.acquire() 3191beb5c7bSAdrian Hunter try: 3201beb5c7bSAdrian Hunter model = model_cache[model_name] 3211beb5c7bSAdrian Hunter except: 3221beb5c7bSAdrian Hunter model = None 3231beb5c7bSAdrian Hunter if model is None: 3241beb5c7bSAdrian Hunter model = create_fn() 3251beb5c7bSAdrian Hunter model_cache[model_name] = model 3261beb5c7bSAdrian Hunter model_cache_lock.release() 3271beb5c7bSAdrian Hunter return model 3281beb5c7bSAdrian Hunter 329ebd70c7dSAdrian Hunter# Find bar 330ebd70c7dSAdrian Hunter 331ebd70c7dSAdrian Hunterclass FindBar(): 332ebd70c7dSAdrian Hunter 333ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 334ebd70c7dSAdrian Hunter self.finder = finder 335ebd70c7dSAdrian Hunter self.context = [] 336ebd70c7dSAdrian Hunter self.last_value = None 337ebd70c7dSAdrian Hunter self.last_pattern = None 338ebd70c7dSAdrian Hunter 339ebd70c7dSAdrian Hunter label = QLabel("Find:") 340ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 341ebd70c7dSAdrian Hunter 342ebd70c7dSAdrian Hunter self.textbox = QComboBox() 343ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 344ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 345ebd70c7dSAdrian Hunter 346ebd70c7dSAdrian Hunter self.progress = QProgressBar() 347ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 348ebd70c7dSAdrian Hunter self.progress.hide() 349ebd70c7dSAdrian Hunter 350ebd70c7dSAdrian Hunter if is_reg_expr: 351ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 352ebd70c7dSAdrian Hunter else: 353ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 354ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 355ebd70c7dSAdrian Hunter 356ebd70c7dSAdrian Hunter self.next_button = QToolButton() 357ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 358ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 359ebd70c7dSAdrian Hunter 360ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 361ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 362ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 363ebd70c7dSAdrian Hunter 364ebd70c7dSAdrian Hunter self.close_button = QToolButton() 365ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 366ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 367ebd70c7dSAdrian Hunter 368ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 369ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 370ebd70c7dSAdrian Hunter 371ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 372ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 373ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 374ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 375ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 376ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 377ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 378ebd70c7dSAdrian Hunter 379ebd70c7dSAdrian Hunter self.bar = QWidget() 380ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 381ebd70c7dSAdrian Hunter self.bar.hide() 382ebd70c7dSAdrian Hunter 383ebd70c7dSAdrian Hunter def Widget(self): 384ebd70c7dSAdrian Hunter return self.bar 385ebd70c7dSAdrian Hunter 386ebd70c7dSAdrian Hunter def Activate(self): 387ebd70c7dSAdrian Hunter self.bar.show() 388ebd70c7dSAdrian Hunter self.textbox.setFocus() 389ebd70c7dSAdrian Hunter 390ebd70c7dSAdrian Hunter def Deactivate(self): 391ebd70c7dSAdrian Hunter self.bar.hide() 392ebd70c7dSAdrian Hunter 393ebd70c7dSAdrian Hunter def Busy(self): 394ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 395ebd70c7dSAdrian Hunter self.pattern.hide() 396ebd70c7dSAdrian Hunter self.next_button.hide() 397ebd70c7dSAdrian Hunter self.prev_button.hide() 398ebd70c7dSAdrian Hunter self.progress.show() 399ebd70c7dSAdrian Hunter 400ebd70c7dSAdrian Hunter def Idle(self): 401ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 402ebd70c7dSAdrian Hunter self.progress.hide() 403ebd70c7dSAdrian Hunter self.pattern.show() 404ebd70c7dSAdrian Hunter self.next_button.show() 405ebd70c7dSAdrian Hunter self.prev_button.show() 406ebd70c7dSAdrian Hunter 407ebd70c7dSAdrian Hunter def Find(self, direction): 408ebd70c7dSAdrian Hunter value = self.textbox.currentText() 409ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 410ebd70c7dSAdrian Hunter self.last_value = value 411ebd70c7dSAdrian Hunter self.last_pattern = pattern 412ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 413ebd70c7dSAdrian Hunter 414ebd70c7dSAdrian Hunter def ValueChanged(self): 415ebd70c7dSAdrian Hunter value = self.textbox.currentText() 416ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 417ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 418ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 419ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 420ebd70c7dSAdrian Hunter if data == None: 421ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 422ebd70c7dSAdrian Hunter else: 423ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 424ebd70c7dSAdrian Hunter self.Find(0) 425ebd70c7dSAdrian Hunter 426ebd70c7dSAdrian Hunter def NextPrev(self, direction): 427ebd70c7dSAdrian Hunter value = self.textbox.currentText() 428ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 429ebd70c7dSAdrian Hunter if value != self.last_value: 430ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 431ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 432ebd70c7dSAdrian Hunter if index < 0: 433ebd70c7dSAdrian Hunter index = self.textbox.count() 434ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 435ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 436ebd70c7dSAdrian Hunter return 437ebd70c7dSAdrian Hunter else: 438ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 439ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 440ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 441ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 442ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 443ebd70c7dSAdrian Hunter self.Find(direction) 444ebd70c7dSAdrian Hunter 445ebd70c7dSAdrian Hunter def NotFound(self): 446ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 447ebd70c7dSAdrian Hunter 448031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 449031c2a00SAdrian Hunter 450031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 451031c2a00SAdrian Hunter 452031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 453031c2a00SAdrian Hunter self.glb = glb 454031c2a00SAdrian Hunter self.row = row 455031c2a00SAdrian Hunter self.parent_item = parent_item 456031c2a00SAdrian Hunter self.query_done = False; 457031c2a00SAdrian Hunter self.child_count = 0 458031c2a00SAdrian Hunter self.child_items = [] 4593ac641f4SAdrian Hunter if parent_item: 4603ac641f4SAdrian Hunter self.level = parent_item.level + 1 4613ac641f4SAdrian Hunter else: 4623ac641f4SAdrian Hunter self.level = 0 463031c2a00SAdrian Hunter 464031c2a00SAdrian Hunter def getChildItem(self, row): 465031c2a00SAdrian Hunter return self.child_items[row] 466031c2a00SAdrian Hunter 467031c2a00SAdrian Hunter def getParentItem(self): 468031c2a00SAdrian Hunter return self.parent_item 469031c2a00SAdrian Hunter 470031c2a00SAdrian Hunter def getRow(self): 471031c2a00SAdrian Hunter return self.row 472031c2a00SAdrian Hunter 473031c2a00SAdrian Hunter def childCount(self): 474031c2a00SAdrian Hunter if not self.query_done: 475031c2a00SAdrian Hunter self.Select() 476031c2a00SAdrian Hunter if not self.child_count: 477031c2a00SAdrian Hunter return -1 478031c2a00SAdrian Hunter return self.child_count 479031c2a00SAdrian Hunter 480031c2a00SAdrian Hunter def hasChildren(self): 481031c2a00SAdrian Hunter if not self.query_done: 482031c2a00SAdrian Hunter return True 483031c2a00SAdrian Hunter return self.child_count > 0 484031c2a00SAdrian Hunter 485031c2a00SAdrian Hunter def getData(self, column): 486031c2a00SAdrian Hunter return self.data[column] 487031c2a00SAdrian Hunter 488031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 489031c2a00SAdrian Hunter 490031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 491031c2a00SAdrian Hunter 492031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 493031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 494031c2a00SAdrian Hunter self.comm_id = comm_id 495031c2a00SAdrian Hunter self.thread_id = thread_id 496031c2a00SAdrian Hunter self.call_path_id = call_path_id 497031c2a00SAdrian Hunter self.branch_count = branch_count 498031c2a00SAdrian Hunter self.time = time 499031c2a00SAdrian Hunter 500031c2a00SAdrian Hunter def Select(self): 501031c2a00SAdrian Hunter self.query_done = True; 502031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 503031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 504031c2a00SAdrian Hunter " FROM calls" 505031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 506031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 507031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 508031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 509031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 510031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 511031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 512031c2a00SAdrian Hunter " ORDER BY call_path_id") 513031c2a00SAdrian Hunter while query.next(): 514031c2a00SAdrian 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) 515031c2a00SAdrian Hunter self.child_items.append(child_item) 516031c2a00SAdrian Hunter self.child_count += 1 517031c2a00SAdrian Hunter 518031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 519031c2a00SAdrian Hunter 520031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 521031c2a00SAdrian Hunter 522031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 523031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 524031c2a00SAdrian Hunter dso = dsoname(dso) 525031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 526031c2a00SAdrian Hunter self.dbid = call_path_id 527031c2a00SAdrian Hunter 528031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 529031c2a00SAdrian Hunter 530031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 531031c2a00SAdrian Hunter 532031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 533031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 534031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 535031c2a00SAdrian Hunter self.dbid = thread_id 536031c2a00SAdrian Hunter 537031c2a00SAdrian Hunter def Select(self): 538031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 539031c2a00SAdrian Hunter for child_item in self.child_items: 540031c2a00SAdrian Hunter self.time += child_item.time 541031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 542031c2a00SAdrian Hunter for child_item in self.child_items: 543031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 544031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 547031c2a00SAdrian Hunter 548031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 549031c2a00SAdrian Hunter 550031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 551031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 552031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 553031c2a00SAdrian Hunter self.dbid = comm_id 554031c2a00SAdrian Hunter 555031c2a00SAdrian Hunter def Select(self): 556031c2a00SAdrian Hunter self.query_done = True; 557031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 558031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 559031c2a00SAdrian Hunter " FROM comm_threads" 560031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 561031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 562031c2a00SAdrian Hunter while query.next(): 563031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 564031c2a00SAdrian Hunter self.child_items.append(child_item) 565031c2a00SAdrian Hunter self.child_count += 1 566031c2a00SAdrian Hunter 567031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 568031c2a00SAdrian Hunter 569031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 570031c2a00SAdrian Hunter 571031c2a00SAdrian Hunter def __init__(self, glb): 572031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 573031c2a00SAdrian Hunter self.dbid = 0 574031c2a00SAdrian Hunter self.query_done = True; 575031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 576031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 577031c2a00SAdrian Hunter while query.next(): 578031c2a00SAdrian Hunter if not query.value(0): 579031c2a00SAdrian Hunter continue 580031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 581031c2a00SAdrian Hunter self.child_items.append(child_item) 582031c2a00SAdrian Hunter self.child_count += 1 583031c2a00SAdrian Hunter 584254c0d82SAdrian Hunter# Context-sensitive call graph data model base 585031c2a00SAdrian Hunter 586254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 587031c2a00SAdrian Hunter 588031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 589254c0d82SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, parent) 590031c2a00SAdrian Hunter 591ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 592ebd70c7dSAdrian Hunter if pattern: 593ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 594ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 595ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 596ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 597ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 598ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 599ebd70c7dSAdrian Hunter # Escape % and _ 600ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 601ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 602ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 603ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 604ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 605ebd70c7dSAdrian Hunter else: 606ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 607ebd70c7dSAdrian Hunter else: 608ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 609254c0d82SAdrian Hunter self.DoFindSelect(query, match) 610ebd70c7dSAdrian Hunter 611ebd70c7dSAdrian Hunter def Found(self, query, found): 612ebd70c7dSAdrian Hunter if found: 613ebd70c7dSAdrian Hunter return self.FindPath(query) 614ebd70c7dSAdrian Hunter return [] 615ebd70c7dSAdrian Hunter 616ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 617ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 618ebd70c7dSAdrian Hunter found = query.first() 619ebd70c7dSAdrian Hunter else: 620ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 621ebd70c7dSAdrian Hunter found = query.next() 622ebd70c7dSAdrian Hunter return self.Found(query, found) 623ebd70c7dSAdrian Hunter 624ebd70c7dSAdrian Hunter def FindNext(self, query): 625ebd70c7dSAdrian Hunter found = query.next() 626ebd70c7dSAdrian Hunter if not found: 627ebd70c7dSAdrian Hunter found = query.first() 628ebd70c7dSAdrian Hunter return self.Found(query, found) 629ebd70c7dSAdrian Hunter 630ebd70c7dSAdrian Hunter def FindPrev(self, query): 631ebd70c7dSAdrian Hunter found = query.previous() 632ebd70c7dSAdrian Hunter if not found: 633ebd70c7dSAdrian Hunter found = query.last() 634ebd70c7dSAdrian Hunter return self.Found(query, found) 635ebd70c7dSAdrian Hunter 636ebd70c7dSAdrian Hunter def FindThread(self, c): 637ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 638ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 639ebd70c7dSAdrian Hunter elif c.direction > 0: 640ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 641ebd70c7dSAdrian Hunter else: 642ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 643ebd70c7dSAdrian Hunter return (True, ids) 644ebd70c7dSAdrian Hunter 645ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 646ebd70c7dSAdrian Hunter class Context(): 647ebd70c7dSAdrian Hunter def __init__(self, *x): 648ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 649ebd70c7dSAdrian Hunter def Update(self, *x): 650ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 651ebd70c7dSAdrian Hunter if len(context): 652ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 653ebd70c7dSAdrian Hunter else: 654ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 655ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 656ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 657ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 658ebd70c7dSAdrian Hunter thread.start() 659ebd70c7dSAdrian Hunter 660ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 661ebd70c7dSAdrian Hunter callback(ids) 662ebd70c7dSAdrian Hunter 663254c0d82SAdrian Hunter# Context-sensitive call graph data model 664254c0d82SAdrian Hunter 665254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 666254c0d82SAdrian Hunter 667254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 668254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 669254c0d82SAdrian Hunter 670254c0d82SAdrian Hunter def GetRoot(self): 671254c0d82SAdrian Hunter return CallGraphRootItem(self.glb) 672254c0d82SAdrian Hunter 673254c0d82SAdrian Hunter def columnCount(self, parent=None): 674254c0d82SAdrian Hunter return 7 675254c0d82SAdrian Hunter 676254c0d82SAdrian Hunter def columnHeader(self, column): 677254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 678254c0d82SAdrian Hunter return headers[column] 679254c0d82SAdrian Hunter 680254c0d82SAdrian Hunter def columnAlignment(self, column): 681254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 682254c0d82SAdrian Hunter return alignment[column] 683254c0d82SAdrian Hunter 684254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 685254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 686254c0d82SAdrian Hunter " FROM calls" 687254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 688254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 689254c0d82SAdrian Hunter " WHERE symbols.name" + match + 690254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 691254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 692254c0d82SAdrian Hunter 693254c0d82SAdrian Hunter def FindPath(self, query): 694254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 695254c0d82SAdrian Hunter # to open the tree at the right place. 696254c0d82SAdrian Hunter ids = [] 697254c0d82SAdrian Hunter parent_id = query.value(0) 698254c0d82SAdrian Hunter while parent_id: 699254c0d82SAdrian Hunter ids.insert(0, parent_id) 700254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 701254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 702254c0d82SAdrian Hunter " FROM call_paths" 703254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 704254c0d82SAdrian Hunter if not q2.next(): 705254c0d82SAdrian Hunter break 706254c0d82SAdrian Hunter parent_id = q2.value(0) 707254c0d82SAdrian Hunter # The call path root is not used 708254c0d82SAdrian Hunter if ids[0] == 1: 709254c0d82SAdrian Hunter del ids[0] 710254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 711254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 712254c0d82SAdrian Hunter return ids 713254c0d82SAdrian Hunter 714ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 715ae8b887cSAdrian Hunter 716ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 717ae8b887cSAdrian Hunter 718ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): 719ae8b887cSAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 720ae8b887cSAdrian Hunter self.comm_id = comm_id 721ae8b887cSAdrian Hunter self.thread_id = thread_id 722ae8b887cSAdrian Hunter self.calls_id = calls_id 723ae8b887cSAdrian Hunter self.branch_count = branch_count 724ae8b887cSAdrian Hunter self.time = time 725ae8b887cSAdrian Hunter 726ae8b887cSAdrian Hunter def Select(self): 727ae8b887cSAdrian Hunter self.query_done = True; 728ae8b887cSAdrian Hunter if self.calls_id == 0: 729ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 730ae8b887cSAdrian Hunter else: 731ae8b887cSAdrian Hunter comm_thread = "" 732ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 733ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" 734ae8b887cSAdrian Hunter " FROM calls" 735ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 736ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 737ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 738ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 739ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 740ae8b887cSAdrian Hunter while query.next(): 741ae8b887cSAdrian 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) 742ae8b887cSAdrian Hunter self.child_items.append(child_item) 743ae8b887cSAdrian Hunter self.child_count += 1 744ae8b887cSAdrian Hunter 745ae8b887cSAdrian Hunter# Call tree data model level three item 746ae8b887cSAdrian Hunter 747ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 748ae8b887cSAdrian Hunter 749ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): 750ae8b887cSAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) 751ae8b887cSAdrian Hunter dso = dsoname(dso) 752ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 753ae8b887cSAdrian Hunter self.dbid = calls_id 754ae8b887cSAdrian Hunter 755ae8b887cSAdrian Hunter# Call tree data model level two item 756ae8b887cSAdrian Hunter 757ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 758ae8b887cSAdrian Hunter 759ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 760ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) 761ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 762ae8b887cSAdrian Hunter self.dbid = thread_id 763ae8b887cSAdrian Hunter 764ae8b887cSAdrian Hunter def Select(self): 765ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 766ae8b887cSAdrian Hunter for child_item in self.child_items: 767ae8b887cSAdrian Hunter self.time += child_item.time 768ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 769ae8b887cSAdrian Hunter for child_item in self.child_items: 770ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 771ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 772ae8b887cSAdrian Hunter 773ae8b887cSAdrian Hunter# Call tree data model level one item 774ae8b887cSAdrian Hunter 775ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 776ae8b887cSAdrian Hunter 777ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 778ae8b887cSAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) 779ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 780ae8b887cSAdrian Hunter self.dbid = comm_id 781ae8b887cSAdrian Hunter 782ae8b887cSAdrian Hunter def Select(self): 783ae8b887cSAdrian Hunter self.query_done = True; 784ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 785ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 786ae8b887cSAdrian Hunter " FROM comm_threads" 787ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 788ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 789ae8b887cSAdrian Hunter while query.next(): 790ae8b887cSAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 791ae8b887cSAdrian Hunter self.child_items.append(child_item) 792ae8b887cSAdrian Hunter self.child_count += 1 793ae8b887cSAdrian Hunter 794ae8b887cSAdrian Hunter# Call tree data model root item 795ae8b887cSAdrian Hunter 796ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 797ae8b887cSAdrian Hunter 798ae8b887cSAdrian Hunter def __init__(self, glb): 799ae8b887cSAdrian Hunter super(CallTreeRootItem, self).__init__(glb, 0, None) 800ae8b887cSAdrian Hunter self.dbid = 0 801ae8b887cSAdrian Hunter self.query_done = True; 802ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 803ae8b887cSAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 804ae8b887cSAdrian Hunter while query.next(): 805ae8b887cSAdrian Hunter if not query.value(0): 806ae8b887cSAdrian Hunter continue 807ae8b887cSAdrian Hunter child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 808ae8b887cSAdrian Hunter self.child_items.append(child_item) 809ae8b887cSAdrian Hunter self.child_count += 1 810ae8b887cSAdrian Hunter 811ae8b887cSAdrian Hunter# Call Tree data model 812ae8b887cSAdrian Hunter 813ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 814ae8b887cSAdrian Hunter 815ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 816ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 817ae8b887cSAdrian Hunter 818ae8b887cSAdrian Hunter def GetRoot(self): 819ae8b887cSAdrian Hunter return CallTreeRootItem(self.glb) 820ae8b887cSAdrian Hunter 821ae8b887cSAdrian Hunter def columnCount(self, parent=None): 822ae8b887cSAdrian Hunter return 7 823ae8b887cSAdrian Hunter 824ae8b887cSAdrian Hunter def columnHeader(self, column): 825ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 826ae8b887cSAdrian Hunter return headers[column] 827ae8b887cSAdrian Hunter 828ae8b887cSAdrian Hunter def columnAlignment(self, column): 829ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 830ae8b887cSAdrian Hunter return alignment[column] 831ae8b887cSAdrian Hunter 832ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 833ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 834ae8b887cSAdrian Hunter " FROM calls" 835ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 836ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 837ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 838ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 839ae8b887cSAdrian Hunter 840ae8b887cSAdrian Hunter def FindPath(self, query): 841ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 842ae8b887cSAdrian Hunter # to open the tree at the right place. 843ae8b887cSAdrian Hunter ids = [] 844ae8b887cSAdrian Hunter parent_id = query.value(0) 845ae8b887cSAdrian Hunter while parent_id: 846ae8b887cSAdrian Hunter ids.insert(0, parent_id) 847ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 848ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 849ae8b887cSAdrian Hunter " FROM calls" 850ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 851ae8b887cSAdrian Hunter if not q2.next(): 852ae8b887cSAdrian Hunter break 853ae8b887cSAdrian Hunter parent_id = q2.value(0) 854ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 855ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 856ae8b887cSAdrian Hunter return ids 857ae8b887cSAdrian Hunter 858ebd70c7dSAdrian Hunter# Vertical widget layout 859ebd70c7dSAdrian Hunter 860ebd70c7dSAdrian Hunterclass VBox(): 861ebd70c7dSAdrian Hunter 862ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 863ebd70c7dSAdrian Hunter self.vbox = QWidget() 864ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 865ebd70c7dSAdrian Hunter 866ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 867ebd70c7dSAdrian Hunter 868ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 869ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 870ebd70c7dSAdrian Hunter if w3: 871ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 872ebd70c7dSAdrian Hunter 873ebd70c7dSAdrian Hunter def Widget(self): 874ebd70c7dSAdrian Hunter return self.vbox 875ebd70c7dSAdrian Hunter 876a731cc4cSAdrian Hunter# Tree window base 8771beb5c7bSAdrian Hunter 878a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 8791beb5c7bSAdrian Hunter 880a731cc4cSAdrian Hunter def __init__(self, parent=None): 881a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 8821beb5c7bSAdrian Hunter 883a731cc4cSAdrian Hunter self.model = None 884a731cc4cSAdrian Hunter self.find_bar = None 8851beb5c7bSAdrian Hunter 886be6e7471SAdrian Hunter self.view = QTreeView() 88796c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 88896c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 889be6e7471SAdrian Hunter 890*9bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 891*9bc4e4bfSAdrian Hunter 892ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 893ebd70c7dSAdrian Hunter if not len(ids): 894ebd70c7dSAdrian Hunter return False 895ebd70c7dSAdrian Hunter parent = QModelIndex() 896ebd70c7dSAdrian Hunter for dbid in ids: 897ebd70c7dSAdrian Hunter found = False 898ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 899ebd70c7dSAdrian Hunter for row in xrange(n): 900ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 901ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 902ebd70c7dSAdrian Hunter found = True 903ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 904ebd70c7dSAdrian Hunter parent = child 905ebd70c7dSAdrian Hunter break 906ebd70c7dSAdrian Hunter if not found: 907ebd70c7dSAdrian Hunter break 908ebd70c7dSAdrian Hunter return found 909ebd70c7dSAdrian Hunter 910ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 911ebd70c7dSAdrian Hunter self.view.setFocus() 912ebd70c7dSAdrian Hunter self.find_bar.Busy() 913ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 914ebd70c7dSAdrian Hunter 915ebd70c7dSAdrian Hunter def FindDone(self, ids): 916ebd70c7dSAdrian Hunter found = True 917ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 918ebd70c7dSAdrian Hunter found = False 919ebd70c7dSAdrian Hunter self.find_bar.Idle() 920ebd70c7dSAdrian Hunter if not found: 921ebd70c7dSAdrian Hunter self.find_bar.NotFound() 922ebd70c7dSAdrian Hunter 923a731cc4cSAdrian Hunter 924a731cc4cSAdrian Hunter# Context-sensitive call graph window 925a731cc4cSAdrian Hunter 926a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 927a731cc4cSAdrian Hunter 928a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 929a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 930a731cc4cSAdrian Hunter 931a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 932a731cc4cSAdrian Hunter 933a731cc4cSAdrian Hunter self.view.setModel(self.model) 934a731cc4cSAdrian Hunter 935a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 936a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 937a731cc4cSAdrian Hunter 938a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 939a731cc4cSAdrian Hunter 940a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 941a731cc4cSAdrian Hunter 942a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 943a731cc4cSAdrian Hunter 944a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 945a731cc4cSAdrian Hunter 946ae8b887cSAdrian Hunter# Call tree window 947ae8b887cSAdrian Hunter 948ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 949ae8b887cSAdrian Hunter 950ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 951ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 952ae8b887cSAdrian Hunter 953ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 954ae8b887cSAdrian Hunter 955ae8b887cSAdrian Hunter self.view.setModel(self.model) 956ae8b887cSAdrian Hunter 957ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 958ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 959ae8b887cSAdrian Hunter 960ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 961ae8b887cSAdrian Hunter 962ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 963ae8b887cSAdrian Hunter 964ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 965ae8b887cSAdrian Hunter 966ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 967ae8b887cSAdrian Hunter 9688392b74bSAdrian Hunter# Child data item finder 9698392b74bSAdrian Hunter 9708392b74bSAdrian Hunterclass ChildDataItemFinder(): 9718392b74bSAdrian Hunter 9728392b74bSAdrian Hunter def __init__(self, root): 9738392b74bSAdrian Hunter self.root = root 9748392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 9758392b74bSAdrian Hunter self.rows = [] 9768392b74bSAdrian Hunter self.pos = 0 9778392b74bSAdrian Hunter 9788392b74bSAdrian Hunter def FindSelect(self): 9798392b74bSAdrian Hunter self.rows = [] 9808392b74bSAdrian Hunter if self.pattern: 9818392b74bSAdrian Hunter pattern = re.compile(self.value) 9828392b74bSAdrian Hunter for child in self.root.child_items: 9838392b74bSAdrian Hunter for column_data in child.data: 9848392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 9858392b74bSAdrian Hunter self.rows.append(child.row) 9868392b74bSAdrian Hunter break 9878392b74bSAdrian Hunter else: 9888392b74bSAdrian Hunter for child in self.root.child_items: 9898392b74bSAdrian Hunter for column_data in child.data: 9908392b74bSAdrian Hunter if self.value in str(column_data): 9918392b74bSAdrian Hunter self.rows.append(child.row) 9928392b74bSAdrian Hunter break 9938392b74bSAdrian Hunter 9948392b74bSAdrian Hunter def FindValue(self): 9958392b74bSAdrian Hunter self.pos = 0 9968392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 9978392b74bSAdrian Hunter self.FindSelect() 9988392b74bSAdrian Hunter if not len(self.rows): 9998392b74bSAdrian Hunter return -1 10008392b74bSAdrian Hunter return self.rows[self.pos] 10018392b74bSAdrian Hunter 10028392b74bSAdrian Hunter def FindThread(self): 10038392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 10048392b74bSAdrian Hunter row = self.FindValue() 10058392b74bSAdrian Hunter elif len(self.rows): 10068392b74bSAdrian Hunter if self.direction > 0: 10078392b74bSAdrian Hunter self.pos += 1 10088392b74bSAdrian Hunter if self.pos >= len(self.rows): 10098392b74bSAdrian Hunter self.pos = 0 10108392b74bSAdrian Hunter else: 10118392b74bSAdrian Hunter self.pos -= 1 10128392b74bSAdrian Hunter if self.pos < 0: 10138392b74bSAdrian Hunter self.pos = len(self.rows) - 1 10148392b74bSAdrian Hunter row = self.rows[self.pos] 10158392b74bSAdrian Hunter else: 10168392b74bSAdrian Hunter row = -1 10178392b74bSAdrian Hunter return (True, row) 10188392b74bSAdrian Hunter 10198392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 10208392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 10218392b74bSAdrian Hunter # Use a thread so the UI is not blocked 10228392b74bSAdrian Hunter thread = Thread(self.FindThread) 10238392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 10248392b74bSAdrian Hunter thread.start() 10258392b74bSAdrian Hunter 10268392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 10278392b74bSAdrian Hunter callback(row) 10288392b74bSAdrian Hunter 10298392b74bSAdrian Hunter# Number of database records to fetch in one go 10308392b74bSAdrian Hunter 10318392b74bSAdrian Hunterglb_chunk_sz = 10000 10328392b74bSAdrian Hunter 10338392b74bSAdrian Hunter# Background process for SQL data fetcher 10348392b74bSAdrian Hunter 10358392b74bSAdrian Hunterclass SQLFetcherProcess(): 10368392b74bSAdrian Hunter 10378392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 10388392b74bSAdrian Hunter # Need a unique connection name 10398392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 10408392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 10418392b74bSAdrian Hunter self.sql = sql 10428392b74bSAdrian Hunter self.buffer = buffer 10438392b74bSAdrian Hunter self.head = head 10448392b74bSAdrian Hunter self.tail = tail 10458392b74bSAdrian Hunter self.fetch_count = fetch_count 10468392b74bSAdrian Hunter self.fetching_done = fetching_done 10478392b74bSAdrian Hunter self.process_target = process_target 10488392b74bSAdrian Hunter self.wait_event = wait_event 10498392b74bSAdrian Hunter self.fetched_event = fetched_event 10508392b74bSAdrian Hunter self.prep = prep 10518392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 10528392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 10538392b74bSAdrian Hunter self.last_id = -1 10548392b74bSAdrian Hunter self.fetched = 0 10558392b74bSAdrian Hunter self.more = True 10568392b74bSAdrian Hunter self.local_head = self.head.value 10578392b74bSAdrian Hunter self.local_tail = self.tail.value 10588392b74bSAdrian Hunter 10598392b74bSAdrian Hunter def Select(self): 10608392b74bSAdrian Hunter if self.query_limit: 10618392b74bSAdrian Hunter if self.query_limit == 1: 10628392b74bSAdrian Hunter return 10638392b74bSAdrian Hunter self.query_limit -= 1 10648392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 10658392b74bSAdrian Hunter QueryExec(self.query, stmt) 10668392b74bSAdrian Hunter 10678392b74bSAdrian Hunter def Next(self): 10688392b74bSAdrian Hunter if not self.query.next(): 10698392b74bSAdrian Hunter self.Select() 10708392b74bSAdrian Hunter if not self.query.next(): 10718392b74bSAdrian Hunter return None 10728392b74bSAdrian Hunter self.last_id = self.query.value(0) 10738392b74bSAdrian Hunter return self.prep(self.query) 10748392b74bSAdrian Hunter 10758392b74bSAdrian Hunter def WaitForTarget(self): 10768392b74bSAdrian Hunter while True: 10778392b74bSAdrian Hunter self.wait_event.clear() 10788392b74bSAdrian Hunter target = self.process_target.value 10798392b74bSAdrian Hunter if target > self.fetched or target < 0: 10808392b74bSAdrian Hunter break 10818392b74bSAdrian Hunter self.wait_event.wait() 10828392b74bSAdrian Hunter return target 10838392b74bSAdrian Hunter 10848392b74bSAdrian Hunter def HasSpace(self, sz): 10858392b74bSAdrian Hunter if self.local_tail <= self.local_head: 10868392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 10878392b74bSAdrian Hunter if space > sz: 10888392b74bSAdrian Hunter return True 10898392b74bSAdrian Hunter if space >= glb_nsz: 10908392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1091beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 10928392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 10938392b74bSAdrian Hunter self.local_head = 0 10948392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 10958392b74bSAdrian Hunter return True 10968392b74bSAdrian Hunter return False 10978392b74bSAdrian Hunter 10988392b74bSAdrian Hunter def WaitForSpace(self, sz): 10998392b74bSAdrian Hunter if self.HasSpace(sz): 11008392b74bSAdrian Hunter return 11018392b74bSAdrian Hunter while True: 11028392b74bSAdrian Hunter self.wait_event.clear() 11038392b74bSAdrian Hunter self.local_tail = self.tail.value 11048392b74bSAdrian Hunter if self.HasSpace(sz): 11058392b74bSAdrian Hunter return 11068392b74bSAdrian Hunter self.wait_event.wait() 11078392b74bSAdrian Hunter 11088392b74bSAdrian Hunter def AddToBuffer(self, obj): 1109beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 11108392b74bSAdrian Hunter n = len(d) 1111beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 11128392b74bSAdrian Hunter sz = n + glb_nsz 11138392b74bSAdrian Hunter self.WaitForSpace(sz) 11148392b74bSAdrian Hunter pos = self.local_head 11158392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 11168392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 11178392b74bSAdrian Hunter self.local_head += sz 11188392b74bSAdrian Hunter 11198392b74bSAdrian Hunter def FetchBatch(self, batch_size): 11208392b74bSAdrian Hunter fetched = 0 11218392b74bSAdrian Hunter while batch_size > fetched: 11228392b74bSAdrian Hunter obj = self.Next() 11238392b74bSAdrian Hunter if obj is None: 11248392b74bSAdrian Hunter self.more = False 11258392b74bSAdrian Hunter break 11268392b74bSAdrian Hunter self.AddToBuffer(obj) 11278392b74bSAdrian Hunter fetched += 1 11288392b74bSAdrian Hunter if fetched: 11298392b74bSAdrian Hunter self.fetched += fetched 11308392b74bSAdrian Hunter with self.fetch_count.get_lock(): 11318392b74bSAdrian Hunter self.fetch_count.value += fetched 11328392b74bSAdrian Hunter self.head.value = self.local_head 11338392b74bSAdrian Hunter self.fetched_event.set() 11348392b74bSAdrian Hunter 11358392b74bSAdrian Hunter def Run(self): 11368392b74bSAdrian Hunter while self.more: 11378392b74bSAdrian Hunter target = self.WaitForTarget() 11388392b74bSAdrian Hunter if target < 0: 11398392b74bSAdrian Hunter break 11408392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 11418392b74bSAdrian Hunter self.FetchBatch(batch_size) 11428392b74bSAdrian Hunter self.fetching_done.value = True 11438392b74bSAdrian Hunter self.fetched_event.set() 11448392b74bSAdrian Hunter 11458392b74bSAdrian Hunterdef SQLFetcherFn(*x): 11468392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 11478392b74bSAdrian Hunter process.Run() 11488392b74bSAdrian Hunter 11498392b74bSAdrian Hunter# SQL data fetcher 11508392b74bSAdrian Hunter 11518392b74bSAdrian Hunterclass SQLFetcher(QObject): 11528392b74bSAdrian Hunter 11538392b74bSAdrian Hunter done = Signal(object) 11548392b74bSAdrian Hunter 11558392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 11568392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 11578392b74bSAdrian Hunter self.process_data = process_data 11588392b74bSAdrian Hunter self.more = True 11598392b74bSAdrian Hunter self.target = 0 11608392b74bSAdrian Hunter self.last_target = 0 11618392b74bSAdrian Hunter self.fetched = 0 11628392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 11638392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 11648392b74bSAdrian Hunter self.head = Value(c_longlong) 11658392b74bSAdrian Hunter self.tail = Value(c_longlong) 11668392b74bSAdrian Hunter self.local_tail = 0 11678392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 11688392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 11698392b74bSAdrian Hunter self.last_count = 0 11708392b74bSAdrian Hunter self.process_target = Value(c_longlong) 11718392b74bSAdrian Hunter self.wait_event = Event() 11728392b74bSAdrian Hunter self.fetched_event = Event() 11738392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 11748392b74bSAdrian 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)) 11758392b74bSAdrian Hunter self.process.start() 11768392b74bSAdrian Hunter self.thread = Thread(self.Thread) 11778392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 11788392b74bSAdrian Hunter self.thread.start() 11798392b74bSAdrian Hunter 11808392b74bSAdrian Hunter def Shutdown(self): 11818392b74bSAdrian Hunter # Tell the thread and process to exit 11828392b74bSAdrian Hunter self.process_target.value = -1 11838392b74bSAdrian Hunter self.wait_event.set() 11848392b74bSAdrian Hunter self.more = False 11858392b74bSAdrian Hunter self.fetching_done.value = True 11868392b74bSAdrian Hunter self.fetched_event.set() 11878392b74bSAdrian Hunter 11888392b74bSAdrian Hunter def Thread(self): 11898392b74bSAdrian Hunter if not self.more: 11908392b74bSAdrian Hunter return True, 0 11918392b74bSAdrian Hunter while True: 11928392b74bSAdrian Hunter self.fetched_event.clear() 11938392b74bSAdrian Hunter fetch_count = self.fetch_count.value 11948392b74bSAdrian Hunter if fetch_count != self.last_count: 11958392b74bSAdrian Hunter break 11968392b74bSAdrian Hunter if self.fetching_done.value: 11978392b74bSAdrian Hunter self.more = False 11988392b74bSAdrian Hunter return True, 0 11998392b74bSAdrian Hunter self.fetched_event.wait() 12008392b74bSAdrian Hunter count = fetch_count - self.last_count 12018392b74bSAdrian Hunter self.last_count = fetch_count 12028392b74bSAdrian Hunter self.fetched += count 12038392b74bSAdrian Hunter return False, count 12048392b74bSAdrian Hunter 12058392b74bSAdrian Hunter def Fetch(self, nr): 12068392b74bSAdrian Hunter if not self.more: 12078392b74bSAdrian Hunter # -1 inidcates there are no more 12088392b74bSAdrian Hunter return -1 12098392b74bSAdrian Hunter result = self.fetched 12108392b74bSAdrian Hunter extra = result + nr - self.target 12118392b74bSAdrian Hunter if extra > 0: 12128392b74bSAdrian Hunter self.target += extra 12138392b74bSAdrian Hunter # process_target < 0 indicates shutting down 12148392b74bSAdrian Hunter if self.process_target.value >= 0: 12158392b74bSAdrian Hunter self.process_target.value = self.target 12168392b74bSAdrian Hunter self.wait_event.set() 12178392b74bSAdrian Hunter return result 12188392b74bSAdrian Hunter 12198392b74bSAdrian Hunter def RemoveFromBuffer(self): 12208392b74bSAdrian Hunter pos = self.local_tail 12218392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 12228392b74bSAdrian Hunter pos = 0 1223beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 12248392b74bSAdrian Hunter if n == 0: 12258392b74bSAdrian Hunter pos = 0 1226beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 12278392b74bSAdrian Hunter pos += glb_nsz 1228beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 12298392b74bSAdrian Hunter self.local_tail = pos + n 12308392b74bSAdrian Hunter return obj 12318392b74bSAdrian Hunter 12328392b74bSAdrian Hunter def ProcessData(self, count): 12338392b74bSAdrian Hunter for i in xrange(count): 12348392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 12358392b74bSAdrian Hunter self.process_data(obj) 12368392b74bSAdrian Hunter self.tail.value = self.local_tail 12378392b74bSAdrian Hunter self.wait_event.set() 12388392b74bSAdrian Hunter self.done.emit(count) 12398392b74bSAdrian Hunter 12408392b74bSAdrian Hunter# Fetch more records bar 12418392b74bSAdrian Hunter 12428392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 12438392b74bSAdrian Hunter 12448392b74bSAdrian Hunter def __init__(self, model, parent): 12458392b74bSAdrian Hunter self.model = model 12468392b74bSAdrian Hunter 12478392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 12488392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12498392b74bSAdrian Hunter 12508392b74bSAdrian Hunter self.fetch_count = QSpinBox() 12518392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 12528392b74bSAdrian Hunter self.fetch_count.setValue(10) 12538392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12548392b74bSAdrian Hunter 12558392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 12568392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12578392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 12588392b74bSAdrian Hunter 12598392b74bSAdrian Hunter self.progress = QProgressBar() 12608392b74bSAdrian Hunter self.progress.setRange(0, 100) 12618392b74bSAdrian Hunter self.progress.hide() 12628392b74bSAdrian Hunter 12638392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 12648392b74bSAdrian Hunter self.done_label.hide() 12658392b74bSAdrian Hunter 12668392b74bSAdrian Hunter self.spacer = QLabel("") 12678392b74bSAdrian Hunter 12688392b74bSAdrian Hunter self.close_button = QToolButton() 12698392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 12708392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 12718392b74bSAdrian Hunter 12728392b74bSAdrian Hunter self.hbox = QHBoxLayout() 12738392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 12748392b74bSAdrian Hunter 12758392b74bSAdrian Hunter self.hbox.addWidget(self.label) 12768392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 12778392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 12788392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 12798392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 12808392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 12818392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 12828392b74bSAdrian Hunter 12838392b74bSAdrian Hunter self.bar = QWidget() 12848392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 12858392b74bSAdrian Hunter self.bar.show() 12868392b74bSAdrian Hunter 12878392b74bSAdrian Hunter self.in_progress = False 12888392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 12898392b74bSAdrian Hunter 12908392b74bSAdrian Hunter self.done = False 12918392b74bSAdrian Hunter 12928392b74bSAdrian Hunter if not model.HasMoreRecords(): 12938392b74bSAdrian Hunter self.Done() 12948392b74bSAdrian Hunter 12958392b74bSAdrian Hunter def Widget(self): 12968392b74bSAdrian Hunter return self.bar 12978392b74bSAdrian Hunter 12988392b74bSAdrian Hunter def Activate(self): 12998392b74bSAdrian Hunter self.bar.show() 13008392b74bSAdrian Hunter self.fetch.setFocus() 13018392b74bSAdrian Hunter 13028392b74bSAdrian Hunter def Deactivate(self): 13038392b74bSAdrian Hunter self.bar.hide() 13048392b74bSAdrian Hunter 13058392b74bSAdrian Hunter def Enable(self, enable): 13068392b74bSAdrian Hunter self.fetch.setEnabled(enable) 13078392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 13088392b74bSAdrian Hunter 13098392b74bSAdrian Hunter def Busy(self): 13108392b74bSAdrian Hunter self.Enable(False) 13118392b74bSAdrian Hunter self.fetch.hide() 13128392b74bSAdrian Hunter self.spacer.hide() 13138392b74bSAdrian Hunter self.progress.show() 13148392b74bSAdrian Hunter 13158392b74bSAdrian Hunter def Idle(self): 13168392b74bSAdrian Hunter self.in_progress = False 13178392b74bSAdrian Hunter self.Enable(True) 13188392b74bSAdrian Hunter self.progress.hide() 13198392b74bSAdrian Hunter self.fetch.show() 13208392b74bSAdrian Hunter self.spacer.show() 13218392b74bSAdrian Hunter 13228392b74bSAdrian Hunter def Target(self): 13238392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 13248392b74bSAdrian Hunter 13258392b74bSAdrian Hunter def Done(self): 13268392b74bSAdrian Hunter self.done = True 13278392b74bSAdrian Hunter self.Idle() 13288392b74bSAdrian Hunter self.label.hide() 13298392b74bSAdrian Hunter self.fetch_count.hide() 13308392b74bSAdrian Hunter self.fetch.hide() 13318392b74bSAdrian Hunter self.spacer.hide() 13328392b74bSAdrian Hunter self.done_label.show() 13338392b74bSAdrian Hunter 13348392b74bSAdrian Hunter def Progress(self, count): 13358392b74bSAdrian Hunter if self.in_progress: 13368392b74bSAdrian Hunter if count: 13378392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 13388392b74bSAdrian Hunter if percent >= 100: 13398392b74bSAdrian Hunter self.Idle() 13408392b74bSAdrian Hunter else: 13418392b74bSAdrian Hunter self.progress.setValue(percent) 13428392b74bSAdrian Hunter if not count: 13438392b74bSAdrian Hunter # Count value of zero means no more records 13448392b74bSAdrian Hunter self.Done() 13458392b74bSAdrian Hunter 13468392b74bSAdrian Hunter def FetchMoreRecords(self): 13478392b74bSAdrian Hunter if self.done: 13488392b74bSAdrian Hunter return 13498392b74bSAdrian Hunter self.progress.setValue(0) 13508392b74bSAdrian Hunter self.Busy() 13518392b74bSAdrian Hunter self.in_progress = True 13528392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 13538392b74bSAdrian Hunter 135476099f98SAdrian Hunter# Brance data model level two item 135576099f98SAdrian Hunter 135676099f98SAdrian Hunterclass BranchLevelTwoItem(): 135776099f98SAdrian Hunter 135876099f98SAdrian Hunter def __init__(self, row, text, parent_item): 135976099f98SAdrian Hunter self.row = row 136076099f98SAdrian Hunter self.parent_item = parent_item 136176099f98SAdrian Hunter self.data = [""] * 8 136276099f98SAdrian Hunter self.data[7] = text 136376099f98SAdrian Hunter self.level = 2 136476099f98SAdrian Hunter 136576099f98SAdrian Hunter def getParentItem(self): 136676099f98SAdrian Hunter return self.parent_item 136776099f98SAdrian Hunter 136876099f98SAdrian Hunter def getRow(self): 136976099f98SAdrian Hunter return self.row 137076099f98SAdrian Hunter 137176099f98SAdrian Hunter def childCount(self): 137276099f98SAdrian Hunter return 0 137376099f98SAdrian Hunter 137476099f98SAdrian Hunter def hasChildren(self): 137576099f98SAdrian Hunter return False 137676099f98SAdrian Hunter 137776099f98SAdrian Hunter def getData(self, column): 137876099f98SAdrian Hunter return self.data[column] 137976099f98SAdrian Hunter 138076099f98SAdrian Hunter# Brance data model level one item 138176099f98SAdrian Hunter 138276099f98SAdrian Hunterclass BranchLevelOneItem(): 138376099f98SAdrian Hunter 138476099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 138576099f98SAdrian Hunter self.glb = glb 138676099f98SAdrian Hunter self.row = row 138776099f98SAdrian Hunter self.parent_item = parent_item 138876099f98SAdrian Hunter self.child_count = 0 138976099f98SAdrian Hunter self.child_items = [] 139076099f98SAdrian Hunter self.data = data[1:] 139176099f98SAdrian Hunter self.dbid = data[0] 139276099f98SAdrian Hunter self.level = 1 139376099f98SAdrian Hunter self.query_done = False 139476099f98SAdrian Hunter 139576099f98SAdrian Hunter def getChildItem(self, row): 139676099f98SAdrian Hunter return self.child_items[row] 139776099f98SAdrian Hunter 139876099f98SAdrian Hunter def getParentItem(self): 139976099f98SAdrian Hunter return self.parent_item 140076099f98SAdrian Hunter 140176099f98SAdrian Hunter def getRow(self): 140276099f98SAdrian Hunter return self.row 140376099f98SAdrian Hunter 140476099f98SAdrian Hunter def Select(self): 140576099f98SAdrian Hunter self.query_done = True 140676099f98SAdrian Hunter 140776099f98SAdrian Hunter if not self.glb.have_disassembler: 140876099f98SAdrian Hunter return 140976099f98SAdrian Hunter 141076099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 141176099f98SAdrian Hunter 141276099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 141376099f98SAdrian Hunter " FROM samples" 141476099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 141576099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 141676099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 141776099f98SAdrian Hunter if not query.next(): 141876099f98SAdrian Hunter return 141976099f98SAdrian Hunter cpu = query.value(0) 142076099f98SAdrian Hunter dso = query.value(1) 142176099f98SAdrian Hunter sym = query.value(2) 142276099f98SAdrian Hunter if dso == 0 or sym == 0: 142376099f98SAdrian Hunter return 142476099f98SAdrian Hunter off = query.value(3) 142576099f98SAdrian Hunter short_name = query.value(4) 142676099f98SAdrian Hunter long_name = query.value(5) 142776099f98SAdrian Hunter build_id = query.value(6) 142876099f98SAdrian Hunter sym_start = query.value(7) 142976099f98SAdrian Hunter ip = query.value(8) 143076099f98SAdrian Hunter 143176099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 143276099f98SAdrian Hunter " FROM samples" 143376099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 143476099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 143576099f98SAdrian Hunter " ORDER BY samples.id" 143676099f98SAdrian Hunter " LIMIT 1") 143776099f98SAdrian Hunter if not query.next(): 143876099f98SAdrian Hunter return 143976099f98SAdrian Hunter if query.value(0) != dso: 144076099f98SAdrian Hunter # Cannot disassemble from one dso to another 144176099f98SAdrian Hunter return 144276099f98SAdrian Hunter bsym = query.value(1) 144376099f98SAdrian Hunter boff = query.value(2) 144476099f98SAdrian Hunter bsym_start = query.value(3) 144576099f98SAdrian Hunter if bsym == 0: 144676099f98SAdrian Hunter return 144776099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 144876099f98SAdrian Hunter if tot <= 0 or tot > 16384: 144976099f98SAdrian Hunter return 145076099f98SAdrian Hunter 145176099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 145276099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 145376099f98SAdrian Hunter if not f: 145476099f98SAdrian Hunter return 145576099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 145676099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 145776099f98SAdrian Hunter 145876099f98SAdrian Hunter buf_sz = tot + 16 145976099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 146076099f98SAdrian Hunter f.seek(sym_start + off) 146176099f98SAdrian Hunter buf.value = f.read(buf_sz) 146276099f98SAdrian Hunter buf_ptr = addressof(buf) 146376099f98SAdrian Hunter i = 0 146476099f98SAdrian Hunter while tot > 0: 146576099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 146676099f98SAdrian Hunter if cnt: 146776099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 146876099f98SAdrian Hunter for k in xrange(cnt): 146976099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 147076099f98SAdrian Hunter i += 1 147176099f98SAdrian Hunter while k < 15: 147276099f98SAdrian Hunter byte_str += " " 147376099f98SAdrian Hunter k += 1 147476099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 147576099f98SAdrian Hunter self.child_count += 1 147676099f98SAdrian Hunter else: 147776099f98SAdrian Hunter return 147876099f98SAdrian Hunter buf_ptr += cnt 147976099f98SAdrian Hunter tot -= cnt 148076099f98SAdrian Hunter buf_sz -= cnt 148176099f98SAdrian Hunter ip += cnt 148276099f98SAdrian Hunter 148376099f98SAdrian Hunter def childCount(self): 148476099f98SAdrian Hunter if not self.query_done: 148576099f98SAdrian Hunter self.Select() 148676099f98SAdrian Hunter if not self.child_count: 148776099f98SAdrian Hunter return -1 148876099f98SAdrian Hunter return self.child_count 148976099f98SAdrian Hunter 149076099f98SAdrian Hunter def hasChildren(self): 149176099f98SAdrian Hunter if not self.query_done: 149276099f98SAdrian Hunter return True 149376099f98SAdrian Hunter return self.child_count > 0 149476099f98SAdrian Hunter 149576099f98SAdrian Hunter def getData(self, column): 149676099f98SAdrian Hunter return self.data[column] 149776099f98SAdrian Hunter 149876099f98SAdrian Hunter# Brance data model root item 149976099f98SAdrian Hunter 150076099f98SAdrian Hunterclass BranchRootItem(): 150176099f98SAdrian Hunter 150276099f98SAdrian Hunter def __init__(self): 150376099f98SAdrian Hunter self.child_count = 0 150476099f98SAdrian Hunter self.child_items = [] 150576099f98SAdrian Hunter self.level = 0 150676099f98SAdrian Hunter 150776099f98SAdrian Hunter def getChildItem(self, row): 150876099f98SAdrian Hunter return self.child_items[row] 150976099f98SAdrian Hunter 151076099f98SAdrian Hunter def getParentItem(self): 151176099f98SAdrian Hunter return None 151276099f98SAdrian Hunter 151376099f98SAdrian Hunter def getRow(self): 151476099f98SAdrian Hunter return 0 151576099f98SAdrian Hunter 151676099f98SAdrian Hunter def childCount(self): 151776099f98SAdrian Hunter return self.child_count 151876099f98SAdrian Hunter 151976099f98SAdrian Hunter def hasChildren(self): 152076099f98SAdrian Hunter return self.child_count > 0 152176099f98SAdrian Hunter 152276099f98SAdrian Hunter def getData(self, column): 152376099f98SAdrian Hunter return "" 152476099f98SAdrian Hunter 152576099f98SAdrian Hunter# Branch data preparation 152676099f98SAdrian Hunter 152776099f98SAdrian Hunterdef BranchDataPrep(query): 152876099f98SAdrian Hunter data = [] 152976099f98SAdrian Hunter for i in xrange(0, 8): 153076099f98SAdrian Hunter data.append(query.value(i)) 153176099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 153276099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 153376099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 153476099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 153576099f98SAdrian Hunter return data 153676099f98SAdrian Hunter 15378453c936SAdrian Hunterdef BranchDataPrepWA(query): 15388453c936SAdrian Hunter data = [] 15398453c936SAdrian Hunter data.append(query.value(0)) 15408453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 15418453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 15428453c936SAdrian Hunter for i in xrange(2, 8): 15438453c936SAdrian Hunter data.append(query.value(i)) 15448453c936SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 15458453c936SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 15468453c936SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 15478453c936SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 15488453c936SAdrian Hunter return data 15498453c936SAdrian Hunter 155076099f98SAdrian Hunter# Branch data model 155176099f98SAdrian Hunter 155276099f98SAdrian Hunterclass BranchModel(TreeModel): 155376099f98SAdrian Hunter 155476099f98SAdrian Hunter progress = Signal(object) 155576099f98SAdrian Hunter 155676099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 1557a448ba23SAdrian Hunter super(BranchModel, self).__init__(glb, parent) 155876099f98SAdrian Hunter self.event_id = event_id 155976099f98SAdrian Hunter self.more = True 156076099f98SAdrian Hunter self.populated = 0 156176099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 156276099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 156376099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 156476099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 156576099f98SAdrian Hunter " FROM samples" 156676099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 156776099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 156876099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 156976099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 157076099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 157176099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 157276099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 157376099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 157476099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 157576099f98SAdrian Hunter " ORDER BY samples.id" 157676099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 15778453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 15788453c936SAdrian Hunter prep = BranchDataPrepWA 15798453c936SAdrian Hunter else: 15808453c936SAdrian Hunter prep = BranchDataPrep 15818453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 158276099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 158376099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 158476099f98SAdrian Hunter 1585a448ba23SAdrian Hunter def GetRoot(self): 1586a448ba23SAdrian Hunter return BranchRootItem() 1587a448ba23SAdrian Hunter 158876099f98SAdrian Hunter def columnCount(self, parent=None): 158976099f98SAdrian Hunter return 8 159076099f98SAdrian Hunter 159176099f98SAdrian Hunter def columnHeader(self, column): 159276099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 159376099f98SAdrian Hunter 159476099f98SAdrian Hunter def columnFont(self, column): 159576099f98SAdrian Hunter if column != 7: 159676099f98SAdrian Hunter return None 159776099f98SAdrian Hunter return QFont("Monospace") 159876099f98SAdrian Hunter 159976099f98SAdrian Hunter def DisplayData(self, item, index): 160076099f98SAdrian Hunter if item.level == 1: 160176099f98SAdrian Hunter self.FetchIfNeeded(item.row) 160276099f98SAdrian Hunter return item.getData(index.column()) 160376099f98SAdrian Hunter 160476099f98SAdrian Hunter def AddSample(self, data): 160576099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 160676099f98SAdrian Hunter self.root.child_items.append(child) 160776099f98SAdrian Hunter self.populated += 1 160876099f98SAdrian Hunter 160976099f98SAdrian Hunter def Update(self, fetched): 161076099f98SAdrian Hunter if not fetched: 161176099f98SAdrian Hunter self.more = False 161276099f98SAdrian Hunter self.progress.emit(0) 161376099f98SAdrian Hunter child_count = self.root.child_count 161476099f98SAdrian Hunter count = self.populated - child_count 161576099f98SAdrian Hunter if count > 0: 161676099f98SAdrian Hunter parent = QModelIndex() 161776099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 161876099f98SAdrian Hunter self.insertRows(child_count, count, parent) 161976099f98SAdrian Hunter self.root.child_count += count 162076099f98SAdrian Hunter self.endInsertRows() 162176099f98SAdrian Hunter self.progress.emit(self.root.child_count) 162276099f98SAdrian Hunter 162376099f98SAdrian Hunter def FetchMoreRecords(self, count): 162476099f98SAdrian Hunter current = self.root.child_count 162576099f98SAdrian Hunter if self.more: 162676099f98SAdrian Hunter self.fetcher.Fetch(count) 162776099f98SAdrian Hunter else: 162876099f98SAdrian Hunter self.progress.emit(0) 162976099f98SAdrian Hunter return current 163076099f98SAdrian Hunter 163176099f98SAdrian Hunter def HasMoreRecords(self): 163276099f98SAdrian Hunter return self.more 163376099f98SAdrian Hunter 16340bf0947aSAdrian Hunter# Report Variables 16350bf0947aSAdrian Hunter 16360bf0947aSAdrian Hunterclass ReportVars(): 16370bf0947aSAdrian Hunter 1638cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1639947cc38dSAdrian Hunter self.name = name 16400bf0947aSAdrian Hunter self.where_clause = where_clause 1641cd358012SAdrian Hunter self.limit = limit 16420bf0947aSAdrian Hunter 16430bf0947aSAdrian Hunter def UniqueId(self): 1644cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 16450bf0947aSAdrian Hunter 164676099f98SAdrian Hunter# Branch window 164776099f98SAdrian Hunter 164876099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 164976099f98SAdrian Hunter 1650947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 165176099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 165276099f98SAdrian Hunter 16530bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 165476099f98SAdrian Hunter 16550bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 165676099f98SAdrian Hunter 165776099f98SAdrian Hunter self.view = QTreeView() 165876099f98SAdrian Hunter self.view.setUniformRowHeights(True) 165996c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 166096c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard 166176099f98SAdrian Hunter self.view.setModel(self.model) 166276099f98SAdrian Hunter 166376099f98SAdrian Hunter self.ResizeColumnsToContents() 166476099f98SAdrian Hunter 1665*9bc4e4bfSAdrian Hunter self.context_menu = TreeContextMenu(self.view) 1666*9bc4e4bfSAdrian Hunter 166776099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 166876099f98SAdrian Hunter 166976099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 167076099f98SAdrian Hunter 167176099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 167276099f98SAdrian Hunter 167376099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 167476099f98SAdrian Hunter 167576099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 167676099f98SAdrian Hunter 1677947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 167876099f98SAdrian Hunter 167976099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 168076099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 168176099f98SAdrian Hunter # so implement a crude alternative 168276099f98SAdrian Hunter mm = "MM" if column else "MMMM" 168376099f98SAdrian Hunter font = self.view.font() 168476099f98SAdrian Hunter metrics = QFontMetrics(font) 168576099f98SAdrian Hunter max = 0 168676099f98SAdrian Hunter for row in xrange(n): 168776099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 168876099f98SAdrian Hunter len = metrics.width(str(val) + mm) 168976099f98SAdrian Hunter max = len if len > max else max 169076099f98SAdrian Hunter val = self.model.columnHeader(column) 169176099f98SAdrian Hunter len = metrics.width(str(val) + mm) 169276099f98SAdrian Hunter max = len if len > max else max 169376099f98SAdrian Hunter self.view.setColumnWidth(column, max) 169476099f98SAdrian Hunter 169576099f98SAdrian Hunter def ResizeColumnsToContents(self): 169676099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 169776099f98SAdrian Hunter if n < 1: 169876099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 169976099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 170076099f98SAdrian Hunter return 170176099f98SAdrian Hunter columns = self.model.columnCount() 170276099f98SAdrian Hunter for i in xrange(columns): 170376099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 170476099f98SAdrian Hunter 170576099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 170676099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 170776099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 170876099f98SAdrian Hunter self.ResizeColumnsToContents() 170976099f98SAdrian Hunter 171076099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 171176099f98SAdrian Hunter self.view.setFocus() 171276099f98SAdrian Hunter self.find_bar.Busy() 171376099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 171476099f98SAdrian Hunter 171576099f98SAdrian Hunter def FindDone(self, row): 171676099f98SAdrian Hunter self.find_bar.Idle() 171776099f98SAdrian Hunter if row >= 0: 171876099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 171976099f98SAdrian Hunter else: 172076099f98SAdrian Hunter self.find_bar.NotFound() 172176099f98SAdrian Hunter 17221c3ca1b3SAdrian Hunter# Line edit data item 17231c3ca1b3SAdrian Hunter 17241c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 17251c3ca1b3SAdrian Hunter 1726cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 17271c3ca1b3SAdrian Hunter self.glb = glb 17281c3ca1b3SAdrian Hunter self.label = label 17291c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 17301c3ca1b3SAdrian Hunter self.parent = parent 17311c3ca1b3SAdrian Hunter self.id = id 17321c3ca1b3SAdrian Hunter 1733cd358012SAdrian Hunter self.value = default 17341c3ca1b3SAdrian Hunter 1735cd358012SAdrian Hunter self.widget = QLineEdit(default) 17361c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 17371c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 17381c3ca1b3SAdrian Hunter self.red = False 17391c3ca1b3SAdrian Hunter self.error = "" 17401c3ca1b3SAdrian Hunter self.validated = True 17411c3ca1b3SAdrian Hunter 17421c3ca1b3SAdrian Hunter if placeholder_text: 17431c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 17441c3ca1b3SAdrian Hunter 17451c3ca1b3SAdrian Hunter def TurnTextRed(self): 17461c3ca1b3SAdrian Hunter if not self.red: 17471c3ca1b3SAdrian Hunter palette = QPalette() 17481c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 17491c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17501c3ca1b3SAdrian Hunter self.red = True 17511c3ca1b3SAdrian Hunter 17521c3ca1b3SAdrian Hunter def TurnTextNormal(self): 17531c3ca1b3SAdrian Hunter if self.red: 17541c3ca1b3SAdrian Hunter palette = QPalette() 17551c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17561c3ca1b3SAdrian Hunter self.red = False 17571c3ca1b3SAdrian Hunter 17581c3ca1b3SAdrian Hunter def InvalidValue(self, value): 17591c3ca1b3SAdrian Hunter self.value = "" 17601c3ca1b3SAdrian Hunter self.TurnTextRed() 17611c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 17621c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 17631c3ca1b3SAdrian Hunter 17641c3ca1b3SAdrian Hunter def Invalidate(self): 17651c3ca1b3SAdrian Hunter self.validated = False 17661c3ca1b3SAdrian Hunter 17671c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 17681c3ca1b3SAdrian Hunter self.value = input_string.strip() 17691c3ca1b3SAdrian Hunter 17701c3ca1b3SAdrian Hunter def Validate(self): 17711c3ca1b3SAdrian Hunter self.validated = True 17721c3ca1b3SAdrian Hunter self.error = "" 17731c3ca1b3SAdrian Hunter self.TurnTextNormal() 17741c3ca1b3SAdrian Hunter self.parent.ClearMessage() 17751c3ca1b3SAdrian Hunter input_string = self.widget.text() 17761c3ca1b3SAdrian Hunter if not len(input_string.strip()): 17771c3ca1b3SAdrian Hunter self.value = "" 17781c3ca1b3SAdrian Hunter return 17791c3ca1b3SAdrian Hunter self.DoValidate(input_string) 17801c3ca1b3SAdrian Hunter 17811c3ca1b3SAdrian Hunter def IsValid(self): 17821c3ca1b3SAdrian Hunter if not self.validated: 17831c3ca1b3SAdrian Hunter self.Validate() 17841c3ca1b3SAdrian Hunter if len(self.error): 17851c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 17861c3ca1b3SAdrian Hunter return False 17871c3ca1b3SAdrian Hunter return True 17881c3ca1b3SAdrian Hunter 17891c3ca1b3SAdrian Hunter def IsNumber(self, value): 17901c3ca1b3SAdrian Hunter try: 17911c3ca1b3SAdrian Hunter x = int(value) 17921c3ca1b3SAdrian Hunter except: 17931c3ca1b3SAdrian Hunter x = 0 17941c3ca1b3SAdrian Hunter return str(x) == value 17951c3ca1b3SAdrian Hunter 17961c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 17971c3ca1b3SAdrian Hunter 17981c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 17991c3ca1b3SAdrian Hunter 18001c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18011c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 18021c3ca1b3SAdrian Hunter 18031c3ca1b3SAdrian Hunter self.column_name = column_name 18041c3ca1b3SAdrian Hunter 18051c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18061c3ca1b3SAdrian Hunter singles = [] 18071c3ca1b3SAdrian Hunter ranges = [] 18081c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18091c3ca1b3SAdrian Hunter if "-" in value: 18101c3ca1b3SAdrian Hunter vrange = value.split("-") 18111c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 18121c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18131c3ca1b3SAdrian Hunter ranges.append(vrange) 18141c3ca1b3SAdrian Hunter else: 18151c3ca1b3SAdrian Hunter if not self.IsNumber(value): 18161c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18171c3ca1b3SAdrian Hunter singles.append(value) 18181c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 18191c3ca1b3SAdrian Hunter if len(singles): 18201c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 18211c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 18221c3ca1b3SAdrian Hunter 1823cd358012SAdrian Hunter# Positive integer dialog data item 1824cd358012SAdrian Hunter 1825cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1826cd358012SAdrian Hunter 1827cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1828cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1829cd358012SAdrian Hunter 1830cd358012SAdrian Hunter def DoValidate(self, input_string): 1831cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1832cd358012SAdrian Hunter return self.InvalidValue(input_string) 1833cd358012SAdrian Hunter value = int(input_string.strip()) 1834cd358012SAdrian Hunter if value <= 0: 1835cd358012SAdrian Hunter return self.InvalidValue(input_string) 1836cd358012SAdrian Hunter self.value = str(value) 1837cd358012SAdrian Hunter 18381c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 18391c3ca1b3SAdrian Hunter 18401c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 18411c3ca1b3SAdrian Hunter 18421c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 18431c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 18441c3ca1b3SAdrian Hunter 18451c3ca1b3SAdrian Hunter self.table_name = table_name 18461c3ca1b3SAdrian Hunter self.match_column = match_column 18471c3ca1b3SAdrian Hunter self.column_name1 = column_name1 18481c3ca1b3SAdrian Hunter self.column_name2 = column_name2 18491c3ca1b3SAdrian Hunter 18501c3ca1b3SAdrian Hunter def ValueToIds(self, value): 18511c3ca1b3SAdrian Hunter ids = [] 18521c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 18531c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 18541c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 18551c3ca1b3SAdrian Hunter if ret: 18561c3ca1b3SAdrian Hunter while query.next(): 18571c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 18581c3ca1b3SAdrian Hunter return ids 18591c3ca1b3SAdrian Hunter 18601c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18611c3ca1b3SAdrian Hunter all_ids = [] 18621c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18631c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 18641c3ca1b3SAdrian Hunter if len(ids): 18651c3ca1b3SAdrian Hunter all_ids.extend(ids) 18661c3ca1b3SAdrian Hunter else: 18671c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18681c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 18691c3ca1b3SAdrian Hunter if self.column_name2: 18701c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 18711c3ca1b3SAdrian Hunter 18721c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 18731c3ca1b3SAdrian Hunter 18741c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 18751c3ca1b3SAdrian Hunter 18761c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18771c3ca1b3SAdrian Hunter self.column_name = column_name 18781c3ca1b3SAdrian Hunter 18791c3ca1b3SAdrian Hunter self.last_id = 0 18801c3ca1b3SAdrian Hunter self.first_time = 0 18811c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 18821c3ca1b3SAdrian Hunter 18831c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 18841c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 18851c3ca1b3SAdrian Hunter if query.next(): 18861c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 18871c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 18881c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 18891c3ca1b3SAdrian Hunter if query.next(): 18901c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 18911c3ca1b3SAdrian Hunter if placeholder_text: 18921c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 18931c3ca1b3SAdrian Hunter 18941c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 18951c3ca1b3SAdrian Hunter 18961c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 18971c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 18981c3ca1b3SAdrian Hunter if query.next(): 18991c3ca1b3SAdrian Hunter return True, int(query.value(0)) 19001c3ca1b3SAdrian Hunter else: 19011c3ca1b3SAdrian Hunter return False, 0 19021c3ca1b3SAdrian Hunter 19031c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 19041c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 19051c3ca1b3SAdrian Hunter while True: 19061c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 19071c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19081c3ca1b3SAdrian Hunter if not query.next(): 19091c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 19101c3ca1b3SAdrian Hunter if not ok: 19111c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 19121c3ca1b3SAdrian Hunter if not ok: 19131c3ca1b3SAdrian Hunter return str(higher_id) 19141c3ca1b3SAdrian Hunter next_id = dbid 19151c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19161c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 19171c3ca1b3SAdrian Hunter if get_floor: 19181c3ca1b3SAdrian Hunter if target_time > next_time: 19191c3ca1b3SAdrian Hunter lower_id = next_id 19201c3ca1b3SAdrian Hunter else: 19211c3ca1b3SAdrian Hunter higher_id = next_id 19221c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19231c3ca1b3SAdrian Hunter return str(higher_id) 19241c3ca1b3SAdrian Hunter else: 19251c3ca1b3SAdrian Hunter if target_time >= next_time: 19261c3ca1b3SAdrian Hunter lower_id = next_id 19271c3ca1b3SAdrian Hunter else: 19281c3ca1b3SAdrian Hunter higher_id = next_id 19291c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19301c3ca1b3SAdrian Hunter return str(lower_id) 19311c3ca1b3SAdrian Hunter 19321c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 19331c3ca1b3SAdrian Hunter mult = 1 19341c3ca1b3SAdrian Hunter suffix = val[-2:] 19351c3ca1b3SAdrian Hunter if suffix == "ms": 19361c3ca1b3SAdrian Hunter mult = 1000000 19371c3ca1b3SAdrian Hunter elif suffix == "us": 19381c3ca1b3SAdrian Hunter mult = 1000 19391c3ca1b3SAdrian Hunter elif suffix == "ns": 19401c3ca1b3SAdrian Hunter mult = 1 19411c3ca1b3SAdrian Hunter else: 19421c3ca1b3SAdrian Hunter return val 19431c3ca1b3SAdrian Hunter val = val[:-2].strip() 19441c3ca1b3SAdrian Hunter if not self.IsNumber(val): 19451c3ca1b3SAdrian Hunter return val 19461c3ca1b3SAdrian Hunter val = int(val) * mult 19471c3ca1b3SAdrian Hunter if val >= 0: 19481c3ca1b3SAdrian Hunter val += self.first_time 19491c3ca1b3SAdrian Hunter else: 19501c3ca1b3SAdrian Hunter val += self.last_time 19511c3ca1b3SAdrian Hunter return str(val) 19521c3ca1b3SAdrian Hunter 19531c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 19541c3ca1b3SAdrian Hunter if vrange[0] == "": 19551c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 19561c3ca1b3SAdrian Hunter if vrange[1] == "": 19571c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 19581c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 19591c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 19601c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 19611c3ca1b3SAdrian Hunter return False 19621c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 19631c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 19641c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 19651c3ca1b3SAdrian Hunter return False 19661c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 19671c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 19681c3ca1b3SAdrian Hunter return True 19691c3ca1b3SAdrian Hunter 19701c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 19711c3ca1b3SAdrian Hunter n = value.count("-") 19721c3ca1b3SAdrian Hunter if n == 1: 19731c3ca1b3SAdrian Hunter pass 19741c3ca1b3SAdrian Hunter elif n == 2: 19751c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 19761c3ca1b3SAdrian Hunter n = 1 19771c3ca1b3SAdrian Hunter elif n == 3: 19781c3ca1b3SAdrian Hunter n = 2 19791c3ca1b3SAdrian Hunter else: 19801c3ca1b3SAdrian Hunter return False 19811c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 19821c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 19831c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 19841c3ca1b3SAdrian Hunter ranges.append(vrange) 19851c3ca1b3SAdrian Hunter return True 19861c3ca1b3SAdrian Hunter return False 19871c3ca1b3SAdrian Hunter 19881c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19891c3ca1b3SAdrian Hunter ranges = [] 19901c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19911c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 19921c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19931c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 19941c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 19951c3ca1b3SAdrian Hunter 19960924cd68SAdrian Hunter# Report Dialog Base 1997210cf1f9SAdrian Hunter 19980924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 1999210cf1f9SAdrian Hunter 20000924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 20010924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 2002210cf1f9SAdrian Hunter 2003210cf1f9SAdrian Hunter self.glb = glb 2004210cf1f9SAdrian Hunter 20050bf0947aSAdrian Hunter self.report_vars = ReportVars() 2006210cf1f9SAdrian Hunter 20070924cd68SAdrian Hunter self.setWindowTitle(title) 2008210cf1f9SAdrian Hunter self.setMinimumWidth(600) 2009210cf1f9SAdrian Hunter 20101c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 2011210cf1f9SAdrian Hunter 20120924cd68SAdrian Hunter self.partial = partial 20130924cd68SAdrian Hunter 2014210cf1f9SAdrian Hunter self.grid = QGridLayout() 2015210cf1f9SAdrian Hunter 2016210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2017210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2018210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2019210cf1f9SAdrian Hunter 2020210cf1f9SAdrian Hunter self.status = QLabel() 2021210cf1f9SAdrian Hunter 2022210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2023210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2024210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2025210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2026210cf1f9SAdrian Hunter 2027210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2028210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2029210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2030210cf1f9SAdrian Hunter 2031210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2032210cf1f9SAdrian Hunter #self.hbox.addStretch() 2033210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2034210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2035210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2036210cf1f9SAdrian Hunter 2037210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2038210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2039210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2040210cf1f9SAdrian Hunter 2041210cf1f9SAdrian Hunter self.setLayout(self.vbox); 2042210cf1f9SAdrian Hunter 2043210cf1f9SAdrian Hunter def Ok(self): 20440bf0947aSAdrian Hunter vars = self.report_vars 20451c3ca1b3SAdrian Hunter for d in self.data_items: 20461c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 20471c3ca1b3SAdrian Hunter vars.name = d.value 2048947cc38dSAdrian Hunter if not vars.name: 2049210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2050210cf1f9SAdrian Hunter return 2051210cf1f9SAdrian Hunter for d in self.data_items: 2052210cf1f9SAdrian Hunter if not d.IsValid(): 2053210cf1f9SAdrian Hunter return 2054210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2055cd358012SAdrian Hunter if d.id == "LIMIT": 2056cd358012SAdrian Hunter vars.limit = d.value 2057cd358012SAdrian Hunter elif len(d.value): 20580bf0947aSAdrian Hunter if len(vars.where_clause): 20590bf0947aSAdrian Hunter vars.where_clause += " AND " 20600bf0947aSAdrian Hunter vars.where_clause += d.value 20610bf0947aSAdrian Hunter if len(vars.where_clause): 20620924cd68SAdrian Hunter if self.partial: 20630bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2064210cf1f9SAdrian Hunter else: 20650bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2066210cf1f9SAdrian Hunter self.accept() 2067210cf1f9SAdrian Hunter 2068210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2069210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2070210cf1f9SAdrian Hunter 2071210cf1f9SAdrian Hunter def ClearMessage(self): 2072210cf1f9SAdrian Hunter self.status.setText("") 2073210cf1f9SAdrian Hunter 20740924cd68SAdrian Hunter# Selected branch report creation dialog 20750924cd68SAdrian Hunter 20760924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 20770924cd68SAdrian Hunter 20780924cd68SAdrian Hunter def __init__(self, glb, parent=None): 20790924cd68SAdrian Hunter title = "Selected Branches" 20801c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 20811c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 20821c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 20831c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 20841c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 20851c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 20861c3ca1b3SAdrian 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), 20871c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 20881c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 20890924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 20900924cd68SAdrian Hunter 209176099f98SAdrian Hunter# Event list 209276099f98SAdrian Hunter 209376099f98SAdrian Hunterdef GetEventList(db): 209476099f98SAdrian Hunter events = [] 209576099f98SAdrian Hunter query = QSqlQuery(db) 209676099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 209776099f98SAdrian Hunter while query.next(): 209876099f98SAdrian Hunter events.append(query.value(0)) 209976099f98SAdrian Hunter return events 210076099f98SAdrian Hunter 2101655cb952SAdrian Hunter# Is a table selectable 2102655cb952SAdrian Hunter 2103ae8b887cSAdrian Hunterdef IsSelectable(db, table, sql = ""): 2104655cb952SAdrian Hunter query = QSqlQuery(db) 2105655cb952SAdrian Hunter try: 2106ae8b887cSAdrian Hunter QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") 2107655cb952SAdrian Hunter except: 2108655cb952SAdrian Hunter return False 2109655cb952SAdrian Hunter return True 2110655cb952SAdrian Hunter 21118392b74bSAdrian Hunter# SQL table data model item 21128392b74bSAdrian Hunter 21138392b74bSAdrian Hunterclass SQLTableItem(): 21148392b74bSAdrian Hunter 21158392b74bSAdrian Hunter def __init__(self, row, data): 21168392b74bSAdrian Hunter self.row = row 21178392b74bSAdrian Hunter self.data = data 21188392b74bSAdrian Hunter 21198392b74bSAdrian Hunter def getData(self, column): 21208392b74bSAdrian Hunter return self.data[column] 21218392b74bSAdrian Hunter 21228392b74bSAdrian Hunter# SQL table data model 21238392b74bSAdrian Hunter 21248392b74bSAdrian Hunterclass SQLTableModel(TableModel): 21258392b74bSAdrian Hunter 21268392b74bSAdrian Hunter progress = Signal(object) 21278392b74bSAdrian Hunter 21288c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 21298392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 21308392b74bSAdrian Hunter self.glb = glb 21318392b74bSAdrian Hunter self.more = True 21328392b74bSAdrian Hunter self.populated = 0 21338c90fef9SAdrian Hunter self.column_headers = column_headers 21348453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 21358392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 21368392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 21378392b74bSAdrian Hunter 21388392b74bSAdrian Hunter def DisplayData(self, item, index): 21398392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 21408392b74bSAdrian Hunter return item.getData(index.column()) 21418392b74bSAdrian Hunter 21428392b74bSAdrian Hunter def AddSample(self, data): 21438392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 21448392b74bSAdrian Hunter self.child_items.append(child) 21458392b74bSAdrian Hunter self.populated += 1 21468392b74bSAdrian Hunter 21478392b74bSAdrian Hunter def Update(self, fetched): 21488392b74bSAdrian Hunter if not fetched: 21498392b74bSAdrian Hunter self.more = False 21508392b74bSAdrian Hunter self.progress.emit(0) 21518392b74bSAdrian Hunter child_count = self.child_count 21528392b74bSAdrian Hunter count = self.populated - child_count 21538392b74bSAdrian Hunter if count > 0: 21548392b74bSAdrian Hunter parent = QModelIndex() 21558392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 21568392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 21578392b74bSAdrian Hunter self.child_count += count 21588392b74bSAdrian Hunter self.endInsertRows() 21598392b74bSAdrian Hunter self.progress.emit(self.child_count) 21608392b74bSAdrian Hunter 21618392b74bSAdrian Hunter def FetchMoreRecords(self, count): 21628392b74bSAdrian Hunter current = self.child_count 21638392b74bSAdrian Hunter if self.more: 21648392b74bSAdrian Hunter self.fetcher.Fetch(count) 21658392b74bSAdrian Hunter else: 21668392b74bSAdrian Hunter self.progress.emit(0) 21678392b74bSAdrian Hunter return current 21688392b74bSAdrian Hunter 21698392b74bSAdrian Hunter def HasMoreRecords(self): 21708392b74bSAdrian Hunter return self.more 21718392b74bSAdrian Hunter 21728c90fef9SAdrian Hunter def columnCount(self, parent=None): 21738c90fef9SAdrian Hunter return len(self.column_headers) 21748c90fef9SAdrian Hunter 21758c90fef9SAdrian Hunter def columnHeader(self, column): 21768c90fef9SAdrian Hunter return self.column_headers[column] 21778c90fef9SAdrian Hunter 21788453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 21798453c936SAdrian Hunter data = [] 21808453c936SAdrian Hunter for i in xrange(count): 21818453c936SAdrian Hunter data.append(query.value(i)) 21828453c936SAdrian Hunter return data 21838453c936SAdrian Hunter 21848392b74bSAdrian Hunter# SQL automatic table data model 21858392b74bSAdrian Hunter 21868392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 21878392b74bSAdrian Hunter 21888392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 21898392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 21908392b74bSAdrian Hunter if table_name == "comm_threads_view": 21918392b74bSAdrian Hunter # For now, comm_threads_view has no id column 21928392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 21938c90fef9SAdrian Hunter column_headers = [] 21948392b74bSAdrian Hunter query = QSqlQuery(glb.db) 21958392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 21968392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 21978392b74bSAdrian Hunter while query.next(): 21988c90fef9SAdrian Hunter column_headers.append(query.value(1)) 21998392b74bSAdrian Hunter if table_name == "sqlite_master": 22008392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22018392b74bSAdrian Hunter else: 22028392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 22038392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 22048392b74bSAdrian Hunter select_table_name = table_name[19:] 22058392b74bSAdrian Hunter schema = "information_schema" 22068392b74bSAdrian Hunter else: 22078392b74bSAdrian Hunter select_table_name = table_name 22088392b74bSAdrian Hunter schema = "public" 22098392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 22108392b74bSAdrian Hunter while query.next(): 22118c90fef9SAdrian Hunter column_headers.append(query.value(0)) 22128453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 22138453c936SAdrian Hunter if table_name == "samples_view": 22148453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 22158453c936SAdrian Hunter if table_name == "samples": 22168453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 22178c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 22188392b74bSAdrian Hunter 22198453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 22208453c936SAdrian Hunter data = [] 22218453c936SAdrian Hunter data.append(query.value(0)) 22228453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22238453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 22248453c936SAdrian Hunter for i in xrange(2, count): 22258453c936SAdrian Hunter data.append(query.value(i)) 22268453c936SAdrian Hunter return data 22278453c936SAdrian Hunter 22288453c936SAdrian Hunter def samples_DataPrep(self, query, count): 22298453c936SAdrian Hunter data = [] 22308453c936SAdrian Hunter for i in xrange(9): 22318453c936SAdrian Hunter data.append(query.value(i)) 22328453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22338453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 22348453c936SAdrian Hunter for i in xrange(10, count): 22358453c936SAdrian Hunter data.append(query.value(i)) 22368453c936SAdrian Hunter return data 22378453c936SAdrian Hunter 22388392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 22398392b74bSAdrian Hunter 22408392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 22418392b74bSAdrian Hunter 22428392b74bSAdrian Hunter def __init__(self, parent=None): 22438392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 22448392b74bSAdrian Hunter 22458392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 22468392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 22478392b74bSAdrian Hunter # so implement a crude alternative 22488392b74bSAdrian Hunter font = self.view.font() 22498392b74bSAdrian Hunter metrics = QFontMetrics(font) 22508392b74bSAdrian Hunter max = 0 22518392b74bSAdrian Hunter for row in xrange(n): 22528392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 22538392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22548392b74bSAdrian Hunter max = len if len > max else max 22558392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 22568392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22578392b74bSAdrian Hunter max = len if len > max else max 22588392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 22598392b74bSAdrian Hunter 22608392b74bSAdrian Hunter def ResizeColumnsToContents(self): 22618392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 22628392b74bSAdrian Hunter if n < 1: 22638392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 22648392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 22658392b74bSAdrian Hunter return 22668392b74bSAdrian Hunter columns = self.data_model.columnCount() 22678392b74bSAdrian Hunter for i in xrange(columns): 22688392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 22698392b74bSAdrian Hunter 22708392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 22718392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 22728392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 22738392b74bSAdrian Hunter self.ResizeColumnsToContents() 22748392b74bSAdrian Hunter 227596c43b9aSAdrian Hunter# Convert value to CSV 227696c43b9aSAdrian Hunter 227796c43b9aSAdrian Hunterdef ToCSValue(val): 227896c43b9aSAdrian Hunter if '"' in val: 227996c43b9aSAdrian Hunter val = val.replace('"', '""') 228096c43b9aSAdrian Hunter if "," in val or '"' in val: 228196c43b9aSAdrian Hunter val = '"' + val + '"' 228296c43b9aSAdrian Hunter return val 228396c43b9aSAdrian Hunter 228496c43b9aSAdrian Hunter# Key to sort table model indexes by row / column, assuming fewer than 1000 columns 228596c43b9aSAdrian Hunter 228696c43b9aSAdrian Hunterglb_max_cols = 1000 228796c43b9aSAdrian Hunter 228896c43b9aSAdrian Hunterdef RowColumnKey(a): 228996c43b9aSAdrian Hunter return a.row() * glb_max_cols + a.column() 229096c43b9aSAdrian Hunter 229196c43b9aSAdrian Hunter# Copy selected table cells to clipboard 229296c43b9aSAdrian Hunter 229396c43b9aSAdrian Hunterdef CopyTableCellsToClipboard(view, as_csv=False, with_hdr=False): 229496c43b9aSAdrian Hunter indexes = sorted(view.selectedIndexes(), key=RowColumnKey) 229596c43b9aSAdrian Hunter idx_cnt = len(indexes) 229696c43b9aSAdrian Hunter if not idx_cnt: 229796c43b9aSAdrian Hunter return 229896c43b9aSAdrian Hunter if idx_cnt == 1: 229996c43b9aSAdrian Hunter with_hdr=False 230096c43b9aSAdrian Hunter min_row = indexes[0].row() 230196c43b9aSAdrian Hunter max_row = indexes[0].row() 230296c43b9aSAdrian Hunter min_col = indexes[0].column() 230396c43b9aSAdrian Hunter max_col = indexes[0].column() 230496c43b9aSAdrian Hunter for i in indexes: 230596c43b9aSAdrian Hunter min_row = min(min_row, i.row()) 230696c43b9aSAdrian Hunter max_row = max(max_row, i.row()) 230796c43b9aSAdrian Hunter min_col = min(min_col, i.column()) 230896c43b9aSAdrian Hunter max_col = max(max_col, i.column()) 230996c43b9aSAdrian Hunter if max_col > glb_max_cols: 231096c43b9aSAdrian Hunter raise RuntimeError("glb_max_cols is too low") 231196c43b9aSAdrian Hunter max_width = [0] * (1 + max_col - min_col) 231296c43b9aSAdrian Hunter for i in indexes: 231396c43b9aSAdrian Hunter c = i.column() - min_col 231496c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(str(i.data()))) 231596c43b9aSAdrian Hunter text = "" 231696c43b9aSAdrian Hunter pad = "" 231796c43b9aSAdrian Hunter sep = "" 231896c43b9aSAdrian Hunter if with_hdr: 231996c43b9aSAdrian Hunter model = indexes[0].model() 232096c43b9aSAdrian Hunter for col in range(min_col, max_col + 1): 232196c43b9aSAdrian Hunter val = model.headerData(col, Qt.Horizontal) 232296c43b9aSAdrian Hunter if as_csv: 232396c43b9aSAdrian Hunter text += sep + ToCSValue(val) 232496c43b9aSAdrian Hunter sep = "," 232596c43b9aSAdrian Hunter else: 232696c43b9aSAdrian Hunter c = col - min_col 232796c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 232896c43b9aSAdrian Hunter width = max_width[c] 232996c43b9aSAdrian Hunter align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole) 233096c43b9aSAdrian Hunter if align & Qt.AlignRight: 233196c43b9aSAdrian Hunter val = val.rjust(width) 233296c43b9aSAdrian Hunter text += pad + sep + val 233396c43b9aSAdrian Hunter pad = " " * (width - len(val)) 233496c43b9aSAdrian Hunter sep = " " 233596c43b9aSAdrian Hunter text += "\n" 233696c43b9aSAdrian Hunter pad = "" 233796c43b9aSAdrian Hunter sep = "" 233896c43b9aSAdrian Hunter last_row = min_row 233996c43b9aSAdrian Hunter for i in indexes: 234096c43b9aSAdrian Hunter if i.row() > last_row: 234196c43b9aSAdrian Hunter last_row = i.row() 234296c43b9aSAdrian Hunter text += "\n" 234396c43b9aSAdrian Hunter pad = "" 234496c43b9aSAdrian Hunter sep = "" 234596c43b9aSAdrian Hunter if as_csv: 234696c43b9aSAdrian Hunter text += sep + ToCSValue(str(i.data())) 234796c43b9aSAdrian Hunter sep = "," 234896c43b9aSAdrian Hunter else: 234996c43b9aSAdrian Hunter width = max_width[i.column() - min_col] 235096c43b9aSAdrian Hunter if i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 235196c43b9aSAdrian Hunter val = str(i.data()).rjust(width) 235296c43b9aSAdrian Hunter else: 235396c43b9aSAdrian Hunter val = str(i.data()) 235496c43b9aSAdrian Hunter text += pad + sep + val 235596c43b9aSAdrian Hunter pad = " " * (width - len(val)) 235696c43b9aSAdrian Hunter sep = " " 235796c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 235896c43b9aSAdrian Hunter 235996c43b9aSAdrian Hunterdef CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False): 236096c43b9aSAdrian Hunter indexes = view.selectedIndexes() 236196c43b9aSAdrian Hunter if not len(indexes): 236296c43b9aSAdrian Hunter return 236396c43b9aSAdrian Hunter 236496c43b9aSAdrian Hunter selection = view.selectionModel() 236596c43b9aSAdrian Hunter 236696c43b9aSAdrian Hunter first = None 236796c43b9aSAdrian Hunter for i in indexes: 236896c43b9aSAdrian Hunter above = view.indexAbove(i) 236996c43b9aSAdrian Hunter if not selection.isSelected(above): 237096c43b9aSAdrian Hunter first = i 237196c43b9aSAdrian Hunter break 237296c43b9aSAdrian Hunter 237396c43b9aSAdrian Hunter if first is None: 237496c43b9aSAdrian Hunter raise RuntimeError("CopyTreeCellsToClipboard internal error") 237596c43b9aSAdrian Hunter 237696c43b9aSAdrian Hunter model = first.model() 237796c43b9aSAdrian Hunter row_cnt = 0 237896c43b9aSAdrian Hunter col_cnt = model.columnCount(first) 237996c43b9aSAdrian Hunter max_width = [0] * col_cnt 238096c43b9aSAdrian Hunter 238196c43b9aSAdrian Hunter indent_sz = 2 238296c43b9aSAdrian Hunter indent_str = " " * indent_sz 238396c43b9aSAdrian Hunter 238496c43b9aSAdrian Hunter expanded_mark_sz = 2 238596c43b9aSAdrian Hunter if sys.version_info[0] == 3: 238696c43b9aSAdrian Hunter expanded_mark = "\u25BC " 238796c43b9aSAdrian Hunter not_expanded_mark = "\u25B6 " 238896c43b9aSAdrian Hunter else: 238996c43b9aSAdrian Hunter expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8") 239096c43b9aSAdrian Hunter not_expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8") 239196c43b9aSAdrian Hunter leaf_mark = " " 239296c43b9aSAdrian Hunter 239396c43b9aSAdrian Hunter if not as_csv: 239496c43b9aSAdrian Hunter pos = first 239596c43b9aSAdrian Hunter while True: 239696c43b9aSAdrian Hunter row_cnt += 1 239796c43b9aSAdrian Hunter row = pos.row() 239896c43b9aSAdrian Hunter for c in range(col_cnt): 239996c43b9aSAdrian Hunter i = pos.sibling(row, c) 240096c43b9aSAdrian Hunter if c: 240196c43b9aSAdrian Hunter n = len(str(i.data())) 240296c43b9aSAdrian Hunter else: 240396c43b9aSAdrian Hunter n = len(str(i.data()).strip()) 240496c43b9aSAdrian Hunter n += (i.internalPointer().level - 1) * indent_sz 240596c43b9aSAdrian Hunter n += expanded_mark_sz 240696c43b9aSAdrian Hunter max_width[c] = max(max_width[c], n) 240796c43b9aSAdrian Hunter pos = view.indexBelow(pos) 240896c43b9aSAdrian Hunter if not selection.isSelected(pos): 240996c43b9aSAdrian Hunter break 241096c43b9aSAdrian Hunter 241196c43b9aSAdrian Hunter text = "" 241296c43b9aSAdrian Hunter pad = "" 241396c43b9aSAdrian Hunter sep = "" 241496c43b9aSAdrian Hunter if with_hdr: 241596c43b9aSAdrian Hunter for c in range(col_cnt): 241696c43b9aSAdrian Hunter val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip() 241796c43b9aSAdrian Hunter if as_csv: 241896c43b9aSAdrian Hunter text += sep + ToCSValue(val) 241996c43b9aSAdrian Hunter sep = "," 242096c43b9aSAdrian Hunter else: 242196c43b9aSAdrian Hunter max_width[c] = max(max_width[c], len(val)) 242296c43b9aSAdrian Hunter width = max_width[c] 242396c43b9aSAdrian Hunter align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole) 242496c43b9aSAdrian Hunter if align & Qt.AlignRight: 242596c43b9aSAdrian Hunter val = val.rjust(width) 242696c43b9aSAdrian Hunter text += pad + sep + val 242796c43b9aSAdrian Hunter pad = " " * (width - len(val)) 242896c43b9aSAdrian Hunter sep = " " 242996c43b9aSAdrian Hunter text += "\n" 243096c43b9aSAdrian Hunter pad = "" 243196c43b9aSAdrian Hunter sep = "" 243296c43b9aSAdrian Hunter 243396c43b9aSAdrian Hunter pos = first 243496c43b9aSAdrian Hunter while True: 243596c43b9aSAdrian Hunter row = pos.row() 243696c43b9aSAdrian Hunter for c in range(col_cnt): 243796c43b9aSAdrian Hunter i = pos.sibling(row, c) 243896c43b9aSAdrian Hunter val = str(i.data()) 243996c43b9aSAdrian Hunter if not c: 244096c43b9aSAdrian Hunter if model.hasChildren(i): 244196c43b9aSAdrian Hunter if view.isExpanded(i): 244296c43b9aSAdrian Hunter mark = expanded_mark 244396c43b9aSAdrian Hunter else: 244496c43b9aSAdrian Hunter mark = not_expanded_mark 244596c43b9aSAdrian Hunter else: 244696c43b9aSAdrian Hunter mark = leaf_mark 244796c43b9aSAdrian Hunter val = indent_str * (i.internalPointer().level - 1) + mark + val.strip() 244896c43b9aSAdrian Hunter if as_csv: 244996c43b9aSAdrian Hunter text += sep + ToCSValue(val) 245096c43b9aSAdrian Hunter sep = "," 245196c43b9aSAdrian Hunter else: 245296c43b9aSAdrian Hunter width = max_width[c] 245396c43b9aSAdrian Hunter if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight: 245496c43b9aSAdrian Hunter val = val.rjust(width) 245596c43b9aSAdrian Hunter text += pad + sep + val 245696c43b9aSAdrian Hunter pad = " " * (width - len(val)) 245796c43b9aSAdrian Hunter sep = " " 245896c43b9aSAdrian Hunter pos = view.indexBelow(pos) 245996c43b9aSAdrian Hunter if not selection.isSelected(pos): 246096c43b9aSAdrian Hunter break 246196c43b9aSAdrian Hunter text = text.rstrip() + "\n" 246296c43b9aSAdrian Hunter pad = "" 246396c43b9aSAdrian Hunter sep = "" 246496c43b9aSAdrian Hunter 246596c43b9aSAdrian Hunter QApplication.clipboard().setText(text) 246696c43b9aSAdrian Hunter 246796c43b9aSAdrian Hunterdef CopyCellsToClipboard(view, as_csv=False, with_hdr=False): 246896c43b9aSAdrian Hunter view.CopyCellsToClipboard(view, as_csv, with_hdr) 246996c43b9aSAdrian Hunter 247096c43b9aSAdrian Hunterdef CopyCellsToClipboardHdr(view): 247196c43b9aSAdrian Hunter CopyCellsToClipboard(view, False, True) 247296c43b9aSAdrian Hunter 247396c43b9aSAdrian Hunterdef CopyCellsToClipboardCSV(view): 247496c43b9aSAdrian Hunter CopyCellsToClipboard(view, True, True) 247596c43b9aSAdrian Hunter 2476*9bc4e4bfSAdrian Hunter# Context menu 2477*9bc4e4bfSAdrian Hunter 2478*9bc4e4bfSAdrian Hunterclass ContextMenu(object): 2479*9bc4e4bfSAdrian Hunter 2480*9bc4e4bfSAdrian Hunter def __init__(self, view): 2481*9bc4e4bfSAdrian Hunter self.view = view 2482*9bc4e4bfSAdrian Hunter self.view.setContextMenuPolicy(Qt.CustomContextMenu) 2483*9bc4e4bfSAdrian Hunter self.view.customContextMenuRequested.connect(self.ShowContextMenu) 2484*9bc4e4bfSAdrian Hunter 2485*9bc4e4bfSAdrian Hunter def ShowContextMenu(self, pos): 2486*9bc4e4bfSAdrian Hunter menu = QMenu(self.view) 2487*9bc4e4bfSAdrian Hunter self.AddActions(menu) 2488*9bc4e4bfSAdrian Hunter menu.exec_(self.view.mapToGlobal(pos)) 2489*9bc4e4bfSAdrian Hunter 2490*9bc4e4bfSAdrian Hunter def AddCopy(self, menu): 2491*9bc4e4bfSAdrian Hunter menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view)) 2492*9bc4e4bfSAdrian Hunter menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 2493*9bc4e4bfSAdrian Hunter 2494*9bc4e4bfSAdrian Hunter def AddActions(self, menu): 2495*9bc4e4bfSAdrian Hunter self.AddCopy(menu) 2496*9bc4e4bfSAdrian Hunter 2497*9bc4e4bfSAdrian Hunterclass TreeContextMenu(ContextMenu): 2498*9bc4e4bfSAdrian Hunter 2499*9bc4e4bfSAdrian Hunter def __init__(self, view): 2500*9bc4e4bfSAdrian Hunter super(TreeContextMenu, self).__init__(view) 2501*9bc4e4bfSAdrian Hunter 2502*9bc4e4bfSAdrian Hunter def AddActions(self, menu): 2503*9bc4e4bfSAdrian Hunter i = self.view.currentIndex() 2504*9bc4e4bfSAdrian Hunter text = str(i.data()).strip() 2505*9bc4e4bfSAdrian Hunter if len(text): 2506*9bc4e4bfSAdrian Hunter menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view)) 2507*9bc4e4bfSAdrian Hunter self.AddCopy(menu) 2508*9bc4e4bfSAdrian Hunter 25098392b74bSAdrian Hunter# Table window 25108392b74bSAdrian Hunter 25118392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 25128392b74bSAdrian Hunter 25138392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 25148392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 25158392b74bSAdrian Hunter 25168392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 25178392b74bSAdrian Hunter 25188392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 25198392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 25208392b74bSAdrian Hunter 25218392b74bSAdrian Hunter self.view = QTableView() 25228392b74bSAdrian Hunter self.view.setModel(self.model) 25238392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 25248392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 25258392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 25268392b74bSAdrian Hunter self.view.setSortingEnabled(True) 252796c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 252896c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 25298392b74bSAdrian Hunter 25308392b74bSAdrian Hunter self.ResizeColumnsToContents() 25318392b74bSAdrian Hunter 2532*9bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 2533*9bc4e4bfSAdrian Hunter 25348392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 25358392b74bSAdrian Hunter 25368392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 25378392b74bSAdrian Hunter 25388392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 25398392b74bSAdrian Hunter 25408392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 25418392b74bSAdrian Hunter 25428392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 25438392b74bSAdrian Hunter 25448392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 25458392b74bSAdrian Hunter 25468392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 25478392b74bSAdrian Hunter self.view.setFocus() 25488392b74bSAdrian Hunter self.find_bar.Busy() 25498392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 25508392b74bSAdrian Hunter 25518392b74bSAdrian Hunter def FindDone(self, row): 25528392b74bSAdrian Hunter self.find_bar.Idle() 25538392b74bSAdrian Hunter if row >= 0: 255435fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 25558392b74bSAdrian Hunter else: 25568392b74bSAdrian Hunter self.find_bar.NotFound() 25578392b74bSAdrian Hunter 25588392b74bSAdrian Hunter# Table list 25598392b74bSAdrian Hunter 25608392b74bSAdrian Hunterdef GetTableList(glb): 25618392b74bSAdrian Hunter tables = [] 25628392b74bSAdrian Hunter query = QSqlQuery(glb.db) 25638392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 25648392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 25658392b74bSAdrian Hunter else: 25668392b74bSAdrian 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") 25678392b74bSAdrian Hunter while query.next(): 25688392b74bSAdrian Hunter tables.append(query.value(0)) 25698392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 25708392b74bSAdrian Hunter tables.append("sqlite_master") 25718392b74bSAdrian Hunter else: 25728392b74bSAdrian Hunter tables.append("information_schema.tables") 25738392b74bSAdrian Hunter tables.append("information_schema.views") 25748392b74bSAdrian Hunter tables.append("information_schema.columns") 25758392b74bSAdrian Hunter return tables 25768392b74bSAdrian Hunter 2577cd358012SAdrian Hunter# Top Calls data model 2578cd358012SAdrian Hunter 2579cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2580cd358012SAdrian Hunter 2581cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2582cd358012SAdrian Hunter text = "" 2583cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2584cd358012SAdrian Hunter text = "::text" 2585cd358012SAdrian Hunter limit = "" 2586cd358012SAdrian Hunter if len(report_vars.limit): 2587cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2588cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2589cd358012SAdrian Hunter " CASE" 2590cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2591cd358012SAdrian Hunter " ELSE short_name" 2592cd358012SAdrian Hunter " END AS dso," 2593cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2594cd358012SAdrian Hunter " CASE" 2595cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2596cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2597cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2598cd358012SAdrian Hunter " ELSE ''" + text + 2599cd358012SAdrian Hunter " END AS flags" 2600cd358012SAdrian Hunter " FROM calls" 2601cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2602cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2603cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2604cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2605cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2606cd358012SAdrian Hunter report_vars.where_clause + 2607cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2608cd358012SAdrian Hunter limit 2609cd358012SAdrian Hunter ) 2610cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2611cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2612cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2613cd358012SAdrian Hunter 2614cd358012SAdrian Hunter def columnAlignment(self, column): 2615cd358012SAdrian Hunter return self.alignment[column] 2616cd358012SAdrian Hunter 2617cd358012SAdrian Hunter# Top Calls report creation dialog 2618cd358012SAdrian Hunter 2619cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2620cd358012SAdrian Hunter 2621cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2622cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2623cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2624cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2625cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2626cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2627cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2628cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2629cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2630cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2631cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2632cd358012SAdrian Hunter 2633cd358012SAdrian Hunter# Top Calls window 2634cd358012SAdrian Hunter 2635cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2636cd358012SAdrian Hunter 2637cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2638cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2639cd358012SAdrian Hunter 2640cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2641cd358012SAdrian Hunter self.model = self.data_model 2642cd358012SAdrian Hunter 2643cd358012SAdrian Hunter self.view = QTableView() 2644cd358012SAdrian Hunter self.view.setModel(self.model) 2645cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2646cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 264796c43b9aSAdrian Hunter self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) 264896c43b9aSAdrian Hunter self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 2649cd358012SAdrian Hunter 2650*9bc4e4bfSAdrian Hunter self.context_menu = ContextMenu(self.view) 2651*9bc4e4bfSAdrian Hunter 2652cd358012SAdrian Hunter self.ResizeColumnsToContents() 2653cd358012SAdrian Hunter 2654cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2655cd358012SAdrian Hunter 2656cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2657cd358012SAdrian Hunter 2658cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2659cd358012SAdrian Hunter 2660cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2661cd358012SAdrian Hunter 2662cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2663cd358012SAdrian Hunter 2664cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2665cd358012SAdrian Hunter 2666cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2667cd358012SAdrian Hunter self.view.setFocus() 2668cd358012SAdrian Hunter self.find_bar.Busy() 2669cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2670cd358012SAdrian Hunter 2671cd358012SAdrian Hunter def FindDone(self, row): 2672cd358012SAdrian Hunter self.find_bar.Idle() 2673cd358012SAdrian Hunter if row >= 0: 2674cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2675cd358012SAdrian Hunter else: 2676cd358012SAdrian Hunter self.find_bar.NotFound() 2677cd358012SAdrian Hunter 26781beb5c7bSAdrian Hunter# Action Definition 26791beb5c7bSAdrian Hunter 26801beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 26811beb5c7bSAdrian Hunter action = QAction(label, parent) 26821beb5c7bSAdrian Hunter if shortcut != None: 26831beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 26841beb5c7bSAdrian Hunter action.setStatusTip(tip) 26851beb5c7bSAdrian Hunter action.triggered.connect(callback) 26861beb5c7bSAdrian Hunter return action 26871beb5c7bSAdrian Hunter 26881beb5c7bSAdrian Hunter# Typical application actions 26891beb5c7bSAdrian Hunter 26901beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 26911beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 26921beb5c7bSAdrian Hunter 26931beb5c7bSAdrian Hunter# Typical MDI actions 26941beb5c7bSAdrian Hunter 26951beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 26961beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 26971beb5c7bSAdrian Hunter 26981beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 26991beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 27001beb5c7bSAdrian Hunter 27011beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 27021beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 27031beb5c7bSAdrian Hunter 27041beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 27051beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 27061beb5c7bSAdrian Hunter 27071beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 27081beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 27091beb5c7bSAdrian Hunter 27101beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 27111beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 27121beb5c7bSAdrian Hunter 27131beb5c7bSAdrian Hunter# Typical MDI window menu 27141beb5c7bSAdrian Hunter 27151beb5c7bSAdrian Hunterclass WindowMenu(): 27161beb5c7bSAdrian Hunter 27171beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 27181beb5c7bSAdrian Hunter self.mdi_area = mdi_area 27191beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 27201beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 27211beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 27221beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 27231beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 27241beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 27251beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 27261beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 27271beb5c7bSAdrian Hunter 27281beb5c7bSAdrian Hunter def Update(self): 27291beb5c7bSAdrian Hunter self.window_menu.clear() 27301beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 27311beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 27321beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 27331beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 27341beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 27351beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 27361beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 27371beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 27381beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 27391beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 27401beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27411beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 27421beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 27431beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27441beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 27451beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 27461beb5c7bSAdrian Hunter if sub_window_count == 0: 27471beb5c7bSAdrian Hunter return 27481beb5c7bSAdrian Hunter self.window_menu.addSeparator() 27491beb5c7bSAdrian Hunter nr = 1 27501beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 27511beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 27521beb5c7bSAdrian Hunter if nr < 10: 27531beb5c7bSAdrian Hunter label = "&" + label 27541beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 27551beb5c7bSAdrian Hunter action.setCheckable(True) 27561beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 27571beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 27581beb5c7bSAdrian Hunter self.window_menu.addAction(action) 27591beb5c7bSAdrian Hunter nr += 1 27601beb5c7bSAdrian Hunter 27611beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 27621beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 27631beb5c7bSAdrian Hunter 276465b24292SAdrian Hunter# Help text 276565b24292SAdrian Hunter 276665b24292SAdrian Hunterglb_help_text = """ 276765b24292SAdrian Hunter<h1>Contents</h1> 276865b24292SAdrian Hunter<style> 276965b24292SAdrian Hunterp.c1 { 277065b24292SAdrian Hunter text-indent: 40px; 277165b24292SAdrian Hunter} 277265b24292SAdrian Hunterp.c2 { 277365b24292SAdrian Hunter text-indent: 80px; 277465b24292SAdrian Hunter} 277565b24292SAdrian Hunter} 277665b24292SAdrian Hunter</style> 277765b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 277865b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2779ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2780ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2781ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2782ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 278365b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 278465b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 278565b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 278665b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 278765b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 278865b24292SAdrian Hunterwidths to suit will display something like: 278965b24292SAdrian Hunter<pre> 279065b24292SAdrian Hunter Call Graph: pt_example 279165b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 279265b24292SAdrian Hunterv- ls 279365b24292SAdrian Hunter v- 2638:2638 279465b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 279565b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 279665b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 279765b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 279865b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 279965b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 280065b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 280165b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 280265b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 280365b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 280465b24292SAdrian Hunter</pre> 280565b24292SAdrian Hunter<h3>Points to note:</h3> 280665b24292SAdrian Hunter<ul> 280765b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 280865b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 280965b24292SAdrian Hunter<li>Subsequent levels are functions</li> 281065b24292SAdrian Hunter<li>'Count' is the number of calls</li> 281165b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 281265b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 281365b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 281465b24292SAdrian Hunter</ul> 281565b24292SAdrian Hunter<h3>Find</h3> 281665b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 281765b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2818ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2819ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2820ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2821ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 282265b24292SAdrian HunterThe All branches report displays all branches in chronological order. 282365b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 282465b24292SAdrian Hunter<h3>Disassembly</h3> 282565b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 282665b24292SAdrian Hunter<ol> 282765b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 282865b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 282965b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 283065b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 283165b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 283265b24292SAdrian Hunter</ol> 283365b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 283465b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 283565b24292SAdrian Hunter<pre> 283665b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 283765b24292SAdrian Huntergit clone https://github.com/intelxed/xed 283865b24292SAdrian Huntercd xed 283965b24292SAdrian Hunter./mfile.py --share 284065b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 284165b24292SAdrian Huntersudo ldconfig 284265b24292SAdrian Hunter</pre> 284365b24292SAdrian Hunter<h3>Find</h3> 284465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 284565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 284665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 2847ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 284865b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 284965b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 2850ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 285165b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 285265b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 285365b24292SAdrian Hunter<pre> 285465b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 285565b24292SAdrian Hunter 100us-200us From 100us to 200us 285665b24292SAdrian Hunter 10ms- From 10ms to the end 285765b24292SAdrian Hunter -100ns The first 100ns 285865b24292SAdrian Hunter -10ms- The last 10ms 285965b24292SAdrian Hunter</pre> 286065b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2861ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 2862cd358012SAdrian 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. 2863cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2864cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 286565b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 286665b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 286765b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 286865b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 286965b24292SAdrian Hunterbut that can be slow for large tables. 287065b24292SAdrian Hunter<p>There are also tables of database meta-information. 287165b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 287265b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 287365b24292SAdrian Hunter<h3>Find</h3> 287465b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 287565b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 287665b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 287735fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 287835fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 287965b24292SAdrian Hunter""" 288065b24292SAdrian Hunter 288165b24292SAdrian Hunter# Help window 288265b24292SAdrian Hunter 288365b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 288465b24292SAdrian Hunter 288565b24292SAdrian Hunter def __init__(self, glb, parent=None): 288665b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 288765b24292SAdrian Hunter 288865b24292SAdrian Hunter self.text = QTextBrowser() 288965b24292SAdrian Hunter self.text.setHtml(glb_help_text) 289065b24292SAdrian Hunter self.text.setReadOnly(True) 289165b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 289265b24292SAdrian Hunter 289365b24292SAdrian Hunter self.setWidget(self.text) 289465b24292SAdrian Hunter 289565b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 289665b24292SAdrian Hunter 289765b24292SAdrian Hunter# Main window that only displays the help text 289865b24292SAdrian Hunter 289965b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 290065b24292SAdrian Hunter 290165b24292SAdrian Hunter def __init__(self, parent=None): 290265b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 290365b24292SAdrian Hunter 290465b24292SAdrian Hunter self.setMinimumSize(200, 100) 290565b24292SAdrian Hunter self.resize(800, 600) 290665b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 290765b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 290865b24292SAdrian Hunter 290965b24292SAdrian Hunter self.text = QTextBrowser() 291065b24292SAdrian Hunter self.text.setHtml(glb_help_text) 291165b24292SAdrian Hunter self.text.setReadOnly(True) 291265b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 291365b24292SAdrian Hunter 291465b24292SAdrian Hunter self.setCentralWidget(self.text) 291565b24292SAdrian Hunter 291682f68e28SAdrian Hunter# Font resize 291782f68e28SAdrian Hunter 291882f68e28SAdrian Hunterdef ResizeFont(widget, diff): 291982f68e28SAdrian Hunter font = widget.font() 292082f68e28SAdrian Hunter sz = font.pointSize() 292182f68e28SAdrian Hunter font.setPointSize(sz + diff) 292282f68e28SAdrian Hunter widget.setFont(font) 292382f68e28SAdrian Hunter 292482f68e28SAdrian Hunterdef ShrinkFont(widget): 292582f68e28SAdrian Hunter ResizeFont(widget, -1) 292682f68e28SAdrian Hunter 292782f68e28SAdrian Hunterdef EnlargeFont(widget): 292882f68e28SAdrian Hunter ResizeFont(widget, 1) 292982f68e28SAdrian Hunter 29301beb5c7bSAdrian Hunter# Unique name for sub-windows 29311beb5c7bSAdrian Hunter 29321beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 29331beb5c7bSAdrian Hunter if nr > 1: 29341beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 29351beb5c7bSAdrian Hunter return name 29361beb5c7bSAdrian Hunter 29371beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 29381beb5c7bSAdrian Hunter nr = 1 29391beb5c7bSAdrian Hunter while True: 29401beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 29411beb5c7bSAdrian Hunter ok = True 29421beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 29431beb5c7bSAdrian Hunter if sub_window.name == unique_name: 29441beb5c7bSAdrian Hunter ok = False 29451beb5c7bSAdrian Hunter break 29461beb5c7bSAdrian Hunter if ok: 29471beb5c7bSAdrian Hunter return unique_name 29481beb5c7bSAdrian Hunter nr += 1 29491beb5c7bSAdrian Hunter 29501beb5c7bSAdrian Hunter# Add a sub-window 29511beb5c7bSAdrian Hunter 29521beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 29531beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 29541beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 29551beb5c7bSAdrian Hunter sub_window.resize(800, 600) 29561beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 29571beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 29581beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 29591beb5c7bSAdrian Hunter sub_window.name = unique_name 29601beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 29611beb5c7bSAdrian Hunter sub_window.show() 29621beb5c7bSAdrian Hunter 2963031c2a00SAdrian Hunter# Main window 2964031c2a00SAdrian Hunter 2965031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 2966031c2a00SAdrian Hunter 2967031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 2968031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 2969031c2a00SAdrian Hunter 2970031c2a00SAdrian Hunter self.glb = glb 2971031c2a00SAdrian Hunter 29721beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 2973031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 2974031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 2975031c2a00SAdrian Hunter 29761beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 29771beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 29781beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 2979031c2a00SAdrian Hunter 29801beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 2981031c2a00SAdrian Hunter 29821beb5c7bSAdrian Hunter menu = self.menuBar() 2983031c2a00SAdrian Hunter 29841beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 29851beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 29861beb5c7bSAdrian Hunter 2987ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 298896c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) 298996c43b9aSAdrian Hunter edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) 2990ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 29918392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 299282f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 299382f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 2994ebd70c7dSAdrian Hunter 29951beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 2996655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 29971beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 29981beb5c7bSAdrian Hunter 2999ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 3000ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 3001ae8b887cSAdrian Hunter 300276099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 300376099f98SAdrian Hunter 3004cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 3005cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 3006cd358012SAdrian Hunter 30078392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 30088392b74bSAdrian Hunter 30091beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 30101beb5c7bSAdrian Hunter 301165b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 301265b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 301365b24292SAdrian Hunter 30144b208453SAdrian Hunter def Try(self, fn): 30154b208453SAdrian Hunter win = self.mdi_area.activeSubWindow() 30164b208453SAdrian Hunter if win: 30174b208453SAdrian Hunter try: 30184b208453SAdrian Hunter fn(win.view) 30194b208453SAdrian Hunter except: 30204b208453SAdrian Hunter pass 30214b208453SAdrian Hunter 302296c43b9aSAdrian Hunter def CopyToClipboard(self): 302396c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardHdr) 302496c43b9aSAdrian Hunter 302596c43b9aSAdrian Hunter def CopyToClipboardCSV(self): 302696c43b9aSAdrian Hunter self.Try(CopyCellsToClipboardCSV) 302796c43b9aSAdrian Hunter 3028ebd70c7dSAdrian Hunter def Find(self): 3029ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 3030ebd70c7dSAdrian Hunter if win: 3031ebd70c7dSAdrian Hunter try: 3032ebd70c7dSAdrian Hunter win.find_bar.Activate() 3033ebd70c7dSAdrian Hunter except: 3034ebd70c7dSAdrian Hunter pass 3035ebd70c7dSAdrian Hunter 30368392b74bSAdrian Hunter def FetchMoreRecords(self): 30378392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 30388392b74bSAdrian Hunter if win: 30398392b74bSAdrian Hunter try: 30408392b74bSAdrian Hunter win.fetch_bar.Activate() 30418392b74bSAdrian Hunter except: 30428392b74bSAdrian Hunter pass 30438392b74bSAdrian Hunter 304482f68e28SAdrian Hunter def ShrinkFont(self): 30454b208453SAdrian Hunter self.Try(ShrinkFont) 304682f68e28SAdrian Hunter 304782f68e28SAdrian Hunter def EnlargeFont(self): 30484b208453SAdrian Hunter self.Try(EnlargeFont) 304982f68e28SAdrian Hunter 305076099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 305176099f98SAdrian Hunter branches_events = 0 305276099f98SAdrian Hunter for event in events: 305376099f98SAdrian Hunter event = event.split(":")[0] 305476099f98SAdrian Hunter if event == "branches": 305576099f98SAdrian Hunter branches_events += 1 305676099f98SAdrian Hunter dbid = 0 305776099f98SAdrian Hunter for event in events: 305876099f98SAdrian Hunter dbid += 1 305976099f98SAdrian Hunter event = event.split(":")[0] 306076099f98SAdrian Hunter if event == "branches": 306176099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 306276099f98SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 3063210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 3064210cf1f9SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) 306576099f98SAdrian Hunter 30668392b74bSAdrian Hunter def TableMenu(self, tables, menu): 30678392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 30688392b74bSAdrian Hunter for table in tables: 30698392b74bSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) 30708392b74bSAdrian Hunter 30711beb5c7bSAdrian Hunter def NewCallGraph(self): 30721beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 3073031c2a00SAdrian Hunter 3074ae8b887cSAdrian Hunter def NewCallTree(self): 3075ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 3076ae8b887cSAdrian Hunter 3077cd358012SAdrian Hunter def NewTopCalls(self): 3078cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 3079cd358012SAdrian Hunter ret = dialog.exec_() 3080cd358012SAdrian Hunter if ret: 3081cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 3082cd358012SAdrian Hunter 308376099f98SAdrian Hunter def NewBranchView(self, event_id): 3084947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 308576099f98SAdrian Hunter 3086210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 3087210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 3088210cf1f9SAdrian Hunter ret = dialog.exec_() 3089210cf1f9SAdrian Hunter if ret: 3090947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 3091210cf1f9SAdrian Hunter 30928392b74bSAdrian Hunter def NewTableView(self, table_name): 30938392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 30948392b74bSAdrian Hunter 309565b24292SAdrian Hunter def Help(self): 309665b24292SAdrian Hunter HelpWindow(self.glb, self) 309765b24292SAdrian Hunter 309876099f98SAdrian Hunter# XED Disassembler 309976099f98SAdrian Hunter 310076099f98SAdrian Hunterclass xed_state_t(Structure): 310176099f98SAdrian Hunter 310276099f98SAdrian Hunter _fields_ = [ 310376099f98SAdrian Hunter ("mode", c_int), 310476099f98SAdrian Hunter ("width", c_int) 310576099f98SAdrian Hunter ] 310676099f98SAdrian Hunter 310776099f98SAdrian Hunterclass XEDInstruction(): 310876099f98SAdrian Hunter 310976099f98SAdrian Hunter def __init__(self, libxed): 311076099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 311176099f98SAdrian Hunter xedd_t = c_byte * 512 311276099f98SAdrian Hunter self.xedd = xedd_t() 311376099f98SAdrian Hunter self.xedp = addressof(self.xedd) 311476099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 311576099f98SAdrian Hunter self.state = xed_state_t() 311676099f98SAdrian Hunter self.statep = addressof(self.state) 311776099f98SAdrian Hunter # Buffer for disassembled instruction text 311876099f98SAdrian Hunter self.buffer = create_string_buffer(256) 311976099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 312076099f98SAdrian Hunter 312176099f98SAdrian Hunterclass LibXED(): 312276099f98SAdrian Hunter 312376099f98SAdrian Hunter def __init__(self): 31245ed4419dSAdrian Hunter try: 312576099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 31265ed4419dSAdrian Hunter except: 31275ed4419dSAdrian Hunter self.libxed = None 31285ed4419dSAdrian Hunter if not self.libxed: 31295ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 313076099f98SAdrian Hunter 313176099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 313276099f98SAdrian Hunter self.xed_tables_init.restype = None 313376099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 313476099f98SAdrian Hunter 313576099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 313676099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 313776099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 313876099f98SAdrian Hunter 313976099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 314076099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 314176099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 314276099f98SAdrian Hunter 314376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 314476099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 314576099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 314676099f98SAdrian Hunter 314776099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 314876099f98SAdrian Hunter self.xed_decode.restype = c_int 314976099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 315076099f98SAdrian Hunter 315176099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 315276099f98SAdrian Hunter self.xed_format_context.restype = c_uint 315376099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 315476099f98SAdrian Hunter 315576099f98SAdrian Hunter self.xed_tables_init() 315676099f98SAdrian Hunter 315776099f98SAdrian Hunter def Instruction(self): 315876099f98SAdrian Hunter return XEDInstruction(self) 315976099f98SAdrian Hunter 316076099f98SAdrian Hunter def SetMode(self, inst, mode): 316176099f98SAdrian Hunter if mode: 316276099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 316376099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 316476099f98SAdrian Hunter else: 316576099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 316676099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 316776099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 316876099f98SAdrian Hunter 316976099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 317076099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 317176099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 317276099f98SAdrian Hunter if err: 317376099f98SAdrian Hunter return 0, "" 317476099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 317576099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 317676099f98SAdrian Hunter if not ok: 317776099f98SAdrian Hunter return 0, "" 3178606bd60aSAdrian Hunter if sys.version_info[0] == 2: 3179606bd60aSAdrian Hunter result = inst.buffer.value 3180606bd60aSAdrian Hunter else: 3181606bd60aSAdrian Hunter result = inst.buffer.value.decode() 318276099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 318376099f98SAdrian Hunter # For now, assume the length is in byte 166 3184606bd60aSAdrian Hunter return inst.xedd[166], result 318576099f98SAdrian Hunter 318676099f98SAdrian Hunterdef TryOpen(file_name): 318776099f98SAdrian Hunter try: 318876099f98SAdrian Hunter return open(file_name, "rb") 318976099f98SAdrian Hunter except: 319076099f98SAdrian Hunter return None 319176099f98SAdrian Hunter 319276099f98SAdrian Hunterdef Is64Bit(f): 319376099f98SAdrian Hunter result = sizeof(c_void_p) 319476099f98SAdrian Hunter # ELF support only 319576099f98SAdrian Hunter pos = f.tell() 319676099f98SAdrian Hunter f.seek(0) 319776099f98SAdrian Hunter header = f.read(7) 319876099f98SAdrian Hunter f.seek(pos) 319976099f98SAdrian Hunter magic = header[0:4] 3200606bd60aSAdrian Hunter if sys.version_info[0] == 2: 320176099f98SAdrian Hunter eclass = ord(header[4]) 320276099f98SAdrian Hunter encoding = ord(header[5]) 320376099f98SAdrian Hunter version = ord(header[6]) 3204606bd60aSAdrian Hunter else: 3205606bd60aSAdrian Hunter eclass = header[4] 3206606bd60aSAdrian Hunter encoding = header[5] 3207606bd60aSAdrian Hunter version = header[6] 320876099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 320976099f98SAdrian Hunter result = True if eclass == 2 else False 321076099f98SAdrian Hunter return result 321176099f98SAdrian Hunter 3212031c2a00SAdrian Hunter# Global data 3213031c2a00SAdrian Hunter 3214031c2a00SAdrian Hunterclass Glb(): 3215031c2a00SAdrian Hunter 3216031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 3217031c2a00SAdrian Hunter self.dbref = dbref 3218031c2a00SAdrian Hunter self.db = db 3219031c2a00SAdrian Hunter self.dbname = dbname 322076099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 322176099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 322276099f98SAdrian Hunter if self.buildid_dir: 322376099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 322476099f98SAdrian Hunter else: 322576099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 3226031c2a00SAdrian Hunter self.app = None 3227031c2a00SAdrian Hunter self.mainwindow = None 32288392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 322976099f98SAdrian Hunter try: 323076099f98SAdrian Hunter self.disassembler = LibXED() 323176099f98SAdrian Hunter self.have_disassembler = True 323276099f98SAdrian Hunter except: 323376099f98SAdrian Hunter self.have_disassembler = False 323476099f98SAdrian Hunter 323576099f98SAdrian Hunter def FileFromBuildId(self, build_id): 323676099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 323776099f98SAdrian Hunter return TryOpen(file_name) 323876099f98SAdrian Hunter 323976099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 324076099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 324176099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 324276099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 324376099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 324476099f98SAdrian Hunter if f: 324576099f98SAdrian Hunter return f 324676099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 324776099f98SAdrian Hunter f = TryOpen(long_name) 324876099f98SAdrian Hunter if f: 324976099f98SAdrian Hunter return f 325076099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 325176099f98SAdrian Hunter if f: 325276099f98SAdrian Hunter return f 325376099f98SAdrian Hunter return None 32548392b74bSAdrian Hunter 32558392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 32568392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 32578392b74bSAdrian Hunter 32588392b74bSAdrian Hunter # Shutdown any background processes or threads 32598392b74bSAdrian Hunter def ShutdownInstances(self): 32608392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 32618392b74bSAdrian Hunter try: 32628392b74bSAdrian Hunter x.Shutdown() 32638392b74bSAdrian Hunter except: 32648392b74bSAdrian Hunter pass 3265031c2a00SAdrian Hunter 3266031c2a00SAdrian Hunter# Database reference 3267031c2a00SAdrian Hunter 3268031c2a00SAdrian Hunterclass DBRef(): 3269031c2a00SAdrian Hunter 3270031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3271031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3272031c2a00SAdrian Hunter self.dbname = dbname 3273031c2a00SAdrian Hunter 3274031c2a00SAdrian Hunter def Open(self, connection_name): 3275031c2a00SAdrian Hunter dbname = self.dbname 3276031c2a00SAdrian Hunter if self.is_sqlite3: 3277031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3278031c2a00SAdrian Hunter else: 3279031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3280031c2a00SAdrian Hunter opts = dbname.split() 3281031c2a00SAdrian Hunter for opt in opts: 3282031c2a00SAdrian Hunter if "=" in opt: 3283031c2a00SAdrian Hunter opt = opt.split("=") 3284031c2a00SAdrian Hunter if opt[0] == "hostname": 3285031c2a00SAdrian Hunter db.setHostName(opt[1]) 3286031c2a00SAdrian Hunter elif opt[0] == "port": 3287031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3288031c2a00SAdrian Hunter elif opt[0] == "username": 3289031c2a00SAdrian Hunter db.setUserName(opt[1]) 3290031c2a00SAdrian Hunter elif opt[0] == "password": 3291031c2a00SAdrian Hunter db.setPassword(opt[1]) 3292031c2a00SAdrian Hunter elif opt[0] == "dbname": 3293031c2a00SAdrian Hunter dbname = opt[1] 3294031c2a00SAdrian Hunter else: 3295031c2a00SAdrian Hunter dbname = opt 3296031c2a00SAdrian Hunter 3297031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3298031c2a00SAdrian Hunter if not db.open(): 3299031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3300031c2a00SAdrian Hunter return db, dbname 3301031c2a00SAdrian Hunter 3302031c2a00SAdrian Hunter# Main 3303031c2a00SAdrian Hunter 3304031c2a00SAdrian Hunterdef Main(): 3305031c2a00SAdrian Hunter if (len(sys.argv) < 2): 3306beda0e72STony Jones printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}"); 3307031c2a00SAdrian Hunter raise Exception("Too few arguments") 3308031c2a00SAdrian Hunter 3309031c2a00SAdrian Hunter dbname = sys.argv[1] 331065b24292SAdrian Hunter if dbname == "--help-only": 331165b24292SAdrian Hunter app = QApplication(sys.argv) 331265b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 331365b24292SAdrian Hunter mainwindow.show() 331465b24292SAdrian Hunter err = app.exec_() 331565b24292SAdrian Hunter sys.exit(err) 3316031c2a00SAdrian Hunter 3317031c2a00SAdrian Hunter is_sqlite3 = False 3318031c2a00SAdrian Hunter try: 3319beda0e72STony Jones f = open(dbname, "rb") 3320beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3321031c2a00SAdrian Hunter is_sqlite3 = True 3322031c2a00SAdrian Hunter f.close() 3323031c2a00SAdrian Hunter except: 3324031c2a00SAdrian Hunter pass 3325031c2a00SAdrian Hunter 3326031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3327031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3328031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3329031c2a00SAdrian Hunter app = QApplication(sys.argv) 3330031c2a00SAdrian Hunter glb.app = app 3331031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3332031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3333031c2a00SAdrian Hunter mainwindow.show() 3334031c2a00SAdrian Hunter err = app.exec_() 33358392b74bSAdrian Hunter glb.ShutdownInstances() 3336031c2a00SAdrian Hunter db.close() 3337031c2a00SAdrian Hunter sys.exit(err) 3338031c2a00SAdrian Hunter 3339031c2a00SAdrian Hunterif __name__ == "__main__": 3340031c2a00SAdrian Hunter Main() 3341