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 91031c2a00SAdrian Hunterimport sys 921beb5c7bSAdrian Hunterimport weakref 931beb5c7bSAdrian Hunterimport threading 94ebd70c7dSAdrian Hunterimport string 958392b74bSAdrian Hunterimport cPickle 968392b74bSAdrian Hunterimport re 978392b74bSAdrian Hunterimport os 98031c2a00SAdrian Hunterfrom PySide.QtCore import * 99031c2a00SAdrian Hunterfrom PySide.QtGui import * 100031c2a00SAdrian Hunterfrom PySide.QtSql import * 101031c2a00SAdrian Hunterfrom decimal import * 1028392b74bSAdrian Hunterfrom ctypes import * 1038392b74bSAdrian Hunterfrom multiprocessing import Process, Array, Value, Event 104031c2a00SAdrian Hunter 105031c2a00SAdrian Hunter# Data formatting helpers 106031c2a00SAdrian Hunter 10776099f98SAdrian Hunterdef tohex(ip): 10876099f98SAdrian Hunter if ip < 0: 10976099f98SAdrian Hunter ip += 1 << 64 11076099f98SAdrian Hunter return "%x" % ip 11176099f98SAdrian Hunter 11276099f98SAdrian Hunterdef offstr(offset): 11376099f98SAdrian Hunter if offset: 11476099f98SAdrian Hunter return "+0x%x" % offset 11576099f98SAdrian Hunter return "" 11676099f98SAdrian Hunter 117031c2a00SAdrian Hunterdef dsoname(name): 118031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 119031c2a00SAdrian Hunter return "[kernel]" 120031c2a00SAdrian Hunter return name 121031c2a00SAdrian Hunter 122210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0): 123210cf1f9SAdrian Hunter pos = s.find(sub) 124210cf1f9SAdrian Hunter if pos < 0: 125210cf1f9SAdrian Hunter return pos 126210cf1f9SAdrian Hunter if n <= 1: 127210cf1f9SAdrian Hunter return offs + pos 128210cf1f9SAdrian Hunter return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) 129210cf1f9SAdrian Hunter 130031c2a00SAdrian Hunter# Percent to one decimal place 131031c2a00SAdrian Hunter 132031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 133031c2a00SAdrian Hunter if not d: 134031c2a00SAdrian Hunter return "0.0" 135031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 136031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 137031c2a00SAdrian Hunter 138031c2a00SAdrian Hunter# Helper for queries that must not fail 139031c2a00SAdrian Hunter 140031c2a00SAdrian Hunterdef QueryExec(query, stmt): 141031c2a00SAdrian Hunter ret = query.exec_(stmt) 142031c2a00SAdrian Hunter if not ret: 143031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 144031c2a00SAdrian Hunter 145ebd70c7dSAdrian Hunter# Background thread 146ebd70c7dSAdrian Hunter 147ebd70c7dSAdrian Hunterclass Thread(QThread): 148ebd70c7dSAdrian Hunter 149ebd70c7dSAdrian Hunter done = Signal(object) 150ebd70c7dSAdrian Hunter 151ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 152ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 153ebd70c7dSAdrian Hunter self.task = task 154ebd70c7dSAdrian Hunter self.param = param 155ebd70c7dSAdrian Hunter 156ebd70c7dSAdrian Hunter def run(self): 157ebd70c7dSAdrian Hunter while True: 158ebd70c7dSAdrian Hunter if self.param is None: 159ebd70c7dSAdrian Hunter done, result = self.task() 160ebd70c7dSAdrian Hunter else: 161ebd70c7dSAdrian Hunter done, result = self.task(self.param) 162ebd70c7dSAdrian Hunter self.done.emit(result) 163ebd70c7dSAdrian Hunter if done: 164ebd70c7dSAdrian Hunter break 165ebd70c7dSAdrian Hunter 166031c2a00SAdrian Hunter# Tree data model 167031c2a00SAdrian Hunter 168031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 169031c2a00SAdrian Hunter 170a448ba23SAdrian Hunter def __init__(self, glb, parent=None): 171031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 172a448ba23SAdrian Hunter self.glb = glb 173a448ba23SAdrian Hunter self.root = self.GetRoot() 174031c2a00SAdrian Hunter self.last_row_read = 0 175031c2a00SAdrian Hunter 176031c2a00SAdrian Hunter def Item(self, parent): 177031c2a00SAdrian Hunter if parent.isValid(): 178031c2a00SAdrian Hunter return parent.internalPointer() 179031c2a00SAdrian Hunter else: 180031c2a00SAdrian Hunter return self.root 181031c2a00SAdrian Hunter 182031c2a00SAdrian Hunter def rowCount(self, parent): 183031c2a00SAdrian Hunter result = self.Item(parent).childCount() 184031c2a00SAdrian Hunter if result < 0: 185031c2a00SAdrian Hunter result = 0 186031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 187031c2a00SAdrian Hunter return result 188031c2a00SAdrian Hunter 189031c2a00SAdrian Hunter def hasChildren(self, parent): 190031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 191031c2a00SAdrian Hunter 192031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 193031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 194031c2a00SAdrian Hunter return self.columnAlignment(section) 195031c2a00SAdrian Hunter if role != Qt.DisplayRole: 196031c2a00SAdrian Hunter return None 197031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 198031c2a00SAdrian Hunter return None 199031c2a00SAdrian Hunter return self.columnHeader(section) 200031c2a00SAdrian Hunter 201031c2a00SAdrian Hunter def parent(self, child): 202031c2a00SAdrian Hunter child_item = child.internalPointer() 203031c2a00SAdrian Hunter if child_item is self.root: 204031c2a00SAdrian Hunter return QModelIndex() 205031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 206031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 207031c2a00SAdrian Hunter 208031c2a00SAdrian Hunter def index(self, row, column, parent): 209031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 210031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 211031c2a00SAdrian Hunter 212031c2a00SAdrian Hunter def DisplayData(self, item, index): 213031c2a00SAdrian Hunter return item.getData(index.column()) 214031c2a00SAdrian Hunter 2158392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2168392b74bSAdrian Hunter if row > self.last_row_read: 2178392b74bSAdrian Hunter self.last_row_read = row 2188392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2198392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2208392b74bSAdrian Hunter 2218392b74bSAdrian Hunter def columnAlignment(self, column): 2228392b74bSAdrian Hunter return Qt.AlignLeft 2238392b74bSAdrian Hunter 2248392b74bSAdrian Hunter def columnFont(self, column): 2258392b74bSAdrian Hunter return None 2268392b74bSAdrian Hunter 2278392b74bSAdrian Hunter def data(self, index, role): 2288392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2298392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2308392b74bSAdrian Hunter if role == Qt.FontRole: 2318392b74bSAdrian Hunter return self.columnFont(index.column()) 2328392b74bSAdrian Hunter if role != Qt.DisplayRole: 2338392b74bSAdrian Hunter return None 2348392b74bSAdrian Hunter item = index.internalPointer() 2358392b74bSAdrian Hunter return self.DisplayData(item, index) 2368392b74bSAdrian Hunter 2378392b74bSAdrian Hunter# Table data model 2388392b74bSAdrian Hunter 2398392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2408392b74bSAdrian Hunter 2418392b74bSAdrian Hunter def __init__(self, parent=None): 2428392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2438392b74bSAdrian Hunter self.child_count = 0 2448392b74bSAdrian Hunter self.child_items = [] 2458392b74bSAdrian Hunter self.last_row_read = 0 2468392b74bSAdrian Hunter 2478392b74bSAdrian Hunter def Item(self, parent): 2488392b74bSAdrian Hunter if parent.isValid(): 2498392b74bSAdrian Hunter return parent.internalPointer() 2508392b74bSAdrian Hunter else: 2518392b74bSAdrian Hunter return self 2528392b74bSAdrian Hunter 2538392b74bSAdrian Hunter def rowCount(self, parent): 2548392b74bSAdrian Hunter return self.child_count 2558392b74bSAdrian Hunter 2568392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2578392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2588392b74bSAdrian Hunter return self.columnAlignment(section) 2598392b74bSAdrian Hunter if role != Qt.DisplayRole: 2608392b74bSAdrian Hunter return None 2618392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2628392b74bSAdrian Hunter return None 2638392b74bSAdrian Hunter return self.columnHeader(section) 2648392b74bSAdrian Hunter 2658392b74bSAdrian Hunter def index(self, row, column, parent): 2668392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 2678392b74bSAdrian Hunter 2688392b74bSAdrian Hunter def DisplayData(self, item, index): 2698392b74bSAdrian Hunter return item.getData(index.column()) 2708392b74bSAdrian Hunter 2718392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2728392b74bSAdrian Hunter if row > self.last_row_read: 2738392b74bSAdrian Hunter self.last_row_read = row 2748392b74bSAdrian Hunter if row + 10 >= self.child_count: 2758392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2768392b74bSAdrian Hunter 277031c2a00SAdrian Hunter def columnAlignment(self, column): 278031c2a00SAdrian Hunter return Qt.AlignLeft 279031c2a00SAdrian Hunter 280031c2a00SAdrian Hunter def columnFont(self, column): 281031c2a00SAdrian Hunter return None 282031c2a00SAdrian Hunter 283031c2a00SAdrian Hunter def data(self, index, role): 284031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 285031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 286031c2a00SAdrian Hunter if role == Qt.FontRole: 287031c2a00SAdrian Hunter return self.columnFont(index.column()) 288031c2a00SAdrian Hunter if role != Qt.DisplayRole: 289031c2a00SAdrian Hunter return None 290031c2a00SAdrian Hunter item = index.internalPointer() 291031c2a00SAdrian Hunter return self.DisplayData(item, index) 292031c2a00SAdrian Hunter 2931beb5c7bSAdrian Hunter# Model cache 2941beb5c7bSAdrian Hunter 2951beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 2961beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 2971beb5c7bSAdrian Hunter 2981beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 2991beb5c7bSAdrian Hunter model_cache_lock.acquire() 3001beb5c7bSAdrian Hunter try: 3011beb5c7bSAdrian Hunter model = model_cache[model_name] 3021beb5c7bSAdrian Hunter except: 3031beb5c7bSAdrian Hunter model = None 3041beb5c7bSAdrian Hunter if model is None: 3051beb5c7bSAdrian Hunter model = create_fn() 3061beb5c7bSAdrian Hunter model_cache[model_name] = model 3071beb5c7bSAdrian Hunter model_cache_lock.release() 3081beb5c7bSAdrian Hunter return model 3091beb5c7bSAdrian Hunter 310ebd70c7dSAdrian Hunter# Find bar 311ebd70c7dSAdrian Hunter 312ebd70c7dSAdrian Hunterclass FindBar(): 313ebd70c7dSAdrian Hunter 314ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 315ebd70c7dSAdrian Hunter self.finder = finder 316ebd70c7dSAdrian Hunter self.context = [] 317ebd70c7dSAdrian Hunter self.last_value = None 318ebd70c7dSAdrian Hunter self.last_pattern = None 319ebd70c7dSAdrian Hunter 320ebd70c7dSAdrian Hunter label = QLabel("Find:") 321ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 322ebd70c7dSAdrian Hunter 323ebd70c7dSAdrian Hunter self.textbox = QComboBox() 324ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 325ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 326ebd70c7dSAdrian Hunter 327ebd70c7dSAdrian Hunter self.progress = QProgressBar() 328ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 329ebd70c7dSAdrian Hunter self.progress.hide() 330ebd70c7dSAdrian Hunter 331ebd70c7dSAdrian Hunter if is_reg_expr: 332ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 333ebd70c7dSAdrian Hunter else: 334ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 335ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 336ebd70c7dSAdrian Hunter 337ebd70c7dSAdrian Hunter self.next_button = QToolButton() 338ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 339ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 340ebd70c7dSAdrian Hunter 341ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 342ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 343ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 344ebd70c7dSAdrian Hunter 345ebd70c7dSAdrian Hunter self.close_button = QToolButton() 346ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 347ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 348ebd70c7dSAdrian Hunter 349ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 350ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 351ebd70c7dSAdrian Hunter 352ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 353ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 354ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 355ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 356ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 357ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 358ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 359ebd70c7dSAdrian Hunter 360ebd70c7dSAdrian Hunter self.bar = QWidget() 361ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 362ebd70c7dSAdrian Hunter self.bar.hide() 363ebd70c7dSAdrian Hunter 364ebd70c7dSAdrian Hunter def Widget(self): 365ebd70c7dSAdrian Hunter return self.bar 366ebd70c7dSAdrian Hunter 367ebd70c7dSAdrian Hunter def Activate(self): 368ebd70c7dSAdrian Hunter self.bar.show() 369ebd70c7dSAdrian Hunter self.textbox.setFocus() 370ebd70c7dSAdrian Hunter 371ebd70c7dSAdrian Hunter def Deactivate(self): 372ebd70c7dSAdrian Hunter self.bar.hide() 373ebd70c7dSAdrian Hunter 374ebd70c7dSAdrian Hunter def Busy(self): 375ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 376ebd70c7dSAdrian Hunter self.pattern.hide() 377ebd70c7dSAdrian Hunter self.next_button.hide() 378ebd70c7dSAdrian Hunter self.prev_button.hide() 379ebd70c7dSAdrian Hunter self.progress.show() 380ebd70c7dSAdrian Hunter 381ebd70c7dSAdrian Hunter def Idle(self): 382ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 383ebd70c7dSAdrian Hunter self.progress.hide() 384ebd70c7dSAdrian Hunter self.pattern.show() 385ebd70c7dSAdrian Hunter self.next_button.show() 386ebd70c7dSAdrian Hunter self.prev_button.show() 387ebd70c7dSAdrian Hunter 388ebd70c7dSAdrian Hunter def Find(self, direction): 389ebd70c7dSAdrian Hunter value = self.textbox.currentText() 390ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 391ebd70c7dSAdrian Hunter self.last_value = value 392ebd70c7dSAdrian Hunter self.last_pattern = pattern 393ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 394ebd70c7dSAdrian Hunter 395ebd70c7dSAdrian Hunter def ValueChanged(self): 396ebd70c7dSAdrian Hunter value = self.textbox.currentText() 397ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 398ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 399ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 400ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 401ebd70c7dSAdrian Hunter if data == None: 402ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 403ebd70c7dSAdrian Hunter else: 404ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 405ebd70c7dSAdrian Hunter self.Find(0) 406ebd70c7dSAdrian Hunter 407ebd70c7dSAdrian Hunter def NextPrev(self, direction): 408ebd70c7dSAdrian Hunter value = self.textbox.currentText() 409ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 410ebd70c7dSAdrian Hunter if value != self.last_value: 411ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 412ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 413ebd70c7dSAdrian Hunter if index < 0: 414ebd70c7dSAdrian Hunter index = self.textbox.count() 415ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 416ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 417ebd70c7dSAdrian Hunter return 418ebd70c7dSAdrian Hunter else: 419ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 420ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 421ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 422ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 423ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 424ebd70c7dSAdrian Hunter self.Find(direction) 425ebd70c7dSAdrian Hunter 426ebd70c7dSAdrian Hunter def NotFound(self): 427ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 428ebd70c7dSAdrian Hunter 429031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 430031c2a00SAdrian Hunter 431031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 432031c2a00SAdrian Hunter 433031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 434031c2a00SAdrian Hunter self.glb = glb 435031c2a00SAdrian Hunter self.row = row 436031c2a00SAdrian Hunter self.parent_item = parent_item 437031c2a00SAdrian Hunter self.query_done = False; 438031c2a00SAdrian Hunter self.child_count = 0 439031c2a00SAdrian Hunter self.child_items = [] 440031c2a00SAdrian Hunter 441031c2a00SAdrian Hunter def getChildItem(self, row): 442031c2a00SAdrian Hunter return self.child_items[row] 443031c2a00SAdrian Hunter 444031c2a00SAdrian Hunter def getParentItem(self): 445031c2a00SAdrian Hunter return self.parent_item 446031c2a00SAdrian Hunter 447031c2a00SAdrian Hunter def getRow(self): 448031c2a00SAdrian Hunter return self.row 449031c2a00SAdrian Hunter 450031c2a00SAdrian Hunter def childCount(self): 451031c2a00SAdrian Hunter if not self.query_done: 452031c2a00SAdrian Hunter self.Select() 453031c2a00SAdrian Hunter if not self.child_count: 454031c2a00SAdrian Hunter return -1 455031c2a00SAdrian Hunter return self.child_count 456031c2a00SAdrian Hunter 457031c2a00SAdrian Hunter def hasChildren(self): 458031c2a00SAdrian Hunter if not self.query_done: 459031c2a00SAdrian Hunter return True 460031c2a00SAdrian Hunter return self.child_count > 0 461031c2a00SAdrian Hunter 462031c2a00SAdrian Hunter def getData(self, column): 463031c2a00SAdrian Hunter return self.data[column] 464031c2a00SAdrian Hunter 465031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 466031c2a00SAdrian Hunter 467031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 468031c2a00SAdrian Hunter 469031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 470031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 471031c2a00SAdrian Hunter self.comm_id = comm_id 472031c2a00SAdrian Hunter self.thread_id = thread_id 473031c2a00SAdrian Hunter self.call_path_id = call_path_id 474031c2a00SAdrian Hunter self.branch_count = branch_count 475031c2a00SAdrian Hunter self.time = time 476031c2a00SAdrian Hunter 477031c2a00SAdrian Hunter def Select(self): 478031c2a00SAdrian Hunter self.query_done = True; 479031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 480031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 481031c2a00SAdrian Hunter " FROM calls" 482031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 483031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 484031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 485031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 486031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 487031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 488031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 489031c2a00SAdrian Hunter " ORDER BY call_path_id") 490031c2a00SAdrian Hunter while query.next(): 491031c2a00SAdrian 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) 492031c2a00SAdrian Hunter self.child_items.append(child_item) 493031c2a00SAdrian Hunter self.child_count += 1 494031c2a00SAdrian Hunter 495031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 496031c2a00SAdrian Hunter 497031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 498031c2a00SAdrian Hunter 499031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 500031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 501031c2a00SAdrian Hunter dso = dsoname(dso) 502031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 503031c2a00SAdrian Hunter self.dbid = call_path_id 504031c2a00SAdrian Hunter 505031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 506031c2a00SAdrian Hunter 507031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 508031c2a00SAdrian Hunter 509031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 510031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 511031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 512031c2a00SAdrian Hunter self.dbid = thread_id 513031c2a00SAdrian Hunter 514031c2a00SAdrian Hunter def Select(self): 515031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 516031c2a00SAdrian Hunter for child_item in self.child_items: 517031c2a00SAdrian Hunter self.time += child_item.time 518031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 519031c2a00SAdrian Hunter for child_item in self.child_items: 520031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 521031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 522031c2a00SAdrian Hunter 523031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 524031c2a00SAdrian Hunter 525031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 526031c2a00SAdrian Hunter 527031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 528031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 529031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 530031c2a00SAdrian Hunter self.dbid = comm_id 531031c2a00SAdrian Hunter 532031c2a00SAdrian Hunter def Select(self): 533031c2a00SAdrian Hunter self.query_done = True; 534031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 535031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 536031c2a00SAdrian Hunter " FROM comm_threads" 537031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 538031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 539031c2a00SAdrian Hunter while query.next(): 540031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 541031c2a00SAdrian Hunter self.child_items.append(child_item) 542031c2a00SAdrian Hunter self.child_count += 1 543031c2a00SAdrian Hunter 544031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 547031c2a00SAdrian Hunter 548031c2a00SAdrian Hunter def __init__(self, glb): 549031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 550031c2a00SAdrian Hunter self.dbid = 0 551031c2a00SAdrian Hunter self.query_done = True; 552031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 553031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 554031c2a00SAdrian Hunter while query.next(): 555031c2a00SAdrian Hunter if not query.value(0): 556031c2a00SAdrian Hunter continue 557031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 558031c2a00SAdrian Hunter self.child_items.append(child_item) 559031c2a00SAdrian Hunter self.child_count += 1 560031c2a00SAdrian Hunter 561*254c0d82SAdrian Hunter# Context-sensitive call graph data model base 562031c2a00SAdrian Hunter 563*254c0d82SAdrian Hunterclass CallGraphModelBase(TreeModel): 564031c2a00SAdrian Hunter 565031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 566*254c0d82SAdrian Hunter super(CallGraphModelBase, self).__init__(glb, parent) 567031c2a00SAdrian Hunter 568ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 569ebd70c7dSAdrian Hunter if pattern: 570ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 571ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 572ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 573ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 574ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 575ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 576ebd70c7dSAdrian Hunter # Escape % and _ 577ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 578ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 579ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 580ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 581ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 582ebd70c7dSAdrian Hunter else: 583ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 584ebd70c7dSAdrian Hunter else: 585ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 586*254c0d82SAdrian Hunter self.DoFindSelect(query, match) 587ebd70c7dSAdrian Hunter 588ebd70c7dSAdrian Hunter def Found(self, query, found): 589ebd70c7dSAdrian Hunter if found: 590ebd70c7dSAdrian Hunter return self.FindPath(query) 591ebd70c7dSAdrian Hunter return [] 592ebd70c7dSAdrian Hunter 593ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 594ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 595ebd70c7dSAdrian Hunter found = query.first() 596ebd70c7dSAdrian Hunter else: 597ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 598ebd70c7dSAdrian Hunter found = query.next() 599ebd70c7dSAdrian Hunter return self.Found(query, found) 600ebd70c7dSAdrian Hunter 601ebd70c7dSAdrian Hunter def FindNext(self, query): 602ebd70c7dSAdrian Hunter found = query.next() 603ebd70c7dSAdrian Hunter if not found: 604ebd70c7dSAdrian Hunter found = query.first() 605ebd70c7dSAdrian Hunter return self.Found(query, found) 606ebd70c7dSAdrian Hunter 607ebd70c7dSAdrian Hunter def FindPrev(self, query): 608ebd70c7dSAdrian Hunter found = query.previous() 609ebd70c7dSAdrian Hunter if not found: 610ebd70c7dSAdrian Hunter found = query.last() 611ebd70c7dSAdrian Hunter return self.Found(query, found) 612ebd70c7dSAdrian Hunter 613ebd70c7dSAdrian Hunter def FindThread(self, c): 614ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 615ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 616ebd70c7dSAdrian Hunter elif c.direction > 0: 617ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 618ebd70c7dSAdrian Hunter else: 619ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 620ebd70c7dSAdrian Hunter return (True, ids) 621ebd70c7dSAdrian Hunter 622ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 623ebd70c7dSAdrian Hunter class Context(): 624ebd70c7dSAdrian Hunter def __init__(self, *x): 625ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 626ebd70c7dSAdrian Hunter def Update(self, *x): 627ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 628ebd70c7dSAdrian Hunter if len(context): 629ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 630ebd70c7dSAdrian Hunter else: 631ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 632ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 633ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 634ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 635ebd70c7dSAdrian Hunter thread.start() 636ebd70c7dSAdrian Hunter 637ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 638ebd70c7dSAdrian Hunter callback(ids) 639ebd70c7dSAdrian Hunter 640*254c0d82SAdrian Hunter# Context-sensitive call graph data model 641*254c0d82SAdrian Hunter 642*254c0d82SAdrian Hunterclass CallGraphModel(CallGraphModelBase): 643*254c0d82SAdrian Hunter 644*254c0d82SAdrian Hunter def __init__(self, glb, parent=None): 645*254c0d82SAdrian Hunter super(CallGraphModel, self).__init__(glb, parent) 646*254c0d82SAdrian Hunter 647*254c0d82SAdrian Hunter def GetRoot(self): 648*254c0d82SAdrian Hunter return CallGraphRootItem(self.glb) 649*254c0d82SAdrian Hunter 650*254c0d82SAdrian Hunter def columnCount(self, parent=None): 651*254c0d82SAdrian Hunter return 7 652*254c0d82SAdrian Hunter 653*254c0d82SAdrian Hunter def columnHeader(self, column): 654*254c0d82SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 655*254c0d82SAdrian Hunter return headers[column] 656*254c0d82SAdrian Hunter 657*254c0d82SAdrian Hunter def columnAlignment(self, column): 658*254c0d82SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 659*254c0d82SAdrian Hunter return alignment[column] 660*254c0d82SAdrian Hunter 661*254c0d82SAdrian Hunter def DoFindSelect(self, query, match): 662*254c0d82SAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 663*254c0d82SAdrian Hunter " FROM calls" 664*254c0d82SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 665*254c0d82SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 666*254c0d82SAdrian Hunter " WHERE symbols.name" + match + 667*254c0d82SAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 668*254c0d82SAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 669*254c0d82SAdrian Hunter 670*254c0d82SAdrian Hunter def FindPath(self, query): 671*254c0d82SAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 672*254c0d82SAdrian Hunter # to open the tree at the right place. 673*254c0d82SAdrian Hunter ids = [] 674*254c0d82SAdrian Hunter parent_id = query.value(0) 675*254c0d82SAdrian Hunter while parent_id: 676*254c0d82SAdrian Hunter ids.insert(0, parent_id) 677*254c0d82SAdrian Hunter q2 = QSqlQuery(self.glb.db) 678*254c0d82SAdrian Hunter QueryExec(q2, "SELECT parent_id" 679*254c0d82SAdrian Hunter " FROM call_paths" 680*254c0d82SAdrian Hunter " WHERE id = " + str(parent_id)) 681*254c0d82SAdrian Hunter if not q2.next(): 682*254c0d82SAdrian Hunter break 683*254c0d82SAdrian Hunter parent_id = q2.value(0) 684*254c0d82SAdrian Hunter # The call path root is not used 685*254c0d82SAdrian Hunter if ids[0] == 1: 686*254c0d82SAdrian Hunter del ids[0] 687*254c0d82SAdrian Hunter ids.insert(0, query.value(2)) 688*254c0d82SAdrian Hunter ids.insert(0, query.value(1)) 689*254c0d82SAdrian Hunter return ids 690*254c0d82SAdrian Hunter 691ebd70c7dSAdrian Hunter# Vertical widget layout 692ebd70c7dSAdrian Hunter 693ebd70c7dSAdrian Hunterclass VBox(): 694ebd70c7dSAdrian Hunter 695ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 696ebd70c7dSAdrian Hunter self.vbox = QWidget() 697ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 698ebd70c7dSAdrian Hunter 699ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 700ebd70c7dSAdrian Hunter 701ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 702ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 703ebd70c7dSAdrian Hunter if w3: 704ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 705ebd70c7dSAdrian Hunter 706ebd70c7dSAdrian Hunter def Widget(self): 707ebd70c7dSAdrian Hunter return self.vbox 708ebd70c7dSAdrian Hunter 709a731cc4cSAdrian Hunter# Tree window base 7101beb5c7bSAdrian Hunter 711a731cc4cSAdrian Hunterclass TreeWindowBase(QMdiSubWindow): 7121beb5c7bSAdrian Hunter 713a731cc4cSAdrian Hunter def __init__(self, parent=None): 714a731cc4cSAdrian Hunter super(TreeWindowBase, self).__init__(parent) 7151beb5c7bSAdrian Hunter 716a731cc4cSAdrian Hunter self.model = None 717a731cc4cSAdrian Hunter self.view = None 718a731cc4cSAdrian Hunter self.find_bar = None 7191beb5c7bSAdrian Hunter 720ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 721ebd70c7dSAdrian Hunter if not len(ids): 722ebd70c7dSAdrian Hunter return False 723ebd70c7dSAdrian Hunter parent = QModelIndex() 724ebd70c7dSAdrian Hunter for dbid in ids: 725ebd70c7dSAdrian Hunter found = False 726ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 727ebd70c7dSAdrian Hunter for row in xrange(n): 728ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 729ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 730ebd70c7dSAdrian Hunter found = True 731ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 732ebd70c7dSAdrian Hunter parent = child 733ebd70c7dSAdrian Hunter break 734ebd70c7dSAdrian Hunter if not found: 735ebd70c7dSAdrian Hunter break 736ebd70c7dSAdrian Hunter return found 737ebd70c7dSAdrian Hunter 738ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 739ebd70c7dSAdrian Hunter self.view.setFocus() 740ebd70c7dSAdrian Hunter self.find_bar.Busy() 741ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 742ebd70c7dSAdrian Hunter 743ebd70c7dSAdrian Hunter def FindDone(self, ids): 744ebd70c7dSAdrian Hunter found = True 745ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 746ebd70c7dSAdrian Hunter found = False 747ebd70c7dSAdrian Hunter self.find_bar.Idle() 748ebd70c7dSAdrian Hunter if not found: 749ebd70c7dSAdrian Hunter self.find_bar.NotFound() 750ebd70c7dSAdrian Hunter 751a731cc4cSAdrian Hunter 752a731cc4cSAdrian Hunter# Context-sensitive call graph window 753a731cc4cSAdrian Hunter 754a731cc4cSAdrian Hunterclass CallGraphWindow(TreeWindowBase): 755a731cc4cSAdrian Hunter 756a731cc4cSAdrian Hunter def __init__(self, glb, parent=None): 757a731cc4cSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 758a731cc4cSAdrian Hunter 759a731cc4cSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 760a731cc4cSAdrian Hunter 761a731cc4cSAdrian Hunter self.view = QTreeView() 762a731cc4cSAdrian Hunter self.view.setModel(self.model) 763a731cc4cSAdrian Hunter 764a731cc4cSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 765a731cc4cSAdrian Hunter self.view.setColumnWidth(c, w) 766a731cc4cSAdrian Hunter 767a731cc4cSAdrian Hunter self.find_bar = FindBar(self, self) 768a731cc4cSAdrian Hunter 769a731cc4cSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 770a731cc4cSAdrian Hunter 771a731cc4cSAdrian Hunter self.setWidget(self.vbox.Widget()) 772a731cc4cSAdrian Hunter 773a731cc4cSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 774a731cc4cSAdrian Hunter 7758392b74bSAdrian Hunter# Child data item finder 7768392b74bSAdrian Hunter 7778392b74bSAdrian Hunterclass ChildDataItemFinder(): 7788392b74bSAdrian Hunter 7798392b74bSAdrian Hunter def __init__(self, root): 7808392b74bSAdrian Hunter self.root = root 7818392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 7828392b74bSAdrian Hunter self.rows = [] 7838392b74bSAdrian Hunter self.pos = 0 7848392b74bSAdrian Hunter 7858392b74bSAdrian Hunter def FindSelect(self): 7868392b74bSAdrian Hunter self.rows = [] 7878392b74bSAdrian Hunter if self.pattern: 7888392b74bSAdrian Hunter pattern = re.compile(self.value) 7898392b74bSAdrian Hunter for child in self.root.child_items: 7908392b74bSAdrian Hunter for column_data in child.data: 7918392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 7928392b74bSAdrian Hunter self.rows.append(child.row) 7938392b74bSAdrian Hunter break 7948392b74bSAdrian Hunter else: 7958392b74bSAdrian Hunter for child in self.root.child_items: 7968392b74bSAdrian Hunter for column_data in child.data: 7978392b74bSAdrian Hunter if self.value in str(column_data): 7988392b74bSAdrian Hunter self.rows.append(child.row) 7998392b74bSAdrian Hunter break 8008392b74bSAdrian Hunter 8018392b74bSAdrian Hunter def FindValue(self): 8028392b74bSAdrian Hunter self.pos = 0 8038392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 8048392b74bSAdrian Hunter self.FindSelect() 8058392b74bSAdrian Hunter if not len(self.rows): 8068392b74bSAdrian Hunter return -1 8078392b74bSAdrian Hunter return self.rows[self.pos] 8088392b74bSAdrian Hunter 8098392b74bSAdrian Hunter def FindThread(self): 8108392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 8118392b74bSAdrian Hunter row = self.FindValue() 8128392b74bSAdrian Hunter elif len(self.rows): 8138392b74bSAdrian Hunter if self.direction > 0: 8148392b74bSAdrian Hunter self.pos += 1 8158392b74bSAdrian Hunter if self.pos >= len(self.rows): 8168392b74bSAdrian Hunter self.pos = 0 8178392b74bSAdrian Hunter else: 8188392b74bSAdrian Hunter self.pos -= 1 8198392b74bSAdrian Hunter if self.pos < 0: 8208392b74bSAdrian Hunter self.pos = len(self.rows) - 1 8218392b74bSAdrian Hunter row = self.rows[self.pos] 8228392b74bSAdrian Hunter else: 8238392b74bSAdrian Hunter row = -1 8248392b74bSAdrian Hunter return (True, row) 8258392b74bSAdrian Hunter 8268392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 8278392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 8288392b74bSAdrian Hunter # Use a thread so the UI is not blocked 8298392b74bSAdrian Hunter thread = Thread(self.FindThread) 8308392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 8318392b74bSAdrian Hunter thread.start() 8328392b74bSAdrian Hunter 8338392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 8348392b74bSAdrian Hunter callback(row) 8358392b74bSAdrian Hunter 8368392b74bSAdrian Hunter# Number of database records to fetch in one go 8378392b74bSAdrian Hunter 8388392b74bSAdrian Hunterglb_chunk_sz = 10000 8398392b74bSAdrian Hunter 8408392b74bSAdrian Hunter# size of pickled integer big enough for record size 8418392b74bSAdrian Hunter 8428392b74bSAdrian Hunterglb_nsz = 8 8438392b74bSAdrian Hunter 8448392b74bSAdrian Hunter# Background process for SQL data fetcher 8458392b74bSAdrian Hunter 8468392b74bSAdrian Hunterclass SQLFetcherProcess(): 8478392b74bSAdrian Hunter 8488392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 8498392b74bSAdrian Hunter # Need a unique connection name 8508392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 8518392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 8528392b74bSAdrian Hunter self.sql = sql 8538392b74bSAdrian Hunter self.buffer = buffer 8548392b74bSAdrian Hunter self.head = head 8558392b74bSAdrian Hunter self.tail = tail 8568392b74bSAdrian Hunter self.fetch_count = fetch_count 8578392b74bSAdrian Hunter self.fetching_done = fetching_done 8588392b74bSAdrian Hunter self.process_target = process_target 8598392b74bSAdrian Hunter self.wait_event = wait_event 8608392b74bSAdrian Hunter self.fetched_event = fetched_event 8618392b74bSAdrian Hunter self.prep = prep 8628392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 8638392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 8648392b74bSAdrian Hunter self.last_id = -1 8658392b74bSAdrian Hunter self.fetched = 0 8668392b74bSAdrian Hunter self.more = True 8678392b74bSAdrian Hunter self.local_head = self.head.value 8688392b74bSAdrian Hunter self.local_tail = self.tail.value 8698392b74bSAdrian Hunter 8708392b74bSAdrian Hunter def Select(self): 8718392b74bSAdrian Hunter if self.query_limit: 8728392b74bSAdrian Hunter if self.query_limit == 1: 8738392b74bSAdrian Hunter return 8748392b74bSAdrian Hunter self.query_limit -= 1 8758392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 8768392b74bSAdrian Hunter QueryExec(self.query, stmt) 8778392b74bSAdrian Hunter 8788392b74bSAdrian Hunter def Next(self): 8798392b74bSAdrian Hunter if not self.query.next(): 8808392b74bSAdrian Hunter self.Select() 8818392b74bSAdrian Hunter if not self.query.next(): 8828392b74bSAdrian Hunter return None 8838392b74bSAdrian Hunter self.last_id = self.query.value(0) 8848392b74bSAdrian Hunter return self.prep(self.query) 8858392b74bSAdrian Hunter 8868392b74bSAdrian Hunter def WaitForTarget(self): 8878392b74bSAdrian Hunter while True: 8888392b74bSAdrian Hunter self.wait_event.clear() 8898392b74bSAdrian Hunter target = self.process_target.value 8908392b74bSAdrian Hunter if target > self.fetched or target < 0: 8918392b74bSAdrian Hunter break 8928392b74bSAdrian Hunter self.wait_event.wait() 8938392b74bSAdrian Hunter return target 8948392b74bSAdrian Hunter 8958392b74bSAdrian Hunter def HasSpace(self, sz): 8968392b74bSAdrian Hunter if self.local_tail <= self.local_head: 8978392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 8988392b74bSAdrian Hunter if space > sz: 8998392b74bSAdrian Hunter return True 9008392b74bSAdrian Hunter if space >= glb_nsz: 9018392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 9028392b74bSAdrian Hunter nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL) 9038392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 9048392b74bSAdrian Hunter self.local_head = 0 9058392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 9068392b74bSAdrian Hunter return True 9078392b74bSAdrian Hunter return False 9088392b74bSAdrian Hunter 9098392b74bSAdrian Hunter def WaitForSpace(self, sz): 9108392b74bSAdrian Hunter if self.HasSpace(sz): 9118392b74bSAdrian Hunter return 9128392b74bSAdrian Hunter while True: 9138392b74bSAdrian Hunter self.wait_event.clear() 9148392b74bSAdrian Hunter self.local_tail = self.tail.value 9158392b74bSAdrian Hunter if self.HasSpace(sz): 9168392b74bSAdrian Hunter return 9178392b74bSAdrian Hunter self.wait_event.wait() 9188392b74bSAdrian Hunter 9198392b74bSAdrian Hunter def AddToBuffer(self, obj): 9208392b74bSAdrian Hunter d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL) 9218392b74bSAdrian Hunter n = len(d) 9228392b74bSAdrian Hunter nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL) 9238392b74bSAdrian Hunter sz = n + glb_nsz 9248392b74bSAdrian Hunter self.WaitForSpace(sz) 9258392b74bSAdrian Hunter pos = self.local_head 9268392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 9278392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 9288392b74bSAdrian Hunter self.local_head += sz 9298392b74bSAdrian Hunter 9308392b74bSAdrian Hunter def FetchBatch(self, batch_size): 9318392b74bSAdrian Hunter fetched = 0 9328392b74bSAdrian Hunter while batch_size > fetched: 9338392b74bSAdrian Hunter obj = self.Next() 9348392b74bSAdrian Hunter if obj is None: 9358392b74bSAdrian Hunter self.more = False 9368392b74bSAdrian Hunter break 9378392b74bSAdrian Hunter self.AddToBuffer(obj) 9388392b74bSAdrian Hunter fetched += 1 9398392b74bSAdrian Hunter if fetched: 9408392b74bSAdrian Hunter self.fetched += fetched 9418392b74bSAdrian Hunter with self.fetch_count.get_lock(): 9428392b74bSAdrian Hunter self.fetch_count.value += fetched 9438392b74bSAdrian Hunter self.head.value = self.local_head 9448392b74bSAdrian Hunter self.fetched_event.set() 9458392b74bSAdrian Hunter 9468392b74bSAdrian Hunter def Run(self): 9478392b74bSAdrian Hunter while self.more: 9488392b74bSAdrian Hunter target = self.WaitForTarget() 9498392b74bSAdrian Hunter if target < 0: 9508392b74bSAdrian Hunter break 9518392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 9528392b74bSAdrian Hunter self.FetchBatch(batch_size) 9538392b74bSAdrian Hunter self.fetching_done.value = True 9548392b74bSAdrian Hunter self.fetched_event.set() 9558392b74bSAdrian Hunter 9568392b74bSAdrian Hunterdef SQLFetcherFn(*x): 9578392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 9588392b74bSAdrian Hunter process.Run() 9598392b74bSAdrian Hunter 9608392b74bSAdrian Hunter# SQL data fetcher 9618392b74bSAdrian Hunter 9628392b74bSAdrian Hunterclass SQLFetcher(QObject): 9638392b74bSAdrian Hunter 9648392b74bSAdrian Hunter done = Signal(object) 9658392b74bSAdrian Hunter 9668392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 9678392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 9688392b74bSAdrian Hunter self.process_data = process_data 9698392b74bSAdrian Hunter self.more = True 9708392b74bSAdrian Hunter self.target = 0 9718392b74bSAdrian Hunter self.last_target = 0 9728392b74bSAdrian Hunter self.fetched = 0 9738392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 9748392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 9758392b74bSAdrian Hunter self.head = Value(c_longlong) 9768392b74bSAdrian Hunter self.tail = Value(c_longlong) 9778392b74bSAdrian Hunter self.local_tail = 0 9788392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 9798392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 9808392b74bSAdrian Hunter self.last_count = 0 9818392b74bSAdrian Hunter self.process_target = Value(c_longlong) 9828392b74bSAdrian Hunter self.wait_event = Event() 9838392b74bSAdrian Hunter self.fetched_event = Event() 9848392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 9858392b74bSAdrian 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)) 9868392b74bSAdrian Hunter self.process.start() 9878392b74bSAdrian Hunter self.thread = Thread(self.Thread) 9888392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 9898392b74bSAdrian Hunter self.thread.start() 9908392b74bSAdrian Hunter 9918392b74bSAdrian Hunter def Shutdown(self): 9928392b74bSAdrian Hunter # Tell the thread and process to exit 9938392b74bSAdrian Hunter self.process_target.value = -1 9948392b74bSAdrian Hunter self.wait_event.set() 9958392b74bSAdrian Hunter self.more = False 9968392b74bSAdrian Hunter self.fetching_done.value = True 9978392b74bSAdrian Hunter self.fetched_event.set() 9988392b74bSAdrian Hunter 9998392b74bSAdrian Hunter def Thread(self): 10008392b74bSAdrian Hunter if not self.more: 10018392b74bSAdrian Hunter return True, 0 10028392b74bSAdrian Hunter while True: 10038392b74bSAdrian Hunter self.fetched_event.clear() 10048392b74bSAdrian Hunter fetch_count = self.fetch_count.value 10058392b74bSAdrian Hunter if fetch_count != self.last_count: 10068392b74bSAdrian Hunter break 10078392b74bSAdrian Hunter if self.fetching_done.value: 10088392b74bSAdrian Hunter self.more = False 10098392b74bSAdrian Hunter return True, 0 10108392b74bSAdrian Hunter self.fetched_event.wait() 10118392b74bSAdrian Hunter count = fetch_count - self.last_count 10128392b74bSAdrian Hunter self.last_count = fetch_count 10138392b74bSAdrian Hunter self.fetched += count 10148392b74bSAdrian Hunter return False, count 10158392b74bSAdrian Hunter 10168392b74bSAdrian Hunter def Fetch(self, nr): 10178392b74bSAdrian Hunter if not self.more: 10188392b74bSAdrian Hunter # -1 inidcates there are no more 10198392b74bSAdrian Hunter return -1 10208392b74bSAdrian Hunter result = self.fetched 10218392b74bSAdrian Hunter extra = result + nr - self.target 10228392b74bSAdrian Hunter if extra > 0: 10238392b74bSAdrian Hunter self.target += extra 10248392b74bSAdrian Hunter # process_target < 0 indicates shutting down 10258392b74bSAdrian Hunter if self.process_target.value >= 0: 10268392b74bSAdrian Hunter self.process_target.value = self.target 10278392b74bSAdrian Hunter self.wait_event.set() 10288392b74bSAdrian Hunter return result 10298392b74bSAdrian Hunter 10308392b74bSAdrian Hunter def RemoveFromBuffer(self): 10318392b74bSAdrian Hunter pos = self.local_tail 10328392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 10338392b74bSAdrian Hunter pos = 0 10348392b74bSAdrian Hunter n = cPickle.loads(self.buffer[pos : pos + glb_nsz]) 10358392b74bSAdrian Hunter if n == 0: 10368392b74bSAdrian Hunter pos = 0 10378392b74bSAdrian Hunter n = cPickle.loads(self.buffer[0 : glb_nsz]) 10388392b74bSAdrian Hunter pos += glb_nsz 10398392b74bSAdrian Hunter obj = cPickle.loads(self.buffer[pos : pos + n]) 10408392b74bSAdrian Hunter self.local_tail = pos + n 10418392b74bSAdrian Hunter return obj 10428392b74bSAdrian Hunter 10438392b74bSAdrian Hunter def ProcessData(self, count): 10448392b74bSAdrian Hunter for i in xrange(count): 10458392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 10468392b74bSAdrian Hunter self.process_data(obj) 10478392b74bSAdrian Hunter self.tail.value = self.local_tail 10488392b74bSAdrian Hunter self.wait_event.set() 10498392b74bSAdrian Hunter self.done.emit(count) 10508392b74bSAdrian Hunter 10518392b74bSAdrian Hunter# Fetch more records bar 10528392b74bSAdrian Hunter 10538392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 10548392b74bSAdrian Hunter 10558392b74bSAdrian Hunter def __init__(self, model, parent): 10568392b74bSAdrian Hunter self.model = model 10578392b74bSAdrian Hunter 10588392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 10598392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10608392b74bSAdrian Hunter 10618392b74bSAdrian Hunter self.fetch_count = QSpinBox() 10628392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 10638392b74bSAdrian Hunter self.fetch_count.setValue(10) 10648392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10658392b74bSAdrian Hunter 10668392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 10678392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10688392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 10698392b74bSAdrian Hunter 10708392b74bSAdrian Hunter self.progress = QProgressBar() 10718392b74bSAdrian Hunter self.progress.setRange(0, 100) 10728392b74bSAdrian Hunter self.progress.hide() 10738392b74bSAdrian Hunter 10748392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 10758392b74bSAdrian Hunter self.done_label.hide() 10768392b74bSAdrian Hunter 10778392b74bSAdrian Hunter self.spacer = QLabel("") 10788392b74bSAdrian Hunter 10798392b74bSAdrian Hunter self.close_button = QToolButton() 10808392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 10818392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 10828392b74bSAdrian Hunter 10838392b74bSAdrian Hunter self.hbox = QHBoxLayout() 10848392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 10858392b74bSAdrian Hunter 10868392b74bSAdrian Hunter self.hbox.addWidget(self.label) 10878392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 10888392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 10898392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 10908392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 10918392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 10928392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 10938392b74bSAdrian Hunter 10948392b74bSAdrian Hunter self.bar = QWidget() 10958392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 10968392b74bSAdrian Hunter self.bar.show() 10978392b74bSAdrian Hunter 10988392b74bSAdrian Hunter self.in_progress = False 10998392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 11008392b74bSAdrian Hunter 11018392b74bSAdrian Hunter self.done = False 11028392b74bSAdrian Hunter 11038392b74bSAdrian Hunter if not model.HasMoreRecords(): 11048392b74bSAdrian Hunter self.Done() 11058392b74bSAdrian Hunter 11068392b74bSAdrian Hunter def Widget(self): 11078392b74bSAdrian Hunter return self.bar 11088392b74bSAdrian Hunter 11098392b74bSAdrian Hunter def Activate(self): 11108392b74bSAdrian Hunter self.bar.show() 11118392b74bSAdrian Hunter self.fetch.setFocus() 11128392b74bSAdrian Hunter 11138392b74bSAdrian Hunter def Deactivate(self): 11148392b74bSAdrian Hunter self.bar.hide() 11158392b74bSAdrian Hunter 11168392b74bSAdrian Hunter def Enable(self, enable): 11178392b74bSAdrian Hunter self.fetch.setEnabled(enable) 11188392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 11198392b74bSAdrian Hunter 11208392b74bSAdrian Hunter def Busy(self): 11218392b74bSAdrian Hunter self.Enable(False) 11228392b74bSAdrian Hunter self.fetch.hide() 11238392b74bSAdrian Hunter self.spacer.hide() 11248392b74bSAdrian Hunter self.progress.show() 11258392b74bSAdrian Hunter 11268392b74bSAdrian Hunter def Idle(self): 11278392b74bSAdrian Hunter self.in_progress = False 11288392b74bSAdrian Hunter self.Enable(True) 11298392b74bSAdrian Hunter self.progress.hide() 11308392b74bSAdrian Hunter self.fetch.show() 11318392b74bSAdrian Hunter self.spacer.show() 11328392b74bSAdrian Hunter 11338392b74bSAdrian Hunter def Target(self): 11348392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 11358392b74bSAdrian Hunter 11368392b74bSAdrian Hunter def Done(self): 11378392b74bSAdrian Hunter self.done = True 11388392b74bSAdrian Hunter self.Idle() 11398392b74bSAdrian Hunter self.label.hide() 11408392b74bSAdrian Hunter self.fetch_count.hide() 11418392b74bSAdrian Hunter self.fetch.hide() 11428392b74bSAdrian Hunter self.spacer.hide() 11438392b74bSAdrian Hunter self.done_label.show() 11448392b74bSAdrian Hunter 11458392b74bSAdrian Hunter def Progress(self, count): 11468392b74bSAdrian Hunter if self.in_progress: 11478392b74bSAdrian Hunter if count: 11488392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 11498392b74bSAdrian Hunter if percent >= 100: 11508392b74bSAdrian Hunter self.Idle() 11518392b74bSAdrian Hunter else: 11528392b74bSAdrian Hunter self.progress.setValue(percent) 11538392b74bSAdrian Hunter if not count: 11548392b74bSAdrian Hunter # Count value of zero means no more records 11558392b74bSAdrian Hunter self.Done() 11568392b74bSAdrian Hunter 11578392b74bSAdrian Hunter def FetchMoreRecords(self): 11588392b74bSAdrian Hunter if self.done: 11598392b74bSAdrian Hunter return 11608392b74bSAdrian Hunter self.progress.setValue(0) 11618392b74bSAdrian Hunter self.Busy() 11628392b74bSAdrian Hunter self.in_progress = True 11638392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 11648392b74bSAdrian Hunter 116576099f98SAdrian Hunter# Brance data model level two item 116676099f98SAdrian Hunter 116776099f98SAdrian Hunterclass BranchLevelTwoItem(): 116876099f98SAdrian Hunter 116976099f98SAdrian Hunter def __init__(self, row, text, parent_item): 117076099f98SAdrian Hunter self.row = row 117176099f98SAdrian Hunter self.parent_item = parent_item 117276099f98SAdrian Hunter self.data = [""] * 8 117376099f98SAdrian Hunter self.data[7] = text 117476099f98SAdrian Hunter self.level = 2 117576099f98SAdrian Hunter 117676099f98SAdrian Hunter def getParentItem(self): 117776099f98SAdrian Hunter return self.parent_item 117876099f98SAdrian Hunter 117976099f98SAdrian Hunter def getRow(self): 118076099f98SAdrian Hunter return self.row 118176099f98SAdrian Hunter 118276099f98SAdrian Hunter def childCount(self): 118376099f98SAdrian Hunter return 0 118476099f98SAdrian Hunter 118576099f98SAdrian Hunter def hasChildren(self): 118676099f98SAdrian Hunter return False 118776099f98SAdrian Hunter 118876099f98SAdrian Hunter def getData(self, column): 118976099f98SAdrian Hunter return self.data[column] 119076099f98SAdrian Hunter 119176099f98SAdrian Hunter# Brance data model level one item 119276099f98SAdrian Hunter 119376099f98SAdrian Hunterclass BranchLevelOneItem(): 119476099f98SAdrian Hunter 119576099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 119676099f98SAdrian Hunter self.glb = glb 119776099f98SAdrian Hunter self.row = row 119876099f98SAdrian Hunter self.parent_item = parent_item 119976099f98SAdrian Hunter self.child_count = 0 120076099f98SAdrian Hunter self.child_items = [] 120176099f98SAdrian Hunter self.data = data[1:] 120276099f98SAdrian Hunter self.dbid = data[0] 120376099f98SAdrian Hunter self.level = 1 120476099f98SAdrian Hunter self.query_done = False 120576099f98SAdrian Hunter 120676099f98SAdrian Hunter def getChildItem(self, row): 120776099f98SAdrian Hunter return self.child_items[row] 120876099f98SAdrian Hunter 120976099f98SAdrian Hunter def getParentItem(self): 121076099f98SAdrian Hunter return self.parent_item 121176099f98SAdrian Hunter 121276099f98SAdrian Hunter def getRow(self): 121376099f98SAdrian Hunter return self.row 121476099f98SAdrian Hunter 121576099f98SAdrian Hunter def Select(self): 121676099f98SAdrian Hunter self.query_done = True 121776099f98SAdrian Hunter 121876099f98SAdrian Hunter if not self.glb.have_disassembler: 121976099f98SAdrian Hunter return 122076099f98SAdrian Hunter 122176099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 122276099f98SAdrian Hunter 122376099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 122476099f98SAdrian Hunter " FROM samples" 122576099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 122676099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 122776099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 122876099f98SAdrian Hunter if not query.next(): 122976099f98SAdrian Hunter return 123076099f98SAdrian Hunter cpu = query.value(0) 123176099f98SAdrian Hunter dso = query.value(1) 123276099f98SAdrian Hunter sym = query.value(2) 123376099f98SAdrian Hunter if dso == 0 or sym == 0: 123476099f98SAdrian Hunter return 123576099f98SAdrian Hunter off = query.value(3) 123676099f98SAdrian Hunter short_name = query.value(4) 123776099f98SAdrian Hunter long_name = query.value(5) 123876099f98SAdrian Hunter build_id = query.value(6) 123976099f98SAdrian Hunter sym_start = query.value(7) 124076099f98SAdrian Hunter ip = query.value(8) 124176099f98SAdrian Hunter 124276099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 124376099f98SAdrian Hunter " FROM samples" 124476099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 124576099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 124676099f98SAdrian Hunter " ORDER BY samples.id" 124776099f98SAdrian Hunter " LIMIT 1") 124876099f98SAdrian Hunter if not query.next(): 124976099f98SAdrian Hunter return 125076099f98SAdrian Hunter if query.value(0) != dso: 125176099f98SAdrian Hunter # Cannot disassemble from one dso to another 125276099f98SAdrian Hunter return 125376099f98SAdrian Hunter bsym = query.value(1) 125476099f98SAdrian Hunter boff = query.value(2) 125576099f98SAdrian Hunter bsym_start = query.value(3) 125676099f98SAdrian Hunter if bsym == 0: 125776099f98SAdrian Hunter return 125876099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 125976099f98SAdrian Hunter if tot <= 0 or tot > 16384: 126076099f98SAdrian Hunter return 126176099f98SAdrian Hunter 126276099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 126376099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 126476099f98SAdrian Hunter if not f: 126576099f98SAdrian Hunter return 126676099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 126776099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 126876099f98SAdrian Hunter 126976099f98SAdrian Hunter buf_sz = tot + 16 127076099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 127176099f98SAdrian Hunter f.seek(sym_start + off) 127276099f98SAdrian Hunter buf.value = f.read(buf_sz) 127376099f98SAdrian Hunter buf_ptr = addressof(buf) 127476099f98SAdrian Hunter i = 0 127576099f98SAdrian Hunter while tot > 0: 127676099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 127776099f98SAdrian Hunter if cnt: 127876099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 127976099f98SAdrian Hunter for k in xrange(cnt): 128076099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 128176099f98SAdrian Hunter i += 1 128276099f98SAdrian Hunter while k < 15: 128376099f98SAdrian Hunter byte_str += " " 128476099f98SAdrian Hunter k += 1 128576099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 128676099f98SAdrian Hunter self.child_count += 1 128776099f98SAdrian Hunter else: 128876099f98SAdrian Hunter return 128976099f98SAdrian Hunter buf_ptr += cnt 129076099f98SAdrian Hunter tot -= cnt 129176099f98SAdrian Hunter buf_sz -= cnt 129276099f98SAdrian Hunter ip += cnt 129376099f98SAdrian Hunter 129476099f98SAdrian Hunter def childCount(self): 129576099f98SAdrian Hunter if not self.query_done: 129676099f98SAdrian Hunter self.Select() 129776099f98SAdrian Hunter if not self.child_count: 129876099f98SAdrian Hunter return -1 129976099f98SAdrian Hunter return self.child_count 130076099f98SAdrian Hunter 130176099f98SAdrian Hunter def hasChildren(self): 130276099f98SAdrian Hunter if not self.query_done: 130376099f98SAdrian Hunter return True 130476099f98SAdrian Hunter return self.child_count > 0 130576099f98SAdrian Hunter 130676099f98SAdrian Hunter def getData(self, column): 130776099f98SAdrian Hunter return self.data[column] 130876099f98SAdrian Hunter 130976099f98SAdrian Hunter# Brance data model root item 131076099f98SAdrian Hunter 131176099f98SAdrian Hunterclass BranchRootItem(): 131276099f98SAdrian Hunter 131376099f98SAdrian Hunter def __init__(self): 131476099f98SAdrian Hunter self.child_count = 0 131576099f98SAdrian Hunter self.child_items = [] 131676099f98SAdrian Hunter self.level = 0 131776099f98SAdrian Hunter 131876099f98SAdrian Hunter def getChildItem(self, row): 131976099f98SAdrian Hunter return self.child_items[row] 132076099f98SAdrian Hunter 132176099f98SAdrian Hunter def getParentItem(self): 132276099f98SAdrian Hunter return None 132376099f98SAdrian Hunter 132476099f98SAdrian Hunter def getRow(self): 132576099f98SAdrian Hunter return 0 132676099f98SAdrian Hunter 132776099f98SAdrian Hunter def childCount(self): 132876099f98SAdrian Hunter return self.child_count 132976099f98SAdrian Hunter 133076099f98SAdrian Hunter def hasChildren(self): 133176099f98SAdrian Hunter return self.child_count > 0 133276099f98SAdrian Hunter 133376099f98SAdrian Hunter def getData(self, column): 133476099f98SAdrian Hunter return "" 133576099f98SAdrian Hunter 133676099f98SAdrian Hunter# Branch data preparation 133776099f98SAdrian Hunter 133876099f98SAdrian Hunterdef BranchDataPrep(query): 133976099f98SAdrian Hunter data = [] 134076099f98SAdrian Hunter for i in xrange(0, 8): 134176099f98SAdrian Hunter data.append(query.value(i)) 134276099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 134376099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 134476099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 134576099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 134676099f98SAdrian Hunter return data 134776099f98SAdrian Hunter 134876099f98SAdrian Hunter# Branch data model 134976099f98SAdrian Hunter 135076099f98SAdrian Hunterclass BranchModel(TreeModel): 135176099f98SAdrian Hunter 135276099f98SAdrian Hunter progress = Signal(object) 135376099f98SAdrian Hunter 135476099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 1355a448ba23SAdrian Hunter super(BranchModel, self).__init__(glb, parent) 135676099f98SAdrian Hunter self.event_id = event_id 135776099f98SAdrian Hunter self.more = True 135876099f98SAdrian Hunter self.populated = 0 135976099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 136076099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 136176099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 136276099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 136376099f98SAdrian Hunter " FROM samples" 136476099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 136576099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 136676099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 136776099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 136876099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 136976099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 137076099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 137176099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 137276099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 137376099f98SAdrian Hunter " ORDER BY samples.id" 137476099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 137576099f98SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample) 137676099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 137776099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 137876099f98SAdrian Hunter 1379a448ba23SAdrian Hunter def GetRoot(self): 1380a448ba23SAdrian Hunter return BranchRootItem() 1381a448ba23SAdrian Hunter 138276099f98SAdrian Hunter def columnCount(self, parent=None): 138376099f98SAdrian Hunter return 8 138476099f98SAdrian Hunter 138576099f98SAdrian Hunter def columnHeader(self, column): 138676099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 138776099f98SAdrian Hunter 138876099f98SAdrian Hunter def columnFont(self, column): 138976099f98SAdrian Hunter if column != 7: 139076099f98SAdrian Hunter return None 139176099f98SAdrian Hunter return QFont("Monospace") 139276099f98SAdrian Hunter 139376099f98SAdrian Hunter def DisplayData(self, item, index): 139476099f98SAdrian Hunter if item.level == 1: 139576099f98SAdrian Hunter self.FetchIfNeeded(item.row) 139676099f98SAdrian Hunter return item.getData(index.column()) 139776099f98SAdrian Hunter 139876099f98SAdrian Hunter def AddSample(self, data): 139976099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 140076099f98SAdrian Hunter self.root.child_items.append(child) 140176099f98SAdrian Hunter self.populated += 1 140276099f98SAdrian Hunter 140376099f98SAdrian Hunter def Update(self, fetched): 140476099f98SAdrian Hunter if not fetched: 140576099f98SAdrian Hunter self.more = False 140676099f98SAdrian Hunter self.progress.emit(0) 140776099f98SAdrian Hunter child_count = self.root.child_count 140876099f98SAdrian Hunter count = self.populated - child_count 140976099f98SAdrian Hunter if count > 0: 141076099f98SAdrian Hunter parent = QModelIndex() 141176099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 141276099f98SAdrian Hunter self.insertRows(child_count, count, parent) 141376099f98SAdrian Hunter self.root.child_count += count 141476099f98SAdrian Hunter self.endInsertRows() 141576099f98SAdrian Hunter self.progress.emit(self.root.child_count) 141676099f98SAdrian Hunter 141776099f98SAdrian Hunter def FetchMoreRecords(self, count): 141876099f98SAdrian Hunter current = self.root.child_count 141976099f98SAdrian Hunter if self.more: 142076099f98SAdrian Hunter self.fetcher.Fetch(count) 142176099f98SAdrian Hunter else: 142276099f98SAdrian Hunter self.progress.emit(0) 142376099f98SAdrian Hunter return current 142476099f98SAdrian Hunter 142576099f98SAdrian Hunter def HasMoreRecords(self): 142676099f98SAdrian Hunter return self.more 142776099f98SAdrian Hunter 14280bf0947aSAdrian Hunter# Report Variables 14290bf0947aSAdrian Hunter 14300bf0947aSAdrian Hunterclass ReportVars(): 14310bf0947aSAdrian Hunter 1432cd358012SAdrian Hunter def __init__(self, name = "", where_clause = "", limit = ""): 1433947cc38dSAdrian Hunter self.name = name 14340bf0947aSAdrian Hunter self.where_clause = where_clause 1435cd358012SAdrian Hunter self.limit = limit 14360bf0947aSAdrian Hunter 14370bf0947aSAdrian Hunter def UniqueId(self): 1438cd358012SAdrian Hunter return str(self.where_clause + ";" + self.limit) 14390bf0947aSAdrian Hunter 144076099f98SAdrian Hunter# Branch window 144176099f98SAdrian Hunter 144276099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 144376099f98SAdrian Hunter 1444947cc38dSAdrian Hunter def __init__(self, glb, event_id, report_vars, parent=None): 144576099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 144676099f98SAdrian Hunter 14470bf0947aSAdrian Hunter model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() 144876099f98SAdrian Hunter 14490bf0947aSAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 145076099f98SAdrian Hunter 145176099f98SAdrian Hunter self.view = QTreeView() 145276099f98SAdrian Hunter self.view.setUniformRowHeights(True) 145376099f98SAdrian Hunter self.view.setModel(self.model) 145476099f98SAdrian Hunter 145576099f98SAdrian Hunter self.ResizeColumnsToContents() 145676099f98SAdrian Hunter 145776099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 145876099f98SAdrian Hunter 145976099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 146076099f98SAdrian Hunter 146176099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 146276099f98SAdrian Hunter 146376099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 146476099f98SAdrian Hunter 146576099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 146676099f98SAdrian Hunter 1467947cc38dSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 146876099f98SAdrian Hunter 146976099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 147076099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 147176099f98SAdrian Hunter # so implement a crude alternative 147276099f98SAdrian Hunter mm = "MM" if column else "MMMM" 147376099f98SAdrian Hunter font = self.view.font() 147476099f98SAdrian Hunter metrics = QFontMetrics(font) 147576099f98SAdrian Hunter max = 0 147676099f98SAdrian Hunter for row in xrange(n): 147776099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 147876099f98SAdrian Hunter len = metrics.width(str(val) + mm) 147976099f98SAdrian Hunter max = len if len > max else max 148076099f98SAdrian Hunter val = self.model.columnHeader(column) 148176099f98SAdrian Hunter len = metrics.width(str(val) + mm) 148276099f98SAdrian Hunter max = len if len > max else max 148376099f98SAdrian Hunter self.view.setColumnWidth(column, max) 148476099f98SAdrian Hunter 148576099f98SAdrian Hunter def ResizeColumnsToContents(self): 148676099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 148776099f98SAdrian Hunter if n < 1: 148876099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 148976099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 149076099f98SAdrian Hunter return 149176099f98SAdrian Hunter columns = self.model.columnCount() 149276099f98SAdrian Hunter for i in xrange(columns): 149376099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 149476099f98SAdrian Hunter 149576099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 149676099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 149776099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 149876099f98SAdrian Hunter self.ResizeColumnsToContents() 149976099f98SAdrian Hunter 150076099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 150176099f98SAdrian Hunter self.view.setFocus() 150276099f98SAdrian Hunter self.find_bar.Busy() 150376099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 150476099f98SAdrian Hunter 150576099f98SAdrian Hunter def FindDone(self, row): 150676099f98SAdrian Hunter self.find_bar.Idle() 150776099f98SAdrian Hunter if row >= 0: 150876099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 150976099f98SAdrian Hunter else: 151076099f98SAdrian Hunter self.find_bar.NotFound() 151176099f98SAdrian Hunter 15121c3ca1b3SAdrian Hunter# Line edit data item 15131c3ca1b3SAdrian Hunter 15141c3ca1b3SAdrian Hunterclass LineEditDataItem(object): 15151c3ca1b3SAdrian Hunter 1516cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 15171c3ca1b3SAdrian Hunter self.glb = glb 15181c3ca1b3SAdrian Hunter self.label = label 15191c3ca1b3SAdrian Hunter self.placeholder_text = placeholder_text 15201c3ca1b3SAdrian Hunter self.parent = parent 15211c3ca1b3SAdrian Hunter self.id = id 15221c3ca1b3SAdrian Hunter 1523cd358012SAdrian Hunter self.value = default 15241c3ca1b3SAdrian Hunter 1525cd358012SAdrian Hunter self.widget = QLineEdit(default) 15261c3ca1b3SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 15271c3ca1b3SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 15281c3ca1b3SAdrian Hunter self.red = False 15291c3ca1b3SAdrian Hunter self.error = "" 15301c3ca1b3SAdrian Hunter self.validated = True 15311c3ca1b3SAdrian Hunter 15321c3ca1b3SAdrian Hunter if placeholder_text: 15331c3ca1b3SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 15341c3ca1b3SAdrian Hunter 15351c3ca1b3SAdrian Hunter def TurnTextRed(self): 15361c3ca1b3SAdrian Hunter if not self.red: 15371c3ca1b3SAdrian Hunter palette = QPalette() 15381c3ca1b3SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 15391c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 15401c3ca1b3SAdrian Hunter self.red = True 15411c3ca1b3SAdrian Hunter 15421c3ca1b3SAdrian Hunter def TurnTextNormal(self): 15431c3ca1b3SAdrian Hunter if self.red: 15441c3ca1b3SAdrian Hunter palette = QPalette() 15451c3ca1b3SAdrian Hunter self.widget.setPalette(palette) 15461c3ca1b3SAdrian Hunter self.red = False 15471c3ca1b3SAdrian Hunter 15481c3ca1b3SAdrian Hunter def InvalidValue(self, value): 15491c3ca1b3SAdrian Hunter self.value = "" 15501c3ca1b3SAdrian Hunter self.TurnTextRed() 15511c3ca1b3SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 15521c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 15531c3ca1b3SAdrian Hunter 15541c3ca1b3SAdrian Hunter def Invalidate(self): 15551c3ca1b3SAdrian Hunter self.validated = False 15561c3ca1b3SAdrian Hunter 15571c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 15581c3ca1b3SAdrian Hunter self.value = input_string.strip() 15591c3ca1b3SAdrian Hunter 15601c3ca1b3SAdrian Hunter def Validate(self): 15611c3ca1b3SAdrian Hunter self.validated = True 15621c3ca1b3SAdrian Hunter self.error = "" 15631c3ca1b3SAdrian Hunter self.TurnTextNormal() 15641c3ca1b3SAdrian Hunter self.parent.ClearMessage() 15651c3ca1b3SAdrian Hunter input_string = self.widget.text() 15661c3ca1b3SAdrian Hunter if not len(input_string.strip()): 15671c3ca1b3SAdrian Hunter self.value = "" 15681c3ca1b3SAdrian Hunter return 15691c3ca1b3SAdrian Hunter self.DoValidate(input_string) 15701c3ca1b3SAdrian Hunter 15711c3ca1b3SAdrian Hunter def IsValid(self): 15721c3ca1b3SAdrian Hunter if not self.validated: 15731c3ca1b3SAdrian Hunter self.Validate() 15741c3ca1b3SAdrian Hunter if len(self.error): 15751c3ca1b3SAdrian Hunter self.parent.ShowMessage(self.error) 15761c3ca1b3SAdrian Hunter return False 15771c3ca1b3SAdrian Hunter return True 15781c3ca1b3SAdrian Hunter 15791c3ca1b3SAdrian Hunter def IsNumber(self, value): 15801c3ca1b3SAdrian Hunter try: 15811c3ca1b3SAdrian Hunter x = int(value) 15821c3ca1b3SAdrian Hunter except: 15831c3ca1b3SAdrian Hunter x = 0 15841c3ca1b3SAdrian Hunter return str(x) == value 15851c3ca1b3SAdrian Hunter 15861c3ca1b3SAdrian Hunter# Non-negative integer ranges dialog data item 15871c3ca1b3SAdrian Hunter 15881c3ca1b3SAdrian Hunterclass NonNegativeIntegerRangesDataItem(LineEditDataItem): 15891c3ca1b3SAdrian Hunter 15901c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 15911c3ca1b3SAdrian Hunter super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 15921c3ca1b3SAdrian Hunter 15931c3ca1b3SAdrian Hunter self.column_name = column_name 15941c3ca1b3SAdrian Hunter 15951c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 15961c3ca1b3SAdrian Hunter singles = [] 15971c3ca1b3SAdrian Hunter ranges = [] 15981c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 15991c3ca1b3SAdrian Hunter if "-" in value: 16001c3ca1b3SAdrian Hunter vrange = value.split("-") 16011c3ca1b3SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 16021c3ca1b3SAdrian Hunter return self.InvalidValue(value) 16031c3ca1b3SAdrian Hunter ranges.append(vrange) 16041c3ca1b3SAdrian Hunter else: 16051c3ca1b3SAdrian Hunter if not self.IsNumber(value): 16061c3ca1b3SAdrian Hunter return self.InvalidValue(value) 16071c3ca1b3SAdrian Hunter singles.append(value) 16081c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 16091c3ca1b3SAdrian Hunter if len(singles): 16101c3ca1b3SAdrian Hunter ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") 16111c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 16121c3ca1b3SAdrian Hunter 1613cd358012SAdrian Hunter# Positive integer dialog data item 1614cd358012SAdrian Hunter 1615cd358012SAdrian Hunterclass PositiveIntegerDataItem(LineEditDataItem): 1616cd358012SAdrian Hunter 1617cd358012SAdrian Hunter def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): 1618cd358012SAdrian Hunter super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 1619cd358012SAdrian Hunter 1620cd358012SAdrian Hunter def DoValidate(self, input_string): 1621cd358012SAdrian Hunter if not self.IsNumber(input_string.strip()): 1622cd358012SAdrian Hunter return self.InvalidValue(input_string) 1623cd358012SAdrian Hunter value = int(input_string.strip()) 1624cd358012SAdrian Hunter if value <= 0: 1625cd358012SAdrian Hunter return self.InvalidValue(input_string) 1626cd358012SAdrian Hunter self.value = str(value) 1627cd358012SAdrian Hunter 16281c3ca1b3SAdrian Hunter# Dialog data item converted and validated using a SQL table 16291c3ca1b3SAdrian Hunter 16301c3ca1b3SAdrian Hunterclass SQLTableDataItem(LineEditDataItem): 16311c3ca1b3SAdrian Hunter 16321c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 16331c3ca1b3SAdrian Hunter super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 16341c3ca1b3SAdrian Hunter 16351c3ca1b3SAdrian Hunter self.table_name = table_name 16361c3ca1b3SAdrian Hunter self.match_column = match_column 16371c3ca1b3SAdrian Hunter self.column_name1 = column_name1 16381c3ca1b3SAdrian Hunter self.column_name2 = column_name2 16391c3ca1b3SAdrian Hunter 16401c3ca1b3SAdrian Hunter def ValueToIds(self, value): 16411c3ca1b3SAdrian Hunter ids = [] 16421c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 16431c3ca1b3SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 16441c3ca1b3SAdrian Hunter ret = query.exec_(stmt) 16451c3ca1b3SAdrian Hunter if ret: 16461c3ca1b3SAdrian Hunter while query.next(): 16471c3ca1b3SAdrian Hunter ids.append(str(query.value(0))) 16481c3ca1b3SAdrian Hunter return ids 16491c3ca1b3SAdrian Hunter 16501c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 16511c3ca1b3SAdrian Hunter all_ids = [] 16521c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 16531c3ca1b3SAdrian Hunter ids = self.ValueToIds(value) 16541c3ca1b3SAdrian Hunter if len(ids): 16551c3ca1b3SAdrian Hunter all_ids.extend(ids) 16561c3ca1b3SAdrian Hunter else: 16571c3ca1b3SAdrian Hunter return self.InvalidValue(value) 16581c3ca1b3SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 16591c3ca1b3SAdrian Hunter if self.column_name2: 16601c3ca1b3SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 16611c3ca1b3SAdrian Hunter 16621c3ca1b3SAdrian Hunter# Sample time ranges dialog data item converted and validated using 'samples' SQL table 16631c3ca1b3SAdrian Hunter 16641c3ca1b3SAdrian Hunterclass SampleTimeRangesDataItem(LineEditDataItem): 16651c3ca1b3SAdrian Hunter 16661c3ca1b3SAdrian Hunter def __init__(self, glb, label, placeholder_text, column_name, parent): 16671c3ca1b3SAdrian Hunter self.column_name = column_name 16681c3ca1b3SAdrian Hunter 16691c3ca1b3SAdrian Hunter self.last_id = 0 16701c3ca1b3SAdrian Hunter self.first_time = 0 16711c3ca1b3SAdrian Hunter self.last_time = 2 ** 64 16721c3ca1b3SAdrian Hunter 16731c3ca1b3SAdrian Hunter query = QSqlQuery(glb.db) 16741c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 16751c3ca1b3SAdrian Hunter if query.next(): 16761c3ca1b3SAdrian Hunter self.last_id = int(query.value(0)) 16771c3ca1b3SAdrian Hunter self.last_time = int(query.value(1)) 16781c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 16791c3ca1b3SAdrian Hunter if query.next(): 16801c3ca1b3SAdrian Hunter self.first_time = int(query.value(0)) 16811c3ca1b3SAdrian Hunter if placeholder_text: 16821c3ca1b3SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 16831c3ca1b3SAdrian Hunter 16841c3ca1b3SAdrian Hunter super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 16851c3ca1b3SAdrian Hunter 16861c3ca1b3SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 16871c3ca1b3SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 16881c3ca1b3SAdrian Hunter if query.next(): 16891c3ca1b3SAdrian Hunter return True, int(query.value(0)) 16901c3ca1b3SAdrian Hunter else: 16911c3ca1b3SAdrian Hunter return False, 0 16921c3ca1b3SAdrian Hunter 16931c3ca1b3SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 16941c3ca1b3SAdrian Hunter query = QSqlQuery(self.glb.db) 16951c3ca1b3SAdrian Hunter while True: 16961c3ca1b3SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 16971c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 16981c3ca1b3SAdrian Hunter if not query.next(): 16991c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 17001c3ca1b3SAdrian Hunter if not ok: 17011c3ca1b3SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 17021c3ca1b3SAdrian Hunter if not ok: 17031c3ca1b3SAdrian Hunter return str(higher_id) 17041c3ca1b3SAdrian Hunter next_id = dbid 17051c3ca1b3SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 17061c3ca1b3SAdrian Hunter next_time = int(query.value(0)) 17071c3ca1b3SAdrian Hunter if get_floor: 17081c3ca1b3SAdrian Hunter if target_time > next_time: 17091c3ca1b3SAdrian Hunter lower_id = next_id 17101c3ca1b3SAdrian Hunter else: 17111c3ca1b3SAdrian Hunter higher_id = next_id 17121c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 17131c3ca1b3SAdrian Hunter return str(higher_id) 17141c3ca1b3SAdrian Hunter else: 17151c3ca1b3SAdrian Hunter if target_time >= next_time: 17161c3ca1b3SAdrian Hunter lower_id = next_id 17171c3ca1b3SAdrian Hunter else: 17181c3ca1b3SAdrian Hunter higher_id = next_id 17191c3ca1b3SAdrian Hunter if higher_id <= lower_id + 1: 17201c3ca1b3SAdrian Hunter return str(lower_id) 17211c3ca1b3SAdrian Hunter 17221c3ca1b3SAdrian Hunter def ConvertRelativeTime(self, val): 17231c3ca1b3SAdrian Hunter mult = 1 17241c3ca1b3SAdrian Hunter suffix = val[-2:] 17251c3ca1b3SAdrian Hunter if suffix == "ms": 17261c3ca1b3SAdrian Hunter mult = 1000000 17271c3ca1b3SAdrian Hunter elif suffix == "us": 17281c3ca1b3SAdrian Hunter mult = 1000 17291c3ca1b3SAdrian Hunter elif suffix == "ns": 17301c3ca1b3SAdrian Hunter mult = 1 17311c3ca1b3SAdrian Hunter else: 17321c3ca1b3SAdrian Hunter return val 17331c3ca1b3SAdrian Hunter val = val[:-2].strip() 17341c3ca1b3SAdrian Hunter if not self.IsNumber(val): 17351c3ca1b3SAdrian Hunter return val 17361c3ca1b3SAdrian Hunter val = int(val) * mult 17371c3ca1b3SAdrian Hunter if val >= 0: 17381c3ca1b3SAdrian Hunter val += self.first_time 17391c3ca1b3SAdrian Hunter else: 17401c3ca1b3SAdrian Hunter val += self.last_time 17411c3ca1b3SAdrian Hunter return str(val) 17421c3ca1b3SAdrian Hunter 17431c3ca1b3SAdrian Hunter def ConvertTimeRange(self, vrange): 17441c3ca1b3SAdrian Hunter if vrange[0] == "": 17451c3ca1b3SAdrian Hunter vrange[0] = str(self.first_time) 17461c3ca1b3SAdrian Hunter if vrange[1] == "": 17471c3ca1b3SAdrian Hunter vrange[1] = str(self.last_time) 17481c3ca1b3SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 17491c3ca1b3SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 17501c3ca1b3SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 17511c3ca1b3SAdrian Hunter return False 17521c3ca1b3SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 17531c3ca1b3SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 17541c3ca1b3SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 17551c3ca1b3SAdrian Hunter return False 17561c3ca1b3SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 17571c3ca1b3SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 17581c3ca1b3SAdrian Hunter return True 17591c3ca1b3SAdrian Hunter 17601c3ca1b3SAdrian Hunter def AddTimeRange(self, value, ranges): 17611c3ca1b3SAdrian Hunter n = value.count("-") 17621c3ca1b3SAdrian Hunter if n == 1: 17631c3ca1b3SAdrian Hunter pass 17641c3ca1b3SAdrian Hunter elif n == 2: 17651c3ca1b3SAdrian Hunter if value.split("-")[1].strip() == "": 17661c3ca1b3SAdrian Hunter n = 1 17671c3ca1b3SAdrian Hunter elif n == 3: 17681c3ca1b3SAdrian Hunter n = 2 17691c3ca1b3SAdrian Hunter else: 17701c3ca1b3SAdrian Hunter return False 17711c3ca1b3SAdrian Hunter pos = findnth(value, "-", n) 17721c3ca1b3SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 17731c3ca1b3SAdrian Hunter if self.ConvertTimeRange(vrange): 17741c3ca1b3SAdrian Hunter ranges.append(vrange) 17751c3ca1b3SAdrian Hunter return True 17761c3ca1b3SAdrian Hunter return False 17771c3ca1b3SAdrian Hunter 17781c3ca1b3SAdrian Hunter def DoValidate(self, input_string): 17791c3ca1b3SAdrian Hunter ranges = [] 17801c3ca1b3SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 17811c3ca1b3SAdrian Hunter if not self.AddTimeRange(value, ranges): 17821c3ca1b3SAdrian Hunter return self.InvalidValue(value) 17831c3ca1b3SAdrian Hunter ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] 17841c3ca1b3SAdrian Hunter self.value = " OR ".join(ranges) 17851c3ca1b3SAdrian Hunter 17860924cd68SAdrian Hunter# Report Dialog Base 1787210cf1f9SAdrian Hunter 17880924cd68SAdrian Hunterclass ReportDialogBase(QDialog): 1789210cf1f9SAdrian Hunter 17900924cd68SAdrian Hunter def __init__(self, glb, title, items, partial, parent=None): 17910924cd68SAdrian Hunter super(ReportDialogBase, self).__init__(parent) 1792210cf1f9SAdrian Hunter 1793210cf1f9SAdrian Hunter self.glb = glb 1794210cf1f9SAdrian Hunter 17950bf0947aSAdrian Hunter self.report_vars = ReportVars() 1796210cf1f9SAdrian Hunter 17970924cd68SAdrian Hunter self.setWindowTitle(title) 1798210cf1f9SAdrian Hunter self.setMinimumWidth(600) 1799210cf1f9SAdrian Hunter 18001c3ca1b3SAdrian Hunter self.data_items = [x(glb, self) for x in items] 1801210cf1f9SAdrian Hunter 18020924cd68SAdrian Hunter self.partial = partial 18030924cd68SAdrian Hunter 1804210cf1f9SAdrian Hunter self.grid = QGridLayout() 1805210cf1f9SAdrian Hunter 1806210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 1807210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 1808210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 1809210cf1f9SAdrian Hunter 1810210cf1f9SAdrian Hunter self.status = QLabel() 1811210cf1f9SAdrian Hunter 1812210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 1813210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 1814210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 1815210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 1816210cf1f9SAdrian Hunter 1817210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 1818210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 1819210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 1820210cf1f9SAdrian Hunter 1821210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 1822210cf1f9SAdrian Hunter #self.hbox.addStretch() 1823210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 1824210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 1825210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 1826210cf1f9SAdrian Hunter 1827210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 1828210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 1829210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 1830210cf1f9SAdrian Hunter 1831210cf1f9SAdrian Hunter self.setLayout(self.vbox); 1832210cf1f9SAdrian Hunter 1833210cf1f9SAdrian Hunter def Ok(self): 18340bf0947aSAdrian Hunter vars = self.report_vars 18351c3ca1b3SAdrian Hunter for d in self.data_items: 18361c3ca1b3SAdrian Hunter if d.id == "REPORTNAME": 18371c3ca1b3SAdrian Hunter vars.name = d.value 1838947cc38dSAdrian Hunter if not vars.name: 1839210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 1840210cf1f9SAdrian Hunter return 1841210cf1f9SAdrian Hunter for d in self.data_items: 1842210cf1f9SAdrian Hunter if not d.IsValid(): 1843210cf1f9SAdrian Hunter return 1844210cf1f9SAdrian Hunter for d in self.data_items[1:]: 1845cd358012SAdrian Hunter if d.id == "LIMIT": 1846cd358012SAdrian Hunter vars.limit = d.value 1847cd358012SAdrian Hunter elif len(d.value): 18480bf0947aSAdrian Hunter if len(vars.where_clause): 18490bf0947aSAdrian Hunter vars.where_clause += " AND " 18500bf0947aSAdrian Hunter vars.where_clause += d.value 18510bf0947aSAdrian Hunter if len(vars.where_clause): 18520924cd68SAdrian Hunter if self.partial: 18530bf0947aSAdrian Hunter vars.where_clause = " AND ( " + vars.where_clause + " ) " 1854210cf1f9SAdrian Hunter else: 18550bf0947aSAdrian Hunter vars.where_clause = " WHERE " + vars.where_clause + " " 1856210cf1f9SAdrian Hunter self.accept() 1857210cf1f9SAdrian Hunter 1858210cf1f9SAdrian Hunter def ShowMessage(self, msg): 1859210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 1860210cf1f9SAdrian Hunter 1861210cf1f9SAdrian Hunter def ClearMessage(self): 1862210cf1f9SAdrian Hunter self.status.setText("") 1863210cf1f9SAdrian Hunter 18640924cd68SAdrian Hunter# Selected branch report creation dialog 18650924cd68SAdrian Hunter 18660924cd68SAdrian Hunterclass SelectedBranchDialog(ReportDialogBase): 18670924cd68SAdrian Hunter 18680924cd68SAdrian Hunter def __init__(self, glb, parent=None): 18690924cd68SAdrian Hunter title = "Selected Branches" 18701c3ca1b3SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 18711c3ca1b3SAdrian Hunter lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), 18721c3ca1b3SAdrian Hunter lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), 18731c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), 18741c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), 18751c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 18761c3ca1b3SAdrian 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), 18771c3ca1b3SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), 18781c3ca1b3SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) 18790924cd68SAdrian Hunter super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) 18800924cd68SAdrian Hunter 188176099f98SAdrian Hunter# Event list 188276099f98SAdrian Hunter 188376099f98SAdrian Hunterdef GetEventList(db): 188476099f98SAdrian Hunter events = [] 188576099f98SAdrian Hunter query = QSqlQuery(db) 188676099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 188776099f98SAdrian Hunter while query.next(): 188876099f98SAdrian Hunter events.append(query.value(0)) 188976099f98SAdrian Hunter return events 189076099f98SAdrian Hunter 1891655cb952SAdrian Hunter# Is a table selectable 1892655cb952SAdrian Hunter 1893655cb952SAdrian Hunterdef IsSelectable(db, table): 1894655cb952SAdrian Hunter query = QSqlQuery(db) 1895655cb952SAdrian Hunter try: 1896655cb952SAdrian Hunter QueryExec(query, "SELECT * FROM " + table + " LIMIT 1") 1897655cb952SAdrian Hunter except: 1898655cb952SAdrian Hunter return False 1899655cb952SAdrian Hunter return True 1900655cb952SAdrian Hunter 19018392b74bSAdrian Hunter# SQL data preparation 19028392b74bSAdrian Hunter 19038392b74bSAdrian Hunterdef SQLTableDataPrep(query, count): 19048392b74bSAdrian Hunter data = [] 19058392b74bSAdrian Hunter for i in xrange(count): 19068392b74bSAdrian Hunter data.append(query.value(i)) 19078392b74bSAdrian Hunter return data 19088392b74bSAdrian Hunter 19098392b74bSAdrian Hunter# SQL table data model item 19108392b74bSAdrian Hunter 19118392b74bSAdrian Hunterclass SQLTableItem(): 19128392b74bSAdrian Hunter 19138392b74bSAdrian Hunter def __init__(self, row, data): 19148392b74bSAdrian Hunter self.row = row 19158392b74bSAdrian Hunter self.data = data 19168392b74bSAdrian Hunter 19178392b74bSAdrian Hunter def getData(self, column): 19188392b74bSAdrian Hunter return self.data[column] 19198392b74bSAdrian Hunter 19208392b74bSAdrian Hunter# SQL table data model 19218392b74bSAdrian Hunter 19228392b74bSAdrian Hunterclass SQLTableModel(TableModel): 19238392b74bSAdrian Hunter 19248392b74bSAdrian Hunter progress = Signal(object) 19258392b74bSAdrian Hunter 19268c90fef9SAdrian Hunter def __init__(self, glb, sql, column_headers, parent=None): 19278392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 19288392b74bSAdrian Hunter self.glb = glb 19298392b74bSAdrian Hunter self.more = True 19308392b74bSAdrian Hunter self.populated = 0 19318c90fef9SAdrian Hunter self.column_headers = column_headers 19328c90fef9SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample) 19338392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 19348392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 19358392b74bSAdrian Hunter 19368392b74bSAdrian Hunter def DisplayData(self, item, index): 19378392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 19388392b74bSAdrian Hunter return item.getData(index.column()) 19398392b74bSAdrian Hunter 19408392b74bSAdrian Hunter def AddSample(self, data): 19418392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 19428392b74bSAdrian Hunter self.child_items.append(child) 19438392b74bSAdrian Hunter self.populated += 1 19448392b74bSAdrian Hunter 19458392b74bSAdrian Hunter def Update(self, fetched): 19468392b74bSAdrian Hunter if not fetched: 19478392b74bSAdrian Hunter self.more = False 19488392b74bSAdrian Hunter self.progress.emit(0) 19498392b74bSAdrian Hunter child_count = self.child_count 19508392b74bSAdrian Hunter count = self.populated - child_count 19518392b74bSAdrian Hunter if count > 0: 19528392b74bSAdrian Hunter parent = QModelIndex() 19538392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 19548392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 19558392b74bSAdrian Hunter self.child_count += count 19568392b74bSAdrian Hunter self.endInsertRows() 19578392b74bSAdrian Hunter self.progress.emit(self.child_count) 19588392b74bSAdrian Hunter 19598392b74bSAdrian Hunter def FetchMoreRecords(self, count): 19608392b74bSAdrian Hunter current = self.child_count 19618392b74bSAdrian Hunter if self.more: 19628392b74bSAdrian Hunter self.fetcher.Fetch(count) 19638392b74bSAdrian Hunter else: 19648392b74bSAdrian Hunter self.progress.emit(0) 19658392b74bSAdrian Hunter return current 19668392b74bSAdrian Hunter 19678392b74bSAdrian Hunter def HasMoreRecords(self): 19688392b74bSAdrian Hunter return self.more 19698392b74bSAdrian Hunter 19708c90fef9SAdrian Hunter def columnCount(self, parent=None): 19718c90fef9SAdrian Hunter return len(self.column_headers) 19728c90fef9SAdrian Hunter 19738c90fef9SAdrian Hunter def columnHeader(self, column): 19748c90fef9SAdrian Hunter return self.column_headers[column] 19758c90fef9SAdrian Hunter 19768392b74bSAdrian Hunter# SQL automatic table data model 19778392b74bSAdrian Hunter 19788392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 19798392b74bSAdrian Hunter 19808392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 19818392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 19828392b74bSAdrian Hunter if table_name == "comm_threads_view": 19838392b74bSAdrian Hunter # For now, comm_threads_view has no id column 19848392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 19858c90fef9SAdrian Hunter column_headers = [] 19868392b74bSAdrian Hunter query = QSqlQuery(glb.db) 19878392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 19888392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 19898392b74bSAdrian Hunter while query.next(): 19908c90fef9SAdrian Hunter column_headers.append(query.value(1)) 19918392b74bSAdrian Hunter if table_name == "sqlite_master": 19928392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 19938392b74bSAdrian Hunter else: 19948392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 19958392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 19968392b74bSAdrian Hunter select_table_name = table_name[19:] 19978392b74bSAdrian Hunter schema = "information_schema" 19988392b74bSAdrian Hunter else: 19998392b74bSAdrian Hunter select_table_name = table_name 20008392b74bSAdrian Hunter schema = "public" 20018392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 20028392b74bSAdrian Hunter while query.next(): 20038c90fef9SAdrian Hunter column_headers.append(query.value(0)) 20048c90fef9SAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) 20058392b74bSAdrian Hunter 20068392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 20078392b74bSAdrian Hunter 20088392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 20098392b74bSAdrian Hunter 20108392b74bSAdrian Hunter def __init__(self, parent=None): 20118392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 20128392b74bSAdrian Hunter 20138392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 20148392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 20158392b74bSAdrian Hunter # so implement a crude alternative 20168392b74bSAdrian Hunter font = self.view.font() 20178392b74bSAdrian Hunter metrics = QFontMetrics(font) 20188392b74bSAdrian Hunter max = 0 20198392b74bSAdrian Hunter for row in xrange(n): 20208392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 20218392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 20228392b74bSAdrian Hunter max = len if len > max else max 20238392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 20248392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 20258392b74bSAdrian Hunter max = len if len > max else max 20268392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 20278392b74bSAdrian Hunter 20288392b74bSAdrian Hunter def ResizeColumnsToContents(self): 20298392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 20308392b74bSAdrian Hunter if n < 1: 20318392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 20328392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 20338392b74bSAdrian Hunter return 20348392b74bSAdrian Hunter columns = self.data_model.columnCount() 20358392b74bSAdrian Hunter for i in xrange(columns): 20368392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 20378392b74bSAdrian Hunter 20388392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 20398392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 20408392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 20418392b74bSAdrian Hunter self.ResizeColumnsToContents() 20428392b74bSAdrian Hunter 20438392b74bSAdrian Hunter# Table window 20448392b74bSAdrian Hunter 20458392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 20468392b74bSAdrian Hunter 20478392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 20488392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 20498392b74bSAdrian Hunter 20508392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 20518392b74bSAdrian Hunter 20528392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 20538392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 20548392b74bSAdrian Hunter 20558392b74bSAdrian Hunter self.view = QTableView() 20568392b74bSAdrian Hunter self.view.setModel(self.model) 20578392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 20588392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 20598392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 20608392b74bSAdrian Hunter self.view.setSortingEnabled(True) 20618392b74bSAdrian Hunter 20628392b74bSAdrian Hunter self.ResizeColumnsToContents() 20638392b74bSAdrian Hunter 20648392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 20658392b74bSAdrian Hunter 20668392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 20678392b74bSAdrian Hunter 20688392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 20698392b74bSAdrian Hunter 20708392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 20718392b74bSAdrian Hunter 20728392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 20738392b74bSAdrian Hunter 20748392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 20758392b74bSAdrian Hunter 20768392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 20778392b74bSAdrian Hunter self.view.setFocus() 20788392b74bSAdrian Hunter self.find_bar.Busy() 20798392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 20808392b74bSAdrian Hunter 20818392b74bSAdrian Hunter def FindDone(self, row): 20828392b74bSAdrian Hunter self.find_bar.Idle() 20838392b74bSAdrian Hunter if row >= 0: 208435fa1ceeSAdrian Hunter self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex()))) 20858392b74bSAdrian Hunter else: 20868392b74bSAdrian Hunter self.find_bar.NotFound() 20878392b74bSAdrian Hunter 20888392b74bSAdrian Hunter# Table list 20898392b74bSAdrian Hunter 20908392b74bSAdrian Hunterdef GetTableList(glb): 20918392b74bSAdrian Hunter tables = [] 20928392b74bSAdrian Hunter query = QSqlQuery(glb.db) 20938392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 20948392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 20958392b74bSAdrian Hunter else: 20968392b74bSAdrian 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") 20978392b74bSAdrian Hunter while query.next(): 20988392b74bSAdrian Hunter tables.append(query.value(0)) 20998392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 21008392b74bSAdrian Hunter tables.append("sqlite_master") 21018392b74bSAdrian Hunter else: 21028392b74bSAdrian Hunter tables.append("information_schema.tables") 21038392b74bSAdrian Hunter tables.append("information_schema.views") 21048392b74bSAdrian Hunter tables.append("information_schema.columns") 21058392b74bSAdrian Hunter return tables 21068392b74bSAdrian Hunter 2107cd358012SAdrian Hunter# Top Calls data model 2108cd358012SAdrian Hunter 2109cd358012SAdrian Hunterclass TopCallsModel(SQLTableModel): 2110cd358012SAdrian Hunter 2111cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2112cd358012SAdrian Hunter text = "" 2113cd358012SAdrian Hunter if not glb.dbref.is_sqlite3: 2114cd358012SAdrian Hunter text = "::text" 2115cd358012SAdrian Hunter limit = "" 2116cd358012SAdrian Hunter if len(report_vars.limit): 2117cd358012SAdrian Hunter limit = " LIMIT " + report_vars.limit 2118cd358012SAdrian Hunter sql = ("SELECT comm, pid, tid, name," 2119cd358012SAdrian Hunter " CASE" 2120cd358012SAdrian Hunter " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text + 2121cd358012SAdrian Hunter " ELSE short_name" 2122cd358012SAdrian Hunter " END AS dso," 2123cd358012SAdrian Hunter " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, " 2124cd358012SAdrian Hunter " CASE" 2125cd358012SAdrian Hunter " WHEN (calls.flags = 1) THEN 'no call'" + text + 2126cd358012SAdrian Hunter " WHEN (calls.flags = 2) THEN 'no return'" + text + 2127cd358012SAdrian Hunter " WHEN (calls.flags = 3) THEN 'no call/return'" + text + 2128cd358012SAdrian Hunter " ELSE ''" + text + 2129cd358012SAdrian Hunter " END AS flags" 2130cd358012SAdrian Hunter " FROM calls" 2131cd358012SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 2132cd358012SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 2133cd358012SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 2134cd358012SAdrian Hunter " INNER JOIN comms ON calls.comm_id = comms.id" 2135cd358012SAdrian Hunter " INNER JOIN threads ON calls.thread_id = threads.id" + 2136cd358012SAdrian Hunter report_vars.where_clause + 2137cd358012SAdrian Hunter " ORDER BY elapsed_time DESC" + 2138cd358012SAdrian Hunter limit 2139cd358012SAdrian Hunter ) 2140cd358012SAdrian Hunter column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags") 2141cd358012SAdrian Hunter self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft) 2142cd358012SAdrian Hunter super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 2143cd358012SAdrian Hunter 2144cd358012SAdrian Hunter def columnAlignment(self, column): 2145cd358012SAdrian Hunter return self.alignment[column] 2146cd358012SAdrian Hunter 2147cd358012SAdrian Hunter# Top Calls report creation dialog 2148cd358012SAdrian Hunter 2149cd358012SAdrian Hunterclass TopCallsDialog(ReportDialogBase): 2150cd358012SAdrian Hunter 2151cd358012SAdrian Hunter def __init__(self, glb, parent=None): 2152cd358012SAdrian Hunter title = "Top Calls by Elapsed Time" 2153cd358012SAdrian Hunter items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), 2154cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p), 2155cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p), 2156cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p), 2157cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p), 2158cd358012SAdrian Hunter lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p), 2159cd358012SAdrian Hunter lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p), 2160cd358012SAdrian Hunter lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100")) 2161cd358012SAdrian Hunter super(TopCallsDialog, self).__init__(glb, title, items, False, parent) 2162cd358012SAdrian Hunter 2163cd358012SAdrian Hunter# Top Calls window 2164cd358012SAdrian Hunter 2165cd358012SAdrian Hunterclass TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 2166cd358012SAdrian Hunter 2167cd358012SAdrian Hunter def __init__(self, glb, report_vars, parent=None): 2168cd358012SAdrian Hunter super(TopCallsWindow, self).__init__(parent) 2169cd358012SAdrian Hunter 2170cd358012SAdrian Hunter self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars)) 2171cd358012SAdrian Hunter self.model = self.data_model 2172cd358012SAdrian Hunter 2173cd358012SAdrian Hunter self.view = QTableView() 2174cd358012SAdrian Hunter self.view.setModel(self.model) 2175cd358012SAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 2176cd358012SAdrian Hunter self.view.verticalHeader().setVisible(False) 2177cd358012SAdrian Hunter 2178cd358012SAdrian Hunter self.ResizeColumnsToContents() 2179cd358012SAdrian Hunter 2180cd358012SAdrian Hunter self.find_bar = FindBar(self, self, True) 2181cd358012SAdrian Hunter 2182cd358012SAdrian Hunter self.finder = ChildDataItemFinder(self.model) 2183cd358012SAdrian Hunter 2184cd358012SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 2185cd358012SAdrian Hunter 2186cd358012SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 2187cd358012SAdrian Hunter 2188cd358012SAdrian Hunter self.setWidget(self.vbox.Widget()) 2189cd358012SAdrian Hunter 2190cd358012SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name) 2191cd358012SAdrian Hunter 2192cd358012SAdrian Hunter def Find(self, value, direction, pattern, context): 2193cd358012SAdrian Hunter self.view.setFocus() 2194cd358012SAdrian Hunter self.find_bar.Busy() 2195cd358012SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 2196cd358012SAdrian Hunter 2197cd358012SAdrian Hunter def FindDone(self, row): 2198cd358012SAdrian Hunter self.find_bar.Idle() 2199cd358012SAdrian Hunter if row >= 0: 2200cd358012SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 2201cd358012SAdrian Hunter else: 2202cd358012SAdrian Hunter self.find_bar.NotFound() 2203cd358012SAdrian Hunter 22041beb5c7bSAdrian Hunter# Action Definition 22051beb5c7bSAdrian Hunter 22061beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 22071beb5c7bSAdrian Hunter action = QAction(label, parent) 22081beb5c7bSAdrian Hunter if shortcut != None: 22091beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 22101beb5c7bSAdrian Hunter action.setStatusTip(tip) 22111beb5c7bSAdrian Hunter action.triggered.connect(callback) 22121beb5c7bSAdrian Hunter return action 22131beb5c7bSAdrian Hunter 22141beb5c7bSAdrian Hunter# Typical application actions 22151beb5c7bSAdrian Hunter 22161beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 22171beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 22181beb5c7bSAdrian Hunter 22191beb5c7bSAdrian Hunter# Typical MDI actions 22201beb5c7bSAdrian Hunter 22211beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 22221beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 22231beb5c7bSAdrian Hunter 22241beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 22251beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 22261beb5c7bSAdrian Hunter 22271beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 22281beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 22291beb5c7bSAdrian Hunter 22301beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 22311beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 22321beb5c7bSAdrian Hunter 22331beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 22341beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 22351beb5c7bSAdrian Hunter 22361beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 22371beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 22381beb5c7bSAdrian Hunter 22391beb5c7bSAdrian Hunter# Typical MDI window menu 22401beb5c7bSAdrian Hunter 22411beb5c7bSAdrian Hunterclass WindowMenu(): 22421beb5c7bSAdrian Hunter 22431beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 22441beb5c7bSAdrian Hunter self.mdi_area = mdi_area 22451beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 22461beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 22471beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 22481beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 22491beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 22501beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 22511beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 22521beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 22531beb5c7bSAdrian Hunter 22541beb5c7bSAdrian Hunter def Update(self): 22551beb5c7bSAdrian Hunter self.window_menu.clear() 22561beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 22571beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 22581beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 22591beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 22601beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 22611beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 22621beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 22631beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 22641beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 22651beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 22661beb5c7bSAdrian Hunter self.window_menu.addSeparator() 22671beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 22681beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 22691beb5c7bSAdrian Hunter self.window_menu.addSeparator() 22701beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 22711beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 22721beb5c7bSAdrian Hunter if sub_window_count == 0: 22731beb5c7bSAdrian Hunter return 22741beb5c7bSAdrian Hunter self.window_menu.addSeparator() 22751beb5c7bSAdrian Hunter nr = 1 22761beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 22771beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 22781beb5c7bSAdrian Hunter if nr < 10: 22791beb5c7bSAdrian Hunter label = "&" + label 22801beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 22811beb5c7bSAdrian Hunter action.setCheckable(True) 22821beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 22831beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 22841beb5c7bSAdrian Hunter self.window_menu.addAction(action) 22851beb5c7bSAdrian Hunter nr += 1 22861beb5c7bSAdrian Hunter 22871beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 22881beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 22891beb5c7bSAdrian Hunter 229065b24292SAdrian Hunter# Help text 229165b24292SAdrian Hunter 229265b24292SAdrian Hunterglb_help_text = """ 229365b24292SAdrian Hunter<h1>Contents</h1> 229465b24292SAdrian Hunter<style> 229565b24292SAdrian Hunterp.c1 { 229665b24292SAdrian Hunter text-indent: 40px; 229765b24292SAdrian Hunter} 229865b24292SAdrian Hunterp.c2 { 229965b24292SAdrian Hunter text-indent: 80px; 230065b24292SAdrian Hunter} 230165b24292SAdrian Hunter} 230265b24292SAdrian Hunter</style> 230365b24292SAdrian Hunter<p class=c1><a href=#reports>1. Reports</a></p> 230465b24292SAdrian Hunter<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 230565b24292SAdrian Hunter<p class=c2><a href=#allbranches>1.2 All branches</a></p> 230665b24292SAdrian Hunter<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> 2307cd358012SAdrian Hunter<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p> 230865b24292SAdrian Hunter<p class=c1><a href=#tables>2. Tables</a></p> 230965b24292SAdrian Hunter<h1 id=reports>1. Reports</h1> 231065b24292SAdrian Hunter<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 231165b24292SAdrian HunterThe result is a GUI window with a tree representing a context-sensitive 231265b24292SAdrian Huntercall-graph. Expanding a couple of levels of the tree and adjusting column 231365b24292SAdrian Hunterwidths to suit will display something like: 231465b24292SAdrian Hunter<pre> 231565b24292SAdrian Hunter Call Graph: pt_example 231665b24292SAdrian HunterCall Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 231765b24292SAdrian Hunterv- ls 231865b24292SAdrian Hunter v- 2638:2638 231965b24292SAdrian Hunter v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 232065b24292SAdrian Hunter |- unknown unknown 1 13198 0.1 1 0.0 232165b24292SAdrian Hunter >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 232265b24292SAdrian Hunter >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 232365b24292SAdrian Hunter v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 232465b24292SAdrian Hunter >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 232565b24292SAdrian Hunter >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 232665b24292SAdrian Hunter >- __libc_csu_init ls 1 10354 0.1 10 0.0 232765b24292SAdrian Hunter |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 232865b24292SAdrian Hunter v- main ls 1 8182043 99.6 180254 99.9 232965b24292SAdrian Hunter</pre> 233065b24292SAdrian Hunter<h3>Points to note:</h3> 233165b24292SAdrian Hunter<ul> 233265b24292SAdrian Hunter<li>The top level is a command name (comm)</li> 233365b24292SAdrian Hunter<li>The next level is a thread (pid:tid)</li> 233465b24292SAdrian Hunter<li>Subsequent levels are functions</li> 233565b24292SAdrian Hunter<li>'Count' is the number of calls</li> 233665b24292SAdrian Hunter<li>'Time' is the elapsed time until the function returns</li> 233765b24292SAdrian Hunter<li>Percentages are relative to the level above</li> 233865b24292SAdrian Hunter<li>'Branch Count' is the total number of branches for that function and all functions that it calls 233965b24292SAdrian Hunter</ul> 234065b24292SAdrian Hunter<h3>Find</h3> 234165b24292SAdrian HunterCtrl-F displays a Find bar which finds function names by either an exact match or a pattern match. 234265b24292SAdrian HunterThe pattern matching symbols are ? for any character and * for zero or more characters. 234365b24292SAdrian Hunter<h2 id=allbranches>1.2 All branches</h2> 234465b24292SAdrian HunterThe All branches report displays all branches in chronological order. 234565b24292SAdrian HunterNot all data is fetched immediately. More records can be fetched using the Fetch bar provided. 234665b24292SAdrian Hunter<h3>Disassembly</h3> 234765b24292SAdrian HunterOpen a branch to display disassembly. This only works if: 234865b24292SAdrian Hunter<ol> 234965b24292SAdrian Hunter<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> 235065b24292SAdrian Hunter<li>The object code is available. Currently, only the perf build ID cache is searched for object code. 235165b24292SAdrian HunterThe default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. 235265b24292SAdrian HunterOne exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), 235365b24292SAdrian Hunteror alternatively, set environment variable PERF_KCORE to the kcore file name.</li> 235465b24292SAdrian Hunter</ol> 235565b24292SAdrian Hunter<h4 id=xed>Intel XED Setup</h4> 235665b24292SAdrian HunterTo use Intel XED, libxed.so must be present. To build and install libxed.so: 235765b24292SAdrian Hunter<pre> 235865b24292SAdrian Huntergit clone https://github.com/intelxed/mbuild.git mbuild 235965b24292SAdrian Huntergit clone https://github.com/intelxed/xed 236065b24292SAdrian Huntercd xed 236165b24292SAdrian Hunter./mfile.py --share 236265b24292SAdrian Huntersudo ./mfile.py --prefix=/usr/local install 236365b24292SAdrian Huntersudo ldconfig 236465b24292SAdrian Hunter</pre> 236565b24292SAdrian Hunter<h3>Find</h3> 236665b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 236765b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 236865b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 236965b24292SAdrian Hunter<h2 id=selectedbranches>1.3 Selected branches</h2> 237065b24292SAdrian HunterThis is the same as the <a href=#allbranches>All branches</a> report but with the data reduced 237165b24292SAdrian Hunterby various selection criteria. A dialog box displays available criteria which are AND'ed together. 237265b24292SAdrian Hunter<h3>1.3.1 Time ranges</h3> 237365b24292SAdrian HunterThe time ranges hint text shows the total time range. Relative time ranges can also be entered in 237465b24292SAdrian Hunterms, us or ns. Also, negative values are relative to the end of trace. Examples: 237565b24292SAdrian Hunter<pre> 237665b24292SAdrian Hunter 81073085947329-81073085958238 From 81073085947329 to 81073085958238 237765b24292SAdrian Hunter 100us-200us From 100us to 200us 237865b24292SAdrian Hunter 10ms- From 10ms to the end 237965b24292SAdrian Hunter -100ns The first 100ns 238065b24292SAdrian Hunter -10ms- The last 10ms 238165b24292SAdrian Hunter</pre> 238265b24292SAdrian HunterN.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2383cd358012SAdrian Hunter<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2> 2384cd358012SAdrian 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. 2385cd358012SAdrian HunterThe data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. 2386cd358012SAdrian HunterIf not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. 238765b24292SAdrian Hunter<h1 id=tables>2. Tables</h1> 238865b24292SAdrian HunterThe Tables menu shows all tables and views in the database. Most tables have an associated view 238965b24292SAdrian Hunterwhich displays the information in a more friendly way. Not all data for large tables is fetched 239065b24292SAdrian Hunterimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, 239165b24292SAdrian Hunterbut that can be slow for large tables. 239265b24292SAdrian Hunter<p>There are also tables of database meta-information. 239365b24292SAdrian HunterFor SQLite3 databases, the sqlite_master table is included. 239465b24292SAdrian HunterFor PostgreSQL databases, information_schema.tables/views/columns are included. 239565b24292SAdrian Hunter<h3>Find</h3> 239665b24292SAdrian HunterCtrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. 239765b24292SAdrian HunterRefer to Python documentation for the regular expression syntax. 239865b24292SAdrian HunterAll columns are searched, but only currently fetched rows are searched. 239935fa1ceeSAdrian Hunter<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous 240035fa1ceeSAdrian Hunterwill go to the next/previous result in id order, instead of display order. 240165b24292SAdrian Hunter""" 240265b24292SAdrian Hunter 240365b24292SAdrian Hunter# Help window 240465b24292SAdrian Hunter 240565b24292SAdrian Hunterclass HelpWindow(QMdiSubWindow): 240665b24292SAdrian Hunter 240765b24292SAdrian Hunter def __init__(self, glb, parent=None): 240865b24292SAdrian Hunter super(HelpWindow, self).__init__(parent) 240965b24292SAdrian Hunter 241065b24292SAdrian Hunter self.text = QTextBrowser() 241165b24292SAdrian Hunter self.text.setHtml(glb_help_text) 241265b24292SAdrian Hunter self.text.setReadOnly(True) 241365b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 241465b24292SAdrian Hunter 241565b24292SAdrian Hunter self.setWidget(self.text) 241665b24292SAdrian Hunter 241765b24292SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") 241865b24292SAdrian Hunter 241965b24292SAdrian Hunter# Main window that only displays the help text 242065b24292SAdrian Hunter 242165b24292SAdrian Hunterclass HelpOnlyWindow(QMainWindow): 242265b24292SAdrian Hunter 242365b24292SAdrian Hunter def __init__(self, parent=None): 242465b24292SAdrian Hunter super(HelpOnlyWindow, self).__init__(parent) 242565b24292SAdrian Hunter 242665b24292SAdrian Hunter self.setMinimumSize(200, 100) 242765b24292SAdrian Hunter self.resize(800, 600) 242865b24292SAdrian Hunter self.setWindowTitle("Exported SQL Viewer Help") 242965b24292SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) 243065b24292SAdrian Hunter 243165b24292SAdrian Hunter self.text = QTextBrowser() 243265b24292SAdrian Hunter self.text.setHtml(glb_help_text) 243365b24292SAdrian Hunter self.text.setReadOnly(True) 243465b24292SAdrian Hunter self.text.setOpenExternalLinks(True) 243565b24292SAdrian Hunter 243665b24292SAdrian Hunter self.setCentralWidget(self.text) 243765b24292SAdrian Hunter 243882f68e28SAdrian Hunter# Font resize 243982f68e28SAdrian Hunter 244082f68e28SAdrian Hunterdef ResizeFont(widget, diff): 244182f68e28SAdrian Hunter font = widget.font() 244282f68e28SAdrian Hunter sz = font.pointSize() 244382f68e28SAdrian Hunter font.setPointSize(sz + diff) 244482f68e28SAdrian Hunter widget.setFont(font) 244582f68e28SAdrian Hunter 244682f68e28SAdrian Hunterdef ShrinkFont(widget): 244782f68e28SAdrian Hunter ResizeFont(widget, -1) 244882f68e28SAdrian Hunter 244982f68e28SAdrian Hunterdef EnlargeFont(widget): 245082f68e28SAdrian Hunter ResizeFont(widget, 1) 245182f68e28SAdrian Hunter 24521beb5c7bSAdrian Hunter# Unique name for sub-windows 24531beb5c7bSAdrian Hunter 24541beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 24551beb5c7bSAdrian Hunter if nr > 1: 24561beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 24571beb5c7bSAdrian Hunter return name 24581beb5c7bSAdrian Hunter 24591beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 24601beb5c7bSAdrian Hunter nr = 1 24611beb5c7bSAdrian Hunter while True: 24621beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 24631beb5c7bSAdrian Hunter ok = True 24641beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 24651beb5c7bSAdrian Hunter if sub_window.name == unique_name: 24661beb5c7bSAdrian Hunter ok = False 24671beb5c7bSAdrian Hunter break 24681beb5c7bSAdrian Hunter if ok: 24691beb5c7bSAdrian Hunter return unique_name 24701beb5c7bSAdrian Hunter nr += 1 24711beb5c7bSAdrian Hunter 24721beb5c7bSAdrian Hunter# Add a sub-window 24731beb5c7bSAdrian Hunter 24741beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 24751beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 24761beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 24771beb5c7bSAdrian Hunter sub_window.resize(800, 600) 24781beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 24791beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 24801beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 24811beb5c7bSAdrian Hunter sub_window.name = unique_name 24821beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 24831beb5c7bSAdrian Hunter sub_window.show() 24841beb5c7bSAdrian Hunter 2485031c2a00SAdrian Hunter# Main window 2486031c2a00SAdrian Hunter 2487031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 2488031c2a00SAdrian Hunter 2489031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 2490031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 2491031c2a00SAdrian Hunter 2492031c2a00SAdrian Hunter self.glb = glb 2493031c2a00SAdrian Hunter 24941beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 2495031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 2496031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 2497031c2a00SAdrian Hunter 24981beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 24991beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 25001beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 2501031c2a00SAdrian Hunter 25021beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 2503031c2a00SAdrian Hunter 25041beb5c7bSAdrian Hunter menu = self.menuBar() 2505031c2a00SAdrian Hunter 25061beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 25071beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 25081beb5c7bSAdrian Hunter 2509ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 2510ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 25118392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 251282f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 251382f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 2514ebd70c7dSAdrian Hunter 25151beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 2516655cb952SAdrian Hunter if IsSelectable(glb.db, "calls"): 25171beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 25181beb5c7bSAdrian Hunter 251976099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 252076099f98SAdrian Hunter 2521cd358012SAdrian Hunter if IsSelectable(glb.db, "calls"): 2522cd358012SAdrian Hunter reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) 2523cd358012SAdrian Hunter 25248392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 25258392b74bSAdrian Hunter 25261beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 25271beb5c7bSAdrian Hunter 252865b24292SAdrian Hunter help_menu = menu.addMenu("&Help") 252965b24292SAdrian Hunter help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) 253065b24292SAdrian Hunter 2531ebd70c7dSAdrian Hunter def Find(self): 2532ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 2533ebd70c7dSAdrian Hunter if win: 2534ebd70c7dSAdrian Hunter try: 2535ebd70c7dSAdrian Hunter win.find_bar.Activate() 2536ebd70c7dSAdrian Hunter except: 2537ebd70c7dSAdrian Hunter pass 2538ebd70c7dSAdrian Hunter 25398392b74bSAdrian Hunter def FetchMoreRecords(self): 25408392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 25418392b74bSAdrian Hunter if win: 25428392b74bSAdrian Hunter try: 25438392b74bSAdrian Hunter win.fetch_bar.Activate() 25448392b74bSAdrian Hunter except: 25458392b74bSAdrian Hunter pass 25468392b74bSAdrian Hunter 254782f68e28SAdrian Hunter def ShrinkFont(self): 254882f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 254982f68e28SAdrian Hunter ShrinkFont(win.view) 255082f68e28SAdrian Hunter 255182f68e28SAdrian Hunter def EnlargeFont(self): 255282f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 255382f68e28SAdrian Hunter EnlargeFont(win.view) 255482f68e28SAdrian Hunter 255576099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 255676099f98SAdrian Hunter branches_events = 0 255776099f98SAdrian Hunter for event in events: 255876099f98SAdrian Hunter event = event.split(":")[0] 255976099f98SAdrian Hunter if event == "branches": 256076099f98SAdrian Hunter branches_events += 1 256176099f98SAdrian Hunter dbid = 0 256276099f98SAdrian Hunter for event in events: 256376099f98SAdrian Hunter dbid += 1 256476099f98SAdrian Hunter event = event.split(":")[0] 256576099f98SAdrian Hunter if event == "branches": 256676099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 256776099f98SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 2568210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 2569210cf1f9SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) 257076099f98SAdrian Hunter 25718392b74bSAdrian Hunter def TableMenu(self, tables, menu): 25728392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 25738392b74bSAdrian Hunter for table in tables: 25748392b74bSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) 25758392b74bSAdrian Hunter 25761beb5c7bSAdrian Hunter def NewCallGraph(self): 25771beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 2578031c2a00SAdrian Hunter 2579cd358012SAdrian Hunter def NewTopCalls(self): 2580cd358012SAdrian Hunter dialog = TopCallsDialog(self.glb, self) 2581cd358012SAdrian Hunter ret = dialog.exec_() 2582cd358012SAdrian Hunter if ret: 2583cd358012SAdrian Hunter TopCallsWindow(self.glb, dialog.report_vars, self) 2584cd358012SAdrian Hunter 258576099f98SAdrian Hunter def NewBranchView(self, event_id): 2586947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, ReportVars(), self) 258776099f98SAdrian Hunter 2588210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 2589210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 2590210cf1f9SAdrian Hunter ret = dialog.exec_() 2591210cf1f9SAdrian Hunter if ret: 2592947cc38dSAdrian Hunter BranchWindow(self.glb, event_id, dialog.report_vars, self) 2593210cf1f9SAdrian Hunter 25948392b74bSAdrian Hunter def NewTableView(self, table_name): 25958392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 25968392b74bSAdrian Hunter 259765b24292SAdrian Hunter def Help(self): 259865b24292SAdrian Hunter HelpWindow(self.glb, self) 259965b24292SAdrian Hunter 260076099f98SAdrian Hunter# XED Disassembler 260176099f98SAdrian Hunter 260276099f98SAdrian Hunterclass xed_state_t(Structure): 260376099f98SAdrian Hunter 260476099f98SAdrian Hunter _fields_ = [ 260576099f98SAdrian Hunter ("mode", c_int), 260676099f98SAdrian Hunter ("width", c_int) 260776099f98SAdrian Hunter ] 260876099f98SAdrian Hunter 260976099f98SAdrian Hunterclass XEDInstruction(): 261076099f98SAdrian Hunter 261176099f98SAdrian Hunter def __init__(self, libxed): 261276099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 261376099f98SAdrian Hunter xedd_t = c_byte * 512 261476099f98SAdrian Hunter self.xedd = xedd_t() 261576099f98SAdrian Hunter self.xedp = addressof(self.xedd) 261676099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 261776099f98SAdrian Hunter self.state = xed_state_t() 261876099f98SAdrian Hunter self.statep = addressof(self.state) 261976099f98SAdrian Hunter # Buffer for disassembled instruction text 262076099f98SAdrian Hunter self.buffer = create_string_buffer(256) 262176099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 262276099f98SAdrian Hunter 262376099f98SAdrian Hunterclass LibXED(): 262476099f98SAdrian Hunter 262576099f98SAdrian Hunter def __init__(self): 26265ed4419dSAdrian Hunter try: 262776099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 26285ed4419dSAdrian Hunter except: 26295ed4419dSAdrian Hunter self.libxed = None 26305ed4419dSAdrian Hunter if not self.libxed: 26315ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 263276099f98SAdrian Hunter 263376099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 263476099f98SAdrian Hunter self.xed_tables_init.restype = None 263576099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 263676099f98SAdrian Hunter 263776099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 263876099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 263976099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 264076099f98SAdrian Hunter 264176099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 264276099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 264376099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 264476099f98SAdrian Hunter 264576099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 264676099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 264776099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 264876099f98SAdrian Hunter 264976099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 265076099f98SAdrian Hunter self.xed_decode.restype = c_int 265176099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 265276099f98SAdrian Hunter 265376099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 265476099f98SAdrian Hunter self.xed_format_context.restype = c_uint 265576099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 265676099f98SAdrian Hunter 265776099f98SAdrian Hunter self.xed_tables_init() 265876099f98SAdrian Hunter 265976099f98SAdrian Hunter def Instruction(self): 266076099f98SAdrian Hunter return XEDInstruction(self) 266176099f98SAdrian Hunter 266276099f98SAdrian Hunter def SetMode(self, inst, mode): 266376099f98SAdrian Hunter if mode: 266476099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 266576099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 266676099f98SAdrian Hunter else: 266776099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 266876099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 266976099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 267076099f98SAdrian Hunter 267176099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 267276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 267376099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 267476099f98SAdrian Hunter if err: 267576099f98SAdrian Hunter return 0, "" 267676099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 267776099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 267876099f98SAdrian Hunter if not ok: 267976099f98SAdrian Hunter return 0, "" 268076099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 268176099f98SAdrian Hunter # For now, assume the length is in byte 166 268276099f98SAdrian Hunter return inst.xedd[166], inst.buffer.value 268376099f98SAdrian Hunter 268476099f98SAdrian Hunterdef TryOpen(file_name): 268576099f98SAdrian Hunter try: 268676099f98SAdrian Hunter return open(file_name, "rb") 268776099f98SAdrian Hunter except: 268876099f98SAdrian Hunter return None 268976099f98SAdrian Hunter 269076099f98SAdrian Hunterdef Is64Bit(f): 269176099f98SAdrian Hunter result = sizeof(c_void_p) 269276099f98SAdrian Hunter # ELF support only 269376099f98SAdrian Hunter pos = f.tell() 269476099f98SAdrian Hunter f.seek(0) 269576099f98SAdrian Hunter header = f.read(7) 269676099f98SAdrian Hunter f.seek(pos) 269776099f98SAdrian Hunter magic = header[0:4] 269876099f98SAdrian Hunter eclass = ord(header[4]) 269976099f98SAdrian Hunter encoding = ord(header[5]) 270076099f98SAdrian Hunter version = ord(header[6]) 270176099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 270276099f98SAdrian Hunter result = True if eclass == 2 else False 270376099f98SAdrian Hunter return result 270476099f98SAdrian Hunter 2705031c2a00SAdrian Hunter# Global data 2706031c2a00SAdrian Hunter 2707031c2a00SAdrian Hunterclass Glb(): 2708031c2a00SAdrian Hunter 2709031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 2710031c2a00SAdrian Hunter self.dbref = dbref 2711031c2a00SAdrian Hunter self.db = db 2712031c2a00SAdrian Hunter self.dbname = dbname 271376099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 271476099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 271576099f98SAdrian Hunter if self.buildid_dir: 271676099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 271776099f98SAdrian Hunter else: 271876099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 2719031c2a00SAdrian Hunter self.app = None 2720031c2a00SAdrian Hunter self.mainwindow = None 27218392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 272276099f98SAdrian Hunter try: 272376099f98SAdrian Hunter self.disassembler = LibXED() 272476099f98SAdrian Hunter self.have_disassembler = True 272576099f98SAdrian Hunter except: 272676099f98SAdrian Hunter self.have_disassembler = False 272776099f98SAdrian Hunter 272876099f98SAdrian Hunter def FileFromBuildId(self, build_id): 272976099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 273076099f98SAdrian Hunter return TryOpen(file_name) 273176099f98SAdrian Hunter 273276099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 273376099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 273476099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 273576099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 273676099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 273776099f98SAdrian Hunter if f: 273876099f98SAdrian Hunter return f 273976099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 274076099f98SAdrian Hunter f = TryOpen(long_name) 274176099f98SAdrian Hunter if f: 274276099f98SAdrian Hunter return f 274376099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 274476099f98SAdrian Hunter if f: 274576099f98SAdrian Hunter return f 274676099f98SAdrian Hunter return None 27478392b74bSAdrian Hunter 27488392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 27498392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 27508392b74bSAdrian Hunter 27518392b74bSAdrian Hunter # Shutdown any background processes or threads 27528392b74bSAdrian Hunter def ShutdownInstances(self): 27538392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 27548392b74bSAdrian Hunter try: 27558392b74bSAdrian Hunter x.Shutdown() 27568392b74bSAdrian Hunter except: 27578392b74bSAdrian Hunter pass 2758031c2a00SAdrian Hunter 2759031c2a00SAdrian Hunter# Database reference 2760031c2a00SAdrian Hunter 2761031c2a00SAdrian Hunterclass DBRef(): 2762031c2a00SAdrian Hunter 2763031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 2764031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 2765031c2a00SAdrian Hunter self.dbname = dbname 2766031c2a00SAdrian Hunter 2767031c2a00SAdrian Hunter def Open(self, connection_name): 2768031c2a00SAdrian Hunter dbname = self.dbname 2769031c2a00SAdrian Hunter if self.is_sqlite3: 2770031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 2771031c2a00SAdrian Hunter else: 2772031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 2773031c2a00SAdrian Hunter opts = dbname.split() 2774031c2a00SAdrian Hunter for opt in opts: 2775031c2a00SAdrian Hunter if "=" in opt: 2776031c2a00SAdrian Hunter opt = opt.split("=") 2777031c2a00SAdrian Hunter if opt[0] == "hostname": 2778031c2a00SAdrian Hunter db.setHostName(opt[1]) 2779031c2a00SAdrian Hunter elif opt[0] == "port": 2780031c2a00SAdrian Hunter db.setPort(int(opt[1])) 2781031c2a00SAdrian Hunter elif opt[0] == "username": 2782031c2a00SAdrian Hunter db.setUserName(opt[1]) 2783031c2a00SAdrian Hunter elif opt[0] == "password": 2784031c2a00SAdrian Hunter db.setPassword(opt[1]) 2785031c2a00SAdrian Hunter elif opt[0] == "dbname": 2786031c2a00SAdrian Hunter dbname = opt[1] 2787031c2a00SAdrian Hunter else: 2788031c2a00SAdrian Hunter dbname = opt 2789031c2a00SAdrian Hunter 2790031c2a00SAdrian Hunter db.setDatabaseName(dbname) 2791031c2a00SAdrian Hunter if not db.open(): 2792031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 2793031c2a00SAdrian Hunter return db, dbname 2794031c2a00SAdrian Hunter 2795031c2a00SAdrian Hunter# Main 2796031c2a00SAdrian Hunter 2797031c2a00SAdrian Hunterdef Main(): 2798031c2a00SAdrian Hunter if (len(sys.argv) < 2): 279965b24292SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}" 2800031c2a00SAdrian Hunter raise Exception("Too few arguments") 2801031c2a00SAdrian Hunter 2802031c2a00SAdrian Hunter dbname = sys.argv[1] 280365b24292SAdrian Hunter if dbname == "--help-only": 280465b24292SAdrian Hunter app = QApplication(sys.argv) 280565b24292SAdrian Hunter mainwindow = HelpOnlyWindow() 280665b24292SAdrian Hunter mainwindow.show() 280765b24292SAdrian Hunter err = app.exec_() 280865b24292SAdrian Hunter sys.exit(err) 2809031c2a00SAdrian Hunter 2810031c2a00SAdrian Hunter is_sqlite3 = False 2811031c2a00SAdrian Hunter try: 2812031c2a00SAdrian Hunter f = open(dbname) 2813031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 2814031c2a00SAdrian Hunter is_sqlite3 = True 2815031c2a00SAdrian Hunter f.close() 2816031c2a00SAdrian Hunter except: 2817031c2a00SAdrian Hunter pass 2818031c2a00SAdrian Hunter 2819031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 2820031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 2821031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 2822031c2a00SAdrian Hunter app = QApplication(sys.argv) 2823031c2a00SAdrian Hunter glb.app = app 2824031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 2825031c2a00SAdrian Hunter glb.mainwindow = mainwindow 2826031c2a00SAdrian Hunter mainwindow.show() 2827031c2a00SAdrian Hunter err = app.exec_() 28288392b74bSAdrian Hunter glb.ShutdownInstances() 2829031c2a00SAdrian Hunter db.close() 2830031c2a00SAdrian Hunter sys.exit(err) 2831031c2a00SAdrian Hunter 2832031c2a00SAdrian Hunterif __name__ == "__main__": 2833031c2a00SAdrian Hunter Main() 2834