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 = [] 459031c2a00SAdrian Hunter 460031c2a00SAdrian Hunter def getChildItem(self, row): 461031c2a00SAdrian Hunter return self.child_items[row] 462031c2a00SAdrian Hunter 463031c2a00SAdrian Hunter def getParentItem(self): 464031c2a00SAdrian Hunter return self.parent_item 465031c2a00SAdrian Hunter 466031c2a00SAdrian Hunter def getRow(self): 467031c2a00SAdrian Hunter return self.row 468031c2a00SAdrian Hunter 469031c2a00SAdrian Hunter def childCount(self): 470031c2a00SAdrian Hunter if not self.query_done: 471031c2a00SAdrian Hunter self.Select() 472031c2a00SAdrian Hunter if not self.child_count: 473031c2a00SAdrian Hunter return -1 474031c2a00SAdrian Hunter return self.child_count 475031c2a00SAdrian Hunter 476031c2a00SAdrian Hunter def hasChildren(self): 477031c2a00SAdrian Hunter if not self.query_done: 478031c2a00SAdrian Hunter return True 479031c2a00SAdrian Hunter return self.child_count > 0 480031c2a00SAdrian Hunter 481031c2a00SAdrian Hunter def getData(self, column): 482031c2a00SAdrian Hunter return self.data[column] 483031c2a00SAdrian Hunter 484031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 485031c2a00SAdrian Hunter 486031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 487031c2a00SAdrian Hunter 488031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 489031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 490031c2a00SAdrian Hunter self.comm_id = comm_id 491031c2a00SAdrian Hunter self.thread_id = thread_id 492031c2a00SAdrian Hunter self.call_path_id = call_path_id 493031c2a00SAdrian Hunter self.branch_count = branch_count 494031c2a00SAdrian Hunter self.time = time 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunter def Select(self): 497031c2a00SAdrian Hunter self.query_done = True; 498031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 499031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 500031c2a00SAdrian Hunter " FROM calls" 501031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 502031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 503031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 504031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 505031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 506031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 507031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 508031c2a00SAdrian Hunter " ORDER BY call_path_id") 509031c2a00SAdrian Hunter while query.next(): 510031c2a00SAdrian 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) 511031c2a00SAdrian Hunter self.child_items.append(child_item) 512031c2a00SAdrian Hunter self.child_count += 1 513031c2a00SAdrian Hunter 514031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 515031c2a00SAdrian Hunter 516031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 517031c2a00SAdrian Hunter 518031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 519031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 520031c2a00SAdrian Hunter dso = dsoname(dso) 521031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 522031c2a00SAdrian Hunter self.dbid = call_path_id 523031c2a00SAdrian Hunter 524031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 525031c2a00SAdrian Hunter 526031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 527031c2a00SAdrian Hunter 528031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 529031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 530031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 531031c2a00SAdrian Hunter self.dbid = thread_id 532031c2a00SAdrian Hunter 533031c2a00SAdrian Hunter def Select(self): 534031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 535031c2a00SAdrian Hunter for child_item in self.child_items: 536031c2a00SAdrian Hunter self.time += child_item.time 537031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 538031c2a00SAdrian Hunter for child_item in self.child_items: 539031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 540031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 541031c2a00SAdrian Hunter 542031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 543031c2a00SAdrian Hunter 544031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 547031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 548031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 549031c2a00SAdrian Hunter self.dbid = comm_id 550031c2a00SAdrian Hunter 551031c2a00SAdrian Hunter def Select(self): 552031c2a00SAdrian Hunter self.query_done = True; 553031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 554031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 555031c2a00SAdrian Hunter " FROM comm_threads" 556031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 557031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 558031c2a00SAdrian Hunter while query.next(): 559031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 560031c2a00SAdrian Hunter self.child_items.append(child_item) 561031c2a00SAdrian Hunter self.child_count += 1 562031c2a00SAdrian Hunter 563031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 564031c2a00SAdrian Hunter 565031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 566031c2a00SAdrian Hunter 567031c2a00SAdrian Hunter def __init__(self, glb): 568031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 569031c2a00SAdrian Hunter self.dbid = 0 570031c2a00SAdrian Hunter self.query_done = True; 571031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 572031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 573031c2a00SAdrian Hunter while query.next(): 574031c2a00SAdrian Hunter if not query.value(0): 575031c2a00SAdrian Hunter continue 576031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 577031c2a00SAdrian Hunter self.child_items.append(child_item) 578031c2a00SAdrian Hunter self.child_count += 1 579031c2a00SAdrian Hunter 580254c0d82SAdrian Hunter# Context-sensitive call graph data model base 581031c2a00SAdrian Hunter 582254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 583031c2a00SAdrian Hunter 584031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 585254c0d82SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, parent) 586031c2a00SAdrian Hunter 587ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 588ebd70c7dSAdrian Hunter if pattern: 589ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 590ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 591ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 592ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 593ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 594ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 595ebd70c7dSAdrian Hunter # Escape % and _ 596ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 597ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 598ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 599ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 600ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 601ebd70c7dSAdrian Hunter else: 602ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 603ebd70c7dSAdrian Hunter else: 604ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 605254c0d82SAdrian Hunter self.DoFindSelect(query, match) 606ebd70c7dSAdrian Hunter 607ebd70c7dSAdrian Hunter def Found(self, query, found): 608ebd70c7dSAdrian Hunter if found: 609ebd70c7dSAdrian Hunter return self.FindPath(query) 610ebd70c7dSAdrian Hunter return [] 611ebd70c7dSAdrian Hunter 612ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 613ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 614ebd70c7dSAdrian Hunter found = query.first() 615ebd70c7dSAdrian Hunter else: 616ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 617ebd70c7dSAdrian Hunter found = query.next() 618ebd70c7dSAdrian Hunter return self.Found(query, found) 619ebd70c7dSAdrian Hunter 620ebd70c7dSAdrian Hunter def FindNext(self, query): 621ebd70c7dSAdrian Hunter found = query.next() 622ebd70c7dSAdrian Hunter if not found: 623ebd70c7dSAdrian Hunter found = query.first() 624ebd70c7dSAdrian Hunter return self.Found(query, found) 625ebd70c7dSAdrian Hunter 626ebd70c7dSAdrian Hunter def FindPrev(self, query): 627ebd70c7dSAdrian Hunter found = query.previous() 628ebd70c7dSAdrian Hunter if not found: 629ebd70c7dSAdrian Hunter found = query.last() 630ebd70c7dSAdrian Hunter return self.Found(query, found) 631ebd70c7dSAdrian Hunter 632ebd70c7dSAdrian Hunter def FindThread(self, c): 633ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 634ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 635ebd70c7dSAdrian Hunter elif c.direction > 0: 636ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 637ebd70c7dSAdrian Hunter else: 638ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 639ebd70c7dSAdrian Hunter return (True, ids) 640ebd70c7dSAdrian Hunter 641ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 642ebd70c7dSAdrian Hunter class Context(): 643ebd70c7dSAdrian Hunter def __init__(self, *x): 644ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 645ebd70c7dSAdrian Hunter def Update(self, *x): 646ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 647ebd70c7dSAdrian Hunter if len(context): 648ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 649ebd70c7dSAdrian Hunter else: 650ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 651ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 652ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 653ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 654ebd70c7dSAdrian Hunter thread.start() 655ebd70c7dSAdrian Hunter 656ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 657ebd70c7dSAdrian Hunter callback(ids) 658ebd70c7dSAdrian Hunter 659254c0d82SAdrian Hunter# Context-sensitive call graph data model 660254c0d82SAdrian Hunter 661254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 662254c0d82SAdrian Hunter 663254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 664254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 665254c0d82SAdrian Hunter 666254c0d82SAdrian Hunter def GetRoot(self): 667254c0d82SAdrian Hunter return CallGraphRootItem(self.glb) 668254c0d82SAdrian Hunter 669254c0d82SAdrian Hunter def columnCount(self, parent=None): 670254c0d82SAdrian Hunter return 7 671254c0d82SAdrian Hunter 672254c0d82SAdrian Hunter def columnHeader(self, column): 673254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 674254c0d82SAdrian Hunter return headers[column] 675254c0d82SAdrian Hunter 676254c0d82SAdrian Hunter def columnAlignment(self, column): 677254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 678254c0d82SAdrian Hunter return alignment[column] 679254c0d82SAdrian Hunter 680254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 681254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 682254c0d82SAdrian Hunter " FROM calls" 683254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 684254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 685254c0d82SAdrian Hunter " WHERE symbols.name" + match + 686254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 687254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 688254c0d82SAdrian Hunter 689254c0d82SAdrian Hunter def FindPath(self, query): 690254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 691254c0d82SAdrian Hunter # to open the tree at the right place. 692254c0d82SAdrian Hunter ids = [] 693254c0d82SAdrian Hunter parent_id = query.value(0) 694254c0d82SAdrian Hunter while parent_id: 695254c0d82SAdrian Hunter ids.insert(0, parent_id) 696254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 697254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 698254c0d82SAdrian Hunter " FROM call_paths" 699254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 700254c0d82SAdrian Hunter if not q2.next(): 701254c0d82SAdrian Hunter break 702254c0d82SAdrian Hunter parent_id = q2.value(0) 703254c0d82SAdrian Hunter # The call path root is not used 704254c0d82SAdrian Hunter if ids[0] == 1: 705254c0d82SAdrian Hunter del ids[0] 706254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 707254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 708254c0d82SAdrian Hunter return ids 709254c0d82SAdrian Hunter 710ae8b887cSAdrian Hunter# Call tree data model level 2+ item base 711ae8b887cSAdrian Hunter 712ae8b887cSAdrian Hunterclass CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): 713ae8b887cSAdrian Hunter 714ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): 715ae8b887cSAdrian Hunter super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 716ae8b887cSAdrian Hunter self.comm_id = comm_id 717ae8b887cSAdrian Hunter self.thread_id = thread_id 718ae8b887cSAdrian Hunter self.calls_id = calls_id 719ae8b887cSAdrian Hunter self.branch_count = branch_count 720ae8b887cSAdrian Hunter self.time = time 721ae8b887cSAdrian Hunter 722ae8b887cSAdrian Hunter def Select(self): 723ae8b887cSAdrian Hunter self.query_done = True; 724ae8b887cSAdrian Hunter if self.calls_id == 0: 725ae8b887cSAdrian Hunter comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) 726ae8b887cSAdrian Hunter else: 727ae8b887cSAdrian Hunter comm_thread = "" 728ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 729ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" 730ae8b887cSAdrian Hunter " FROM calls" 731ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 732ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 733ae8b887cSAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 734ae8b887cSAdrian Hunter " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + 735ae8b887cSAdrian Hunter " ORDER BY call_time, calls.id") 736ae8b887cSAdrian Hunter while query.next(): 737ae8b887cSAdrian 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) 738ae8b887cSAdrian Hunter self.child_items.append(child_item) 739ae8b887cSAdrian Hunter self.child_count += 1 740ae8b887cSAdrian Hunter 741ae8b887cSAdrian Hunter# Call tree data model level three item 742ae8b887cSAdrian Hunter 743ae8b887cSAdrian Hunterclass CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): 744ae8b887cSAdrian Hunter 745ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): 746ae8b887cSAdrian Hunter super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) 747ae8b887cSAdrian Hunter dso = dsoname(dso) 748ae8b887cSAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 749ae8b887cSAdrian Hunter self.dbid = calls_id 750ae8b887cSAdrian Hunter 751ae8b887cSAdrian Hunter# Call tree data model level two item 752ae8b887cSAdrian Hunter 753ae8b887cSAdrian Hunterclass CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): 754ae8b887cSAdrian Hunter 755ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 756ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) 757ae8b887cSAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 758ae8b887cSAdrian Hunter self.dbid = thread_id 759ae8b887cSAdrian Hunter 760ae8b887cSAdrian Hunter def Select(self): 761ae8b887cSAdrian Hunter super(CallTreeLevelTwoItem, self).Select() 762ae8b887cSAdrian Hunter for child_item in self.child_items: 763ae8b887cSAdrian Hunter self.time += child_item.time 764ae8b887cSAdrian Hunter self.branch_count += child_item.branch_count 765ae8b887cSAdrian Hunter for child_item in self.child_items: 766ae8b887cSAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 767ae8b887cSAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 768ae8b887cSAdrian Hunter 769ae8b887cSAdrian Hunter# Call tree data model level one item 770ae8b887cSAdrian Hunter 771ae8b887cSAdrian Hunterclass CallTreeLevelOneItem(CallGraphLevelItemBase): 772ae8b887cSAdrian Hunter 773ae8b887cSAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 774ae8b887cSAdrian Hunter super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) 775ae8b887cSAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 776ae8b887cSAdrian Hunter self.dbid = comm_id 777ae8b887cSAdrian Hunter 778ae8b887cSAdrian Hunter def Select(self): 779ae8b887cSAdrian Hunter self.query_done = True; 780ae8b887cSAdrian Hunter query = QSqlQuery(self.glb.db) 781ae8b887cSAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 782ae8b887cSAdrian Hunter " FROM comm_threads" 783ae8b887cSAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 784ae8b887cSAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 785ae8b887cSAdrian Hunter while query.next(): 786ae8b887cSAdrian Hunter child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 787ae8b887cSAdrian Hunter self.child_items.append(child_item) 788ae8b887cSAdrian Hunter self.child_count += 1 789ae8b887cSAdrian Hunter 790ae8b887cSAdrian Hunter# Call tree data model root item 791ae8b887cSAdrian Hunter 792ae8b887cSAdrian Hunterclass CallTreeRootItem(CallGraphLevelItemBase): 793ae8b887cSAdrian Hunter 794ae8b887cSAdrian Hunter def __init__(self, glb): 795ae8b887cSAdrian Hunter super(CallTreeRootItem, self).__init__(glb, 0, None) 796ae8b887cSAdrian Hunter self.dbid = 0 797ae8b887cSAdrian Hunter self.query_done = True; 798ae8b887cSAdrian Hunter query = QSqlQuery(glb.db) 799ae8b887cSAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 800ae8b887cSAdrian Hunter while query.next(): 801ae8b887cSAdrian Hunter if not query.value(0): 802ae8b887cSAdrian Hunter continue 803ae8b887cSAdrian Hunter child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 804ae8b887cSAdrian Hunter self.child_items.append(child_item) 805ae8b887cSAdrian Hunter self.child_count += 1 806ae8b887cSAdrian Hunter 807ae8b887cSAdrian Hunter# Call Tree data model 808ae8b887cSAdrian Hunter 809ae8b887cSAdrian Hunterclass CallTreeModel(CallGraphModelBase): 810ae8b887cSAdrian Hunter 811ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 812ae8b887cSAdrian Hunter super(CallTreeModel, self).__init__(glb, parent) 813ae8b887cSAdrian Hunter 814ae8b887cSAdrian Hunter def GetRoot(self): 815ae8b887cSAdrian Hunter return CallTreeRootItem(self.glb) 816ae8b887cSAdrian Hunter 817ae8b887cSAdrian Hunter def columnCount(self, parent=None): 818ae8b887cSAdrian Hunter return 7 819ae8b887cSAdrian Hunter 820ae8b887cSAdrian Hunter def columnHeader(self, column): 821ae8b887cSAdrian Hunter headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 822ae8b887cSAdrian Hunter return headers[column] 823ae8b887cSAdrian Hunter 824ae8b887cSAdrian Hunter def columnAlignment(self, column): 825ae8b887cSAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 826ae8b887cSAdrian Hunter return alignment[column] 827ae8b887cSAdrian Hunter 828ae8b887cSAdrian Hunter def DoFindSelect(self, query, match): 829ae8b887cSAdrian Hunter QueryExec(query, "SELECT calls.id, comm_id, thread_id" 830ae8b887cSAdrian Hunter " FROM calls" 831ae8b887cSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 832ae8b887cSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 833ae8b887cSAdrian Hunter " WHERE symbols.name" + match + 834ae8b887cSAdrian Hunter " ORDER BY comm_id, thread_id, call_time, calls.id") 835ae8b887cSAdrian Hunter 836ae8b887cSAdrian Hunter def FindPath(self, query): 837ae8b887cSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 838ae8b887cSAdrian Hunter # to open the tree at the right place. 839ae8b887cSAdrian Hunter ids = [] 840ae8b887cSAdrian Hunter parent_id = query.value(0) 841ae8b887cSAdrian Hunter while parent_id: 842ae8b887cSAdrian Hunter ids.insert(0, parent_id) 843ae8b887cSAdrian Hunter q2 = QSqlQuery(self.glb.db) 844ae8b887cSAdrian Hunter QueryExec(q2, "SELECT parent_id" 845ae8b887cSAdrian Hunter " FROM calls" 846ae8b887cSAdrian Hunter " WHERE id = " + str(parent_id)) 847ae8b887cSAdrian Hunter if not q2.next(): 848ae8b887cSAdrian Hunter break 849ae8b887cSAdrian Hunter parent_id = q2.value(0) 850ae8b887cSAdrian Hunter ids.insert(0, query.value(2)) 851ae8b887cSAdrian Hunter ids.insert(0, query.value(1)) 852ae8b887cSAdrian Hunter return ids 853ae8b887cSAdrian Hunter 854ebd70c7dSAdrian Hunter# Vertical widget layout 855ebd70c7dSAdrian Hunter 856ebd70c7dSAdrian Hunterclass VBox(): 857ebd70c7dSAdrian Hunter 858ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 859ebd70c7dSAdrian Hunter self.vbox = QWidget() 860ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 861ebd70c7dSAdrian Hunter 862ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 863ebd70c7dSAdrian Hunter 864ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 865ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 866ebd70c7dSAdrian Hunter if w3: 867ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 868ebd70c7dSAdrian Hunter 869ebd70c7dSAdrian Hunter def Widget(self): 870ebd70c7dSAdrian Hunter return self.vbox 871ebd70c7dSAdrian Hunter 872a731cc4cSAdrian Hunter# Tree window base 8731beb5c7bSAdrian Hunter 874a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 8751beb5c7bSAdrian Hunter 876a731cc4cSAdrian Hunter def __init__(self, parent=None): 877a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 8781beb5c7bSAdrian Hunter 879a731cc4cSAdrian Hunter self.model = None 880a731cc4cSAdrian Hunter self.find_bar = None 8811beb5c7bSAdrian Hunter 882*be6e7471SAdrian Hunter self.view = QTreeView() 883*be6e7471SAdrian Hunter 884ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 885ebd70c7dSAdrian Hunter if not len(ids): 886ebd70c7dSAdrian Hunter return False 887ebd70c7dSAdrian Hunter parent = QModelIndex() 888ebd70c7dSAdrian Hunter for dbid in ids: 889ebd70c7dSAdrian Hunter found = False 890ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 891ebd70c7dSAdrian Hunter for row in xrange(n): 892ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 893ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 894ebd70c7dSAdrian Hunter found = True 895ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 896ebd70c7dSAdrian Hunter parent = child 897ebd70c7dSAdrian Hunter break 898ebd70c7dSAdrian Hunter if not found: 899ebd70c7dSAdrian Hunter break 900ebd70c7dSAdrian Hunter return found 901ebd70c7dSAdrian Hunter 902ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 903ebd70c7dSAdrian Hunter self.view.setFocus() 904ebd70c7dSAdrian Hunter self.find_bar.Busy() 905ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 906ebd70c7dSAdrian Hunter 907ebd70c7dSAdrian Hunter def FindDone(self, ids): 908ebd70c7dSAdrian Hunter found = True 909ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 910ebd70c7dSAdrian Hunter found = False 911ebd70c7dSAdrian Hunter self.find_bar.Idle() 912ebd70c7dSAdrian Hunter if not found: 913ebd70c7dSAdrian Hunter self.find_bar.NotFound() 914ebd70c7dSAdrian Hunter 915a731cc4cSAdrian Hunter 916a731cc4cSAdrian Hunter# Context-sensitive call graph window 917a731cc4cSAdrian Hunter 918a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 919a731cc4cSAdrian Hunter 920a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 921a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 922a731cc4cSAdrian Hunter 923a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 924a731cc4cSAdrian Hunter 925a731cc4cSAdrian Hunter self.view.setModel(self.model) 926a731cc4cSAdrian Hunter 927a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 928a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 929a731cc4cSAdrian Hunter 930a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 931a731cc4cSAdrian Hunter 932a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 933a731cc4cSAdrian Hunter 934a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 935a731cc4cSAdrian Hunter 936a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 937a731cc4cSAdrian Hunter 938ae8b887cSAdrian Hunter# Call tree window 939ae8b887cSAdrian Hunter 940ae8b887cSAdrian Hunterclass CallTreeWindow(TreeWindowBase): 941ae8b887cSAdrian Hunter 942ae8b887cSAdrian Hunter def __init__(self, glb, parent=None): 943ae8b887cSAdrian Hunter super(CallTreeWindow, self).__init__(parent) 944ae8b887cSAdrian Hunter 945ae8b887cSAdrian Hunter self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) 946ae8b887cSAdrian Hunter 947ae8b887cSAdrian Hunter self.view.setModel(self.model) 948ae8b887cSAdrian Hunter 949ae8b887cSAdrian Hunter for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): 950ae8b887cSAdrian Hunter self.view.setColumnWidth(c, w) 951ae8b887cSAdrian Hunter 952ae8b887cSAdrian Hunter self.find_bar = FindBar(self, self) 953ae8b887cSAdrian Hunter 954ae8b887cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 955ae8b887cSAdrian Hunter 956ae8b887cSAdrian Hunter self.setWidget(self.vbox.Widget()) 957ae8b887cSAdrian Hunter 958ae8b887cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") 959ae8b887cSAdrian Hunter 9608392b74bSAdrian Hunter# Child data item finder 9618392b74bSAdrian Hunter 9628392b74bSAdrian Hunterclass ChildDataItemFinder(): 9638392b74bSAdrian Hunter 9648392b74bSAdrian Hunter def __init__(self, root): 9658392b74bSAdrian Hunter self.root = root 9668392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 9678392b74bSAdrian Hunter self.rows = [] 9688392b74bSAdrian Hunter self.pos = 0 9698392b74bSAdrian Hunter 9708392b74bSAdrian Hunter def FindSelect(self): 9718392b74bSAdrian Hunter self.rows = [] 9728392b74bSAdrian Hunter if self.pattern: 9738392b74bSAdrian Hunter pattern = re.compile(self.value) 9748392b74bSAdrian Hunter for child in self.root.child_items: 9758392b74bSAdrian Hunter for column_data in child.data: 9768392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 9778392b74bSAdrian Hunter self.rows.append(child.row) 9788392b74bSAdrian Hunter break 9798392b74bSAdrian Hunter else: 9808392b74bSAdrian Hunter for child in self.root.child_items: 9818392b74bSAdrian Hunter for column_data in child.data: 9828392b74bSAdrian Hunter if self.value in str(column_data): 9838392b74bSAdrian Hunter self.rows.append(child.row) 9848392b74bSAdrian Hunter break 9858392b74bSAdrian Hunter 9868392b74bSAdrian Hunter def FindValue(self): 9878392b74bSAdrian Hunter self.pos = 0 9888392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 9898392b74bSAdrian Hunter self.FindSelect() 9908392b74bSAdrian Hunter if not len(self.rows): 9918392b74bSAdrian Hunter return -1 9928392b74bSAdrian Hunter return self.rows[self.pos] 9938392b74bSAdrian Hunter 9948392b74bSAdrian Hunter def FindThread(self): 9958392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 9968392b74bSAdrian Hunter row = self.FindValue() 9978392b74bSAdrian Hunter elif len(self.rows): 9988392b74bSAdrian Hunter if self.direction > 0: 9998392b74bSAdrian Hunter self.pos += 1 10008392b74bSAdrian Hunter if self.pos >= len(self.rows): 10018392b74bSAdrian Hunter self.pos = 0 10028392b74bSAdrian Hunter else: 10038392b74bSAdrian Hunter self.pos -= 1 10048392b74bSAdrian Hunter if self.pos < 0: 10058392b74bSAdrian Hunter self.pos = len(self.rows) - 1 10068392b74bSAdrian Hunter row = self.rows[self.pos] 10078392b74bSAdrian Hunter else: 10088392b74bSAdrian Hunter row = -1 10098392b74bSAdrian Hunter return (True, row) 10108392b74bSAdrian Hunter 10118392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 10128392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 10138392b74bSAdrian Hunter # Use a thread so the UI is not blocked 10148392b74bSAdrian Hunter thread = Thread(self.FindThread) 10158392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 10168392b74bSAdrian Hunter thread.start() 10178392b74bSAdrian Hunter 10188392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 10198392b74bSAdrian Hunter callback(row) 10208392b74bSAdrian Hunter 10218392b74bSAdrian Hunter# Number of database records to fetch in one go 10228392b74bSAdrian Hunter 10238392b74bSAdrian Hunterglb_chunk_sz = 10000 10248392b74bSAdrian Hunter 10258392b74bSAdrian Hunter# Background process for SQL data fetcher 10268392b74bSAdrian Hunter 10278392b74bSAdrian Hunterclass SQLFetcherProcess(): 10288392b74bSAdrian Hunter 10298392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 10308392b74bSAdrian Hunter # Need a unique connection name 10318392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 10328392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 10338392b74bSAdrian Hunter self.sql = sql 10348392b74bSAdrian Hunter self.buffer = buffer 10358392b74bSAdrian Hunter self.head = head 10368392b74bSAdrian Hunter self.tail = tail 10378392b74bSAdrian Hunter self.fetch_count = fetch_count 10388392b74bSAdrian Hunter self.fetching_done = fetching_done 10398392b74bSAdrian Hunter self.process_target = process_target 10408392b74bSAdrian Hunter self.wait_event = wait_event 10418392b74bSAdrian Hunter self.fetched_event = fetched_event 10428392b74bSAdrian Hunter self.prep = prep 10438392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 10448392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 10458392b74bSAdrian Hunter self.last_id = -1 10468392b74bSAdrian Hunter self.fetched = 0 10478392b74bSAdrian Hunter self.more = True 10488392b74bSAdrian Hunter self.local_head = self.head.value 10498392b74bSAdrian Hunter self.local_tail = self.tail.value 10508392b74bSAdrian Hunter 10518392b74bSAdrian Hunter def Select(self): 10528392b74bSAdrian Hunter if self.query_limit: 10538392b74bSAdrian Hunter if self.query_limit == 1: 10548392b74bSAdrian Hunter return 10558392b74bSAdrian Hunter self.query_limit -= 1 10568392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 10578392b74bSAdrian Hunter QueryExec(self.query, stmt) 10588392b74bSAdrian Hunter 10598392b74bSAdrian Hunter def Next(self): 10608392b74bSAdrian Hunter if not self.query.next(): 10618392b74bSAdrian Hunter self.Select() 10628392b74bSAdrian Hunter if not self.query.next(): 10638392b74bSAdrian Hunter return None 10648392b74bSAdrian Hunter self.last_id = self.query.value(0) 10658392b74bSAdrian Hunter return self.prep(self.query) 10668392b74bSAdrian Hunter 10678392b74bSAdrian Hunter def WaitForTarget(self): 10688392b74bSAdrian Hunter while True: 10698392b74bSAdrian Hunter self.wait_event.clear() 10708392b74bSAdrian Hunter target = self.process_target.value 10718392b74bSAdrian Hunter if target > self.fetched or target < 0: 10728392b74bSAdrian Hunter break 10738392b74bSAdrian Hunter self.wait_event.wait() 10748392b74bSAdrian Hunter return target 10758392b74bSAdrian Hunter 10768392b74bSAdrian Hunter def HasSpace(self, sz): 10778392b74bSAdrian Hunter if self.local_tail <= self.local_head: 10788392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 10798392b74bSAdrian Hunter if space > sz: 10808392b74bSAdrian Hunter return True 10818392b74bSAdrian Hunter if space >= glb_nsz: 10828392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 1083beda0e72STony Jones nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) 10848392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 10858392b74bSAdrian Hunter self.local_head = 0 10868392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 10878392b74bSAdrian Hunter return True 10888392b74bSAdrian Hunter return False 10898392b74bSAdrian Hunter 10908392b74bSAdrian Hunter def WaitForSpace(self, sz): 10918392b74bSAdrian Hunter if self.HasSpace(sz): 10928392b74bSAdrian Hunter return 10938392b74bSAdrian Hunter while True: 10948392b74bSAdrian Hunter self.wait_event.clear() 10958392b74bSAdrian Hunter self.local_tail = self.tail.value 10968392b74bSAdrian Hunter if self.HasSpace(sz): 10978392b74bSAdrian Hunter return 10988392b74bSAdrian Hunter self.wait_event.wait() 10998392b74bSAdrian Hunter 11008392b74bSAdrian Hunter def AddToBuffer(self, obj): 1101beda0e72STony Jones d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) 11028392b74bSAdrian Hunter n = len(d) 1103beda0e72STony Jones nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) 11048392b74bSAdrian Hunter sz = n + glb_nsz 11058392b74bSAdrian Hunter self.WaitForSpace(sz) 11068392b74bSAdrian Hunter pos = self.local_head 11078392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 11088392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 11098392b74bSAdrian Hunter self.local_head += sz 11108392b74bSAdrian Hunter 11118392b74bSAdrian Hunter def FetchBatch(self, batch_size): 11128392b74bSAdrian Hunter fetched = 0 11138392b74bSAdrian Hunter while batch_size > fetched: 11148392b74bSAdrian Hunter obj = self.Next() 11158392b74bSAdrian Hunter if obj is None: 11168392b74bSAdrian Hunter self.more = False 11178392b74bSAdrian Hunter break 11188392b74bSAdrian Hunter self.AddToBuffer(obj) 11198392b74bSAdrian Hunter fetched += 1 11208392b74bSAdrian Hunter if fetched: 11218392b74bSAdrian Hunter self.fetched += fetched 11228392b74bSAdrian Hunter with self.fetch_count.get_lock(): 11238392b74bSAdrian Hunter self.fetch_count.value += fetched 11248392b74bSAdrian Hunter self.head.value = self.local_head 11258392b74bSAdrian Hunter self.fetched_event.set() 11268392b74bSAdrian Hunter 11278392b74bSAdrian Hunter def Run(self): 11288392b74bSAdrian Hunter while self.more: 11298392b74bSAdrian Hunter target = self.WaitForTarget() 11308392b74bSAdrian Hunter if target < 0: 11318392b74bSAdrian Hunter break 11328392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 11338392b74bSAdrian Hunter self.FetchBatch(batch_size) 11348392b74bSAdrian Hunter self.fetching_done.value = True 11358392b74bSAdrian Hunter self.fetched_event.set() 11368392b74bSAdrian Hunter 11378392b74bSAdrian Hunterdef SQLFetcherFn(*x): 11388392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 11398392b74bSAdrian Hunter process.Run() 11408392b74bSAdrian Hunter 11418392b74bSAdrian Hunter# SQL data fetcher 11428392b74bSAdrian Hunter 11438392b74bSAdrian Hunterclass SQLFetcher(QObject): 11448392b74bSAdrian Hunter 11458392b74bSAdrian Hunter done = Signal(object) 11468392b74bSAdrian Hunter 11478392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 11488392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 11498392b74bSAdrian Hunter self.process_data = process_data 11508392b74bSAdrian Hunter self.more = True 11518392b74bSAdrian Hunter self.target = 0 11528392b74bSAdrian Hunter self.last_target = 0 11538392b74bSAdrian Hunter self.fetched = 0 11548392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 11558392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 11568392b74bSAdrian Hunter self.head = Value(c_longlong) 11578392b74bSAdrian Hunter self.tail = Value(c_longlong) 11588392b74bSAdrian Hunter self.local_tail = 0 11598392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 11608392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 11618392b74bSAdrian Hunter self.last_count = 0 11628392b74bSAdrian Hunter self.process_target = Value(c_longlong) 11638392b74bSAdrian Hunter self.wait_event = Event() 11648392b74bSAdrian Hunter self.fetched_event = Event() 11658392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 11668392b74bSAdrian 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)) 11678392b74bSAdrian Hunter self.process.start() 11688392b74bSAdrian Hunter self.thread = Thread(self.Thread) 11698392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 11708392b74bSAdrian Hunter self.thread.start() 11718392b74bSAdrian Hunter 11728392b74bSAdrian Hunter def Shutdown(self): 11738392b74bSAdrian Hunter # Tell the thread and process to exit 11748392b74bSAdrian Hunter self.process_target.value = -1 11758392b74bSAdrian Hunter self.wait_event.set() 11768392b74bSAdrian Hunter self.more = False 11778392b74bSAdrian Hunter self.fetching_done.value = True 11788392b74bSAdrian Hunter self.fetched_event.set() 11798392b74bSAdrian Hunter 11808392b74bSAdrian Hunter def Thread(self): 11818392b74bSAdrian Hunter if not self.more: 11828392b74bSAdrian Hunter return True, 0 11838392b74bSAdrian Hunter while True: 11848392b74bSAdrian Hunter self.fetched_event.clear() 11858392b74bSAdrian Hunter fetch_count = self.fetch_count.value 11868392b74bSAdrian Hunter if fetch_count != self.last_count: 11878392b74bSAdrian Hunter break 11888392b74bSAdrian Hunter if self.fetching_done.value: 11898392b74bSAdrian Hunter self.more = False 11908392b74bSAdrian Hunter return True, 0 11918392b74bSAdrian Hunter self.fetched_event.wait() 11928392b74bSAdrian Hunter count = fetch_count - self.last_count 11938392b74bSAdrian Hunter self.last_count = fetch_count 11948392b74bSAdrian Hunter self.fetched += count 11958392b74bSAdrian Hunter return False, count 11968392b74bSAdrian Hunter 11978392b74bSAdrian Hunter def Fetch(self, nr): 11988392b74bSAdrian Hunter if not self.more: 11998392b74bSAdrian Hunter # -1 inidcates there are no more 12008392b74bSAdrian Hunter return -1 12018392b74bSAdrian Hunter result = self.fetched 12028392b74bSAdrian Hunter extra = result + nr - self.target 12038392b74bSAdrian Hunter if extra > 0: 12048392b74bSAdrian Hunter self.target += extra 12058392b74bSAdrian Hunter # process_target < 0 indicates shutting down 12068392b74bSAdrian Hunter if self.process_target.value >= 0: 12078392b74bSAdrian Hunter self.process_target.value = self.target 12088392b74bSAdrian Hunter self.wait_event.set() 12098392b74bSAdrian Hunter return result 12108392b74bSAdrian Hunter 12118392b74bSAdrian Hunter def RemoveFromBuffer(self): 12128392b74bSAdrian Hunter pos = self.local_tail 12138392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 12148392b74bSAdrian Hunter pos = 0 1215beda0e72STony Jones n = pickle.loads(self.buffer[pos : pos + glb_nsz]) 12168392b74bSAdrian Hunter if n == 0: 12178392b74bSAdrian Hunter pos = 0 1218beda0e72STony Jones n = pickle.loads(self.buffer[0 : glb_nsz]) 12198392b74bSAdrian Hunter pos += glb_nsz 1220beda0e72STony Jones obj = pickle.loads(self.buffer[pos : pos + n]) 12218392b74bSAdrian Hunter self.local_tail = pos + n 12228392b74bSAdrian Hunter return obj 12238392b74bSAdrian Hunter 12248392b74bSAdrian Hunter def ProcessData(self, count): 12258392b74bSAdrian Hunter for i in xrange(count): 12268392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 12278392b74bSAdrian Hunter self.process_data(obj) 12288392b74bSAdrian Hunter self.tail.value = self.local_tail 12298392b74bSAdrian Hunter self.wait_event.set() 12308392b74bSAdrian Hunter self.done.emit(count) 12318392b74bSAdrian Hunter 12328392b74bSAdrian Hunter# Fetch more records bar 12338392b74bSAdrian Hunter 12348392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 12358392b74bSAdrian Hunter 12368392b74bSAdrian Hunter def __init__(self, model, parent): 12378392b74bSAdrian Hunter self.model = model 12388392b74bSAdrian Hunter 12398392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 12408392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12418392b74bSAdrian Hunter 12428392b74bSAdrian Hunter self.fetch_count = QSpinBox() 12438392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 12448392b74bSAdrian Hunter self.fetch_count.setValue(10) 12458392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12468392b74bSAdrian Hunter 12478392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 12488392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 12498392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 12508392b74bSAdrian Hunter 12518392b74bSAdrian Hunter self.progress = QProgressBar() 12528392b74bSAdrian Hunter self.progress.setRange(0, 100) 12538392b74bSAdrian Hunter self.progress.hide() 12548392b74bSAdrian Hunter 12558392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 12568392b74bSAdrian Hunter self.done_label.hide() 12578392b74bSAdrian Hunter 12588392b74bSAdrian Hunter self.spacer = QLabel("") 12598392b74bSAdrian Hunter 12608392b74bSAdrian Hunter self.close_button = QToolButton() 12618392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 12628392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 12638392b74bSAdrian Hunter 12648392b74bSAdrian Hunter self.hbox = QHBoxLayout() 12658392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 12668392b74bSAdrian Hunter 12678392b74bSAdrian Hunter self.hbox.addWidget(self.label) 12688392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 12698392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 12708392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 12718392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 12728392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 12738392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 12748392b74bSAdrian Hunter 12758392b74bSAdrian Hunter self.bar = QWidget() 12768392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 12778392b74bSAdrian Hunter self.bar.show() 12788392b74bSAdrian Hunter 12798392b74bSAdrian Hunter self.in_progress = False 12808392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 12818392b74bSAdrian Hunter 12828392b74bSAdrian Hunter self.done = False 12838392b74bSAdrian Hunter 12848392b74bSAdrian Hunter if not model.HasMoreRecords(): 12858392b74bSAdrian Hunter self.Done() 12868392b74bSAdrian Hunter 12878392b74bSAdrian Hunter def Widget(self): 12888392b74bSAdrian Hunter return self.bar 12898392b74bSAdrian Hunter 12908392b74bSAdrian Hunter def Activate(self): 12918392b74bSAdrian Hunter self.bar.show() 12928392b74bSAdrian Hunter self.fetch.setFocus() 12938392b74bSAdrian Hunter 12948392b74bSAdrian Hunter def Deactivate(self): 12958392b74bSAdrian Hunter self.bar.hide() 12968392b74bSAdrian Hunter 12978392b74bSAdrian Hunter def Enable(self, enable): 12988392b74bSAdrian Hunter self.fetch.setEnabled(enable) 12998392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 13008392b74bSAdrian Hunter 13018392b74bSAdrian Hunter def Busy(self): 13028392b74bSAdrian Hunter self.Enable(False) 13038392b74bSAdrian Hunter self.fetch.hide() 13048392b74bSAdrian Hunter self.spacer.hide() 13058392b74bSAdrian Hunter self.progress.show() 13068392b74bSAdrian Hunter 13078392b74bSAdrian Hunter def Idle(self): 13088392b74bSAdrian Hunter self.in_progress = False 13098392b74bSAdrian Hunter self.Enable(True) 13108392b74bSAdrian Hunter self.progress.hide() 13118392b74bSAdrian Hunter self.fetch.show() 13128392b74bSAdrian Hunter self.spacer.show() 13138392b74bSAdrian Hunter 13148392b74bSAdrian Hunter def Target(self): 13158392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 13168392b74bSAdrian Hunter 13178392b74bSAdrian Hunter def Done(self): 13188392b74bSAdrian Hunter self.done = True 13198392b74bSAdrian Hunter self.Idle() 13208392b74bSAdrian Hunter self.label.hide() 13218392b74bSAdrian Hunter self.fetch_count.hide() 13228392b74bSAdrian Hunter self.fetch.hide() 13238392b74bSAdrian Hunter self.spacer.hide() 13248392b74bSAdrian Hunter self.done_label.show() 13258392b74bSAdrian Hunter 13268392b74bSAdrian Hunter def Progress(self, count): 13278392b74bSAdrian Hunter if self.in_progress: 13288392b74bSAdrian Hunter if count: 13298392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 13308392b74bSAdrian Hunter if percent >= 100: 13318392b74bSAdrian Hunter self.Idle() 13328392b74bSAdrian Hunter else: 13338392b74bSAdrian Hunter self.progress.setValue(percent) 13348392b74bSAdrian Hunter if not count: 13358392b74bSAdrian Hunter # Count value of zero means no more records 13368392b74bSAdrian Hunter self.Done() 13378392b74bSAdrian Hunter 13388392b74bSAdrian Hunter def FetchMoreRecords(self): 13398392b74bSAdrian Hunter if self.done: 13408392b74bSAdrian Hunter return 13418392b74bSAdrian Hunter self.progress.setValue(0) 13428392b74bSAdrian Hunter self.Busy() 13438392b74bSAdrian Hunter self.in_progress = True 13448392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 13458392b74bSAdrian Hunter 134676099f98SAdrian Hunter# Brance data model level two item 134776099f98SAdrian Hunter 134876099f98SAdrian Hunterclass BranchLevelTwoItem(): 134976099f98SAdrian Hunter 135076099f98SAdrian Hunter def __init__(self, row, text, parent_item): 135176099f98SAdrian Hunter self.row = row 135276099f98SAdrian Hunter self.parent_item = parent_item 135376099f98SAdrian Hunter self.data = [""] * 8 135476099f98SAdrian Hunter self.data[7] = text 135576099f98SAdrian Hunter self.level = 2 135676099f98SAdrian Hunter 135776099f98SAdrian Hunter def getParentItem(self): 135876099f98SAdrian Hunter return self.parent_item 135976099f98SAdrian Hunter 136076099f98SAdrian Hunter def getRow(self): 136176099f98SAdrian Hunter return self.row 136276099f98SAdrian Hunter 136376099f98SAdrian Hunter def childCount(self): 136476099f98SAdrian Hunter return 0 136576099f98SAdrian Hunter 136676099f98SAdrian Hunter def hasChildren(self): 136776099f98SAdrian Hunter return False 136876099f98SAdrian Hunter 136976099f98SAdrian Hunter def getData(self, column): 137076099f98SAdrian Hunter return self.data[column] 137176099f98SAdrian Hunter 137276099f98SAdrian Hunter# Brance data model level one item 137376099f98SAdrian Hunter 137476099f98SAdrian Hunterclass BranchLevelOneItem(): 137576099f98SAdrian Hunter 137676099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 137776099f98SAdrian Hunter self.glb = glb 137876099f98SAdrian Hunter self.row = row 137976099f98SAdrian Hunter self.parent_item = parent_item 138076099f98SAdrian Hunter self.child_count = 0 138176099f98SAdrian Hunter self.child_items = [] 138276099f98SAdrian Hunter self.data = data[1:] 138376099f98SAdrian Hunter self.dbid = data[0] 138476099f98SAdrian Hunter self.level = 1 138576099f98SAdrian Hunter self.query_done = False 138676099f98SAdrian Hunter 138776099f98SAdrian Hunter def getChildItem(self, row): 138876099f98SAdrian Hunter return self.child_items[row] 138976099f98SAdrian Hunter 139076099f98SAdrian Hunter def getParentItem(self): 139176099f98SAdrian Hunter return self.parent_item 139276099f98SAdrian Hunter 139376099f98SAdrian Hunter def getRow(self): 139476099f98SAdrian Hunter return self.row 139576099f98SAdrian Hunter 139676099f98SAdrian Hunter def Select(self): 139776099f98SAdrian Hunter self.query_done = True 139876099f98SAdrian Hunter 139976099f98SAdrian Hunter if not self.glb.have_disassembler: 140076099f98SAdrian Hunter return 140176099f98SAdrian Hunter 140276099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 140376099f98SAdrian Hunter 140476099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 140576099f98SAdrian Hunter " FROM samples" 140676099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 140776099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 140876099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 140976099f98SAdrian Hunter if not query.next(): 141076099f98SAdrian Hunter return 141176099f98SAdrian Hunter cpu = query.value(0) 141276099f98SAdrian Hunter dso = query.value(1) 141376099f98SAdrian Hunter sym = query.value(2) 141476099f98SAdrian Hunter if dso == 0 or sym == 0: 141576099f98SAdrian Hunter return 141676099f98SAdrian Hunter off = query.value(3) 141776099f98SAdrian Hunter short_name = query.value(4) 141876099f98SAdrian Hunter long_name = query.value(5) 141976099f98SAdrian Hunter build_id = query.value(6) 142076099f98SAdrian Hunter sym_start = query.value(7) 142176099f98SAdrian Hunter ip = query.value(8) 142276099f98SAdrian Hunter 142376099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 142476099f98SAdrian Hunter " FROM samples" 142576099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 142676099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 142776099f98SAdrian Hunter " ORDER BY samples.id" 142876099f98SAdrian Hunter " LIMIT 1") 142976099f98SAdrian Hunter if not query.next(): 143076099f98SAdrian Hunter return 143176099f98SAdrian Hunter if query.value(0) != dso: 143276099f98SAdrian Hunter # Cannot disassemble from one dso to another 143376099f98SAdrian Hunter return 143476099f98SAdrian Hunter bsym = query.value(1) 143576099f98SAdrian Hunter boff = query.value(2) 143676099f98SAdrian Hunter bsym_start = query.value(3) 143776099f98SAdrian Hunter if bsym == 0: 143876099f98SAdrian Hunter return 143976099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 144076099f98SAdrian Hunter if tot <= 0 or tot > 16384: 144176099f98SAdrian Hunter return 144276099f98SAdrian Hunter 144376099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 144476099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 144576099f98SAdrian Hunter if not f: 144676099f98SAdrian Hunter return 144776099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 144876099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 144976099f98SAdrian Hunter 145076099f98SAdrian Hunter buf_sz = tot + 16 145176099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 145276099f98SAdrian Hunter f.seek(sym_start + off) 145376099f98SAdrian Hunter buf.value = f.read(buf_sz) 145476099f98SAdrian Hunter buf_ptr = addressof(buf) 145576099f98SAdrian Hunter i = 0 145676099f98SAdrian Hunter while tot > 0: 145776099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 145876099f98SAdrian Hunter if cnt: 145976099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 146076099f98SAdrian Hunter for k in xrange(cnt): 146176099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 146276099f98SAdrian Hunter i += 1 146376099f98SAdrian Hunter while k < 15: 146476099f98SAdrian Hunter byte_str += " " 146576099f98SAdrian Hunter k += 1 146676099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 146776099f98SAdrian Hunter self.child_count += 1 146876099f98SAdrian Hunter else: 146976099f98SAdrian Hunter return 147076099f98SAdrian Hunter buf_ptr += cnt 147176099f98SAdrian Hunter tot -= cnt 147276099f98SAdrian Hunter buf_sz -= cnt 147376099f98SAdrian Hunter ip += cnt 147476099f98SAdrian Hunter 147576099f98SAdrian Hunter def childCount(self): 147676099f98SAdrian Hunter if not self.query_done: 147776099f98SAdrian Hunter self.Select() 147876099f98SAdrian Hunter if not self.child_count: 147976099f98SAdrian Hunter return -1 148076099f98SAdrian Hunter return self.child_count 148176099f98SAdrian Hunter 148276099f98SAdrian Hunter def hasChildren(self): 148376099f98SAdrian Hunter if not self.query_done: 148476099f98SAdrian Hunter return True 148576099f98SAdrian Hunter return self.child_count > 0 148676099f98SAdrian Hunter 148776099f98SAdrian Hunter def getData(self, column): 148876099f98SAdrian Hunter return self.data[column] 148976099f98SAdrian Hunter 149076099f98SAdrian Hunter# Brance data model root item 149176099f98SAdrian Hunter 149276099f98SAdrian Hunterclass BranchRootItem(): 149376099f98SAdrian Hunter 149476099f98SAdrian Hunter def __init__(self): 149576099f98SAdrian Hunter self.child_count = 0 149676099f98SAdrian Hunter self.child_items = [] 149776099f98SAdrian Hunter self.level = 0 149876099f98SAdrian Hunter 149976099f98SAdrian Hunter def getChildItem(self, row): 150076099f98SAdrian Hunter return self.child_items[row] 150176099f98SAdrian Hunter 150276099f98SAdrian Hunter def getParentItem(self): 150376099f98SAdrian Hunter return None 150476099f98SAdrian Hunter 150576099f98SAdrian Hunter def getRow(self): 150676099f98SAdrian Hunter return 0 150776099f98SAdrian Hunter 150876099f98SAdrian Hunter def childCount(self): 150976099f98SAdrian Hunter return self.child_count 151076099f98SAdrian Hunter 151176099f98SAdrian Hunter def hasChildren(self): 151276099f98SAdrian Hunter return self.child_count > 0 151376099f98SAdrian Hunter 151476099f98SAdrian Hunter def getData(self, column): 151576099f98SAdrian Hunter return "" 151676099f98SAdrian Hunter 151776099f98SAdrian Hunter# Branch data preparation 151876099f98SAdrian Hunter 151976099f98SAdrian Hunterdef BranchDataPrep(query): 152076099f98SAdrian Hunter data = [] 152176099f98SAdrian Hunter for i in xrange(0, 8): 152276099f98SAdrian Hunter data.append(query.value(i)) 152376099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 152476099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 152576099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 152676099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 152776099f98SAdrian Hunter return data 152876099f98SAdrian Hunter 15298453c936SAdrian Hunterdef BranchDataPrepWA(query): 15308453c936SAdrian Hunter data = [] 15318453c936SAdrian Hunter data.append(query.value(0)) 15328453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 15338453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 15348453c936SAdrian Hunter for i in xrange(2, 8): 15358453c936SAdrian Hunter data.append(query.value(i)) 15368453c936SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 15378453c936SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 15388453c936SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 15398453c936SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 15408453c936SAdrian Hunter return data 15418453c936SAdrian Hunter 154276099f98SAdrian Hunter# Branch data model 154376099f98SAdrian Hunter 154476099f98SAdrian Hunterclass BranchModel(TreeModel): 154576099f98SAdrian Hunter 154676099f98SAdrian Hunter progress = Signal(object) 154776099f98SAdrian Hunter 154876099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 1549a448ba23SAdrian Hunter super(BranchModel, self).__init__(glb, parent) 155076099f98SAdrian Hunter self.event_id = event_id 155176099f98SAdrian Hunter self.more = True 155276099f98SAdrian Hunter self.populated = 0 155376099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 155476099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 155576099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 155676099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 155776099f98SAdrian Hunter " FROM samples" 155876099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 155976099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 156076099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 156176099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 156276099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 156376099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 156476099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 156576099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 156676099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 156776099f98SAdrian Hunter " ORDER BY samples.id" 156876099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 15698453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 15708453c936SAdrian Hunter prep = BranchDataPrepWA 15718453c936SAdrian Hunter else: 15728453c936SAdrian Hunter prep = BranchDataPrep 15738453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) 157476099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 157576099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 157676099f98SAdrian Hunter 1577a448ba23SAdrian Hunter def GetRoot(self): 1578a448ba23SAdrian Hunter return BranchRootItem() 1579a448ba23SAdrian Hunter 158076099f98SAdrian Hunter def columnCount(self, parent=None): 158176099f98SAdrian Hunter return 8 158276099f98SAdrian Hunter 158376099f98SAdrian Hunter def columnHeader(self, column): 158476099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 158576099f98SAdrian Hunter 158676099f98SAdrian Hunter def columnFont(self, column): 158776099f98SAdrian Hunter if column != 7: 158876099f98SAdrian Hunter return None 158976099f98SAdrian Hunter return QFont("Monospace") 159076099f98SAdrian Hunter 159176099f98SAdrian Hunter def DisplayData(self, item, index): 159276099f98SAdrian Hunter if item.level == 1: 159376099f98SAdrian Hunter self.FetchIfNeeded(item.row) 159476099f98SAdrian Hunter return item.getData(index.column()) 159576099f98SAdrian Hunter 159676099f98SAdrian Hunter def AddSample(self, data): 159776099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 159876099f98SAdrian Hunter self.root.child_items.append(child) 159976099f98SAdrian Hunter self.populated += 1 160076099f98SAdrian Hunter 160176099f98SAdrian Hunter def Update(self, fetched): 160276099f98SAdrian Hunter if not fetched: 160376099f98SAdrian Hunter self.more = False 160476099f98SAdrian Hunter self.progress.emit(0) 160576099f98SAdrian Hunter child_count = self.root.child_count 160676099f98SAdrian Hunter count = self.populated - child_count 160776099f98SAdrian Hunter if count > 0: 160876099f98SAdrian Hunter parent = QModelIndex() 160976099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 161076099f98SAdrian Hunter self.insertRows(child_count, count, parent) 161176099f98SAdrian Hunter self.root.child_count += count 161276099f98SAdrian Hunter self.endInsertRows() 161376099f98SAdrian Hunter self.progress.emit(self.root.child_count) 161476099f98SAdrian Hunter 161576099f98SAdrian Hunter def FetchMoreRecords(self, count): 161676099f98SAdrian Hunter current = self.root.child_count 161776099f98SAdrian Hunter if self.more: 161876099f98SAdrian Hunter self.fetcher.Fetch(count) 161976099f98SAdrian Hunter else: 162076099f98SAdrian Hunter self.progress.emit(0) 162176099f98SAdrian Hunter return current 162276099f98SAdrian Hunter 162376099f98SAdrian Hunter def HasMoreRecords(self): 162476099f98SAdrian Hunter return self.more 162576099f98SAdrian Hunter 16260bf0947aSAdrian Hunter# Report Variables 16270bf0947aSAdrian Hunter 16280bf0947aSAdrian Hunterclass ReportVars(): 16290bf0947aSAdrian Hunter 1630cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1631947cc38dSAdrian Hunter self.name = name 16320bf0947aSAdrian Hunter self.where_clause = where_clause 1633cd358012SAdrian Hunter self.limit = limit 16340bf0947aSAdrian Hunter 16350bf0947aSAdrian Hunter def UniqueId(self): 1636cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 16370bf0947aSAdrian Hunter 163876099f98SAdrian Hunter# Branch window 163976099f98SAdrian Hunter 164076099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 164176099f98SAdrian Hunter 1642947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 164376099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 164476099f98SAdrian Hunter 16450bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 164676099f98SAdrian Hunter 16470bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 164876099f98SAdrian Hunter 164976099f98SAdrian Hunter self.view = QTreeView() 165076099f98SAdrian Hunter self.view.setUniformRowHeights(True) 165176099f98SAdrian Hunter self.view.setModel(self.model) 165276099f98SAdrian Hunter 165376099f98SAdrian Hunter self.ResizeColumnsToContents() 165476099f98SAdrian Hunter 165576099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 165676099f98SAdrian Hunter 165776099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 165876099f98SAdrian Hunter 165976099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 166076099f98SAdrian Hunter 166176099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 166276099f98SAdrian Hunter 166376099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 166476099f98SAdrian Hunter 1665947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 166676099f98SAdrian Hunter 166776099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 166876099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 166976099f98SAdrian Hunter # so implement a crude alternative 167076099f98SAdrian Hunter mm = "MM" if column else "MMMM" 167176099f98SAdrian Hunter font = self.view.font() 167276099f98SAdrian Hunter metrics = QFontMetrics(font) 167376099f98SAdrian Hunter max = 0 167476099f98SAdrian Hunter for row in xrange(n): 167576099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 167676099f98SAdrian Hunter len = metrics.width(str(val) + mm) 167776099f98SAdrian Hunter max = len if len > max else max 167876099f98SAdrian Hunter val = self.model.columnHeader(column) 167976099f98SAdrian Hunter len = metrics.width(str(val) + mm) 168076099f98SAdrian Hunter max = len if len > max else max 168176099f98SAdrian Hunter self.view.setColumnWidth(column, max) 168276099f98SAdrian Hunter 168376099f98SAdrian Hunter def ResizeColumnsToContents(self): 168476099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 168576099f98SAdrian Hunter if n < 1: 168676099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 168776099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 168876099f98SAdrian Hunter return 168976099f98SAdrian Hunter columns = self.model.columnCount() 169076099f98SAdrian Hunter for i in xrange(columns): 169176099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 169276099f98SAdrian Hunter 169376099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 169476099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 169576099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 169676099f98SAdrian Hunter self.ResizeColumnsToContents() 169776099f98SAdrian Hunter 169876099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 169976099f98SAdrian Hunter self.view.setFocus() 170076099f98SAdrian Hunter self.find_bar.Busy() 170176099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 170276099f98SAdrian Hunter 170376099f98SAdrian Hunter def FindDone(self, row): 170476099f98SAdrian Hunter self.find_bar.Idle() 170576099f98SAdrian Hunter if row >= 0: 170676099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 170776099f98SAdrian Hunter else: 170876099f98SAdrian Hunter self.find_bar.NotFound() 170976099f98SAdrian Hunter 17101c3ca1b3SAdrian Hunter# Line edit data item 17111c3ca1b3SAdrian Hunter 17121c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 17131c3ca1b3SAdrian Hunter 1714cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 17151c3ca1b3SAdrian Hunter self.glb = glb 17161c3ca1b3SAdrian Hunter self.label = label 17171c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 17181c3ca1b3SAdrian Hunter self.parent = parent 17191c3ca1b3SAdrian Hunter self.id = id 17201c3ca1b3SAdrian Hunter 1721cd358012SAdrian Hunter self.value = default 17221c3ca1b3SAdrian Hunter 1723cd358012SAdrian Hunter self.widget = QLineEdit(default) 17241c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 17251c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 17261c3ca1b3SAdrian Hunter self.red = False 17271c3ca1b3SAdrian Hunter self.error = "" 17281c3ca1b3SAdrian Hunter self.validated = True 17291c3ca1b3SAdrian Hunter 17301c3ca1b3SAdrian Hunter if placeholder_text: 17311c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 17321c3ca1b3SAdrian Hunter 17331c3ca1b3SAdrian Hunter def TurnTextRed(self): 17341c3ca1b3SAdrian Hunter if not self.red: 17351c3ca1b3SAdrian Hunter palette = QPalette() 17361c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 17371c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17381c3ca1b3SAdrian Hunter self.red = True 17391c3ca1b3SAdrian Hunter 17401c3ca1b3SAdrian Hunter def TurnTextNormal(self): 17411c3ca1b3SAdrian Hunter if self.red: 17421c3ca1b3SAdrian Hunter palette = QPalette() 17431c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 17441c3ca1b3SAdrian Hunter self.red = False 17451c3ca1b3SAdrian Hunter 17461c3ca1b3SAdrian Hunter def InvalidValue(self, value): 17471c3ca1b3SAdrian Hunter self.value = "" 17481c3ca1b3SAdrian Hunter self.TurnTextRed() 17491c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 17501c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 17511c3ca1b3SAdrian Hunter 17521c3ca1b3SAdrian Hunter def Invalidate(self): 17531c3ca1b3SAdrian Hunter self.validated = False 17541c3ca1b3SAdrian Hunter 17551c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 17561c3ca1b3SAdrian Hunter self.value = input_string.strip() 17571c3ca1b3SAdrian Hunter 17581c3ca1b3SAdrian Hunter def Validate(self): 17591c3ca1b3SAdrian Hunter self.validated = True 17601c3ca1b3SAdrian Hunter self.error = "" 17611c3ca1b3SAdrian Hunter self.TurnTextNormal() 17621c3ca1b3SAdrian Hunter self.parent.ClearMessage() 17631c3ca1b3SAdrian Hunter input_string = self.widget.text() 17641c3ca1b3SAdrian Hunter if not len(input_string.strip()): 17651c3ca1b3SAdrian Hunter self.value = "" 17661c3ca1b3SAdrian Hunter return 17671c3ca1b3SAdrian Hunter self.DoValidate(input_string) 17681c3ca1b3SAdrian Hunter 17691c3ca1b3SAdrian Hunter def IsValid(self): 17701c3ca1b3SAdrian Hunter if not self.validated: 17711c3ca1b3SAdrian Hunter self.Validate() 17721c3ca1b3SAdrian Hunter if len(self.error): 17731c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 17741c3ca1b3SAdrian Hunter return False 17751c3ca1b3SAdrian Hunter return True 17761c3ca1b3SAdrian Hunter 17771c3ca1b3SAdrian Hunter def IsNumber(self, value): 17781c3ca1b3SAdrian Hunter try: 17791c3ca1b3SAdrian Hunter x = int(value) 17801c3ca1b3SAdrian Hunter except: 17811c3ca1b3SAdrian Hunter x = 0 17821c3ca1b3SAdrian Hunter return str(x) == value 17831c3ca1b3SAdrian Hunter 17841c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 17851c3ca1b3SAdrian Hunter 17861c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 17871c3ca1b3SAdrian Hunter 17881c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 17891c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 17901c3ca1b3SAdrian Hunter 17911c3ca1b3SAdrian Hunter self.column_name = column_name 17921c3ca1b3SAdrian Hunter 17931c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 17941c3ca1b3SAdrian Hunter singles = [] 17951c3ca1b3SAdrian Hunter ranges = [] 17961c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 17971c3ca1b3SAdrian Hunter if "-" in value: 17981c3ca1b3SAdrian Hunter vrange = value.split("-") 17991c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 18001c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18011c3ca1b3SAdrian Hunter ranges.append(vrange) 18021c3ca1b3SAdrian Hunter else: 18031c3ca1b3SAdrian Hunter if not self.IsNumber(value): 18041c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18051c3ca1b3SAdrian Hunter singles.append(value) 18061c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 18071c3ca1b3SAdrian Hunter if len(singles): 18081c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 18091c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 18101c3ca1b3SAdrian Hunter 1811cd358012SAdrian Hunter# Positive integer dialog data item 1812cd358012SAdrian Hunter 1813cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1814cd358012SAdrian Hunter 1815cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1816cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1817cd358012SAdrian Hunter 1818cd358012SAdrian Hunter def DoValidate(self, input_string): 1819cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1820cd358012SAdrian Hunter return self.InvalidValue(input_string) 1821cd358012SAdrian Hunter value = int(input_string.strip()) 1822cd358012SAdrian Hunter if value <= 0: 1823cd358012SAdrian Hunter return self.InvalidValue(input_string) 1824cd358012SAdrian Hunter self.value = str(value) 1825cd358012SAdrian Hunter 18261c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 18271c3ca1b3SAdrian Hunter 18281c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 18291c3ca1b3SAdrian Hunter 18301c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 18311c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 18321c3ca1b3SAdrian Hunter 18331c3ca1b3SAdrian Hunter self.table_name = table_name 18341c3ca1b3SAdrian Hunter self.match_column = match_column 18351c3ca1b3SAdrian Hunter self.column_name1 = column_name1 18361c3ca1b3SAdrian Hunter self.column_name2 = column_name2 18371c3ca1b3SAdrian Hunter 18381c3ca1b3SAdrian Hunter def ValueToIds(self, value): 18391c3ca1b3SAdrian Hunter ids = [] 18401c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 18411c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 18421c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 18431c3ca1b3SAdrian Hunter if ret: 18441c3ca1b3SAdrian Hunter while query.next(): 18451c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 18461c3ca1b3SAdrian Hunter return ids 18471c3ca1b3SAdrian Hunter 18481c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 18491c3ca1b3SAdrian Hunter all_ids = [] 18501c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 18511c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 18521c3ca1b3SAdrian Hunter if len(ids): 18531c3ca1b3SAdrian Hunter all_ids.extend(ids) 18541c3ca1b3SAdrian Hunter else: 18551c3ca1b3SAdrian Hunter return self.InvalidValue(value) 18561c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 18571c3ca1b3SAdrian Hunter if self.column_name2: 18581c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 18591c3ca1b3SAdrian Hunter 18601c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 18611c3ca1b3SAdrian Hunter 18621c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 18631c3ca1b3SAdrian Hunter 18641c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 18651c3ca1b3SAdrian Hunter self.column_name = column_name 18661c3ca1b3SAdrian Hunter 18671c3ca1b3SAdrian Hunter self.last_id = 0 18681c3ca1b3SAdrian Hunter self.first_time = 0 18691c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 18701c3ca1b3SAdrian Hunter 18711c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 18721c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 18731c3ca1b3SAdrian Hunter if query.next(): 18741c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 18751c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 18761c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 18771c3ca1b3SAdrian Hunter if query.next(): 18781c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 18791c3ca1b3SAdrian Hunter if placeholder_text: 18801c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 18811c3ca1b3SAdrian Hunter 18821c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 18831c3ca1b3SAdrian Hunter 18841c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 18851c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 18861c3ca1b3SAdrian Hunter if query.next(): 18871c3ca1b3SAdrian Hunter return True, int(query.value(0)) 18881c3ca1b3SAdrian Hunter else: 18891c3ca1b3SAdrian Hunter return False, 0 18901c3ca1b3SAdrian Hunter 18911c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 18921c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 18931c3ca1b3SAdrian Hunter while True: 18941c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 18951c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 18961c3ca1b3SAdrian Hunter if not query.next(): 18971c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 18981c3ca1b3SAdrian Hunter if not ok: 18991c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 19001c3ca1b3SAdrian Hunter if not ok: 19011c3ca1b3SAdrian Hunter return str(higher_id) 19021c3ca1b3SAdrian Hunter next_id = dbid 19031c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 19041c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 19051c3ca1b3SAdrian Hunter if get_floor: 19061c3ca1b3SAdrian Hunter if target_time > next_time: 19071c3ca1b3SAdrian Hunter lower_id = next_id 19081c3ca1b3SAdrian Hunter else: 19091c3ca1b3SAdrian Hunter higher_id = next_id 19101c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19111c3ca1b3SAdrian Hunter return str(higher_id) 19121c3ca1b3SAdrian Hunter else: 19131c3ca1b3SAdrian Hunter if target_time >= next_time: 19141c3ca1b3SAdrian Hunter lower_id = next_id 19151c3ca1b3SAdrian Hunter else: 19161c3ca1b3SAdrian Hunter higher_id = next_id 19171c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 19181c3ca1b3SAdrian Hunter return str(lower_id) 19191c3ca1b3SAdrian Hunter 19201c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 19211c3ca1b3SAdrian Hunter mult = 1 19221c3ca1b3SAdrian Hunter suffix = val[-2:] 19231c3ca1b3SAdrian Hunter if suffix == "ms": 19241c3ca1b3SAdrian Hunter mult = 1000000 19251c3ca1b3SAdrian Hunter elif suffix == "us": 19261c3ca1b3SAdrian Hunter mult = 1000 19271c3ca1b3SAdrian Hunter elif suffix == "ns": 19281c3ca1b3SAdrian Hunter mult = 1 19291c3ca1b3SAdrian Hunter else: 19301c3ca1b3SAdrian Hunter return val 19311c3ca1b3SAdrian Hunter val = val[:-2].strip() 19321c3ca1b3SAdrian Hunter if not self.IsNumber(val): 19331c3ca1b3SAdrian Hunter return val 19341c3ca1b3SAdrian Hunter val = int(val) * mult 19351c3ca1b3SAdrian Hunter if val >= 0: 19361c3ca1b3SAdrian Hunter val += self.first_time 19371c3ca1b3SAdrian Hunter else: 19381c3ca1b3SAdrian Hunter val += self.last_time 19391c3ca1b3SAdrian Hunter return str(val) 19401c3ca1b3SAdrian Hunter 19411c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 19421c3ca1b3SAdrian Hunter if vrange[0] == "": 19431c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 19441c3ca1b3SAdrian Hunter if vrange[1] == "": 19451c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 19461c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 19471c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 19481c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 19491c3ca1b3SAdrian Hunter return False 19501c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 19511c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 19521c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 19531c3ca1b3SAdrian Hunter return False 19541c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 19551c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 19561c3ca1b3SAdrian Hunter return True 19571c3ca1b3SAdrian Hunter 19581c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 19591c3ca1b3SAdrian Hunter n = value.count("-") 19601c3ca1b3SAdrian Hunter if n == 1: 19611c3ca1b3SAdrian Hunter pass 19621c3ca1b3SAdrian Hunter elif n == 2: 19631c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 19641c3ca1b3SAdrian Hunter n = 1 19651c3ca1b3SAdrian Hunter elif n == 3: 19661c3ca1b3SAdrian Hunter n = 2 19671c3ca1b3SAdrian Hunter else: 19681c3ca1b3SAdrian Hunter return False 19691c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 19701c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 19711c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 19721c3ca1b3SAdrian Hunter ranges.append(vrange) 19731c3ca1b3SAdrian Hunter return True 19741c3ca1b3SAdrian Hunter return False 19751c3ca1b3SAdrian Hunter 19761c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 19771c3ca1b3SAdrian Hunter ranges = [] 19781c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 19791c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 19801c3ca1b3SAdrian Hunter return self.InvalidValue(value) 19811c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 19821c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 19831c3ca1b3SAdrian Hunter 19840924cd68SAdrian Hunter# Report Dialog Base 1985210cf1f9SAdrian Hunter 19860924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 1987210cf1f9SAdrian Hunter 19880924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 19890924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 1990210cf1f9SAdrian Hunter 1991210cf1f9SAdrian Hunter self.glb = glb 1992210cf1f9SAdrian Hunter 19930bf0947aSAdrian Hunter self.report_vars = ReportVars() 1994210cf1f9SAdrian Hunter 19950924cd68SAdrian Hunter self.setWindowTitle(title) 1996210cf1f9SAdrian Hunter self.setMinimumWidth(600) 1997210cf1f9SAdrian Hunter 19981c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 1999210cf1f9SAdrian Hunter 20000924cd68SAdrian Hunter self.partial = partial 20010924cd68SAdrian Hunter 2002210cf1f9SAdrian Hunter self.grid = QGridLayout() 2003210cf1f9SAdrian Hunter 2004210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 2005210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 2006210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 2007210cf1f9SAdrian Hunter 2008210cf1f9SAdrian Hunter self.status = QLabel() 2009210cf1f9SAdrian Hunter 2010210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 2011210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 2012210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 2013210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2014210cf1f9SAdrian Hunter 2015210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 2016210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 2017210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 2018210cf1f9SAdrian Hunter 2019210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 2020210cf1f9SAdrian Hunter #self.hbox.addStretch() 2021210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 2022210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 2023210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 2024210cf1f9SAdrian Hunter 2025210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 2026210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 2027210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 2028210cf1f9SAdrian Hunter 2029210cf1f9SAdrian Hunter self.setLayout(self.vbox); 2030210cf1f9SAdrian Hunter 2031210cf1f9SAdrian Hunter def Ok(self): 20320bf0947aSAdrian Hunter vars = self.report_vars 20331c3ca1b3SAdrian Hunter for d in self.data_items: 20341c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 20351c3ca1b3SAdrian Hunter vars.name = d.value 2036947cc38dSAdrian Hunter if not vars.name: 2037210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 2038210cf1f9SAdrian Hunter return 2039210cf1f9SAdrian Hunter for d in self.data_items: 2040210cf1f9SAdrian Hunter if not d.IsValid(): 2041210cf1f9SAdrian Hunter return 2042210cf1f9SAdrian Hunter for d in self.data_items[1:]: 2043cd358012SAdrian Hunter if d.id == "LIMIT": 2044cd358012SAdrian Hunter vars.limit = d.value 2045cd358012SAdrian Hunter elif len(d.value): 20460bf0947aSAdrian Hunter if len(vars.where_clause): 20470bf0947aSAdrian Hunter vars.where_clause += " AND " 20480bf0947aSAdrian Hunter vars.where_clause += d.value 20490bf0947aSAdrian Hunter if len(vars.where_clause): 20500924cd68SAdrian Hunter if self.partial: 20510bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 2052210cf1f9SAdrian Hunter else: 20530bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 2054210cf1f9SAdrian Hunter self.accept() 2055210cf1f9SAdrian Hunter 2056210cf1f9SAdrian Hunter def ShowMessage(self, msg): 2057210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 2058210cf1f9SAdrian Hunter 2059210cf1f9SAdrian Hunter def ClearMessage(self): 2060210cf1f9SAdrian Hunter self.status.setText("") 2061210cf1f9SAdrian Hunter 20620924cd68SAdrian Hunter# Selected branch report creation dialog 20630924cd68SAdrian Hunter 20640924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 20650924cd68SAdrian Hunter 20660924cd68SAdrian Hunter def __init__(self, glb, parent=None): 20670924cd68SAdrian Hunter title = "Selected Branches" 20681c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 20691c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 20701c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 20711c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 20721c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 20731c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 20741c3ca1b3SAdrian 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), 20751c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 20761c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 20770924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 20780924cd68SAdrian Hunter 207976099f98SAdrian Hunter# Event list 208076099f98SAdrian Hunter 208176099f98SAdrian Hunterdef GetEventList(db): 208276099f98SAdrian Hunter events = [] 208376099f98SAdrian Hunter query = QSqlQuery(db) 208476099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 208576099f98SAdrian Hunter while query.next(): 208676099f98SAdrian Hunter events.append(query.value(0)) 208776099f98SAdrian Hunter return events 208876099f98SAdrian Hunter 2089655cb952SAdrian Hunter# Is a table selectable 2090655cb952SAdrian Hunter 2091ae8b887cSAdrian Hunterdef IsSelectable(db, table, sql = ""): 2092655cb952SAdrian Hunter query = QSqlQuery(db) 2093655cb952SAdrian Hunter try: 2094ae8b887cSAdrian Hunter QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") 2095655cb952SAdrian Hunter except: 2096655cb952SAdrian Hunter return False 2097655cb952SAdrian Hunter return True 2098655cb952SAdrian Hunter 20998392b74bSAdrian Hunter# SQL table data model item 21008392b74bSAdrian Hunter 21018392b74bSAdrian Hunterclass SQLTableItem(): 21028392b74bSAdrian Hunter 21038392b74bSAdrian Hunter def __init__(self, row, data): 21048392b74bSAdrian Hunter self.row = row 21058392b74bSAdrian Hunter self.data = data 21068392b74bSAdrian Hunter 21078392b74bSAdrian Hunter def getData(self, column): 21088392b74bSAdrian Hunter return self.data[column] 21098392b74bSAdrian Hunter 21108392b74bSAdrian Hunter# SQL table data model 21118392b74bSAdrian Hunter 21128392b74bSAdrian Hunterclass SQLTableModel(TableModel): 21138392b74bSAdrian Hunter 21148392b74bSAdrian Hunter progress = Signal(object) 21158392b74bSAdrian Hunter 21168c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 21178392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 21188392b74bSAdrian Hunter self.glb = glb 21198392b74bSAdrian Hunter self.more = True 21208392b74bSAdrian Hunter self.populated = 0 21218c90fef9SAdrian Hunter self.column_headers = column_headers 21228453c936SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): self.SQLTableDataPrep(x, y), self.AddSample) 21238392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 21248392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 21258392b74bSAdrian Hunter 21268392b74bSAdrian Hunter def DisplayData(self, item, index): 21278392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 21288392b74bSAdrian Hunter return item.getData(index.column()) 21298392b74bSAdrian Hunter 21308392b74bSAdrian Hunter def AddSample(self, data): 21318392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 21328392b74bSAdrian Hunter self.child_items.append(child) 21338392b74bSAdrian Hunter self.populated += 1 21348392b74bSAdrian Hunter 21358392b74bSAdrian Hunter def Update(self, fetched): 21368392b74bSAdrian Hunter if not fetched: 21378392b74bSAdrian Hunter self.more = False 21388392b74bSAdrian Hunter self.progress.emit(0) 21398392b74bSAdrian Hunter child_count = self.child_count 21408392b74bSAdrian Hunter count = self.populated - child_count 21418392b74bSAdrian Hunter if count > 0: 21428392b74bSAdrian Hunter parent = QModelIndex() 21438392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 21448392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 21458392b74bSAdrian Hunter self.child_count += count 21468392b74bSAdrian Hunter self.endInsertRows() 21478392b74bSAdrian Hunter self.progress.emit(self.child_count) 21488392b74bSAdrian Hunter 21498392b74bSAdrian Hunter def FetchMoreRecords(self, count): 21508392b74bSAdrian Hunter current = self.child_count 21518392b74bSAdrian Hunter if self.more: 21528392b74bSAdrian Hunter self.fetcher.Fetch(count) 21538392b74bSAdrian Hunter else: 21548392b74bSAdrian Hunter self.progress.emit(0) 21558392b74bSAdrian Hunter return current 21568392b74bSAdrian Hunter 21578392b74bSAdrian Hunter def HasMoreRecords(self): 21588392b74bSAdrian Hunter return self.more 21598392b74bSAdrian Hunter 21608c90fef9SAdrian Hunter def columnCount(self, parent=None): 21618c90fef9SAdrian Hunter return len(self.column_headers) 21628c90fef9SAdrian Hunter 21638c90fef9SAdrian Hunter def columnHeader(self, column): 21648c90fef9SAdrian Hunter return self.column_headers[column] 21658c90fef9SAdrian Hunter 21668453c936SAdrian Hunter def SQLTableDataPrep(self, query, count): 21678453c936SAdrian Hunter data = [] 21688453c936SAdrian Hunter for i in xrange(count): 21698453c936SAdrian Hunter data.append(query.value(i)) 21708453c936SAdrian Hunter return data 21718453c936SAdrian Hunter 21728392b74bSAdrian Hunter# SQL automatic table data model 21738392b74bSAdrian Hunter 21748392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 21758392b74bSAdrian Hunter 21768392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 21778392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 21788392b74bSAdrian Hunter if table_name == "comm_threads_view": 21798392b74bSAdrian Hunter # For now, comm_threads_view has no id column 21808392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 21818c90fef9SAdrian Hunter column_headers = [] 21828392b74bSAdrian Hunter query = QSqlQuery(glb.db) 21838392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 21848392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 21858392b74bSAdrian Hunter while query.next(): 21868c90fef9SAdrian Hunter column_headers.append(query.value(1)) 21878392b74bSAdrian Hunter if table_name == "sqlite_master": 21888392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 21898392b74bSAdrian Hunter else: 21908392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 21918392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 21928392b74bSAdrian Hunter select_table_name = table_name[19:] 21938392b74bSAdrian Hunter schema = "information_schema" 21948392b74bSAdrian Hunter else: 21958392b74bSAdrian Hunter select_table_name = table_name 21968392b74bSAdrian Hunter schema = "public" 21978392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 21988392b74bSAdrian Hunter while query.next(): 21998c90fef9SAdrian Hunter column_headers.append(query.value(0)) 22008453c936SAdrian Hunter if pyside_version_1 and sys.version_info[0] == 3: 22018453c936SAdrian Hunter if table_name == "samples_view": 22028453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_view_DataPrep 22038453c936SAdrian Hunter if table_name == "samples": 22048453c936SAdrian Hunter self.SQLTableDataPrep = self.samples_DataPrep 22058c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 22068392b74bSAdrian Hunter 22078453c936SAdrian Hunter def samples_view_DataPrep(self, query, count): 22088453c936SAdrian Hunter data = [] 22098453c936SAdrian Hunter data.append(query.value(0)) 22108453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22118453c936SAdrian Hunter data.append("{:>19}".format(query.value(1))) 22128453c936SAdrian Hunter for i in xrange(2, count): 22138453c936SAdrian Hunter data.append(query.value(i)) 22148453c936SAdrian Hunter return data 22158453c936SAdrian Hunter 22168453c936SAdrian Hunter def samples_DataPrep(self, query, count): 22178453c936SAdrian Hunter data = [] 22188453c936SAdrian Hunter for i in xrange(9): 22198453c936SAdrian Hunter data.append(query.value(i)) 22208453c936SAdrian Hunter # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string 22218453c936SAdrian Hunter data.append("{:>19}".format(query.value(9))) 22228453c936SAdrian Hunter for i in xrange(10, count): 22238453c936SAdrian Hunter data.append(query.value(i)) 22248453c936SAdrian Hunter return data 22258453c936SAdrian Hunter 22268392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 22278392b74bSAdrian Hunter 22288392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 22298392b74bSAdrian Hunter 22308392b74bSAdrian Hunter def __init__(self, parent=None): 22318392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 22328392b74bSAdrian Hunter 22338392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 22348392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 22358392b74bSAdrian Hunter # so implement a crude alternative 22368392b74bSAdrian Hunter font = self.view.font() 22378392b74bSAdrian Hunter metrics = QFontMetrics(font) 22388392b74bSAdrian Hunter max = 0 22398392b74bSAdrian Hunter for row in xrange(n): 22408392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 22418392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22428392b74bSAdrian Hunter max = len if len > max else max 22438392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 22448392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 22458392b74bSAdrian Hunter max = len if len > max else max 22468392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 22478392b74bSAdrian Hunter 22488392b74bSAdrian Hunter def ResizeColumnsToContents(self): 22498392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 22508392b74bSAdrian Hunter if n < 1: 22518392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 22528392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 22538392b74bSAdrian Hunter return 22548392b74bSAdrian Hunter columns = self.data_model.columnCount() 22558392b74bSAdrian Hunter for i in xrange(columns): 22568392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 22578392b74bSAdrian Hunter 22588392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 22598392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 22608392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 22618392b74bSAdrian Hunter self.ResizeColumnsToContents() 22628392b74bSAdrian Hunter 22638392b74bSAdrian Hunter# Table window 22648392b74bSAdrian Hunter 22658392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 22668392b74bSAdrian Hunter 22678392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 22688392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 22698392b74bSAdrian Hunter 22708392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 22718392b74bSAdrian Hunter 22728392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 22738392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 22748392b74bSAdrian Hunter 22758392b74bSAdrian Hunter self.view = QTableView() 22768392b74bSAdrian Hunter self.view.setModel(self.model) 22778392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 22788392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 22798392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 22808392b74bSAdrian Hunter self.view.setSortingEnabled(True) 22818392b74bSAdrian Hunter 22828392b74bSAdrian Hunter self.ResizeColumnsToContents() 22838392b74bSAdrian Hunter 22848392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 22858392b74bSAdrian Hunter 22868392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 22878392b74bSAdrian Hunter 22888392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 22898392b74bSAdrian Hunter 22908392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 22918392b74bSAdrian Hunter 22928392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 22938392b74bSAdrian Hunter 22948392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 22958392b74bSAdrian Hunter 22968392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 22978392b74bSAdrian Hunter self.view.setFocus() 22988392b74bSAdrian Hunter self.find_bar.Busy() 22998392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 23008392b74bSAdrian Hunter 23018392b74bSAdrian Hunter def FindDone(self, row): 23028392b74bSAdrian Hunter self.find_bar.Idle() 23038392b74bSAdrian Hunter if row >= 0: 230435fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 23058392b74bSAdrian Hunter else: 23068392b74bSAdrian Hunter self.find_bar.NotFound() 23078392b74bSAdrian Hunter 23088392b74bSAdrian Hunter# Table list 23098392b74bSAdrian Hunter 23108392b74bSAdrian Hunterdef GetTableList(glb): 23118392b74bSAdrian Hunter tables = [] 23128392b74bSAdrian Hunter query = QSqlQuery(glb.db) 23138392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 23148392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 23158392b74bSAdrian Hunter else: 23168392b74bSAdrian 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") 23178392b74bSAdrian Hunter while query.next(): 23188392b74bSAdrian Hunter tables.append(query.value(0)) 23198392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 23208392b74bSAdrian Hunter tables.append("sqlite_master") 23218392b74bSAdrian Hunter else: 23228392b74bSAdrian Hunter tables.append("information_schema.tables") 23238392b74bSAdrian Hunter tables.append("information_schema.views") 23248392b74bSAdrian Hunter tables.append("information_schema.columns") 23258392b74bSAdrian Hunter return tables 23268392b74bSAdrian Hunter 2327cd358012SAdrian Hunter# Top Calls data model 2328cd358012SAdrian Hunter 2329cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2330cd358012SAdrian Hunter 2331cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2332cd358012SAdrian Hunter text = "" 2333cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2334cd358012SAdrian Hunter text = "::text" 2335cd358012SAdrian Hunter limit = "" 2336cd358012SAdrian Hunter if len(report_vars.limit): 2337cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2338cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2339cd358012SAdrian Hunter " CASE" 2340cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2341cd358012SAdrian Hunter " ELSE short_name" 2342cd358012SAdrian Hunter " END AS dso," 2343cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2344cd358012SAdrian Hunter " CASE" 2345cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2346cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2347cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2348cd358012SAdrian Hunter " ELSE ''" + text + 2349cd358012SAdrian Hunter " END AS flags" 2350cd358012SAdrian Hunter " FROM calls" 2351cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2352cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2353cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2354cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2355cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2356cd358012SAdrian Hunter report_vars.where_clause + 2357cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2358cd358012SAdrian Hunter limit 2359cd358012SAdrian Hunter ) 2360cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2361cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2362cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2363cd358012SAdrian Hunter 2364cd358012SAdrian Hunter def columnAlignment(self, column): 2365cd358012SAdrian Hunter return self.alignment[column] 2366cd358012SAdrian Hunter 2367cd358012SAdrian Hunter# Top Calls report creation dialog 2368cd358012SAdrian Hunter 2369cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2370cd358012SAdrian Hunter 2371cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2372cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2373cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2374cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2375cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2376cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2377cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2378cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2379cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2380cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2381cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2382cd358012SAdrian Hunter 2383cd358012SAdrian Hunter# Top Calls window 2384cd358012SAdrian Hunter 2385cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2386cd358012SAdrian Hunter 2387cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2388cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2389cd358012SAdrian Hunter 2390cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2391cd358012SAdrian Hunter self.model = self.data_model 2392cd358012SAdrian Hunter 2393cd358012SAdrian Hunter self.view = QTableView() 2394cd358012SAdrian Hunter self.view.setModel(self.model) 2395cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2396cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 2397cd358012SAdrian Hunter 2398cd358012SAdrian Hunter self.ResizeColumnsToContents() 2399cd358012SAdrian Hunter 2400cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2401cd358012SAdrian Hunter 2402cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2403cd358012SAdrian Hunter 2404cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2405cd358012SAdrian Hunter 2406cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2407cd358012SAdrian Hunter 2408cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2409cd358012SAdrian Hunter 2410cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2411cd358012SAdrian Hunter 2412cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2413cd358012SAdrian Hunter self.view.setFocus() 2414cd358012SAdrian Hunter self.find_bar.Busy() 2415cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2416cd358012SAdrian Hunter 2417cd358012SAdrian Hunter def FindDone(self, row): 2418cd358012SAdrian Hunter self.find_bar.Idle() 2419cd358012SAdrian Hunter if row >= 0: 2420cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2421cd358012SAdrian Hunter else: 2422cd358012SAdrian Hunter self.find_bar.NotFound() 2423cd358012SAdrian Hunter 24241beb5c7bSAdrian Hunter# Action Definition 24251beb5c7bSAdrian Hunter 24261beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 24271beb5c7bSAdrian Hunter action = QAction(label, parent) 24281beb5c7bSAdrian Hunter if shortcut != None: 24291beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 24301beb5c7bSAdrian Hunter action.setStatusTip(tip) 24311beb5c7bSAdrian Hunter action.triggered.connect(callback) 24321beb5c7bSAdrian Hunter return action 24331beb5c7bSAdrian Hunter 24341beb5c7bSAdrian Hunter# Typical application actions 24351beb5c7bSAdrian Hunter 24361beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 24371beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 24381beb5c7bSAdrian Hunter 24391beb5c7bSAdrian Hunter# Typical MDI actions 24401beb5c7bSAdrian Hunter 24411beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 24421beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 24431beb5c7bSAdrian Hunter 24441beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 24451beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 24461beb5c7bSAdrian Hunter 24471beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 24481beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 24491beb5c7bSAdrian Hunter 24501beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 24511beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 24521beb5c7bSAdrian Hunter 24531beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 24541beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 24551beb5c7bSAdrian Hunter 24561beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 24571beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 24581beb5c7bSAdrian Hunter 24591beb5c7bSAdrian Hunter# Typical MDI window menu 24601beb5c7bSAdrian Hunter 24611beb5c7bSAdrian Hunterclass WindowMenu(): 24621beb5c7bSAdrian Hunter 24631beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 24641beb5c7bSAdrian Hunter self.mdi_area = mdi_area 24651beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 24661beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 24671beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 24681beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 24691beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 24701beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 24711beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 24721beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 24731beb5c7bSAdrian Hunter 24741beb5c7bSAdrian Hunter def Update(self): 24751beb5c7bSAdrian Hunter self.window_menu.clear() 24761beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 24771beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 24781beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 24791beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 24801beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 24811beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 24821beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 24831beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 24841beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 24851beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 24861beb5c7bSAdrian Hunter self.window_menu.addSeparator() 24871beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 24881beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 24891beb5c7bSAdrian Hunter self.window_menu.addSeparator() 24901beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 24911beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 24921beb5c7bSAdrian Hunter if sub_window_count == 0: 24931beb5c7bSAdrian Hunter return 24941beb5c7bSAdrian Hunter self.window_menu.addSeparator() 24951beb5c7bSAdrian Hunter nr = 1 24961beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 24971beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 24981beb5c7bSAdrian Hunter if nr < 10: 24991beb5c7bSAdrian Hunter label = "&" + label 25001beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 25011beb5c7bSAdrian Hunter action.setCheckable(True) 25021beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 25031beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 25041beb5c7bSAdrian Hunter self.window_menu.addAction(action) 25051beb5c7bSAdrian Hunter nr += 1 25061beb5c7bSAdrian Hunter 25071beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 25081beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 25091beb5c7bSAdrian Hunter 251065b24292SAdrian Hunter# Help text 251165b24292SAdrian Hunter 251265b24292SAdrian Hunterglb_help_text = """ 251365b24292SAdrian Hunter<h1>Contents</h1> 251465b24292SAdrian Hunter<style> 251565b24292SAdrian Hunterp.c1 { 251665b24292SAdrian Hunter text-indent: 40px; 251765b24292SAdrian Hunter} 251865b24292SAdrian Hunterp.c2 { 251965b24292SAdrian Hunter text-indent: 80px; 252065b24292SAdrian Hunter} 252165b24292SAdrian Hunter} 252265b24292SAdrian Hunter</style> 252365b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 252465b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2525ae8b887cSAdrian Hunter<p class=c2><a href=#calltree>1.2 Call Tree</a></p> 2526ae8b887cSAdrian Hunter<p class=c2><a href=#allbranches>1.3 All branches</a></p> 2527ae8b887cSAdrian Hunter<p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> 2528ae8b887cSAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> 252965b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 253065b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 253165b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 253265b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 253365b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 253465b24292SAdrian Hunterwidths to suit will display something like: 253565b24292SAdrian Hunter<pre> 253665b24292SAdrian Hunter Call Graph: pt_example 253765b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 253865b24292SAdrian Hunterv- ls 253965b24292SAdrian Hunter v- 2638:2638 254065b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 254165b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 254265b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 254365b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 254465b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 254565b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 254665b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 254765b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 254865b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 254965b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 255065b24292SAdrian Hunter</pre> 255165b24292SAdrian Hunter<h3>Points to note:</h3> 255265b24292SAdrian Hunter<ul> 255365b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 255465b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 255565b24292SAdrian Hunter<li>Subsequent levels are functions</li> 255665b24292SAdrian Hunter<li>'Count' is the number of calls</li> 255765b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 255865b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 255965b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 256065b24292SAdrian Hunter</ul> 256165b24292SAdrian Hunter<h3>Find</h3> 256265b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 256365b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 2564ae8b887cSAdrian Hunter<h2 id=calltree>1.2 Call Tree</h2> 2565ae8b887cSAdrian HunterThe Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. 2566ae8b887cSAdrian HunterAlso the 'Count' column, which would be always 1, is replaced by the 'Call Time'. 2567ae8b887cSAdrian Hunter<h2 id=allbranches>1.3 All branches</h2> 256865b24292SAdrian HunterThe All branches report displays all branches in chronological order. 256965b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 257065b24292SAdrian Hunter<h3>Disassembly</h3> 257165b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 257265b24292SAdrian Hunter<ol> 257365b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 257465b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 257565b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 257665b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 257765b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 257865b24292SAdrian Hunter</ol> 257965b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 258065b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 258165b24292SAdrian Hunter<pre> 258265b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 258365b24292SAdrian Huntergit clone https://github.com/intelxed/xed 258465b24292SAdrian Huntercd xed 258565b24292SAdrian Hunter./mfile.py --share 258665b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 258765b24292SAdrian Huntersudo ldconfig 258865b24292SAdrian Hunter</pre> 258965b24292SAdrian Hunter<h3>Find</h3> 259065b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 259165b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 259265b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 2593ae8b887cSAdrian Hunter<h2 id=selectedbranches>1.4 Selected branches</h2> 259465b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 259565b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 2596ae8b887cSAdrian Hunter<h3>1.4.1 Time ranges</h3> 259765b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 259865b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 259965b24292SAdrian Hunter<pre> 260065b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 260165b24292SAdrian Hunter 100us-200us From 100us to 200us 260265b24292SAdrian Hunter 10ms- From 10ms to the end 260365b24292SAdrian Hunter -100ns The first 100ns 260465b24292SAdrian Hunter -10ms- The last 10ms 260565b24292SAdrian Hunter</pre> 260665b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2607ae8b887cSAdrian Hunter<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> 2608cd358012SAdrian 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. 2609cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2610cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 261165b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 261265b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 261365b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 261465b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 261565b24292SAdrian Hunterbut that can be slow for large tables. 261665b24292SAdrian Hunter<p>There are also tables of database meta-information. 261765b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 261865b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 261965b24292SAdrian Hunter<h3>Find</h3> 262065b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 262165b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 262265b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 262335fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 262435fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 262565b24292SAdrian Hunter""" 262665b24292SAdrian Hunter 262765b24292SAdrian Hunter# Help window 262865b24292SAdrian Hunter 262965b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 263065b24292SAdrian Hunter 263165b24292SAdrian Hunter def __init__(self, glb, parent=None): 263265b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 263365b24292SAdrian Hunter 263465b24292SAdrian Hunter self.text = QTextBrowser() 263565b24292SAdrian Hunter self.text.setHtml(glb_help_text) 263665b24292SAdrian Hunter self.text.setReadOnly(True) 263765b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 263865b24292SAdrian Hunter 263965b24292SAdrian Hunter self.setWidget(self.text) 264065b24292SAdrian Hunter 264165b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 264265b24292SAdrian Hunter 264365b24292SAdrian Hunter# Main window that only displays the help text 264465b24292SAdrian Hunter 264565b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 264665b24292SAdrian Hunter 264765b24292SAdrian Hunter def __init__(self, parent=None): 264865b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 264965b24292SAdrian Hunter 265065b24292SAdrian Hunter self.setMinimumSize(200, 100) 265165b24292SAdrian Hunter self.resize(800, 600) 265265b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 265365b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 265465b24292SAdrian Hunter 265565b24292SAdrian Hunter self.text = QTextBrowser() 265665b24292SAdrian Hunter self.text.setHtml(glb_help_text) 265765b24292SAdrian Hunter self.text.setReadOnly(True) 265865b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 265965b24292SAdrian Hunter 266065b24292SAdrian Hunter self.setCentralWidget(self.text) 266165b24292SAdrian Hunter 266282f68e28SAdrian Hunter# Font resize 266382f68e28SAdrian Hunter 266482f68e28SAdrian Hunterdef ResizeFont(widget, diff): 266582f68e28SAdrian Hunter font = widget.font() 266682f68e28SAdrian Hunter sz = font.pointSize() 266782f68e28SAdrian Hunter font.setPointSize(sz + diff) 266882f68e28SAdrian Hunter widget.setFont(font) 266982f68e28SAdrian Hunter 267082f68e28SAdrian Hunterdef ShrinkFont(widget): 267182f68e28SAdrian Hunter ResizeFont(widget, -1) 267282f68e28SAdrian Hunter 267382f68e28SAdrian Hunterdef EnlargeFont(widget): 267482f68e28SAdrian Hunter ResizeFont(widget, 1) 267582f68e28SAdrian Hunter 26761beb5c7bSAdrian Hunter# Unique name for sub-windows 26771beb5c7bSAdrian Hunter 26781beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 26791beb5c7bSAdrian Hunter if nr > 1: 26801beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 26811beb5c7bSAdrian Hunter return name 26821beb5c7bSAdrian Hunter 26831beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 26841beb5c7bSAdrian Hunter nr = 1 26851beb5c7bSAdrian Hunter while True: 26861beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 26871beb5c7bSAdrian Hunter ok = True 26881beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 26891beb5c7bSAdrian Hunter if sub_window.name == unique_name: 26901beb5c7bSAdrian Hunter ok = False 26911beb5c7bSAdrian Hunter break 26921beb5c7bSAdrian Hunter if ok: 26931beb5c7bSAdrian Hunter return unique_name 26941beb5c7bSAdrian Hunter nr += 1 26951beb5c7bSAdrian Hunter 26961beb5c7bSAdrian Hunter# Add a sub-window 26971beb5c7bSAdrian Hunter 26981beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 26991beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 27001beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 27011beb5c7bSAdrian Hunter sub_window.resize(800, 600) 27021beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 27031beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 27041beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 27051beb5c7bSAdrian Hunter sub_window.name = unique_name 27061beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 27071beb5c7bSAdrian Hunter sub_window.show() 27081beb5c7bSAdrian Hunter 2709031c2a00SAdrian Hunter# Main window 2710031c2a00SAdrian Hunter 2711031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 2712031c2a00SAdrian Hunter 2713031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 2714031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 2715031c2a00SAdrian Hunter 2716031c2a00SAdrian Hunter self.glb = glb 2717031c2a00SAdrian Hunter 27181beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 2719031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 2720031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 2721031c2a00SAdrian Hunter 27221beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 27231beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 27241beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 2725031c2a00SAdrian Hunter 27261beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 2727031c2a00SAdrian Hunter 27281beb5c7bSAdrian Hunter menu = self.menuBar() 2729031c2a00SAdrian Hunter 27301beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 27311beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 27321beb5c7bSAdrian Hunter 2733ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 2734ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 27358392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 273682f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 273782f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 2738ebd70c7dSAdrian Hunter 27391beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 2740655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 27411beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 27421beb5c7bSAdrian Hunter 2743ae8b887cSAdrian Hunter if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): 2744ae8b887cSAdrian Hunter reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) 2745ae8b887cSAdrian Hunter 274676099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 274776099f98SAdrian Hunter 2748cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 2749cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 2750cd358012SAdrian Hunter 27518392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 27528392b74bSAdrian Hunter 27531beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 27541beb5c7bSAdrian Hunter 275565b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 275665b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 275765b24292SAdrian Hunter 2758ebd70c7dSAdrian Hunter def Find(self): 2759ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 2760ebd70c7dSAdrian Hunter if win: 2761ebd70c7dSAdrian Hunter try: 2762ebd70c7dSAdrian Hunter win.find_bar.Activate() 2763ebd70c7dSAdrian Hunter except: 2764ebd70c7dSAdrian Hunter pass 2765ebd70c7dSAdrian Hunter 27668392b74bSAdrian Hunter def FetchMoreRecords(self): 27678392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 27688392b74bSAdrian Hunter if win: 27698392b74bSAdrian Hunter try: 27708392b74bSAdrian Hunter win.fetch_bar.Activate() 27718392b74bSAdrian Hunter except: 27728392b74bSAdrian Hunter pass 27738392b74bSAdrian Hunter 277482f68e28SAdrian Hunter def ShrinkFont(self): 277582f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 277682f68e28SAdrian Hunter ShrinkFont(win.view) 277782f68e28SAdrian Hunter 277882f68e28SAdrian Hunter def EnlargeFont(self): 277982f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 278082f68e28SAdrian Hunter EnlargeFont(win.view) 278182f68e28SAdrian Hunter 278276099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 278376099f98SAdrian Hunter branches_events = 0 278476099f98SAdrian Hunter for event in events: 278576099f98SAdrian Hunter event = event.split(":")[0] 278676099f98SAdrian Hunter if event == "branches": 278776099f98SAdrian Hunter branches_events += 1 278876099f98SAdrian Hunter dbid = 0 278976099f98SAdrian Hunter for event in events: 279076099f98SAdrian Hunter dbid += 1 279176099f98SAdrian Hunter event = event.split(":")[0] 279276099f98SAdrian Hunter if event == "branches": 279376099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 279476099f98SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 2795210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 2796210cf1f9SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) 279776099f98SAdrian Hunter 27988392b74bSAdrian Hunter def TableMenu(self, tables, menu): 27998392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 28008392b74bSAdrian Hunter for table in tables: 28018392b74bSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) 28028392b74bSAdrian Hunter 28031beb5c7bSAdrian Hunter def NewCallGraph(self): 28041beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 2805031c2a00SAdrian Hunter 2806ae8b887cSAdrian Hunter def NewCallTree(self): 2807ae8b887cSAdrian Hunter CallTreeWindow(self.glb, self) 2808ae8b887cSAdrian Hunter 2809cd358012SAdrian Hunter def NewTopCalls(self): 2810cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 2811cd358012SAdrian Hunter ret = dialog.exec_() 2812cd358012SAdrian Hunter if ret: 2813cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 2814cd358012SAdrian Hunter 281576099f98SAdrian Hunter def NewBranchView(self, event_id): 2816947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 281776099f98SAdrian Hunter 2818210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 2819210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 2820210cf1f9SAdrian Hunter ret = dialog.exec_() 2821210cf1f9SAdrian Hunter if ret: 2822947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 2823210cf1f9SAdrian Hunter 28248392b74bSAdrian Hunter def NewTableView(self, table_name): 28258392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 28268392b74bSAdrian Hunter 282765b24292SAdrian Hunter def Help(self): 282865b24292SAdrian Hunter HelpWindow(self.glb, self) 282965b24292SAdrian Hunter 283076099f98SAdrian Hunter# XED Disassembler 283176099f98SAdrian Hunter 283276099f98SAdrian Hunterclass xed_state_t(Structure): 283376099f98SAdrian Hunter 283476099f98SAdrian Hunter _fields_ = [ 283576099f98SAdrian Hunter ("mode", c_int), 283676099f98SAdrian Hunter ("width", c_int) 283776099f98SAdrian Hunter ] 283876099f98SAdrian Hunter 283976099f98SAdrian Hunterclass XEDInstruction(): 284076099f98SAdrian Hunter 284176099f98SAdrian Hunter def __init__(self, libxed): 284276099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 284376099f98SAdrian Hunter xedd_t = c_byte * 512 284476099f98SAdrian Hunter self.xedd = xedd_t() 284576099f98SAdrian Hunter self.xedp = addressof(self.xedd) 284676099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 284776099f98SAdrian Hunter self.state = xed_state_t() 284876099f98SAdrian Hunter self.statep = addressof(self.state) 284976099f98SAdrian Hunter # Buffer for disassembled instruction text 285076099f98SAdrian Hunter self.buffer = create_string_buffer(256) 285176099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 285276099f98SAdrian Hunter 285376099f98SAdrian Hunterclass LibXED(): 285476099f98SAdrian Hunter 285576099f98SAdrian Hunter def __init__(self): 28565ed4419dSAdrian Hunter try: 285776099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 28585ed4419dSAdrian Hunter except: 28595ed4419dSAdrian Hunter self.libxed = None 28605ed4419dSAdrian Hunter if not self.libxed: 28615ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 286276099f98SAdrian Hunter 286376099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 286476099f98SAdrian Hunter self.xed_tables_init.restype = None 286576099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 286676099f98SAdrian Hunter 286776099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 286876099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 286976099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 287076099f98SAdrian Hunter 287176099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 287276099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 287376099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 287476099f98SAdrian Hunter 287576099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 287676099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 287776099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 287876099f98SAdrian Hunter 287976099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 288076099f98SAdrian Hunter self.xed_decode.restype = c_int 288176099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 288276099f98SAdrian Hunter 288376099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 288476099f98SAdrian Hunter self.xed_format_context.restype = c_uint 288576099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 288676099f98SAdrian Hunter 288776099f98SAdrian Hunter self.xed_tables_init() 288876099f98SAdrian Hunter 288976099f98SAdrian Hunter def Instruction(self): 289076099f98SAdrian Hunter return XEDInstruction(self) 289176099f98SAdrian Hunter 289276099f98SAdrian Hunter def SetMode(self, inst, mode): 289376099f98SAdrian Hunter if mode: 289476099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 289576099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 289676099f98SAdrian Hunter else: 289776099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 289876099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 289976099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 290076099f98SAdrian Hunter 290176099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 290276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 290376099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 290476099f98SAdrian Hunter if err: 290576099f98SAdrian Hunter return 0, "" 290676099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 290776099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 290876099f98SAdrian Hunter if not ok: 290976099f98SAdrian Hunter return 0, "" 2910606bd60aSAdrian Hunter if sys.version_info[0] == 2: 2911606bd60aSAdrian Hunter result = inst.buffer.value 2912606bd60aSAdrian Hunter else: 2913606bd60aSAdrian Hunter result = inst.buffer.value.decode() 291476099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 291576099f98SAdrian Hunter # For now, assume the length is in byte 166 2916606bd60aSAdrian Hunter return inst.xedd[166], result 291776099f98SAdrian Hunter 291876099f98SAdrian Hunterdef TryOpen(file_name): 291976099f98SAdrian Hunter try: 292076099f98SAdrian Hunter return open(file_name, "rb") 292176099f98SAdrian Hunter except: 292276099f98SAdrian Hunter return None 292376099f98SAdrian Hunter 292476099f98SAdrian Hunterdef Is64Bit(f): 292576099f98SAdrian Hunter result = sizeof(c_void_p) 292676099f98SAdrian Hunter # ELF support only 292776099f98SAdrian Hunter pos = f.tell() 292876099f98SAdrian Hunter f.seek(0) 292976099f98SAdrian Hunter header = f.read(7) 293076099f98SAdrian Hunter f.seek(pos) 293176099f98SAdrian Hunter magic = header[0:4] 2932606bd60aSAdrian Hunter if sys.version_info[0] == 2: 293376099f98SAdrian Hunter eclass = ord(header[4]) 293476099f98SAdrian Hunter encoding = ord(header[5]) 293576099f98SAdrian Hunter version = ord(header[6]) 2936606bd60aSAdrian Hunter else: 2937606bd60aSAdrian Hunter eclass = header[4] 2938606bd60aSAdrian Hunter encoding = header[5] 2939606bd60aSAdrian Hunter version = header[6] 294076099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 294176099f98SAdrian Hunter result = True if eclass == 2 else False 294276099f98SAdrian Hunter return result 294376099f98SAdrian Hunter 2944031c2a00SAdrian Hunter# Global data 2945031c2a00SAdrian Hunter 2946031c2a00SAdrian Hunterclass Glb(): 2947031c2a00SAdrian Hunter 2948031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 2949031c2a00SAdrian Hunter self.dbref = dbref 2950031c2a00SAdrian Hunter self.db = db 2951031c2a00SAdrian Hunter self.dbname = dbname 295276099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 295376099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 295476099f98SAdrian Hunter if self.buildid_dir: 295576099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 295676099f98SAdrian Hunter else: 295776099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 2958031c2a00SAdrian Hunter self.app = None 2959031c2a00SAdrian Hunter self.mainwindow = None 29608392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 296176099f98SAdrian Hunter try: 296276099f98SAdrian Hunter self.disassembler = LibXED() 296376099f98SAdrian Hunter self.have_disassembler = True 296476099f98SAdrian Hunter except: 296576099f98SAdrian Hunter self.have_disassembler = False 296676099f98SAdrian Hunter 296776099f98SAdrian Hunter def FileFromBuildId(self, build_id): 296876099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 296976099f98SAdrian Hunter return TryOpen(file_name) 297076099f98SAdrian Hunter 297176099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 297276099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 297376099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 297476099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 297576099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 297676099f98SAdrian Hunter if f: 297776099f98SAdrian Hunter return f 297876099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 297976099f98SAdrian Hunter f = TryOpen(long_name) 298076099f98SAdrian Hunter if f: 298176099f98SAdrian Hunter return f 298276099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 298376099f98SAdrian Hunter if f: 298476099f98SAdrian Hunter return f 298576099f98SAdrian Hunter return None 29868392b74bSAdrian Hunter 29878392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 29888392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 29898392b74bSAdrian Hunter 29908392b74bSAdrian Hunter # Shutdown any background processes or threads 29918392b74bSAdrian Hunter def ShutdownInstances(self): 29928392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 29938392b74bSAdrian Hunter try: 29948392b74bSAdrian Hunter x.Shutdown() 29958392b74bSAdrian Hunter except: 29968392b74bSAdrian Hunter pass 2997031c2a00SAdrian Hunter 2998031c2a00SAdrian Hunter# Database reference 2999031c2a00SAdrian Hunter 3000031c2a00SAdrian Hunterclass DBRef(): 3001031c2a00SAdrian Hunter 3002031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 3003031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 3004031c2a00SAdrian Hunter self.dbname = dbname 3005031c2a00SAdrian Hunter 3006031c2a00SAdrian Hunter def Open(self, connection_name): 3007031c2a00SAdrian Hunter dbname = self.dbname 3008031c2a00SAdrian Hunter if self.is_sqlite3: 3009031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 3010031c2a00SAdrian Hunter else: 3011031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 3012031c2a00SAdrian Hunter opts = dbname.split() 3013031c2a00SAdrian Hunter for opt in opts: 3014031c2a00SAdrian Hunter if "=" in opt: 3015031c2a00SAdrian Hunter opt = opt.split("=") 3016031c2a00SAdrian Hunter if opt[0] == "hostname": 3017031c2a00SAdrian Hunter db.setHostName(opt[1]) 3018031c2a00SAdrian Hunter elif opt[0] == "port": 3019031c2a00SAdrian Hunter db.setPort(int(opt[1])) 3020031c2a00SAdrian Hunter elif opt[0] == "username": 3021031c2a00SAdrian Hunter db.setUserName(opt[1]) 3022031c2a00SAdrian Hunter elif opt[0] == "password": 3023031c2a00SAdrian Hunter db.setPassword(opt[1]) 3024031c2a00SAdrian Hunter elif opt[0] == "dbname": 3025031c2a00SAdrian Hunter dbname = opt[1] 3026031c2a00SAdrian Hunter else: 3027031c2a00SAdrian Hunter dbname = opt 3028031c2a00SAdrian Hunter 3029031c2a00SAdrian Hunter db.setDatabaseName(dbname) 3030031c2a00SAdrian Hunter if not db.open(): 3031031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 3032031c2a00SAdrian Hunter return db, dbname 3033031c2a00SAdrian Hunter 3034031c2a00SAdrian Hunter# Main 3035031c2a00SAdrian Hunter 3036031c2a00SAdrian Hunterdef Main(): 3037031c2a00SAdrian Hunter if (len(sys.argv) < 2): 3038beda0e72STony Jones printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}"); 3039031c2a00SAdrian Hunter raise Exception("Too few arguments") 3040031c2a00SAdrian Hunter 3041031c2a00SAdrian Hunter dbname = sys.argv[1] 304265b24292SAdrian Hunter if dbname == "--help-only": 304365b24292SAdrian Hunter app = QApplication(sys.argv) 304465b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 304565b24292SAdrian Hunter mainwindow.show() 304665b24292SAdrian Hunter err = app.exec_() 304765b24292SAdrian Hunter sys.exit(err) 3048031c2a00SAdrian Hunter 3049031c2a00SAdrian Hunter is_sqlite3 = False 3050031c2a00SAdrian Hunter try: 3051beda0e72STony Jones f = open(dbname, "rb") 3052beda0e72STony Jones if f.read(15) == b'SQLite format 3': 3053031c2a00SAdrian Hunter is_sqlite3 = True 3054031c2a00SAdrian Hunter f.close() 3055031c2a00SAdrian Hunter except: 3056031c2a00SAdrian Hunter pass 3057031c2a00SAdrian Hunter 3058031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 3059031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 3060031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 3061031c2a00SAdrian Hunter app = QApplication(sys.argv) 3062031c2a00SAdrian Hunter glb.app = app 3063031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 3064031c2a00SAdrian Hunter glb.mainwindow = mainwindow 3065031c2a00SAdrian Hunter mainwindow.show() 3066031c2a00SAdrian Hunter err = app.exec_() 30678392b74bSAdrian Hunter glb.ShutdownInstances() 3068031c2a00SAdrian Hunter db.close() 3069031c2a00SAdrian Hunter sys.exit(err) 3070031c2a00SAdrian Hunter 3071031c2a00SAdrian Hunterif __name__ == "__main__": 3072031c2a00SAdrian Hunter Main() 3073