1031c2a00SAdrian Hunter#!/usr/bin/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 122*210cf1f9SAdrian Hunterdef findnth(s, sub, n, offs=0): 123*210cf1f9SAdrian Hunter pos = s.find(sub) 124*210cf1f9SAdrian Hunter if pos < 0: 125*210cf1f9SAdrian Hunter return pos 126*210cf1f9SAdrian Hunter if n <= 1: 127*210cf1f9SAdrian Hunter return offs + pos 128*210cf1f9SAdrian Hunter return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) 129*210cf1f9SAdrian 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 170031c2a00SAdrian Hunter def __init__(self, root, parent=None): 171031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 172031c2a00SAdrian Hunter self.root = root 173031c2a00SAdrian Hunter self.last_row_read = 0 174031c2a00SAdrian Hunter 175031c2a00SAdrian Hunter def Item(self, parent): 176031c2a00SAdrian Hunter if parent.isValid(): 177031c2a00SAdrian Hunter return parent.internalPointer() 178031c2a00SAdrian Hunter else: 179031c2a00SAdrian Hunter return self.root 180031c2a00SAdrian Hunter 181031c2a00SAdrian Hunter def rowCount(self, parent): 182031c2a00SAdrian Hunter result = self.Item(parent).childCount() 183031c2a00SAdrian Hunter if result < 0: 184031c2a00SAdrian Hunter result = 0 185031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 186031c2a00SAdrian Hunter return result 187031c2a00SAdrian Hunter 188031c2a00SAdrian Hunter def hasChildren(self, parent): 189031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 190031c2a00SAdrian Hunter 191031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 192031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 193031c2a00SAdrian Hunter return self.columnAlignment(section) 194031c2a00SAdrian Hunter if role != Qt.DisplayRole: 195031c2a00SAdrian Hunter return None 196031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 197031c2a00SAdrian Hunter return None 198031c2a00SAdrian Hunter return self.columnHeader(section) 199031c2a00SAdrian Hunter 200031c2a00SAdrian Hunter def parent(self, child): 201031c2a00SAdrian Hunter child_item = child.internalPointer() 202031c2a00SAdrian Hunter if child_item is self.root: 203031c2a00SAdrian Hunter return QModelIndex() 204031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 205031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 206031c2a00SAdrian Hunter 207031c2a00SAdrian Hunter def index(self, row, column, parent): 208031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 209031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 210031c2a00SAdrian Hunter 211031c2a00SAdrian Hunter def DisplayData(self, item, index): 212031c2a00SAdrian Hunter return item.getData(index.column()) 213031c2a00SAdrian Hunter 2148392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2158392b74bSAdrian Hunter if row > self.last_row_read: 2168392b74bSAdrian Hunter self.last_row_read = row 2178392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2188392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2198392b74bSAdrian Hunter 2208392b74bSAdrian Hunter def columnAlignment(self, column): 2218392b74bSAdrian Hunter return Qt.AlignLeft 2228392b74bSAdrian Hunter 2238392b74bSAdrian Hunter def columnFont(self, column): 2248392b74bSAdrian Hunter return None 2258392b74bSAdrian Hunter 2268392b74bSAdrian Hunter def data(self, index, role): 2278392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2288392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2298392b74bSAdrian Hunter if role == Qt.FontRole: 2308392b74bSAdrian Hunter return self.columnFont(index.column()) 2318392b74bSAdrian Hunter if role != Qt.DisplayRole: 2328392b74bSAdrian Hunter return None 2338392b74bSAdrian Hunter item = index.internalPointer() 2348392b74bSAdrian Hunter return self.DisplayData(item, index) 2358392b74bSAdrian Hunter 2368392b74bSAdrian Hunter# Table data model 2378392b74bSAdrian Hunter 2388392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2398392b74bSAdrian Hunter 2408392b74bSAdrian Hunter def __init__(self, parent=None): 2418392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2428392b74bSAdrian Hunter self.child_count = 0 2438392b74bSAdrian Hunter self.child_items = [] 2448392b74bSAdrian Hunter self.last_row_read = 0 2458392b74bSAdrian Hunter 2468392b74bSAdrian Hunter def Item(self, parent): 2478392b74bSAdrian Hunter if parent.isValid(): 2488392b74bSAdrian Hunter return parent.internalPointer() 2498392b74bSAdrian Hunter else: 2508392b74bSAdrian Hunter return self 2518392b74bSAdrian Hunter 2528392b74bSAdrian Hunter def rowCount(self, parent): 2538392b74bSAdrian Hunter return self.child_count 2548392b74bSAdrian Hunter 2558392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2568392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2578392b74bSAdrian Hunter return self.columnAlignment(section) 2588392b74bSAdrian Hunter if role != Qt.DisplayRole: 2598392b74bSAdrian Hunter return None 2608392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2618392b74bSAdrian Hunter return None 2628392b74bSAdrian Hunter return self.columnHeader(section) 2638392b74bSAdrian Hunter 2648392b74bSAdrian Hunter def index(self, row, column, parent): 2658392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 2668392b74bSAdrian Hunter 2678392b74bSAdrian Hunter def DisplayData(self, item, index): 2688392b74bSAdrian Hunter return item.getData(index.column()) 2698392b74bSAdrian Hunter 2708392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2718392b74bSAdrian Hunter if row > self.last_row_read: 2728392b74bSAdrian Hunter self.last_row_read = row 2738392b74bSAdrian Hunter if row + 10 >= self.child_count: 2748392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2758392b74bSAdrian Hunter 276031c2a00SAdrian Hunter def columnAlignment(self, column): 277031c2a00SAdrian Hunter return Qt.AlignLeft 278031c2a00SAdrian Hunter 279031c2a00SAdrian Hunter def columnFont(self, column): 280031c2a00SAdrian Hunter return None 281031c2a00SAdrian Hunter 282031c2a00SAdrian Hunter def data(self, index, role): 283031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 284031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 285031c2a00SAdrian Hunter if role == Qt.FontRole: 286031c2a00SAdrian Hunter return self.columnFont(index.column()) 287031c2a00SAdrian Hunter if role != Qt.DisplayRole: 288031c2a00SAdrian Hunter return None 289031c2a00SAdrian Hunter item = index.internalPointer() 290031c2a00SAdrian Hunter return self.DisplayData(item, index) 291031c2a00SAdrian Hunter 2921beb5c7bSAdrian Hunter# Model cache 2931beb5c7bSAdrian Hunter 2941beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 2951beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 2961beb5c7bSAdrian Hunter 2971beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 2981beb5c7bSAdrian Hunter model_cache_lock.acquire() 2991beb5c7bSAdrian Hunter try: 3001beb5c7bSAdrian Hunter model = model_cache[model_name] 3011beb5c7bSAdrian Hunter except: 3021beb5c7bSAdrian Hunter model = None 3031beb5c7bSAdrian Hunter if model is None: 3041beb5c7bSAdrian Hunter model = create_fn() 3051beb5c7bSAdrian Hunter model_cache[model_name] = model 3061beb5c7bSAdrian Hunter model_cache_lock.release() 3071beb5c7bSAdrian Hunter return model 3081beb5c7bSAdrian Hunter 309ebd70c7dSAdrian Hunter# Find bar 310ebd70c7dSAdrian Hunter 311ebd70c7dSAdrian Hunterclass FindBar(): 312ebd70c7dSAdrian Hunter 313ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 314ebd70c7dSAdrian Hunter self.finder = finder 315ebd70c7dSAdrian Hunter self.context = [] 316ebd70c7dSAdrian Hunter self.last_value = None 317ebd70c7dSAdrian Hunter self.last_pattern = None 318ebd70c7dSAdrian Hunter 319ebd70c7dSAdrian Hunter label = QLabel("Find:") 320ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 321ebd70c7dSAdrian Hunter 322ebd70c7dSAdrian Hunter self.textbox = QComboBox() 323ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 324ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 325ebd70c7dSAdrian Hunter 326ebd70c7dSAdrian Hunter self.progress = QProgressBar() 327ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 328ebd70c7dSAdrian Hunter self.progress.hide() 329ebd70c7dSAdrian Hunter 330ebd70c7dSAdrian Hunter if is_reg_expr: 331ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 332ebd70c7dSAdrian Hunter else: 333ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 334ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 335ebd70c7dSAdrian Hunter 336ebd70c7dSAdrian Hunter self.next_button = QToolButton() 337ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 338ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 339ebd70c7dSAdrian Hunter 340ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 341ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 342ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 343ebd70c7dSAdrian Hunter 344ebd70c7dSAdrian Hunter self.close_button = QToolButton() 345ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 346ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 347ebd70c7dSAdrian Hunter 348ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 349ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 350ebd70c7dSAdrian Hunter 351ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 352ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 353ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 354ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 355ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 356ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 357ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 358ebd70c7dSAdrian Hunter 359ebd70c7dSAdrian Hunter self.bar = QWidget() 360ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 361ebd70c7dSAdrian Hunter self.bar.hide() 362ebd70c7dSAdrian Hunter 363ebd70c7dSAdrian Hunter def Widget(self): 364ebd70c7dSAdrian Hunter return self.bar 365ebd70c7dSAdrian Hunter 366ebd70c7dSAdrian Hunter def Activate(self): 367ebd70c7dSAdrian Hunter self.bar.show() 368ebd70c7dSAdrian Hunter self.textbox.setFocus() 369ebd70c7dSAdrian Hunter 370ebd70c7dSAdrian Hunter def Deactivate(self): 371ebd70c7dSAdrian Hunter self.bar.hide() 372ebd70c7dSAdrian Hunter 373ebd70c7dSAdrian Hunter def Busy(self): 374ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 375ebd70c7dSAdrian Hunter self.pattern.hide() 376ebd70c7dSAdrian Hunter self.next_button.hide() 377ebd70c7dSAdrian Hunter self.prev_button.hide() 378ebd70c7dSAdrian Hunter self.progress.show() 379ebd70c7dSAdrian Hunter 380ebd70c7dSAdrian Hunter def Idle(self): 381ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 382ebd70c7dSAdrian Hunter self.progress.hide() 383ebd70c7dSAdrian Hunter self.pattern.show() 384ebd70c7dSAdrian Hunter self.next_button.show() 385ebd70c7dSAdrian Hunter self.prev_button.show() 386ebd70c7dSAdrian Hunter 387ebd70c7dSAdrian Hunter def Find(self, direction): 388ebd70c7dSAdrian Hunter value = self.textbox.currentText() 389ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 390ebd70c7dSAdrian Hunter self.last_value = value 391ebd70c7dSAdrian Hunter self.last_pattern = pattern 392ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 393ebd70c7dSAdrian Hunter 394ebd70c7dSAdrian Hunter def ValueChanged(self): 395ebd70c7dSAdrian Hunter value = self.textbox.currentText() 396ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 397ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 398ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 399ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 400ebd70c7dSAdrian Hunter if data == None: 401ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 402ebd70c7dSAdrian Hunter else: 403ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 404ebd70c7dSAdrian Hunter self.Find(0) 405ebd70c7dSAdrian Hunter 406ebd70c7dSAdrian Hunter def NextPrev(self, direction): 407ebd70c7dSAdrian Hunter value = self.textbox.currentText() 408ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 409ebd70c7dSAdrian Hunter if value != self.last_value: 410ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 411ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 412ebd70c7dSAdrian Hunter if index < 0: 413ebd70c7dSAdrian Hunter index = self.textbox.count() 414ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 415ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 416ebd70c7dSAdrian Hunter return 417ebd70c7dSAdrian Hunter else: 418ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 419ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 420ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 421ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 422ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 423ebd70c7dSAdrian Hunter self.Find(direction) 424ebd70c7dSAdrian Hunter 425ebd70c7dSAdrian Hunter def NotFound(self): 426ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 427ebd70c7dSAdrian Hunter 428031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 429031c2a00SAdrian Hunter 430031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 431031c2a00SAdrian Hunter 432031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 433031c2a00SAdrian Hunter self.glb = glb 434031c2a00SAdrian Hunter self.row = row 435031c2a00SAdrian Hunter self.parent_item = parent_item 436031c2a00SAdrian Hunter self.query_done = False; 437031c2a00SAdrian Hunter self.child_count = 0 438031c2a00SAdrian Hunter self.child_items = [] 439031c2a00SAdrian Hunter 440031c2a00SAdrian Hunter def getChildItem(self, row): 441031c2a00SAdrian Hunter return self.child_items[row] 442031c2a00SAdrian Hunter 443031c2a00SAdrian Hunter def getParentItem(self): 444031c2a00SAdrian Hunter return self.parent_item 445031c2a00SAdrian Hunter 446031c2a00SAdrian Hunter def getRow(self): 447031c2a00SAdrian Hunter return self.row 448031c2a00SAdrian Hunter 449031c2a00SAdrian Hunter def childCount(self): 450031c2a00SAdrian Hunter if not self.query_done: 451031c2a00SAdrian Hunter self.Select() 452031c2a00SAdrian Hunter if not self.child_count: 453031c2a00SAdrian Hunter return -1 454031c2a00SAdrian Hunter return self.child_count 455031c2a00SAdrian Hunter 456031c2a00SAdrian Hunter def hasChildren(self): 457031c2a00SAdrian Hunter if not self.query_done: 458031c2a00SAdrian Hunter return True 459031c2a00SAdrian Hunter return self.child_count > 0 460031c2a00SAdrian Hunter 461031c2a00SAdrian Hunter def getData(self, column): 462031c2a00SAdrian Hunter return self.data[column] 463031c2a00SAdrian Hunter 464031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 465031c2a00SAdrian Hunter 466031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 467031c2a00SAdrian Hunter 468031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 469031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 470031c2a00SAdrian Hunter self.comm_id = comm_id 471031c2a00SAdrian Hunter self.thread_id = thread_id 472031c2a00SAdrian Hunter self.call_path_id = call_path_id 473031c2a00SAdrian Hunter self.branch_count = branch_count 474031c2a00SAdrian Hunter self.time = time 475031c2a00SAdrian Hunter 476031c2a00SAdrian Hunter def Select(self): 477031c2a00SAdrian Hunter self.query_done = True; 478031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 479031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 480031c2a00SAdrian Hunter " FROM calls" 481031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 482031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 483031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 484031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 485031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 486031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 487031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 488031c2a00SAdrian Hunter " ORDER BY call_path_id") 489031c2a00SAdrian Hunter while query.next(): 490031c2a00SAdrian 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) 491031c2a00SAdrian Hunter self.child_items.append(child_item) 492031c2a00SAdrian Hunter self.child_count += 1 493031c2a00SAdrian Hunter 494031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 497031c2a00SAdrian Hunter 498031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 499031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 500031c2a00SAdrian Hunter dso = dsoname(dso) 501031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 502031c2a00SAdrian Hunter self.dbid = call_path_id 503031c2a00SAdrian Hunter 504031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 505031c2a00SAdrian Hunter 506031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 507031c2a00SAdrian Hunter 508031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 509031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 510031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 511031c2a00SAdrian Hunter self.dbid = thread_id 512031c2a00SAdrian Hunter 513031c2a00SAdrian Hunter def Select(self): 514031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 515031c2a00SAdrian Hunter for child_item in self.child_items: 516031c2a00SAdrian Hunter self.time += child_item.time 517031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 518031c2a00SAdrian Hunter for child_item in self.child_items: 519031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 520031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 521031c2a00SAdrian Hunter 522031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 523031c2a00SAdrian Hunter 524031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 525031c2a00SAdrian Hunter 526031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 527031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 528031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 529031c2a00SAdrian Hunter self.dbid = comm_id 530031c2a00SAdrian Hunter 531031c2a00SAdrian Hunter def Select(self): 532031c2a00SAdrian Hunter self.query_done = True; 533031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 534031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 535031c2a00SAdrian Hunter " FROM comm_threads" 536031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 537031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 538031c2a00SAdrian Hunter while query.next(): 539031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 540031c2a00SAdrian Hunter self.child_items.append(child_item) 541031c2a00SAdrian Hunter self.child_count += 1 542031c2a00SAdrian Hunter 543031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 544031c2a00SAdrian Hunter 545031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 546031c2a00SAdrian Hunter 547031c2a00SAdrian Hunter def __init__(self, glb): 548031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 549031c2a00SAdrian Hunter self.dbid = 0 550031c2a00SAdrian Hunter self.query_done = True; 551031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 552031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 553031c2a00SAdrian Hunter while query.next(): 554031c2a00SAdrian Hunter if not query.value(0): 555031c2a00SAdrian Hunter continue 556031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 557031c2a00SAdrian Hunter self.child_items.append(child_item) 558031c2a00SAdrian Hunter self.child_count += 1 559031c2a00SAdrian Hunter 560031c2a00SAdrian Hunter# Context-sensitive call graph data model 561031c2a00SAdrian Hunter 562031c2a00SAdrian Hunterclass CallGraphModel(TreeModel): 563031c2a00SAdrian Hunter 564031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 565031c2a00SAdrian Hunter super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) 566031c2a00SAdrian Hunter self.glb = glb 567031c2a00SAdrian Hunter 568031c2a00SAdrian Hunter def columnCount(self, parent=None): 569031c2a00SAdrian Hunter return 7 570031c2a00SAdrian Hunter 571031c2a00SAdrian Hunter def columnHeader(self, column): 572031c2a00SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 573031c2a00SAdrian Hunter return headers[column] 574031c2a00SAdrian Hunter 575031c2a00SAdrian Hunter def columnAlignment(self, column): 576031c2a00SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 577031c2a00SAdrian Hunter return alignment[column] 578031c2a00SAdrian Hunter 579ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 580ebd70c7dSAdrian Hunter if pattern: 581ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 582ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 583ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 584ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 585ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 586ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 587ebd70c7dSAdrian Hunter # Escape % and _ 588ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 589ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 590ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 591ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 592ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 593ebd70c7dSAdrian Hunter else: 594ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 595ebd70c7dSAdrian Hunter else: 596ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 597ebd70c7dSAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 598ebd70c7dSAdrian Hunter " FROM calls" 599ebd70c7dSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 600ebd70c7dSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 601ebd70c7dSAdrian Hunter " WHERE symbols.name" + match + 602ebd70c7dSAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 603ebd70c7dSAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 604ebd70c7dSAdrian Hunter 605ebd70c7dSAdrian Hunter def FindPath(self, query): 606ebd70c7dSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 607ebd70c7dSAdrian Hunter # to open the tree at the right place. 608ebd70c7dSAdrian Hunter ids = [] 609ebd70c7dSAdrian Hunter parent_id = query.value(0) 610ebd70c7dSAdrian Hunter while parent_id: 611ebd70c7dSAdrian Hunter ids.insert(0, parent_id) 612ebd70c7dSAdrian Hunter q2 = QSqlQuery(self.glb.db) 613ebd70c7dSAdrian Hunter QueryExec(q2, "SELECT parent_id" 614ebd70c7dSAdrian Hunter " FROM call_paths" 615ebd70c7dSAdrian Hunter " WHERE id = " + str(parent_id)) 616ebd70c7dSAdrian Hunter if not q2.next(): 617ebd70c7dSAdrian Hunter break 618ebd70c7dSAdrian Hunter parent_id = q2.value(0) 619ebd70c7dSAdrian Hunter # The call path root is not used 620ebd70c7dSAdrian Hunter if ids[0] == 1: 621ebd70c7dSAdrian Hunter del ids[0] 622ebd70c7dSAdrian Hunter ids.insert(0, query.value(2)) 623ebd70c7dSAdrian Hunter ids.insert(0, query.value(1)) 624ebd70c7dSAdrian Hunter return ids 625ebd70c7dSAdrian Hunter 626ebd70c7dSAdrian Hunter def Found(self, query, found): 627ebd70c7dSAdrian Hunter if found: 628ebd70c7dSAdrian Hunter return self.FindPath(query) 629ebd70c7dSAdrian Hunter return [] 630ebd70c7dSAdrian Hunter 631ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 632ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 633ebd70c7dSAdrian Hunter found = query.first() 634ebd70c7dSAdrian Hunter else: 635ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 636ebd70c7dSAdrian Hunter found = query.next() 637ebd70c7dSAdrian Hunter return self.Found(query, found) 638ebd70c7dSAdrian Hunter 639ebd70c7dSAdrian Hunter def FindNext(self, query): 640ebd70c7dSAdrian Hunter found = query.next() 641ebd70c7dSAdrian Hunter if not found: 642ebd70c7dSAdrian Hunter found = query.first() 643ebd70c7dSAdrian Hunter return self.Found(query, found) 644ebd70c7dSAdrian Hunter 645ebd70c7dSAdrian Hunter def FindPrev(self, query): 646ebd70c7dSAdrian Hunter found = query.previous() 647ebd70c7dSAdrian Hunter if not found: 648ebd70c7dSAdrian Hunter found = query.last() 649ebd70c7dSAdrian Hunter return self.Found(query, found) 650ebd70c7dSAdrian Hunter 651ebd70c7dSAdrian Hunter def FindThread(self, c): 652ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 653ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 654ebd70c7dSAdrian Hunter elif c.direction > 0: 655ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 656ebd70c7dSAdrian Hunter else: 657ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 658ebd70c7dSAdrian Hunter return (True, ids) 659ebd70c7dSAdrian Hunter 660ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 661ebd70c7dSAdrian Hunter class Context(): 662ebd70c7dSAdrian Hunter def __init__(self, *x): 663ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 664ebd70c7dSAdrian Hunter def Update(self, *x): 665ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 666ebd70c7dSAdrian Hunter if len(context): 667ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 668ebd70c7dSAdrian Hunter else: 669ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 670ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 671ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 672ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 673ebd70c7dSAdrian Hunter thread.start() 674ebd70c7dSAdrian Hunter 675ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 676ebd70c7dSAdrian Hunter callback(ids) 677ebd70c7dSAdrian Hunter 678ebd70c7dSAdrian Hunter# Vertical widget layout 679ebd70c7dSAdrian Hunter 680ebd70c7dSAdrian Hunterclass VBox(): 681ebd70c7dSAdrian Hunter 682ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 683ebd70c7dSAdrian Hunter self.vbox = QWidget() 684ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 685ebd70c7dSAdrian Hunter 686ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 687ebd70c7dSAdrian Hunter 688ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 689ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 690ebd70c7dSAdrian Hunter if w3: 691ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 692ebd70c7dSAdrian Hunter 693ebd70c7dSAdrian Hunter def Widget(self): 694ebd70c7dSAdrian Hunter return self.vbox 695ebd70c7dSAdrian Hunter 6961beb5c7bSAdrian Hunter# Context-sensitive call graph window 6971beb5c7bSAdrian Hunter 6981beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow): 6991beb5c7bSAdrian Hunter 7001beb5c7bSAdrian Hunter def __init__(self, glb, parent=None): 7011beb5c7bSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 7021beb5c7bSAdrian Hunter 7031beb5c7bSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 7041beb5c7bSAdrian Hunter 7051beb5c7bSAdrian Hunter self.view = QTreeView() 7061beb5c7bSAdrian Hunter self.view.setModel(self.model) 7071beb5c7bSAdrian Hunter 7081beb5c7bSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 7091beb5c7bSAdrian Hunter self.view.setColumnWidth(c, w) 7101beb5c7bSAdrian Hunter 711ebd70c7dSAdrian Hunter self.find_bar = FindBar(self, self) 712ebd70c7dSAdrian Hunter 713ebd70c7dSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 714ebd70c7dSAdrian Hunter 715ebd70c7dSAdrian Hunter self.setWidget(self.vbox.Widget()) 7161beb5c7bSAdrian Hunter 7171beb5c7bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 7181beb5c7bSAdrian Hunter 719ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 720ebd70c7dSAdrian Hunter if not len(ids): 721ebd70c7dSAdrian Hunter return False 722ebd70c7dSAdrian Hunter parent = QModelIndex() 723ebd70c7dSAdrian Hunter for dbid in ids: 724ebd70c7dSAdrian Hunter found = False 725ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 726ebd70c7dSAdrian Hunter for row in xrange(n): 727ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 728ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 729ebd70c7dSAdrian Hunter found = True 730ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 731ebd70c7dSAdrian Hunter parent = child 732ebd70c7dSAdrian Hunter break 733ebd70c7dSAdrian Hunter if not found: 734ebd70c7dSAdrian Hunter break 735ebd70c7dSAdrian Hunter return found 736ebd70c7dSAdrian Hunter 737ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 738ebd70c7dSAdrian Hunter self.view.setFocus() 739ebd70c7dSAdrian Hunter self.find_bar.Busy() 740ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 741ebd70c7dSAdrian Hunter 742ebd70c7dSAdrian Hunter def FindDone(self, ids): 743ebd70c7dSAdrian Hunter found = True 744ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 745ebd70c7dSAdrian Hunter found = False 746ebd70c7dSAdrian Hunter self.find_bar.Idle() 747ebd70c7dSAdrian Hunter if not found: 748ebd70c7dSAdrian Hunter self.find_bar.NotFound() 749ebd70c7dSAdrian Hunter 7508392b74bSAdrian Hunter# Child data item finder 7518392b74bSAdrian Hunter 7528392b74bSAdrian Hunterclass ChildDataItemFinder(): 7538392b74bSAdrian Hunter 7548392b74bSAdrian Hunter def __init__(self, root): 7558392b74bSAdrian Hunter self.root = root 7568392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 7578392b74bSAdrian Hunter self.rows = [] 7588392b74bSAdrian Hunter self.pos = 0 7598392b74bSAdrian Hunter 7608392b74bSAdrian Hunter def FindSelect(self): 7618392b74bSAdrian Hunter self.rows = [] 7628392b74bSAdrian Hunter if self.pattern: 7638392b74bSAdrian Hunter pattern = re.compile(self.value) 7648392b74bSAdrian Hunter for child in self.root.child_items: 7658392b74bSAdrian Hunter for column_data in child.data: 7668392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 7678392b74bSAdrian Hunter self.rows.append(child.row) 7688392b74bSAdrian Hunter break 7698392b74bSAdrian Hunter else: 7708392b74bSAdrian Hunter for child in self.root.child_items: 7718392b74bSAdrian Hunter for column_data in child.data: 7728392b74bSAdrian Hunter if self.value in str(column_data): 7738392b74bSAdrian Hunter self.rows.append(child.row) 7748392b74bSAdrian Hunter break 7758392b74bSAdrian Hunter 7768392b74bSAdrian Hunter def FindValue(self): 7778392b74bSAdrian Hunter self.pos = 0 7788392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 7798392b74bSAdrian Hunter self.FindSelect() 7808392b74bSAdrian Hunter if not len(self.rows): 7818392b74bSAdrian Hunter return -1 7828392b74bSAdrian Hunter return self.rows[self.pos] 7838392b74bSAdrian Hunter 7848392b74bSAdrian Hunter def FindThread(self): 7858392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 7868392b74bSAdrian Hunter row = self.FindValue() 7878392b74bSAdrian Hunter elif len(self.rows): 7888392b74bSAdrian Hunter if self.direction > 0: 7898392b74bSAdrian Hunter self.pos += 1 7908392b74bSAdrian Hunter if self.pos >= len(self.rows): 7918392b74bSAdrian Hunter self.pos = 0 7928392b74bSAdrian Hunter else: 7938392b74bSAdrian Hunter self.pos -= 1 7948392b74bSAdrian Hunter if self.pos < 0: 7958392b74bSAdrian Hunter self.pos = len(self.rows) - 1 7968392b74bSAdrian Hunter row = self.rows[self.pos] 7978392b74bSAdrian Hunter else: 7988392b74bSAdrian Hunter row = -1 7998392b74bSAdrian Hunter return (True, row) 8008392b74bSAdrian Hunter 8018392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 8028392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 8038392b74bSAdrian Hunter # Use a thread so the UI is not blocked 8048392b74bSAdrian Hunter thread = Thread(self.FindThread) 8058392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 8068392b74bSAdrian Hunter thread.start() 8078392b74bSAdrian Hunter 8088392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 8098392b74bSAdrian Hunter callback(row) 8108392b74bSAdrian Hunter 8118392b74bSAdrian Hunter# Number of database records to fetch in one go 8128392b74bSAdrian Hunter 8138392b74bSAdrian Hunterglb_chunk_sz = 10000 8148392b74bSAdrian Hunter 8158392b74bSAdrian Hunter# size of pickled integer big enough for record size 8168392b74bSAdrian Hunter 8178392b74bSAdrian Hunterglb_nsz = 8 8188392b74bSAdrian Hunter 8198392b74bSAdrian Hunter# Background process for SQL data fetcher 8208392b74bSAdrian Hunter 8218392b74bSAdrian Hunterclass SQLFetcherProcess(): 8228392b74bSAdrian Hunter 8238392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 8248392b74bSAdrian Hunter # Need a unique connection name 8258392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 8268392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 8278392b74bSAdrian Hunter self.sql = sql 8288392b74bSAdrian Hunter self.buffer = buffer 8298392b74bSAdrian Hunter self.head = head 8308392b74bSAdrian Hunter self.tail = tail 8318392b74bSAdrian Hunter self.fetch_count = fetch_count 8328392b74bSAdrian Hunter self.fetching_done = fetching_done 8338392b74bSAdrian Hunter self.process_target = process_target 8348392b74bSAdrian Hunter self.wait_event = wait_event 8358392b74bSAdrian Hunter self.fetched_event = fetched_event 8368392b74bSAdrian Hunter self.prep = prep 8378392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 8388392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 8398392b74bSAdrian Hunter self.last_id = -1 8408392b74bSAdrian Hunter self.fetched = 0 8418392b74bSAdrian Hunter self.more = True 8428392b74bSAdrian Hunter self.local_head = self.head.value 8438392b74bSAdrian Hunter self.local_tail = self.tail.value 8448392b74bSAdrian Hunter 8458392b74bSAdrian Hunter def Select(self): 8468392b74bSAdrian Hunter if self.query_limit: 8478392b74bSAdrian Hunter if self.query_limit == 1: 8488392b74bSAdrian Hunter return 8498392b74bSAdrian Hunter self.query_limit -= 1 8508392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 8518392b74bSAdrian Hunter QueryExec(self.query, stmt) 8528392b74bSAdrian Hunter 8538392b74bSAdrian Hunter def Next(self): 8548392b74bSAdrian Hunter if not self.query.next(): 8558392b74bSAdrian Hunter self.Select() 8568392b74bSAdrian Hunter if not self.query.next(): 8578392b74bSAdrian Hunter return None 8588392b74bSAdrian Hunter self.last_id = self.query.value(0) 8598392b74bSAdrian Hunter return self.prep(self.query) 8608392b74bSAdrian Hunter 8618392b74bSAdrian Hunter def WaitForTarget(self): 8628392b74bSAdrian Hunter while True: 8638392b74bSAdrian Hunter self.wait_event.clear() 8648392b74bSAdrian Hunter target = self.process_target.value 8658392b74bSAdrian Hunter if target > self.fetched or target < 0: 8668392b74bSAdrian Hunter break 8678392b74bSAdrian Hunter self.wait_event.wait() 8688392b74bSAdrian Hunter return target 8698392b74bSAdrian Hunter 8708392b74bSAdrian Hunter def HasSpace(self, sz): 8718392b74bSAdrian Hunter if self.local_tail <= self.local_head: 8728392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 8738392b74bSAdrian Hunter if space > sz: 8748392b74bSAdrian Hunter return True 8758392b74bSAdrian Hunter if space >= glb_nsz: 8768392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 8778392b74bSAdrian Hunter nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL) 8788392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 8798392b74bSAdrian Hunter self.local_head = 0 8808392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 8818392b74bSAdrian Hunter return True 8828392b74bSAdrian Hunter return False 8838392b74bSAdrian Hunter 8848392b74bSAdrian Hunter def WaitForSpace(self, sz): 8858392b74bSAdrian Hunter if self.HasSpace(sz): 8868392b74bSAdrian Hunter return 8878392b74bSAdrian Hunter while True: 8888392b74bSAdrian Hunter self.wait_event.clear() 8898392b74bSAdrian Hunter self.local_tail = self.tail.value 8908392b74bSAdrian Hunter if self.HasSpace(sz): 8918392b74bSAdrian Hunter return 8928392b74bSAdrian Hunter self.wait_event.wait() 8938392b74bSAdrian Hunter 8948392b74bSAdrian Hunter def AddToBuffer(self, obj): 8958392b74bSAdrian Hunter d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL) 8968392b74bSAdrian Hunter n = len(d) 8978392b74bSAdrian Hunter nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL) 8988392b74bSAdrian Hunter sz = n + glb_nsz 8998392b74bSAdrian Hunter self.WaitForSpace(sz) 9008392b74bSAdrian Hunter pos = self.local_head 9018392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 9028392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 9038392b74bSAdrian Hunter self.local_head += sz 9048392b74bSAdrian Hunter 9058392b74bSAdrian Hunter def FetchBatch(self, batch_size): 9068392b74bSAdrian Hunter fetched = 0 9078392b74bSAdrian Hunter while batch_size > fetched: 9088392b74bSAdrian Hunter obj = self.Next() 9098392b74bSAdrian Hunter if obj is None: 9108392b74bSAdrian Hunter self.more = False 9118392b74bSAdrian Hunter break 9128392b74bSAdrian Hunter self.AddToBuffer(obj) 9138392b74bSAdrian Hunter fetched += 1 9148392b74bSAdrian Hunter if fetched: 9158392b74bSAdrian Hunter self.fetched += fetched 9168392b74bSAdrian Hunter with self.fetch_count.get_lock(): 9178392b74bSAdrian Hunter self.fetch_count.value += fetched 9188392b74bSAdrian Hunter self.head.value = self.local_head 9198392b74bSAdrian Hunter self.fetched_event.set() 9208392b74bSAdrian Hunter 9218392b74bSAdrian Hunter def Run(self): 9228392b74bSAdrian Hunter while self.more: 9238392b74bSAdrian Hunter target = self.WaitForTarget() 9248392b74bSAdrian Hunter if target < 0: 9258392b74bSAdrian Hunter break 9268392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 9278392b74bSAdrian Hunter self.FetchBatch(batch_size) 9288392b74bSAdrian Hunter self.fetching_done.value = True 9298392b74bSAdrian Hunter self.fetched_event.set() 9308392b74bSAdrian Hunter 9318392b74bSAdrian Hunterdef SQLFetcherFn(*x): 9328392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 9338392b74bSAdrian Hunter process.Run() 9348392b74bSAdrian Hunter 9358392b74bSAdrian Hunter# SQL data fetcher 9368392b74bSAdrian Hunter 9378392b74bSAdrian Hunterclass SQLFetcher(QObject): 9388392b74bSAdrian Hunter 9398392b74bSAdrian Hunter done = Signal(object) 9408392b74bSAdrian Hunter 9418392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 9428392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 9438392b74bSAdrian Hunter self.process_data = process_data 9448392b74bSAdrian Hunter self.more = True 9458392b74bSAdrian Hunter self.target = 0 9468392b74bSAdrian Hunter self.last_target = 0 9478392b74bSAdrian Hunter self.fetched = 0 9488392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 9498392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 9508392b74bSAdrian Hunter self.head = Value(c_longlong) 9518392b74bSAdrian Hunter self.tail = Value(c_longlong) 9528392b74bSAdrian Hunter self.local_tail = 0 9538392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 9548392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 9558392b74bSAdrian Hunter self.last_count = 0 9568392b74bSAdrian Hunter self.process_target = Value(c_longlong) 9578392b74bSAdrian Hunter self.wait_event = Event() 9588392b74bSAdrian Hunter self.fetched_event = Event() 9598392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 9608392b74bSAdrian 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)) 9618392b74bSAdrian Hunter self.process.start() 9628392b74bSAdrian Hunter self.thread = Thread(self.Thread) 9638392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 9648392b74bSAdrian Hunter self.thread.start() 9658392b74bSAdrian Hunter 9668392b74bSAdrian Hunter def Shutdown(self): 9678392b74bSAdrian Hunter # Tell the thread and process to exit 9688392b74bSAdrian Hunter self.process_target.value = -1 9698392b74bSAdrian Hunter self.wait_event.set() 9708392b74bSAdrian Hunter self.more = False 9718392b74bSAdrian Hunter self.fetching_done.value = True 9728392b74bSAdrian Hunter self.fetched_event.set() 9738392b74bSAdrian Hunter 9748392b74bSAdrian Hunter def Thread(self): 9758392b74bSAdrian Hunter if not self.more: 9768392b74bSAdrian Hunter return True, 0 9778392b74bSAdrian Hunter while True: 9788392b74bSAdrian Hunter self.fetched_event.clear() 9798392b74bSAdrian Hunter fetch_count = self.fetch_count.value 9808392b74bSAdrian Hunter if fetch_count != self.last_count: 9818392b74bSAdrian Hunter break 9828392b74bSAdrian Hunter if self.fetching_done.value: 9838392b74bSAdrian Hunter self.more = False 9848392b74bSAdrian Hunter return True, 0 9858392b74bSAdrian Hunter self.fetched_event.wait() 9868392b74bSAdrian Hunter count = fetch_count - self.last_count 9878392b74bSAdrian Hunter self.last_count = fetch_count 9888392b74bSAdrian Hunter self.fetched += count 9898392b74bSAdrian Hunter return False, count 9908392b74bSAdrian Hunter 9918392b74bSAdrian Hunter def Fetch(self, nr): 9928392b74bSAdrian Hunter if not self.more: 9938392b74bSAdrian Hunter # -1 inidcates there are no more 9948392b74bSAdrian Hunter return -1 9958392b74bSAdrian Hunter result = self.fetched 9968392b74bSAdrian Hunter extra = result + nr - self.target 9978392b74bSAdrian Hunter if extra > 0: 9988392b74bSAdrian Hunter self.target += extra 9998392b74bSAdrian Hunter # process_target < 0 indicates shutting down 10008392b74bSAdrian Hunter if self.process_target.value >= 0: 10018392b74bSAdrian Hunter self.process_target.value = self.target 10028392b74bSAdrian Hunter self.wait_event.set() 10038392b74bSAdrian Hunter return result 10048392b74bSAdrian Hunter 10058392b74bSAdrian Hunter def RemoveFromBuffer(self): 10068392b74bSAdrian Hunter pos = self.local_tail 10078392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 10088392b74bSAdrian Hunter pos = 0 10098392b74bSAdrian Hunter n = cPickle.loads(self.buffer[pos : pos + glb_nsz]) 10108392b74bSAdrian Hunter if n == 0: 10118392b74bSAdrian Hunter pos = 0 10128392b74bSAdrian Hunter n = cPickle.loads(self.buffer[0 : glb_nsz]) 10138392b74bSAdrian Hunter pos += glb_nsz 10148392b74bSAdrian Hunter obj = cPickle.loads(self.buffer[pos : pos + n]) 10158392b74bSAdrian Hunter self.local_tail = pos + n 10168392b74bSAdrian Hunter return obj 10178392b74bSAdrian Hunter 10188392b74bSAdrian Hunter def ProcessData(self, count): 10198392b74bSAdrian Hunter for i in xrange(count): 10208392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 10218392b74bSAdrian Hunter self.process_data(obj) 10228392b74bSAdrian Hunter self.tail.value = self.local_tail 10238392b74bSAdrian Hunter self.wait_event.set() 10248392b74bSAdrian Hunter self.done.emit(count) 10258392b74bSAdrian Hunter 10268392b74bSAdrian Hunter# Fetch more records bar 10278392b74bSAdrian Hunter 10288392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 10298392b74bSAdrian Hunter 10308392b74bSAdrian Hunter def __init__(self, model, parent): 10318392b74bSAdrian Hunter self.model = model 10328392b74bSAdrian Hunter 10338392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 10348392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10358392b74bSAdrian Hunter 10368392b74bSAdrian Hunter self.fetch_count = QSpinBox() 10378392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 10388392b74bSAdrian Hunter self.fetch_count.setValue(10) 10398392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10408392b74bSAdrian Hunter 10418392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 10428392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10438392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 10448392b74bSAdrian Hunter 10458392b74bSAdrian Hunter self.progress = QProgressBar() 10468392b74bSAdrian Hunter self.progress.setRange(0, 100) 10478392b74bSAdrian Hunter self.progress.hide() 10488392b74bSAdrian Hunter 10498392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 10508392b74bSAdrian Hunter self.done_label.hide() 10518392b74bSAdrian Hunter 10528392b74bSAdrian Hunter self.spacer = QLabel("") 10538392b74bSAdrian Hunter 10548392b74bSAdrian Hunter self.close_button = QToolButton() 10558392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 10568392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 10578392b74bSAdrian Hunter 10588392b74bSAdrian Hunter self.hbox = QHBoxLayout() 10598392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 10608392b74bSAdrian Hunter 10618392b74bSAdrian Hunter self.hbox.addWidget(self.label) 10628392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 10638392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 10648392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 10658392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 10668392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 10678392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 10688392b74bSAdrian Hunter 10698392b74bSAdrian Hunter self.bar = QWidget() 10708392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 10718392b74bSAdrian Hunter self.bar.show() 10728392b74bSAdrian Hunter 10738392b74bSAdrian Hunter self.in_progress = False 10748392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 10758392b74bSAdrian Hunter 10768392b74bSAdrian Hunter self.done = False 10778392b74bSAdrian Hunter 10788392b74bSAdrian Hunter if not model.HasMoreRecords(): 10798392b74bSAdrian Hunter self.Done() 10808392b74bSAdrian Hunter 10818392b74bSAdrian Hunter def Widget(self): 10828392b74bSAdrian Hunter return self.bar 10838392b74bSAdrian Hunter 10848392b74bSAdrian Hunter def Activate(self): 10858392b74bSAdrian Hunter self.bar.show() 10868392b74bSAdrian Hunter self.fetch.setFocus() 10878392b74bSAdrian Hunter 10888392b74bSAdrian Hunter def Deactivate(self): 10898392b74bSAdrian Hunter self.bar.hide() 10908392b74bSAdrian Hunter 10918392b74bSAdrian Hunter def Enable(self, enable): 10928392b74bSAdrian Hunter self.fetch.setEnabled(enable) 10938392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 10948392b74bSAdrian Hunter 10958392b74bSAdrian Hunter def Busy(self): 10968392b74bSAdrian Hunter self.Enable(False) 10978392b74bSAdrian Hunter self.fetch.hide() 10988392b74bSAdrian Hunter self.spacer.hide() 10998392b74bSAdrian Hunter self.progress.show() 11008392b74bSAdrian Hunter 11018392b74bSAdrian Hunter def Idle(self): 11028392b74bSAdrian Hunter self.in_progress = False 11038392b74bSAdrian Hunter self.Enable(True) 11048392b74bSAdrian Hunter self.progress.hide() 11058392b74bSAdrian Hunter self.fetch.show() 11068392b74bSAdrian Hunter self.spacer.show() 11078392b74bSAdrian Hunter 11088392b74bSAdrian Hunter def Target(self): 11098392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 11108392b74bSAdrian Hunter 11118392b74bSAdrian Hunter def Done(self): 11128392b74bSAdrian Hunter self.done = True 11138392b74bSAdrian Hunter self.Idle() 11148392b74bSAdrian Hunter self.label.hide() 11158392b74bSAdrian Hunter self.fetch_count.hide() 11168392b74bSAdrian Hunter self.fetch.hide() 11178392b74bSAdrian Hunter self.spacer.hide() 11188392b74bSAdrian Hunter self.done_label.show() 11198392b74bSAdrian Hunter 11208392b74bSAdrian Hunter def Progress(self, count): 11218392b74bSAdrian Hunter if self.in_progress: 11228392b74bSAdrian Hunter if count: 11238392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 11248392b74bSAdrian Hunter if percent >= 100: 11258392b74bSAdrian Hunter self.Idle() 11268392b74bSAdrian Hunter else: 11278392b74bSAdrian Hunter self.progress.setValue(percent) 11288392b74bSAdrian Hunter if not count: 11298392b74bSAdrian Hunter # Count value of zero means no more records 11308392b74bSAdrian Hunter self.Done() 11318392b74bSAdrian Hunter 11328392b74bSAdrian Hunter def FetchMoreRecords(self): 11338392b74bSAdrian Hunter if self.done: 11348392b74bSAdrian Hunter return 11358392b74bSAdrian Hunter self.progress.setValue(0) 11368392b74bSAdrian Hunter self.Busy() 11378392b74bSAdrian Hunter self.in_progress = True 11388392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 11398392b74bSAdrian Hunter 114076099f98SAdrian Hunter# Brance data model level two item 114176099f98SAdrian Hunter 114276099f98SAdrian Hunterclass BranchLevelTwoItem(): 114376099f98SAdrian Hunter 114476099f98SAdrian Hunter def __init__(self, row, text, parent_item): 114576099f98SAdrian Hunter self.row = row 114676099f98SAdrian Hunter self.parent_item = parent_item 114776099f98SAdrian Hunter self.data = [""] * 8 114876099f98SAdrian Hunter self.data[7] = text 114976099f98SAdrian Hunter self.level = 2 115076099f98SAdrian Hunter 115176099f98SAdrian Hunter def getParentItem(self): 115276099f98SAdrian Hunter return self.parent_item 115376099f98SAdrian Hunter 115476099f98SAdrian Hunter def getRow(self): 115576099f98SAdrian Hunter return self.row 115676099f98SAdrian Hunter 115776099f98SAdrian Hunter def childCount(self): 115876099f98SAdrian Hunter return 0 115976099f98SAdrian Hunter 116076099f98SAdrian Hunter def hasChildren(self): 116176099f98SAdrian Hunter return False 116276099f98SAdrian Hunter 116376099f98SAdrian Hunter def getData(self, column): 116476099f98SAdrian Hunter return self.data[column] 116576099f98SAdrian Hunter 116676099f98SAdrian Hunter# Brance data model level one item 116776099f98SAdrian Hunter 116876099f98SAdrian Hunterclass BranchLevelOneItem(): 116976099f98SAdrian Hunter 117076099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 117176099f98SAdrian Hunter self.glb = glb 117276099f98SAdrian Hunter self.row = row 117376099f98SAdrian Hunter self.parent_item = parent_item 117476099f98SAdrian Hunter self.child_count = 0 117576099f98SAdrian Hunter self.child_items = [] 117676099f98SAdrian Hunter self.data = data[1:] 117776099f98SAdrian Hunter self.dbid = data[0] 117876099f98SAdrian Hunter self.level = 1 117976099f98SAdrian Hunter self.query_done = False 118076099f98SAdrian Hunter 118176099f98SAdrian Hunter def getChildItem(self, row): 118276099f98SAdrian Hunter return self.child_items[row] 118376099f98SAdrian Hunter 118476099f98SAdrian Hunter def getParentItem(self): 118576099f98SAdrian Hunter return self.parent_item 118676099f98SAdrian Hunter 118776099f98SAdrian Hunter def getRow(self): 118876099f98SAdrian Hunter return self.row 118976099f98SAdrian Hunter 119076099f98SAdrian Hunter def Select(self): 119176099f98SAdrian Hunter self.query_done = True 119276099f98SAdrian Hunter 119376099f98SAdrian Hunter if not self.glb.have_disassembler: 119476099f98SAdrian Hunter return 119576099f98SAdrian Hunter 119676099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 119776099f98SAdrian Hunter 119876099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 119976099f98SAdrian Hunter " FROM samples" 120076099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 120176099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 120276099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 120376099f98SAdrian Hunter if not query.next(): 120476099f98SAdrian Hunter return 120576099f98SAdrian Hunter cpu = query.value(0) 120676099f98SAdrian Hunter dso = query.value(1) 120776099f98SAdrian Hunter sym = query.value(2) 120876099f98SAdrian Hunter if dso == 0 or sym == 0: 120976099f98SAdrian Hunter return 121076099f98SAdrian Hunter off = query.value(3) 121176099f98SAdrian Hunter short_name = query.value(4) 121276099f98SAdrian Hunter long_name = query.value(5) 121376099f98SAdrian Hunter build_id = query.value(6) 121476099f98SAdrian Hunter sym_start = query.value(7) 121576099f98SAdrian Hunter ip = query.value(8) 121676099f98SAdrian Hunter 121776099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 121876099f98SAdrian Hunter " FROM samples" 121976099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 122076099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 122176099f98SAdrian Hunter " ORDER BY samples.id" 122276099f98SAdrian Hunter " LIMIT 1") 122376099f98SAdrian Hunter if not query.next(): 122476099f98SAdrian Hunter return 122576099f98SAdrian Hunter if query.value(0) != dso: 122676099f98SAdrian Hunter # Cannot disassemble from one dso to another 122776099f98SAdrian Hunter return 122876099f98SAdrian Hunter bsym = query.value(1) 122976099f98SAdrian Hunter boff = query.value(2) 123076099f98SAdrian Hunter bsym_start = query.value(3) 123176099f98SAdrian Hunter if bsym == 0: 123276099f98SAdrian Hunter return 123376099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 123476099f98SAdrian Hunter if tot <= 0 or tot > 16384: 123576099f98SAdrian Hunter return 123676099f98SAdrian Hunter 123776099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 123876099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 123976099f98SAdrian Hunter if not f: 124076099f98SAdrian Hunter return 124176099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 124276099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 124376099f98SAdrian Hunter 124476099f98SAdrian Hunter buf_sz = tot + 16 124576099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 124676099f98SAdrian Hunter f.seek(sym_start + off) 124776099f98SAdrian Hunter buf.value = f.read(buf_sz) 124876099f98SAdrian Hunter buf_ptr = addressof(buf) 124976099f98SAdrian Hunter i = 0 125076099f98SAdrian Hunter while tot > 0: 125176099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 125276099f98SAdrian Hunter if cnt: 125376099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 125476099f98SAdrian Hunter for k in xrange(cnt): 125576099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 125676099f98SAdrian Hunter i += 1 125776099f98SAdrian Hunter while k < 15: 125876099f98SAdrian Hunter byte_str += " " 125976099f98SAdrian Hunter k += 1 126076099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 126176099f98SAdrian Hunter self.child_count += 1 126276099f98SAdrian Hunter else: 126376099f98SAdrian Hunter return 126476099f98SAdrian Hunter buf_ptr += cnt 126576099f98SAdrian Hunter tot -= cnt 126676099f98SAdrian Hunter buf_sz -= cnt 126776099f98SAdrian Hunter ip += cnt 126876099f98SAdrian Hunter 126976099f98SAdrian Hunter def childCount(self): 127076099f98SAdrian Hunter if not self.query_done: 127176099f98SAdrian Hunter self.Select() 127276099f98SAdrian Hunter if not self.child_count: 127376099f98SAdrian Hunter return -1 127476099f98SAdrian Hunter return self.child_count 127576099f98SAdrian Hunter 127676099f98SAdrian Hunter def hasChildren(self): 127776099f98SAdrian Hunter if not self.query_done: 127876099f98SAdrian Hunter return True 127976099f98SAdrian Hunter return self.child_count > 0 128076099f98SAdrian Hunter 128176099f98SAdrian Hunter def getData(self, column): 128276099f98SAdrian Hunter return self.data[column] 128376099f98SAdrian Hunter 128476099f98SAdrian Hunter# Brance data model root item 128576099f98SAdrian Hunter 128676099f98SAdrian Hunterclass BranchRootItem(): 128776099f98SAdrian Hunter 128876099f98SAdrian Hunter def __init__(self): 128976099f98SAdrian Hunter self.child_count = 0 129076099f98SAdrian Hunter self.child_items = [] 129176099f98SAdrian Hunter self.level = 0 129276099f98SAdrian Hunter 129376099f98SAdrian Hunter def getChildItem(self, row): 129476099f98SAdrian Hunter return self.child_items[row] 129576099f98SAdrian Hunter 129676099f98SAdrian Hunter def getParentItem(self): 129776099f98SAdrian Hunter return None 129876099f98SAdrian Hunter 129976099f98SAdrian Hunter def getRow(self): 130076099f98SAdrian Hunter return 0 130176099f98SAdrian Hunter 130276099f98SAdrian Hunter def childCount(self): 130376099f98SAdrian Hunter return self.child_count 130476099f98SAdrian Hunter 130576099f98SAdrian Hunter def hasChildren(self): 130676099f98SAdrian Hunter return self.child_count > 0 130776099f98SAdrian Hunter 130876099f98SAdrian Hunter def getData(self, column): 130976099f98SAdrian Hunter return "" 131076099f98SAdrian Hunter 131176099f98SAdrian Hunter# Branch data preparation 131276099f98SAdrian Hunter 131376099f98SAdrian Hunterdef BranchDataPrep(query): 131476099f98SAdrian Hunter data = [] 131576099f98SAdrian Hunter for i in xrange(0, 8): 131676099f98SAdrian Hunter data.append(query.value(i)) 131776099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 131876099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 131976099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 132076099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 132176099f98SAdrian Hunter return data 132276099f98SAdrian Hunter 132376099f98SAdrian Hunter# Branch data model 132476099f98SAdrian Hunter 132576099f98SAdrian Hunterclass BranchModel(TreeModel): 132676099f98SAdrian Hunter 132776099f98SAdrian Hunter progress = Signal(object) 132876099f98SAdrian Hunter 132976099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 133076099f98SAdrian Hunter super(BranchModel, self).__init__(BranchRootItem(), parent) 133176099f98SAdrian Hunter self.glb = glb 133276099f98SAdrian Hunter self.event_id = event_id 133376099f98SAdrian Hunter self.more = True 133476099f98SAdrian Hunter self.populated = 0 133576099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 133676099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 133776099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 133876099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 133976099f98SAdrian Hunter " FROM samples" 134076099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 134176099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 134276099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 134376099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 134476099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 134576099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 134676099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 134776099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 134876099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 134976099f98SAdrian Hunter " ORDER BY samples.id" 135076099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 135176099f98SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample) 135276099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 135376099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 135476099f98SAdrian Hunter 135576099f98SAdrian Hunter def columnCount(self, parent=None): 135676099f98SAdrian Hunter return 8 135776099f98SAdrian Hunter 135876099f98SAdrian Hunter def columnHeader(self, column): 135976099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 136076099f98SAdrian Hunter 136176099f98SAdrian Hunter def columnFont(self, column): 136276099f98SAdrian Hunter if column != 7: 136376099f98SAdrian Hunter return None 136476099f98SAdrian Hunter return QFont("Monospace") 136576099f98SAdrian Hunter 136676099f98SAdrian Hunter def DisplayData(self, item, index): 136776099f98SAdrian Hunter if item.level == 1: 136876099f98SAdrian Hunter self.FetchIfNeeded(item.row) 136976099f98SAdrian Hunter return item.getData(index.column()) 137076099f98SAdrian Hunter 137176099f98SAdrian Hunter def AddSample(self, data): 137276099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 137376099f98SAdrian Hunter self.root.child_items.append(child) 137476099f98SAdrian Hunter self.populated += 1 137576099f98SAdrian Hunter 137676099f98SAdrian Hunter def Update(self, fetched): 137776099f98SAdrian Hunter if not fetched: 137876099f98SAdrian Hunter self.more = False 137976099f98SAdrian Hunter self.progress.emit(0) 138076099f98SAdrian Hunter child_count = self.root.child_count 138176099f98SAdrian Hunter count = self.populated - child_count 138276099f98SAdrian Hunter if count > 0: 138376099f98SAdrian Hunter parent = QModelIndex() 138476099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 138576099f98SAdrian Hunter self.insertRows(child_count, count, parent) 138676099f98SAdrian Hunter self.root.child_count += count 138776099f98SAdrian Hunter self.endInsertRows() 138876099f98SAdrian Hunter self.progress.emit(self.root.child_count) 138976099f98SAdrian Hunter 139076099f98SAdrian Hunter def FetchMoreRecords(self, count): 139176099f98SAdrian Hunter current = self.root.child_count 139276099f98SAdrian Hunter if self.more: 139376099f98SAdrian Hunter self.fetcher.Fetch(count) 139476099f98SAdrian Hunter else: 139576099f98SAdrian Hunter self.progress.emit(0) 139676099f98SAdrian Hunter return current 139776099f98SAdrian Hunter 139876099f98SAdrian Hunter def HasMoreRecords(self): 139976099f98SAdrian Hunter return self.more 140076099f98SAdrian Hunter 140176099f98SAdrian Hunter# Branch window 140276099f98SAdrian Hunter 140376099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 140476099f98SAdrian Hunter 140576099f98SAdrian Hunter def __init__(self, glb, event_id, name, where_clause, parent=None): 140676099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 140776099f98SAdrian Hunter 140876099f98SAdrian Hunter model_name = "Branch Events " + str(event_id) 140976099f98SAdrian Hunter if len(where_clause): 141076099f98SAdrian Hunter model_name = where_clause + " " + model_name 141176099f98SAdrian Hunter 141276099f98SAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) 141376099f98SAdrian Hunter 141476099f98SAdrian Hunter self.view = QTreeView() 141576099f98SAdrian Hunter self.view.setUniformRowHeights(True) 141676099f98SAdrian Hunter self.view.setModel(self.model) 141776099f98SAdrian Hunter 141876099f98SAdrian Hunter self.ResizeColumnsToContents() 141976099f98SAdrian Hunter 142076099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 142176099f98SAdrian Hunter 142276099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 142376099f98SAdrian Hunter 142476099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 142576099f98SAdrian Hunter 142676099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 142776099f98SAdrian Hunter 142876099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 142976099f98SAdrian Hunter 143076099f98SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") 143176099f98SAdrian Hunter 143276099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 143376099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 143476099f98SAdrian Hunter # so implement a crude alternative 143576099f98SAdrian Hunter mm = "MM" if column else "MMMM" 143676099f98SAdrian Hunter font = self.view.font() 143776099f98SAdrian Hunter metrics = QFontMetrics(font) 143876099f98SAdrian Hunter max = 0 143976099f98SAdrian Hunter for row in xrange(n): 144076099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 144176099f98SAdrian Hunter len = metrics.width(str(val) + mm) 144276099f98SAdrian Hunter max = len if len > max else max 144376099f98SAdrian Hunter val = self.model.columnHeader(column) 144476099f98SAdrian Hunter len = metrics.width(str(val) + mm) 144576099f98SAdrian Hunter max = len if len > max else max 144676099f98SAdrian Hunter self.view.setColumnWidth(column, max) 144776099f98SAdrian Hunter 144876099f98SAdrian Hunter def ResizeColumnsToContents(self): 144976099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 145076099f98SAdrian Hunter if n < 1: 145176099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 145276099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 145376099f98SAdrian Hunter return 145476099f98SAdrian Hunter columns = self.model.columnCount() 145576099f98SAdrian Hunter for i in xrange(columns): 145676099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 145776099f98SAdrian Hunter 145876099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 145976099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 146076099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 146176099f98SAdrian Hunter self.ResizeColumnsToContents() 146276099f98SAdrian Hunter 146376099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 146476099f98SAdrian Hunter self.view.setFocus() 146576099f98SAdrian Hunter self.find_bar.Busy() 146676099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 146776099f98SAdrian Hunter 146876099f98SAdrian Hunter def FindDone(self, row): 146976099f98SAdrian Hunter self.find_bar.Idle() 147076099f98SAdrian Hunter if row >= 0: 147176099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 147276099f98SAdrian Hunter else: 147376099f98SAdrian Hunter self.find_bar.NotFound() 147476099f98SAdrian Hunter 1475*210cf1f9SAdrian Hunter# Dialog data item converted and validated using a SQL table 1476*210cf1f9SAdrian Hunter 1477*210cf1f9SAdrian Hunterclass SQLTableDialogDataItem(): 1478*210cf1f9SAdrian Hunter 1479*210cf1f9SAdrian Hunter def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 1480*210cf1f9SAdrian Hunter self.glb = glb 1481*210cf1f9SAdrian Hunter self.label = label 1482*210cf1f9SAdrian Hunter self.placeholder_text = placeholder_text 1483*210cf1f9SAdrian Hunter self.table_name = table_name 1484*210cf1f9SAdrian Hunter self.match_column = match_column 1485*210cf1f9SAdrian Hunter self.column_name1 = column_name1 1486*210cf1f9SAdrian Hunter self.column_name2 = column_name2 1487*210cf1f9SAdrian Hunter self.parent = parent 1488*210cf1f9SAdrian Hunter 1489*210cf1f9SAdrian Hunter self.value = "" 1490*210cf1f9SAdrian Hunter 1491*210cf1f9SAdrian Hunter self.widget = QLineEdit() 1492*210cf1f9SAdrian Hunter self.widget.editingFinished.connect(self.Validate) 1493*210cf1f9SAdrian Hunter self.widget.textChanged.connect(self.Invalidate) 1494*210cf1f9SAdrian Hunter self.red = False 1495*210cf1f9SAdrian Hunter self.error = "" 1496*210cf1f9SAdrian Hunter self.validated = True 1497*210cf1f9SAdrian Hunter 1498*210cf1f9SAdrian Hunter self.last_id = 0 1499*210cf1f9SAdrian Hunter self.first_time = 0 1500*210cf1f9SAdrian Hunter self.last_time = 2 ** 64 1501*210cf1f9SAdrian Hunter if self.table_name == "<timeranges>": 1502*210cf1f9SAdrian Hunter query = QSqlQuery(self.glb.db) 1503*210cf1f9SAdrian Hunter QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") 1504*210cf1f9SAdrian Hunter if query.next(): 1505*210cf1f9SAdrian Hunter self.last_id = int(query.value(0)) 1506*210cf1f9SAdrian Hunter self.last_time = int(query.value(1)) 1507*210cf1f9SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") 1508*210cf1f9SAdrian Hunter if query.next(): 1509*210cf1f9SAdrian Hunter self.first_time = int(query.value(0)) 1510*210cf1f9SAdrian Hunter if placeholder_text: 1511*210cf1f9SAdrian Hunter placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 1512*210cf1f9SAdrian Hunter 1513*210cf1f9SAdrian Hunter if placeholder_text: 1514*210cf1f9SAdrian Hunter self.widget.setPlaceholderText(placeholder_text) 1515*210cf1f9SAdrian Hunter 1516*210cf1f9SAdrian Hunter def ValueToIds(self, value): 1517*210cf1f9SAdrian Hunter ids = [] 1518*210cf1f9SAdrian Hunter query = QSqlQuery(self.glb.db) 1519*210cf1f9SAdrian Hunter stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" 1520*210cf1f9SAdrian Hunter ret = query.exec_(stmt) 1521*210cf1f9SAdrian Hunter if ret: 1522*210cf1f9SAdrian Hunter while query.next(): 1523*210cf1f9SAdrian Hunter ids.append(str(query.value(0))) 1524*210cf1f9SAdrian Hunter return ids 1525*210cf1f9SAdrian Hunter 1526*210cf1f9SAdrian Hunter def IdBetween(self, query, lower_id, higher_id, order): 1527*210cf1f9SAdrian Hunter QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 1528*210cf1f9SAdrian Hunter if query.next(): 1529*210cf1f9SAdrian Hunter return True, int(query.value(0)) 1530*210cf1f9SAdrian Hunter else: 1531*210cf1f9SAdrian Hunter return False, 0 1532*210cf1f9SAdrian Hunter 1533*210cf1f9SAdrian Hunter def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): 1534*210cf1f9SAdrian Hunter query = QSqlQuery(self.glb.db) 1535*210cf1f9SAdrian Hunter while True: 1536*210cf1f9SAdrian Hunter next_id = int((lower_id + higher_id) / 2) 1537*210cf1f9SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 1538*210cf1f9SAdrian Hunter if not query.next(): 1539*210cf1f9SAdrian Hunter ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") 1540*210cf1f9SAdrian Hunter if not ok: 1541*210cf1f9SAdrian Hunter ok, dbid = self.IdBetween(query, next_id, higher_id, "") 1542*210cf1f9SAdrian Hunter if not ok: 1543*210cf1f9SAdrian Hunter return str(higher_id) 1544*210cf1f9SAdrian Hunter next_id = dbid 1545*210cf1f9SAdrian Hunter QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) 1546*210cf1f9SAdrian Hunter next_time = int(query.value(0)) 1547*210cf1f9SAdrian Hunter if get_floor: 1548*210cf1f9SAdrian Hunter if target_time > next_time: 1549*210cf1f9SAdrian Hunter lower_id = next_id 1550*210cf1f9SAdrian Hunter else: 1551*210cf1f9SAdrian Hunter higher_id = next_id 1552*210cf1f9SAdrian Hunter if higher_id <= lower_id + 1: 1553*210cf1f9SAdrian Hunter return str(higher_id) 1554*210cf1f9SAdrian Hunter else: 1555*210cf1f9SAdrian Hunter if target_time >= next_time: 1556*210cf1f9SAdrian Hunter lower_id = next_id 1557*210cf1f9SAdrian Hunter else: 1558*210cf1f9SAdrian Hunter higher_id = next_id 1559*210cf1f9SAdrian Hunter if higher_id <= lower_id + 1: 1560*210cf1f9SAdrian Hunter return str(lower_id) 1561*210cf1f9SAdrian Hunter 1562*210cf1f9SAdrian Hunter def ConvertRelativeTime(self, val): 1563*210cf1f9SAdrian Hunter print "val ", val 1564*210cf1f9SAdrian Hunter mult = 1 1565*210cf1f9SAdrian Hunter suffix = val[-2:] 1566*210cf1f9SAdrian Hunter if suffix == "ms": 1567*210cf1f9SAdrian Hunter mult = 1000000 1568*210cf1f9SAdrian Hunter elif suffix == "us": 1569*210cf1f9SAdrian Hunter mult = 1000 1570*210cf1f9SAdrian Hunter elif suffix == "ns": 1571*210cf1f9SAdrian Hunter mult = 1 1572*210cf1f9SAdrian Hunter else: 1573*210cf1f9SAdrian Hunter return val 1574*210cf1f9SAdrian Hunter val = val[:-2].strip() 1575*210cf1f9SAdrian Hunter if not self.IsNumber(val): 1576*210cf1f9SAdrian Hunter return val 1577*210cf1f9SAdrian Hunter val = int(val) * mult 1578*210cf1f9SAdrian Hunter if val >= 0: 1579*210cf1f9SAdrian Hunter val += self.first_time 1580*210cf1f9SAdrian Hunter else: 1581*210cf1f9SAdrian Hunter val += self.last_time 1582*210cf1f9SAdrian Hunter return str(val) 1583*210cf1f9SAdrian Hunter 1584*210cf1f9SAdrian Hunter def ConvertTimeRange(self, vrange): 1585*210cf1f9SAdrian Hunter print "vrange ", vrange 1586*210cf1f9SAdrian Hunter if vrange[0] == "": 1587*210cf1f9SAdrian Hunter vrange[0] = str(self.first_time) 1588*210cf1f9SAdrian Hunter if vrange[1] == "": 1589*210cf1f9SAdrian Hunter vrange[1] = str(self.last_time) 1590*210cf1f9SAdrian Hunter vrange[0] = self.ConvertRelativeTime(vrange[0]) 1591*210cf1f9SAdrian Hunter vrange[1] = self.ConvertRelativeTime(vrange[1]) 1592*210cf1f9SAdrian Hunter print "vrange2 ", vrange 1593*210cf1f9SAdrian Hunter if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 1594*210cf1f9SAdrian Hunter return False 1595*210cf1f9SAdrian Hunter print "ok1" 1596*210cf1f9SAdrian Hunter beg_range = max(int(vrange[0]), self.first_time) 1597*210cf1f9SAdrian Hunter end_range = min(int(vrange[1]), self.last_time) 1598*210cf1f9SAdrian Hunter if beg_range > self.last_time or end_range < self.first_time: 1599*210cf1f9SAdrian Hunter return False 1600*210cf1f9SAdrian Hunter print "ok2" 1601*210cf1f9SAdrian Hunter vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 1602*210cf1f9SAdrian Hunter vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 1603*210cf1f9SAdrian Hunter print "vrange3 ", vrange 1604*210cf1f9SAdrian Hunter return True 1605*210cf1f9SAdrian Hunter 1606*210cf1f9SAdrian Hunter def AddTimeRange(self, value, ranges): 1607*210cf1f9SAdrian Hunter print "value ", value 1608*210cf1f9SAdrian Hunter n = value.count("-") 1609*210cf1f9SAdrian Hunter if n == 1: 1610*210cf1f9SAdrian Hunter pass 1611*210cf1f9SAdrian Hunter elif n == 2: 1612*210cf1f9SAdrian Hunter if value.split("-")[1].strip() == "": 1613*210cf1f9SAdrian Hunter n = 1 1614*210cf1f9SAdrian Hunter elif n == 3: 1615*210cf1f9SAdrian Hunter n = 2 1616*210cf1f9SAdrian Hunter else: 1617*210cf1f9SAdrian Hunter return False 1618*210cf1f9SAdrian Hunter pos = findnth(value, "-", n) 1619*210cf1f9SAdrian Hunter vrange = [value[:pos].strip() ,value[pos+1:].strip()] 1620*210cf1f9SAdrian Hunter if self.ConvertTimeRange(vrange): 1621*210cf1f9SAdrian Hunter ranges.append(vrange) 1622*210cf1f9SAdrian Hunter return True 1623*210cf1f9SAdrian Hunter return False 1624*210cf1f9SAdrian Hunter 1625*210cf1f9SAdrian Hunter def InvalidValue(self, value): 1626*210cf1f9SAdrian Hunter self.value = "" 1627*210cf1f9SAdrian Hunter palette = QPalette() 1628*210cf1f9SAdrian Hunter palette.setColor(QPalette.Text,Qt.red) 1629*210cf1f9SAdrian Hunter self.widget.setPalette(palette) 1630*210cf1f9SAdrian Hunter self.red = True 1631*210cf1f9SAdrian Hunter self.error = self.label + " invalid value '" + value + "'" 1632*210cf1f9SAdrian Hunter self.parent.ShowMessage(self.error) 1633*210cf1f9SAdrian Hunter 1634*210cf1f9SAdrian Hunter def IsNumber(self, value): 1635*210cf1f9SAdrian Hunter try: 1636*210cf1f9SAdrian Hunter x = int(value) 1637*210cf1f9SAdrian Hunter except: 1638*210cf1f9SAdrian Hunter x = 0 1639*210cf1f9SAdrian Hunter return str(x) == value 1640*210cf1f9SAdrian Hunter 1641*210cf1f9SAdrian Hunter def Invalidate(self): 1642*210cf1f9SAdrian Hunter self.validated = False 1643*210cf1f9SAdrian Hunter 1644*210cf1f9SAdrian Hunter def Validate(self): 1645*210cf1f9SAdrian Hunter input_string = self.widget.text() 1646*210cf1f9SAdrian Hunter self.validated = True 1647*210cf1f9SAdrian Hunter if self.red: 1648*210cf1f9SAdrian Hunter palette = QPalette() 1649*210cf1f9SAdrian Hunter self.widget.setPalette(palette) 1650*210cf1f9SAdrian Hunter self.red = False 1651*210cf1f9SAdrian Hunter if not len(input_string.strip()): 1652*210cf1f9SAdrian Hunter self.error = "" 1653*210cf1f9SAdrian Hunter self.value = "" 1654*210cf1f9SAdrian Hunter return 1655*210cf1f9SAdrian Hunter if self.table_name == "<timeranges>": 1656*210cf1f9SAdrian Hunter ranges = [] 1657*210cf1f9SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 1658*210cf1f9SAdrian Hunter if not self.AddTimeRange(value, ranges): 1659*210cf1f9SAdrian Hunter return self.InvalidValue(value) 1660*210cf1f9SAdrian Hunter ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] 1661*210cf1f9SAdrian Hunter self.value = " OR ".join(ranges) 1662*210cf1f9SAdrian Hunter elif self.table_name == "<ranges>": 1663*210cf1f9SAdrian Hunter singles = [] 1664*210cf1f9SAdrian Hunter ranges = [] 1665*210cf1f9SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 1666*210cf1f9SAdrian Hunter if "-" in value: 1667*210cf1f9SAdrian Hunter vrange = value.split("-") 1668*210cf1f9SAdrian Hunter if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 1669*210cf1f9SAdrian Hunter return self.InvalidValue(value) 1670*210cf1f9SAdrian Hunter ranges.append(vrange) 1671*210cf1f9SAdrian Hunter else: 1672*210cf1f9SAdrian Hunter if not self.IsNumber(value): 1673*210cf1f9SAdrian Hunter return self.InvalidValue(value) 1674*210cf1f9SAdrian Hunter singles.append(value) 1675*210cf1f9SAdrian Hunter ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] 1676*210cf1f9SAdrian Hunter if len(singles): 1677*210cf1f9SAdrian Hunter ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")") 1678*210cf1f9SAdrian Hunter self.value = " OR ".join(ranges) 1679*210cf1f9SAdrian Hunter elif self.table_name: 1680*210cf1f9SAdrian Hunter all_ids = [] 1681*210cf1f9SAdrian Hunter for value in [x.strip() for x in input_string.split(",")]: 1682*210cf1f9SAdrian Hunter ids = self.ValueToIds(value) 1683*210cf1f9SAdrian Hunter if len(ids): 1684*210cf1f9SAdrian Hunter all_ids.extend(ids) 1685*210cf1f9SAdrian Hunter else: 1686*210cf1f9SAdrian Hunter return self.InvalidValue(value) 1687*210cf1f9SAdrian Hunter self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" 1688*210cf1f9SAdrian Hunter if self.column_name2: 1689*210cf1f9SAdrian Hunter self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" 1690*210cf1f9SAdrian Hunter else: 1691*210cf1f9SAdrian Hunter self.value = input_string.strip() 1692*210cf1f9SAdrian Hunter self.error = "" 1693*210cf1f9SAdrian Hunter self.parent.ClearMessage() 1694*210cf1f9SAdrian Hunter 1695*210cf1f9SAdrian Hunter def IsValid(self): 1696*210cf1f9SAdrian Hunter if not self.validated: 1697*210cf1f9SAdrian Hunter self.Validate() 1698*210cf1f9SAdrian Hunter if len(self.error): 1699*210cf1f9SAdrian Hunter self.parent.ShowMessage(self.error) 1700*210cf1f9SAdrian Hunter return False 1701*210cf1f9SAdrian Hunter return True 1702*210cf1f9SAdrian Hunter 1703*210cf1f9SAdrian Hunter# Selected branch report creation dialog 1704*210cf1f9SAdrian Hunter 1705*210cf1f9SAdrian Hunterclass SelectedBranchDialog(QDialog): 1706*210cf1f9SAdrian Hunter 1707*210cf1f9SAdrian Hunter def __init__(self, glb, parent=None): 1708*210cf1f9SAdrian Hunter super(SelectedBranchDialog, self).__init__(parent) 1709*210cf1f9SAdrian Hunter 1710*210cf1f9SAdrian Hunter self.glb = glb 1711*210cf1f9SAdrian Hunter 1712*210cf1f9SAdrian Hunter self.name = "" 1713*210cf1f9SAdrian Hunter self.where_clause = "" 1714*210cf1f9SAdrian Hunter 1715*210cf1f9SAdrian Hunter self.setWindowTitle("Selected Branches") 1716*210cf1f9SAdrian Hunter self.setMinimumWidth(600) 1717*210cf1f9SAdrian Hunter 1718*210cf1f9SAdrian Hunter items = ( 1719*210cf1f9SAdrian Hunter ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), 1720*210cf1f9SAdrian Hunter ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), 1721*210cf1f9SAdrian Hunter ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""), 1722*210cf1f9SAdrian Hunter ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""), 1723*210cf1f9SAdrian Hunter ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""), 1724*210cf1f9SAdrian Hunter ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""), 1725*210cf1f9SAdrian Hunter ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"), 1726*210cf1f9SAdrian Hunter ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"), 1727*210cf1f9SAdrian Hunter ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""), 1728*210cf1f9SAdrian Hunter ) 1729*210cf1f9SAdrian Hunter self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items] 1730*210cf1f9SAdrian Hunter 1731*210cf1f9SAdrian Hunter self.grid = QGridLayout() 1732*210cf1f9SAdrian Hunter 1733*210cf1f9SAdrian Hunter for row in xrange(len(self.data_items)): 1734*210cf1f9SAdrian Hunter self.grid.addWidget(QLabel(self.data_items[row].label), row, 0) 1735*210cf1f9SAdrian Hunter self.grid.addWidget(self.data_items[row].widget, row, 1) 1736*210cf1f9SAdrian Hunter 1737*210cf1f9SAdrian Hunter self.status = QLabel() 1738*210cf1f9SAdrian Hunter 1739*210cf1f9SAdrian Hunter self.ok_button = QPushButton("Ok", self) 1740*210cf1f9SAdrian Hunter self.ok_button.setDefault(True) 1741*210cf1f9SAdrian Hunter self.ok_button.released.connect(self.Ok) 1742*210cf1f9SAdrian Hunter self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 1743*210cf1f9SAdrian Hunter 1744*210cf1f9SAdrian Hunter self.cancel_button = QPushButton("Cancel", self) 1745*210cf1f9SAdrian Hunter self.cancel_button.released.connect(self.reject) 1746*210cf1f9SAdrian Hunter self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 1747*210cf1f9SAdrian Hunter 1748*210cf1f9SAdrian Hunter self.hbox = QHBoxLayout() 1749*210cf1f9SAdrian Hunter #self.hbox.addStretch() 1750*210cf1f9SAdrian Hunter self.hbox.addWidget(self.status) 1751*210cf1f9SAdrian Hunter self.hbox.addWidget(self.ok_button) 1752*210cf1f9SAdrian Hunter self.hbox.addWidget(self.cancel_button) 1753*210cf1f9SAdrian Hunter 1754*210cf1f9SAdrian Hunter self.vbox = QVBoxLayout() 1755*210cf1f9SAdrian Hunter self.vbox.addLayout(self.grid) 1756*210cf1f9SAdrian Hunter self.vbox.addLayout(self.hbox) 1757*210cf1f9SAdrian Hunter 1758*210cf1f9SAdrian Hunter self.setLayout(self.vbox); 1759*210cf1f9SAdrian Hunter 1760*210cf1f9SAdrian Hunter def Ok(self): 1761*210cf1f9SAdrian Hunter self.name = self.data_items[0].value 1762*210cf1f9SAdrian Hunter if not self.name: 1763*210cf1f9SAdrian Hunter self.ShowMessage("Report name is required") 1764*210cf1f9SAdrian Hunter return 1765*210cf1f9SAdrian Hunter for d in self.data_items: 1766*210cf1f9SAdrian Hunter if not d.IsValid(): 1767*210cf1f9SAdrian Hunter return 1768*210cf1f9SAdrian Hunter for d in self.data_items[1:]: 1769*210cf1f9SAdrian Hunter if len(d.value): 1770*210cf1f9SAdrian Hunter if len(self.where_clause): 1771*210cf1f9SAdrian Hunter self.where_clause += " AND " 1772*210cf1f9SAdrian Hunter self.where_clause += d.value 1773*210cf1f9SAdrian Hunter if len(self.where_clause): 1774*210cf1f9SAdrian Hunter self.where_clause = " AND ( " + self.where_clause + " ) " 1775*210cf1f9SAdrian Hunter else: 1776*210cf1f9SAdrian Hunter self.ShowMessage("No selection") 1777*210cf1f9SAdrian Hunter return 1778*210cf1f9SAdrian Hunter self.accept() 1779*210cf1f9SAdrian Hunter 1780*210cf1f9SAdrian Hunter def ShowMessage(self, msg): 1781*210cf1f9SAdrian Hunter self.status.setText("<font color=#FF0000>" + msg) 1782*210cf1f9SAdrian Hunter 1783*210cf1f9SAdrian Hunter def ClearMessage(self): 1784*210cf1f9SAdrian Hunter self.status.setText("") 1785*210cf1f9SAdrian Hunter 178676099f98SAdrian Hunter# Event list 178776099f98SAdrian Hunter 178876099f98SAdrian Hunterdef GetEventList(db): 178976099f98SAdrian Hunter events = [] 179076099f98SAdrian Hunter query = QSqlQuery(db) 179176099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 179276099f98SAdrian Hunter while query.next(): 179376099f98SAdrian Hunter events.append(query.value(0)) 179476099f98SAdrian Hunter return events 179576099f98SAdrian Hunter 17968392b74bSAdrian Hunter# SQL data preparation 17978392b74bSAdrian Hunter 17988392b74bSAdrian Hunterdef SQLTableDataPrep(query, count): 17998392b74bSAdrian Hunter data = [] 18008392b74bSAdrian Hunter for i in xrange(count): 18018392b74bSAdrian Hunter data.append(query.value(i)) 18028392b74bSAdrian Hunter return data 18038392b74bSAdrian Hunter 18048392b74bSAdrian Hunter# SQL table data model item 18058392b74bSAdrian Hunter 18068392b74bSAdrian Hunterclass SQLTableItem(): 18078392b74bSAdrian Hunter 18088392b74bSAdrian Hunter def __init__(self, row, data): 18098392b74bSAdrian Hunter self.row = row 18108392b74bSAdrian Hunter self.data = data 18118392b74bSAdrian Hunter 18128392b74bSAdrian Hunter def getData(self, column): 18138392b74bSAdrian Hunter return self.data[column] 18148392b74bSAdrian Hunter 18158392b74bSAdrian Hunter# SQL table data model 18168392b74bSAdrian Hunter 18178392b74bSAdrian Hunterclass SQLTableModel(TableModel): 18188392b74bSAdrian Hunter 18198392b74bSAdrian Hunter progress = Signal(object) 18208392b74bSAdrian Hunter 18218392b74bSAdrian Hunter def __init__(self, glb, sql, column_count, parent=None): 18228392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 18238392b74bSAdrian Hunter self.glb = glb 18248392b74bSAdrian Hunter self.more = True 18258392b74bSAdrian Hunter self.populated = 0 18268392b74bSAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample) 18278392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 18288392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 18298392b74bSAdrian Hunter 18308392b74bSAdrian Hunter def DisplayData(self, item, index): 18318392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 18328392b74bSAdrian Hunter return item.getData(index.column()) 18338392b74bSAdrian Hunter 18348392b74bSAdrian Hunter def AddSample(self, data): 18358392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 18368392b74bSAdrian Hunter self.child_items.append(child) 18378392b74bSAdrian Hunter self.populated += 1 18388392b74bSAdrian Hunter 18398392b74bSAdrian Hunter def Update(self, fetched): 18408392b74bSAdrian Hunter if not fetched: 18418392b74bSAdrian Hunter self.more = False 18428392b74bSAdrian Hunter self.progress.emit(0) 18438392b74bSAdrian Hunter child_count = self.child_count 18448392b74bSAdrian Hunter count = self.populated - child_count 18458392b74bSAdrian Hunter if count > 0: 18468392b74bSAdrian Hunter parent = QModelIndex() 18478392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 18488392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 18498392b74bSAdrian Hunter self.child_count += count 18508392b74bSAdrian Hunter self.endInsertRows() 18518392b74bSAdrian Hunter self.progress.emit(self.child_count) 18528392b74bSAdrian Hunter 18538392b74bSAdrian Hunter def FetchMoreRecords(self, count): 18548392b74bSAdrian Hunter current = self.child_count 18558392b74bSAdrian Hunter if self.more: 18568392b74bSAdrian Hunter self.fetcher.Fetch(count) 18578392b74bSAdrian Hunter else: 18588392b74bSAdrian Hunter self.progress.emit(0) 18598392b74bSAdrian Hunter return current 18608392b74bSAdrian Hunter 18618392b74bSAdrian Hunter def HasMoreRecords(self): 18628392b74bSAdrian Hunter return self.more 18638392b74bSAdrian Hunter 18648392b74bSAdrian Hunter# SQL automatic table data model 18658392b74bSAdrian Hunter 18668392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 18678392b74bSAdrian Hunter 18688392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 18698392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 18708392b74bSAdrian Hunter if table_name == "comm_threads_view": 18718392b74bSAdrian Hunter # For now, comm_threads_view has no id column 18728392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 18738392b74bSAdrian Hunter self.column_headers = [] 18748392b74bSAdrian Hunter query = QSqlQuery(glb.db) 18758392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 18768392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 18778392b74bSAdrian Hunter while query.next(): 18788392b74bSAdrian Hunter self.column_headers.append(query.value(1)) 18798392b74bSAdrian Hunter if table_name == "sqlite_master": 18808392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 18818392b74bSAdrian Hunter else: 18828392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 18838392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 18848392b74bSAdrian Hunter select_table_name = table_name[19:] 18858392b74bSAdrian Hunter schema = "information_schema" 18868392b74bSAdrian Hunter else: 18878392b74bSAdrian Hunter select_table_name = table_name 18888392b74bSAdrian Hunter schema = "public" 18898392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 18908392b74bSAdrian Hunter while query.next(): 18918392b74bSAdrian Hunter self.column_headers.append(query.value(0)) 18928392b74bSAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent) 18938392b74bSAdrian Hunter 18948392b74bSAdrian Hunter def columnCount(self, parent=None): 18958392b74bSAdrian Hunter return len(self.column_headers) 18968392b74bSAdrian Hunter 18978392b74bSAdrian Hunter def columnHeader(self, column): 18988392b74bSAdrian Hunter return self.column_headers[column] 18998392b74bSAdrian Hunter 19008392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 19018392b74bSAdrian Hunter 19028392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 19038392b74bSAdrian Hunter 19048392b74bSAdrian Hunter def __init__(self, parent=None): 19058392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 19068392b74bSAdrian Hunter 19078392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 19088392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 19098392b74bSAdrian Hunter # so implement a crude alternative 19108392b74bSAdrian Hunter font = self.view.font() 19118392b74bSAdrian Hunter metrics = QFontMetrics(font) 19128392b74bSAdrian Hunter max = 0 19138392b74bSAdrian Hunter for row in xrange(n): 19148392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 19158392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 19168392b74bSAdrian Hunter max = len if len > max else max 19178392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 19188392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 19198392b74bSAdrian Hunter max = len if len > max else max 19208392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 19218392b74bSAdrian Hunter 19228392b74bSAdrian Hunter def ResizeColumnsToContents(self): 19238392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 19248392b74bSAdrian Hunter if n < 1: 19258392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 19268392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 19278392b74bSAdrian Hunter return 19288392b74bSAdrian Hunter columns = self.data_model.columnCount() 19298392b74bSAdrian Hunter for i in xrange(columns): 19308392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 19318392b74bSAdrian Hunter 19328392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 19338392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 19348392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 19358392b74bSAdrian Hunter self.ResizeColumnsToContents() 19368392b74bSAdrian Hunter 19378392b74bSAdrian Hunter# Table window 19388392b74bSAdrian Hunter 19398392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 19408392b74bSAdrian Hunter 19418392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 19428392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 19438392b74bSAdrian Hunter 19448392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 19458392b74bSAdrian Hunter 19468392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 19478392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 19488392b74bSAdrian Hunter 19498392b74bSAdrian Hunter self.view = QTableView() 19508392b74bSAdrian Hunter self.view.setModel(self.model) 19518392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 19528392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 19538392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 19548392b74bSAdrian Hunter self.view.setSortingEnabled(True) 19558392b74bSAdrian Hunter 19568392b74bSAdrian Hunter self.ResizeColumnsToContents() 19578392b74bSAdrian Hunter 19588392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 19598392b74bSAdrian Hunter 19608392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 19618392b74bSAdrian Hunter 19628392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 19638392b74bSAdrian Hunter 19648392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 19658392b74bSAdrian Hunter 19668392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 19678392b74bSAdrian Hunter 19688392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 19698392b74bSAdrian Hunter 19708392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 19718392b74bSAdrian Hunter self.view.setFocus() 19728392b74bSAdrian Hunter self.find_bar.Busy() 19738392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 19748392b74bSAdrian Hunter 19758392b74bSAdrian Hunter def FindDone(self, row): 19768392b74bSAdrian Hunter self.find_bar.Idle() 19778392b74bSAdrian Hunter if row >= 0: 19788392b74bSAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 19798392b74bSAdrian Hunter else: 19808392b74bSAdrian Hunter self.find_bar.NotFound() 19818392b74bSAdrian Hunter 19828392b74bSAdrian Hunter# Table list 19838392b74bSAdrian Hunter 19848392b74bSAdrian Hunterdef GetTableList(glb): 19858392b74bSAdrian Hunter tables = [] 19868392b74bSAdrian Hunter query = QSqlQuery(glb.db) 19878392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 19888392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 19898392b74bSAdrian Hunter else: 19908392b74bSAdrian 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") 19918392b74bSAdrian Hunter while query.next(): 19928392b74bSAdrian Hunter tables.append(query.value(0)) 19938392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 19948392b74bSAdrian Hunter tables.append("sqlite_master") 19958392b74bSAdrian Hunter else: 19968392b74bSAdrian Hunter tables.append("information_schema.tables") 19978392b74bSAdrian Hunter tables.append("information_schema.views") 19988392b74bSAdrian Hunter tables.append("information_schema.columns") 19998392b74bSAdrian Hunter return tables 20008392b74bSAdrian Hunter 20011beb5c7bSAdrian Hunter# Action Definition 20021beb5c7bSAdrian Hunter 20031beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 20041beb5c7bSAdrian Hunter action = QAction(label, parent) 20051beb5c7bSAdrian Hunter if shortcut != None: 20061beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 20071beb5c7bSAdrian Hunter action.setStatusTip(tip) 20081beb5c7bSAdrian Hunter action.triggered.connect(callback) 20091beb5c7bSAdrian Hunter return action 20101beb5c7bSAdrian Hunter 20111beb5c7bSAdrian Hunter# Typical application actions 20121beb5c7bSAdrian Hunter 20131beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 20141beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 20151beb5c7bSAdrian Hunter 20161beb5c7bSAdrian Hunter# Typical MDI actions 20171beb5c7bSAdrian Hunter 20181beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 20191beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 20201beb5c7bSAdrian Hunter 20211beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 20221beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 20231beb5c7bSAdrian Hunter 20241beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 20251beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 20261beb5c7bSAdrian Hunter 20271beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 20281beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 20291beb5c7bSAdrian Hunter 20301beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 20311beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 20321beb5c7bSAdrian Hunter 20331beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 20341beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 20351beb5c7bSAdrian Hunter 20361beb5c7bSAdrian Hunter# Typical MDI window menu 20371beb5c7bSAdrian Hunter 20381beb5c7bSAdrian Hunterclass WindowMenu(): 20391beb5c7bSAdrian Hunter 20401beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 20411beb5c7bSAdrian Hunter self.mdi_area = mdi_area 20421beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 20431beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 20441beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 20451beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 20461beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 20471beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 20481beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 20491beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 20501beb5c7bSAdrian Hunter 20511beb5c7bSAdrian Hunter def Update(self): 20521beb5c7bSAdrian Hunter self.window_menu.clear() 20531beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 20541beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 20551beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 20561beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 20571beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 20581beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 20591beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 20601beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 20611beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 20621beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 20631beb5c7bSAdrian Hunter self.window_menu.addSeparator() 20641beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 20651beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 20661beb5c7bSAdrian Hunter self.window_menu.addSeparator() 20671beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 20681beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 20691beb5c7bSAdrian Hunter if sub_window_count == 0: 20701beb5c7bSAdrian Hunter return 20711beb5c7bSAdrian Hunter self.window_menu.addSeparator() 20721beb5c7bSAdrian Hunter nr = 1 20731beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 20741beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 20751beb5c7bSAdrian Hunter if nr < 10: 20761beb5c7bSAdrian Hunter label = "&" + label 20771beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 20781beb5c7bSAdrian Hunter action.setCheckable(True) 20791beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 20801beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 20811beb5c7bSAdrian Hunter self.window_menu.addAction(action) 20821beb5c7bSAdrian Hunter nr += 1 20831beb5c7bSAdrian Hunter 20841beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 20851beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 20861beb5c7bSAdrian Hunter 208782f68e28SAdrian Hunter# Font resize 208882f68e28SAdrian Hunter 208982f68e28SAdrian Hunterdef ResizeFont(widget, diff): 209082f68e28SAdrian Hunter font = widget.font() 209182f68e28SAdrian Hunter sz = font.pointSize() 209282f68e28SAdrian Hunter font.setPointSize(sz + diff) 209382f68e28SAdrian Hunter widget.setFont(font) 209482f68e28SAdrian Hunter 209582f68e28SAdrian Hunterdef ShrinkFont(widget): 209682f68e28SAdrian Hunter ResizeFont(widget, -1) 209782f68e28SAdrian Hunter 209882f68e28SAdrian Hunterdef EnlargeFont(widget): 209982f68e28SAdrian Hunter ResizeFont(widget, 1) 210082f68e28SAdrian Hunter 21011beb5c7bSAdrian Hunter# Unique name for sub-windows 21021beb5c7bSAdrian Hunter 21031beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 21041beb5c7bSAdrian Hunter if nr > 1: 21051beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 21061beb5c7bSAdrian Hunter return name 21071beb5c7bSAdrian Hunter 21081beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 21091beb5c7bSAdrian Hunter nr = 1 21101beb5c7bSAdrian Hunter while True: 21111beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 21121beb5c7bSAdrian Hunter ok = True 21131beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 21141beb5c7bSAdrian Hunter if sub_window.name == unique_name: 21151beb5c7bSAdrian Hunter ok = False 21161beb5c7bSAdrian Hunter break 21171beb5c7bSAdrian Hunter if ok: 21181beb5c7bSAdrian Hunter return unique_name 21191beb5c7bSAdrian Hunter nr += 1 21201beb5c7bSAdrian Hunter 21211beb5c7bSAdrian Hunter# Add a sub-window 21221beb5c7bSAdrian Hunter 21231beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 21241beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 21251beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 21261beb5c7bSAdrian Hunter sub_window.resize(800, 600) 21271beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 21281beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 21291beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 21301beb5c7bSAdrian Hunter sub_window.name = unique_name 21311beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 21321beb5c7bSAdrian Hunter sub_window.show() 21331beb5c7bSAdrian Hunter 2134031c2a00SAdrian Hunter# Main window 2135031c2a00SAdrian Hunter 2136031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 2137031c2a00SAdrian Hunter 2138031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 2139031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 2140031c2a00SAdrian Hunter 2141031c2a00SAdrian Hunter self.glb = glb 2142031c2a00SAdrian Hunter 21431beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 2144031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 2145031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 2146031c2a00SAdrian Hunter 21471beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 21481beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 21491beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 2150031c2a00SAdrian Hunter 21511beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 2152031c2a00SAdrian Hunter 21531beb5c7bSAdrian Hunter menu = self.menuBar() 2154031c2a00SAdrian Hunter 21551beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 21561beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 21571beb5c7bSAdrian Hunter 2158ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 2159ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 21608392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 216182f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 216282f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 2163ebd70c7dSAdrian Hunter 21641beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 21651beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 21661beb5c7bSAdrian Hunter 216776099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 216876099f98SAdrian Hunter 21698392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 21708392b74bSAdrian Hunter 21711beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 21721beb5c7bSAdrian Hunter 2173ebd70c7dSAdrian Hunter def Find(self): 2174ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 2175ebd70c7dSAdrian Hunter if win: 2176ebd70c7dSAdrian Hunter try: 2177ebd70c7dSAdrian Hunter win.find_bar.Activate() 2178ebd70c7dSAdrian Hunter except: 2179ebd70c7dSAdrian Hunter pass 2180ebd70c7dSAdrian Hunter 21818392b74bSAdrian Hunter def FetchMoreRecords(self): 21828392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 21838392b74bSAdrian Hunter if win: 21848392b74bSAdrian Hunter try: 21858392b74bSAdrian Hunter win.fetch_bar.Activate() 21868392b74bSAdrian Hunter except: 21878392b74bSAdrian Hunter pass 21888392b74bSAdrian Hunter 218982f68e28SAdrian Hunter def ShrinkFont(self): 219082f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 219182f68e28SAdrian Hunter ShrinkFont(win.view) 219282f68e28SAdrian Hunter 219382f68e28SAdrian Hunter def EnlargeFont(self): 219482f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 219582f68e28SAdrian Hunter EnlargeFont(win.view) 219682f68e28SAdrian Hunter 219776099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 219876099f98SAdrian Hunter branches_events = 0 219976099f98SAdrian Hunter for event in events: 220076099f98SAdrian Hunter event = event.split(":")[0] 220176099f98SAdrian Hunter if event == "branches": 220276099f98SAdrian Hunter branches_events += 1 220376099f98SAdrian Hunter dbid = 0 220476099f98SAdrian Hunter for event in events: 220576099f98SAdrian Hunter dbid += 1 220676099f98SAdrian Hunter event = event.split(":")[0] 220776099f98SAdrian Hunter if event == "branches": 220876099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 220976099f98SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 2210*210cf1f9SAdrian Hunter label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" 2211*210cf1f9SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) 221276099f98SAdrian Hunter 22138392b74bSAdrian Hunter def TableMenu(self, tables, menu): 22148392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 22158392b74bSAdrian Hunter for table in tables: 22168392b74bSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) 22178392b74bSAdrian Hunter 22181beb5c7bSAdrian Hunter def NewCallGraph(self): 22191beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 2220031c2a00SAdrian Hunter 222176099f98SAdrian Hunter def NewBranchView(self, event_id): 222276099f98SAdrian Hunter BranchWindow(self.glb, event_id, "", "", self) 222376099f98SAdrian Hunter 2224*210cf1f9SAdrian Hunter def NewSelectedBranchView(self, event_id): 2225*210cf1f9SAdrian Hunter dialog = SelectedBranchDialog(self.glb, self) 2226*210cf1f9SAdrian Hunter ret = dialog.exec_() 2227*210cf1f9SAdrian Hunter if ret: 2228*210cf1f9SAdrian Hunter BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self) 2229*210cf1f9SAdrian Hunter 22308392b74bSAdrian Hunter def NewTableView(self, table_name): 22318392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 22328392b74bSAdrian Hunter 223376099f98SAdrian Hunter# XED Disassembler 223476099f98SAdrian Hunter 223576099f98SAdrian Hunterclass xed_state_t(Structure): 223676099f98SAdrian Hunter 223776099f98SAdrian Hunter _fields_ = [ 223876099f98SAdrian Hunter ("mode", c_int), 223976099f98SAdrian Hunter ("width", c_int) 224076099f98SAdrian Hunter ] 224176099f98SAdrian Hunter 224276099f98SAdrian Hunterclass XEDInstruction(): 224376099f98SAdrian Hunter 224476099f98SAdrian Hunter def __init__(self, libxed): 224576099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 224676099f98SAdrian Hunter xedd_t = c_byte * 512 224776099f98SAdrian Hunter self.xedd = xedd_t() 224876099f98SAdrian Hunter self.xedp = addressof(self.xedd) 224976099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 225076099f98SAdrian Hunter self.state = xed_state_t() 225176099f98SAdrian Hunter self.statep = addressof(self.state) 225276099f98SAdrian Hunter # Buffer for disassembled instruction text 225376099f98SAdrian Hunter self.buffer = create_string_buffer(256) 225476099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 225576099f98SAdrian Hunter 225676099f98SAdrian Hunterclass LibXED(): 225776099f98SAdrian Hunter 225876099f98SAdrian Hunter def __init__(self): 22595ed4419dSAdrian Hunter try: 226076099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 22615ed4419dSAdrian Hunter except: 22625ed4419dSAdrian Hunter self.libxed = None 22635ed4419dSAdrian Hunter if not self.libxed: 22645ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 226576099f98SAdrian Hunter 226676099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 226776099f98SAdrian Hunter self.xed_tables_init.restype = None 226876099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 226976099f98SAdrian Hunter 227076099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 227176099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 227276099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 227376099f98SAdrian Hunter 227476099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 227576099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 227676099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 227776099f98SAdrian Hunter 227876099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 227976099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 228076099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 228176099f98SAdrian Hunter 228276099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 228376099f98SAdrian Hunter self.xed_decode.restype = c_int 228476099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 228576099f98SAdrian Hunter 228676099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 228776099f98SAdrian Hunter self.xed_format_context.restype = c_uint 228876099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 228976099f98SAdrian Hunter 229076099f98SAdrian Hunter self.xed_tables_init() 229176099f98SAdrian Hunter 229276099f98SAdrian Hunter def Instruction(self): 229376099f98SAdrian Hunter return XEDInstruction(self) 229476099f98SAdrian Hunter 229576099f98SAdrian Hunter def SetMode(self, inst, mode): 229676099f98SAdrian Hunter if mode: 229776099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 229876099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 229976099f98SAdrian Hunter else: 230076099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 230176099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 230276099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 230376099f98SAdrian Hunter 230476099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 230576099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 230676099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 230776099f98SAdrian Hunter if err: 230876099f98SAdrian Hunter return 0, "" 230976099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 231076099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 231176099f98SAdrian Hunter if not ok: 231276099f98SAdrian Hunter return 0, "" 231376099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 231476099f98SAdrian Hunter # For now, assume the length is in byte 166 231576099f98SAdrian Hunter return inst.xedd[166], inst.buffer.value 231676099f98SAdrian Hunter 231776099f98SAdrian Hunterdef TryOpen(file_name): 231876099f98SAdrian Hunter try: 231976099f98SAdrian Hunter return open(file_name, "rb") 232076099f98SAdrian Hunter except: 232176099f98SAdrian Hunter return None 232276099f98SAdrian Hunter 232376099f98SAdrian Hunterdef Is64Bit(f): 232476099f98SAdrian Hunter result = sizeof(c_void_p) 232576099f98SAdrian Hunter # ELF support only 232676099f98SAdrian Hunter pos = f.tell() 232776099f98SAdrian Hunter f.seek(0) 232876099f98SAdrian Hunter header = f.read(7) 232976099f98SAdrian Hunter f.seek(pos) 233076099f98SAdrian Hunter magic = header[0:4] 233176099f98SAdrian Hunter eclass = ord(header[4]) 233276099f98SAdrian Hunter encoding = ord(header[5]) 233376099f98SAdrian Hunter version = ord(header[6]) 233476099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 233576099f98SAdrian Hunter result = True if eclass == 2 else False 233676099f98SAdrian Hunter return result 233776099f98SAdrian Hunter 2338031c2a00SAdrian Hunter# Global data 2339031c2a00SAdrian Hunter 2340031c2a00SAdrian Hunterclass Glb(): 2341031c2a00SAdrian Hunter 2342031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 2343031c2a00SAdrian Hunter self.dbref = dbref 2344031c2a00SAdrian Hunter self.db = db 2345031c2a00SAdrian Hunter self.dbname = dbname 234676099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 234776099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 234876099f98SAdrian Hunter if self.buildid_dir: 234976099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 235076099f98SAdrian Hunter else: 235176099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 2352031c2a00SAdrian Hunter self.app = None 2353031c2a00SAdrian Hunter self.mainwindow = None 23548392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 235576099f98SAdrian Hunter try: 235676099f98SAdrian Hunter self.disassembler = LibXED() 235776099f98SAdrian Hunter self.have_disassembler = True 235876099f98SAdrian Hunter except: 235976099f98SAdrian Hunter self.have_disassembler = False 236076099f98SAdrian Hunter 236176099f98SAdrian Hunter def FileFromBuildId(self, build_id): 236276099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 236376099f98SAdrian Hunter return TryOpen(file_name) 236476099f98SAdrian Hunter 236576099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 236676099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 236776099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 236876099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 236976099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 237076099f98SAdrian Hunter if f: 237176099f98SAdrian Hunter return f 237276099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 237376099f98SAdrian Hunter f = TryOpen(long_name) 237476099f98SAdrian Hunter if f: 237576099f98SAdrian Hunter return f 237676099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 237776099f98SAdrian Hunter if f: 237876099f98SAdrian Hunter return f 237976099f98SAdrian Hunter return None 23808392b74bSAdrian Hunter 23818392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 23828392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 23838392b74bSAdrian Hunter 23848392b74bSAdrian Hunter # Shutdown any background processes or threads 23858392b74bSAdrian Hunter def ShutdownInstances(self): 23868392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 23878392b74bSAdrian Hunter try: 23888392b74bSAdrian Hunter x.Shutdown() 23898392b74bSAdrian Hunter except: 23908392b74bSAdrian Hunter pass 2391031c2a00SAdrian Hunter 2392031c2a00SAdrian Hunter# Database reference 2393031c2a00SAdrian Hunter 2394031c2a00SAdrian Hunterclass DBRef(): 2395031c2a00SAdrian Hunter 2396031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 2397031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 2398031c2a00SAdrian Hunter self.dbname = dbname 2399031c2a00SAdrian Hunter 2400031c2a00SAdrian Hunter def Open(self, connection_name): 2401031c2a00SAdrian Hunter dbname = self.dbname 2402031c2a00SAdrian Hunter if self.is_sqlite3: 2403031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 2404031c2a00SAdrian Hunter else: 2405031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 2406031c2a00SAdrian Hunter opts = dbname.split() 2407031c2a00SAdrian Hunter for opt in opts: 2408031c2a00SAdrian Hunter if "=" in opt: 2409031c2a00SAdrian Hunter opt = opt.split("=") 2410031c2a00SAdrian Hunter if opt[0] == "hostname": 2411031c2a00SAdrian Hunter db.setHostName(opt[1]) 2412031c2a00SAdrian Hunter elif opt[0] == "port": 2413031c2a00SAdrian Hunter db.setPort(int(opt[1])) 2414031c2a00SAdrian Hunter elif opt[0] == "username": 2415031c2a00SAdrian Hunter db.setUserName(opt[1]) 2416031c2a00SAdrian Hunter elif opt[0] == "password": 2417031c2a00SAdrian Hunter db.setPassword(opt[1]) 2418031c2a00SAdrian Hunter elif opt[0] == "dbname": 2419031c2a00SAdrian Hunter dbname = opt[1] 2420031c2a00SAdrian Hunter else: 2421031c2a00SAdrian Hunter dbname = opt 2422031c2a00SAdrian Hunter 2423031c2a00SAdrian Hunter db.setDatabaseName(dbname) 2424031c2a00SAdrian Hunter if not db.open(): 2425031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 2426031c2a00SAdrian Hunter return db, dbname 2427031c2a00SAdrian Hunter 2428031c2a00SAdrian Hunter# Main 2429031c2a00SAdrian Hunter 2430031c2a00SAdrian Hunterdef Main(): 2431031c2a00SAdrian Hunter if (len(sys.argv) < 2): 2432031c2a00SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 2433031c2a00SAdrian Hunter raise Exception("Too few arguments") 2434031c2a00SAdrian Hunter 2435031c2a00SAdrian Hunter dbname = sys.argv[1] 2436031c2a00SAdrian Hunter 2437031c2a00SAdrian Hunter is_sqlite3 = False 2438031c2a00SAdrian Hunter try: 2439031c2a00SAdrian Hunter f = open(dbname) 2440031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 2441031c2a00SAdrian Hunter is_sqlite3 = True 2442031c2a00SAdrian Hunter f.close() 2443031c2a00SAdrian Hunter except: 2444031c2a00SAdrian Hunter pass 2445031c2a00SAdrian Hunter 2446031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 2447031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 2448031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 2449031c2a00SAdrian Hunter app = QApplication(sys.argv) 2450031c2a00SAdrian Hunter glb.app = app 2451031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 2452031c2a00SAdrian Hunter glb.mainwindow = mainwindow 2453031c2a00SAdrian Hunter mainwindow.show() 2454031c2a00SAdrian Hunter err = app.exec_() 24558392b74bSAdrian Hunter glb.ShutdownInstances() 2456031c2a00SAdrian Hunter db.close() 2457031c2a00SAdrian Hunter sys.exit(err) 2458031c2a00SAdrian Hunter 2459031c2a00SAdrian Hunterif __name__ == "__main__": 2460031c2a00SAdrian Hunter Main() 2461