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 122031c2a00SAdrian Hunter# Percent to one decimal place 123031c2a00SAdrian Hunter 124031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 125031c2a00SAdrian Hunter if not d: 126031c2a00SAdrian Hunter return "0.0" 127031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 128031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 129031c2a00SAdrian Hunter 130031c2a00SAdrian Hunter# Helper for queries that must not fail 131031c2a00SAdrian Hunter 132031c2a00SAdrian Hunterdef QueryExec(query, stmt): 133031c2a00SAdrian Hunter ret = query.exec_(stmt) 134031c2a00SAdrian Hunter if not ret: 135031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 136031c2a00SAdrian Hunter 137ebd70c7dSAdrian Hunter# Background thread 138ebd70c7dSAdrian Hunter 139ebd70c7dSAdrian Hunterclass Thread(QThread): 140ebd70c7dSAdrian Hunter 141ebd70c7dSAdrian Hunter done = Signal(object) 142ebd70c7dSAdrian Hunter 143ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 144ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 145ebd70c7dSAdrian Hunter self.task = task 146ebd70c7dSAdrian Hunter self.param = param 147ebd70c7dSAdrian Hunter 148ebd70c7dSAdrian Hunter def run(self): 149ebd70c7dSAdrian Hunter while True: 150ebd70c7dSAdrian Hunter if self.param is None: 151ebd70c7dSAdrian Hunter done, result = self.task() 152ebd70c7dSAdrian Hunter else: 153ebd70c7dSAdrian Hunter done, result = self.task(self.param) 154ebd70c7dSAdrian Hunter self.done.emit(result) 155ebd70c7dSAdrian Hunter if done: 156ebd70c7dSAdrian Hunter break 157ebd70c7dSAdrian Hunter 158031c2a00SAdrian Hunter# Tree data model 159031c2a00SAdrian Hunter 160031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 161031c2a00SAdrian Hunter 162031c2a00SAdrian Hunter def __init__(self, root, parent=None): 163031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 164031c2a00SAdrian Hunter self.root = root 165031c2a00SAdrian Hunter self.last_row_read = 0 166031c2a00SAdrian Hunter 167031c2a00SAdrian Hunter def Item(self, parent): 168031c2a00SAdrian Hunter if parent.isValid(): 169031c2a00SAdrian Hunter return parent.internalPointer() 170031c2a00SAdrian Hunter else: 171031c2a00SAdrian Hunter return self.root 172031c2a00SAdrian Hunter 173031c2a00SAdrian Hunter def rowCount(self, parent): 174031c2a00SAdrian Hunter result = self.Item(parent).childCount() 175031c2a00SAdrian Hunter if result < 0: 176031c2a00SAdrian Hunter result = 0 177031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 178031c2a00SAdrian Hunter return result 179031c2a00SAdrian Hunter 180031c2a00SAdrian Hunter def hasChildren(self, parent): 181031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 182031c2a00SAdrian Hunter 183031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 184031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 185031c2a00SAdrian Hunter return self.columnAlignment(section) 186031c2a00SAdrian Hunter if role != Qt.DisplayRole: 187031c2a00SAdrian Hunter return None 188031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 189031c2a00SAdrian Hunter return None 190031c2a00SAdrian Hunter return self.columnHeader(section) 191031c2a00SAdrian Hunter 192031c2a00SAdrian Hunter def parent(self, child): 193031c2a00SAdrian Hunter child_item = child.internalPointer() 194031c2a00SAdrian Hunter if child_item is self.root: 195031c2a00SAdrian Hunter return QModelIndex() 196031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 197031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 198031c2a00SAdrian Hunter 199031c2a00SAdrian Hunter def index(self, row, column, parent): 200031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 201031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 202031c2a00SAdrian Hunter 203031c2a00SAdrian Hunter def DisplayData(self, item, index): 204031c2a00SAdrian Hunter return item.getData(index.column()) 205031c2a00SAdrian Hunter 2068392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2078392b74bSAdrian Hunter if row > self.last_row_read: 2088392b74bSAdrian Hunter self.last_row_read = row 2098392b74bSAdrian Hunter if row + 10 >= self.root.child_count: 2108392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2118392b74bSAdrian Hunter 2128392b74bSAdrian Hunter def columnAlignment(self, column): 2138392b74bSAdrian Hunter return Qt.AlignLeft 2148392b74bSAdrian Hunter 2158392b74bSAdrian Hunter def columnFont(self, column): 2168392b74bSAdrian Hunter return None 2178392b74bSAdrian Hunter 2188392b74bSAdrian Hunter def data(self, index, role): 2198392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2208392b74bSAdrian Hunter return self.columnAlignment(index.column()) 2218392b74bSAdrian Hunter if role == Qt.FontRole: 2228392b74bSAdrian Hunter return self.columnFont(index.column()) 2238392b74bSAdrian Hunter if role != Qt.DisplayRole: 2248392b74bSAdrian Hunter return None 2258392b74bSAdrian Hunter item = index.internalPointer() 2268392b74bSAdrian Hunter return self.DisplayData(item, index) 2278392b74bSAdrian Hunter 2288392b74bSAdrian Hunter# Table data model 2298392b74bSAdrian Hunter 2308392b74bSAdrian Hunterclass TableModel(QAbstractTableModel): 2318392b74bSAdrian Hunter 2328392b74bSAdrian Hunter def __init__(self, parent=None): 2338392b74bSAdrian Hunter super(TableModel, self).__init__(parent) 2348392b74bSAdrian Hunter self.child_count = 0 2358392b74bSAdrian Hunter self.child_items = [] 2368392b74bSAdrian Hunter self.last_row_read = 0 2378392b74bSAdrian Hunter 2388392b74bSAdrian Hunter def Item(self, parent): 2398392b74bSAdrian Hunter if parent.isValid(): 2408392b74bSAdrian Hunter return parent.internalPointer() 2418392b74bSAdrian Hunter else: 2428392b74bSAdrian Hunter return self 2438392b74bSAdrian Hunter 2448392b74bSAdrian Hunter def rowCount(self, parent): 2458392b74bSAdrian Hunter return self.child_count 2468392b74bSAdrian Hunter 2478392b74bSAdrian Hunter def headerData(self, section, orientation, role): 2488392b74bSAdrian Hunter if role == Qt.TextAlignmentRole: 2498392b74bSAdrian Hunter return self.columnAlignment(section) 2508392b74bSAdrian Hunter if role != Qt.DisplayRole: 2518392b74bSAdrian Hunter return None 2528392b74bSAdrian Hunter if orientation != Qt.Horizontal: 2538392b74bSAdrian Hunter return None 2548392b74bSAdrian Hunter return self.columnHeader(section) 2558392b74bSAdrian Hunter 2568392b74bSAdrian Hunter def index(self, row, column, parent): 2578392b74bSAdrian Hunter return self.createIndex(row, column, self.child_items[row]) 2588392b74bSAdrian Hunter 2598392b74bSAdrian Hunter def DisplayData(self, item, index): 2608392b74bSAdrian Hunter return item.getData(index.column()) 2618392b74bSAdrian Hunter 2628392b74bSAdrian Hunter def FetchIfNeeded(self, row): 2638392b74bSAdrian Hunter if row > self.last_row_read: 2648392b74bSAdrian Hunter self.last_row_read = row 2658392b74bSAdrian Hunter if row + 10 >= self.child_count: 2668392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 2678392b74bSAdrian Hunter 268031c2a00SAdrian Hunter def columnAlignment(self, column): 269031c2a00SAdrian Hunter return Qt.AlignLeft 270031c2a00SAdrian Hunter 271031c2a00SAdrian Hunter def columnFont(self, column): 272031c2a00SAdrian Hunter return None 273031c2a00SAdrian Hunter 274031c2a00SAdrian Hunter def data(self, index, role): 275031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 276031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 277031c2a00SAdrian Hunter if role == Qt.FontRole: 278031c2a00SAdrian Hunter return self.columnFont(index.column()) 279031c2a00SAdrian Hunter if role != Qt.DisplayRole: 280031c2a00SAdrian Hunter return None 281031c2a00SAdrian Hunter item = index.internalPointer() 282031c2a00SAdrian Hunter return self.DisplayData(item, index) 283031c2a00SAdrian Hunter 2841beb5c7bSAdrian Hunter# Model cache 2851beb5c7bSAdrian Hunter 2861beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 2871beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 2881beb5c7bSAdrian Hunter 2891beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 2901beb5c7bSAdrian Hunter model_cache_lock.acquire() 2911beb5c7bSAdrian Hunter try: 2921beb5c7bSAdrian Hunter model = model_cache[model_name] 2931beb5c7bSAdrian Hunter except: 2941beb5c7bSAdrian Hunter model = None 2951beb5c7bSAdrian Hunter if model is None: 2961beb5c7bSAdrian Hunter model = create_fn() 2971beb5c7bSAdrian Hunter model_cache[model_name] = model 2981beb5c7bSAdrian Hunter model_cache_lock.release() 2991beb5c7bSAdrian Hunter return model 3001beb5c7bSAdrian Hunter 301ebd70c7dSAdrian Hunter# Find bar 302ebd70c7dSAdrian Hunter 303ebd70c7dSAdrian Hunterclass FindBar(): 304ebd70c7dSAdrian Hunter 305ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 306ebd70c7dSAdrian Hunter self.finder = finder 307ebd70c7dSAdrian Hunter self.context = [] 308ebd70c7dSAdrian Hunter self.last_value = None 309ebd70c7dSAdrian Hunter self.last_pattern = None 310ebd70c7dSAdrian Hunter 311ebd70c7dSAdrian Hunter label = QLabel("Find:") 312ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 313ebd70c7dSAdrian Hunter 314ebd70c7dSAdrian Hunter self.textbox = QComboBox() 315ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 316ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 317ebd70c7dSAdrian Hunter 318ebd70c7dSAdrian Hunter self.progress = QProgressBar() 319ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 320ebd70c7dSAdrian Hunter self.progress.hide() 321ebd70c7dSAdrian Hunter 322ebd70c7dSAdrian Hunter if is_reg_expr: 323ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 324ebd70c7dSAdrian Hunter else: 325ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 326ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 327ebd70c7dSAdrian Hunter 328ebd70c7dSAdrian Hunter self.next_button = QToolButton() 329ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 330ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 331ebd70c7dSAdrian Hunter 332ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 333ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 334ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 335ebd70c7dSAdrian Hunter 336ebd70c7dSAdrian Hunter self.close_button = QToolButton() 337ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 338ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 339ebd70c7dSAdrian Hunter 340ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 341ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 342ebd70c7dSAdrian Hunter 343ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 344ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 345ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 346ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 347ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 348ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 349ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 350ebd70c7dSAdrian Hunter 351ebd70c7dSAdrian Hunter self.bar = QWidget() 352ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 353ebd70c7dSAdrian Hunter self.bar.hide() 354ebd70c7dSAdrian Hunter 355ebd70c7dSAdrian Hunter def Widget(self): 356ebd70c7dSAdrian Hunter return self.bar 357ebd70c7dSAdrian Hunter 358ebd70c7dSAdrian Hunter def Activate(self): 359ebd70c7dSAdrian Hunter self.bar.show() 360ebd70c7dSAdrian Hunter self.textbox.setFocus() 361ebd70c7dSAdrian Hunter 362ebd70c7dSAdrian Hunter def Deactivate(self): 363ebd70c7dSAdrian Hunter self.bar.hide() 364ebd70c7dSAdrian Hunter 365ebd70c7dSAdrian Hunter def Busy(self): 366ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 367ebd70c7dSAdrian Hunter self.pattern.hide() 368ebd70c7dSAdrian Hunter self.next_button.hide() 369ebd70c7dSAdrian Hunter self.prev_button.hide() 370ebd70c7dSAdrian Hunter self.progress.show() 371ebd70c7dSAdrian Hunter 372ebd70c7dSAdrian Hunter def Idle(self): 373ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 374ebd70c7dSAdrian Hunter self.progress.hide() 375ebd70c7dSAdrian Hunter self.pattern.show() 376ebd70c7dSAdrian Hunter self.next_button.show() 377ebd70c7dSAdrian Hunter self.prev_button.show() 378ebd70c7dSAdrian Hunter 379ebd70c7dSAdrian Hunter def Find(self, direction): 380ebd70c7dSAdrian Hunter value = self.textbox.currentText() 381ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 382ebd70c7dSAdrian Hunter self.last_value = value 383ebd70c7dSAdrian Hunter self.last_pattern = pattern 384ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 385ebd70c7dSAdrian Hunter 386ebd70c7dSAdrian Hunter def ValueChanged(self): 387ebd70c7dSAdrian Hunter value = self.textbox.currentText() 388ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 389ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 390ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 391ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 392ebd70c7dSAdrian Hunter if data == None: 393ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 394ebd70c7dSAdrian Hunter else: 395ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 396ebd70c7dSAdrian Hunter self.Find(0) 397ebd70c7dSAdrian Hunter 398ebd70c7dSAdrian Hunter def NextPrev(self, direction): 399ebd70c7dSAdrian Hunter value = self.textbox.currentText() 400ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 401ebd70c7dSAdrian Hunter if value != self.last_value: 402ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 403ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 404ebd70c7dSAdrian Hunter if index < 0: 405ebd70c7dSAdrian Hunter index = self.textbox.count() 406ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 407ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 408ebd70c7dSAdrian Hunter return 409ebd70c7dSAdrian Hunter else: 410ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 411ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 412ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 413ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 414ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 415ebd70c7dSAdrian Hunter self.Find(direction) 416ebd70c7dSAdrian Hunter 417ebd70c7dSAdrian Hunter def NotFound(self): 418ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 419ebd70c7dSAdrian Hunter 420031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 421031c2a00SAdrian Hunter 422031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 423031c2a00SAdrian Hunter 424031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 425031c2a00SAdrian Hunter self.glb = glb 426031c2a00SAdrian Hunter self.row = row 427031c2a00SAdrian Hunter self.parent_item = parent_item 428031c2a00SAdrian Hunter self.query_done = False; 429031c2a00SAdrian Hunter self.child_count = 0 430031c2a00SAdrian Hunter self.child_items = [] 431031c2a00SAdrian Hunter 432031c2a00SAdrian Hunter def getChildItem(self, row): 433031c2a00SAdrian Hunter return self.child_items[row] 434031c2a00SAdrian Hunter 435031c2a00SAdrian Hunter def getParentItem(self): 436031c2a00SAdrian Hunter return self.parent_item 437031c2a00SAdrian Hunter 438031c2a00SAdrian Hunter def getRow(self): 439031c2a00SAdrian Hunter return self.row 440031c2a00SAdrian Hunter 441031c2a00SAdrian Hunter def childCount(self): 442031c2a00SAdrian Hunter if not self.query_done: 443031c2a00SAdrian Hunter self.Select() 444031c2a00SAdrian Hunter if not self.child_count: 445031c2a00SAdrian Hunter return -1 446031c2a00SAdrian Hunter return self.child_count 447031c2a00SAdrian Hunter 448031c2a00SAdrian Hunter def hasChildren(self): 449031c2a00SAdrian Hunter if not self.query_done: 450031c2a00SAdrian Hunter return True 451031c2a00SAdrian Hunter return self.child_count > 0 452031c2a00SAdrian Hunter 453031c2a00SAdrian Hunter def getData(self, column): 454031c2a00SAdrian Hunter return self.data[column] 455031c2a00SAdrian Hunter 456031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 457031c2a00SAdrian Hunter 458031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 459031c2a00SAdrian Hunter 460031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 461031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 462031c2a00SAdrian Hunter self.comm_id = comm_id 463031c2a00SAdrian Hunter self.thread_id = thread_id 464031c2a00SAdrian Hunter self.call_path_id = call_path_id 465031c2a00SAdrian Hunter self.branch_count = branch_count 466031c2a00SAdrian Hunter self.time = time 467031c2a00SAdrian Hunter 468031c2a00SAdrian Hunter def Select(self): 469031c2a00SAdrian Hunter self.query_done = True; 470031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 471031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 472031c2a00SAdrian Hunter " FROM calls" 473031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 474031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 475031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 476031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 477031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 478031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 479031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 480031c2a00SAdrian Hunter " ORDER BY call_path_id") 481031c2a00SAdrian Hunter while query.next(): 482031c2a00SAdrian 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) 483031c2a00SAdrian Hunter self.child_items.append(child_item) 484031c2a00SAdrian Hunter self.child_count += 1 485031c2a00SAdrian Hunter 486031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 487031c2a00SAdrian Hunter 488031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 489031c2a00SAdrian Hunter 490031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 491031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 492031c2a00SAdrian Hunter dso = dsoname(dso) 493031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 494031c2a00SAdrian Hunter self.dbid = call_path_id 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 497031c2a00SAdrian Hunter 498031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 499031c2a00SAdrian Hunter 500031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 501031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 502031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 503031c2a00SAdrian Hunter self.dbid = thread_id 504031c2a00SAdrian Hunter 505031c2a00SAdrian Hunter def Select(self): 506031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 507031c2a00SAdrian Hunter for child_item in self.child_items: 508031c2a00SAdrian Hunter self.time += child_item.time 509031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 510031c2a00SAdrian Hunter for child_item in self.child_items: 511031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 512031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 513031c2a00SAdrian Hunter 514031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 515031c2a00SAdrian Hunter 516031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 517031c2a00SAdrian Hunter 518031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 519031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 520031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 521031c2a00SAdrian Hunter self.dbid = comm_id 522031c2a00SAdrian Hunter 523031c2a00SAdrian Hunter def Select(self): 524031c2a00SAdrian Hunter self.query_done = True; 525031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 526031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 527031c2a00SAdrian Hunter " FROM comm_threads" 528031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 529031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 530031c2a00SAdrian Hunter while query.next(): 531031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 532031c2a00SAdrian Hunter self.child_items.append(child_item) 533031c2a00SAdrian Hunter self.child_count += 1 534031c2a00SAdrian Hunter 535031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 536031c2a00SAdrian Hunter 537031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 538031c2a00SAdrian Hunter 539031c2a00SAdrian Hunter def __init__(self, glb): 540031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 541031c2a00SAdrian Hunter self.dbid = 0 542031c2a00SAdrian Hunter self.query_done = True; 543031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 544031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 545031c2a00SAdrian Hunter while query.next(): 546031c2a00SAdrian Hunter if not query.value(0): 547031c2a00SAdrian Hunter continue 548031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 549031c2a00SAdrian Hunter self.child_items.append(child_item) 550031c2a00SAdrian Hunter self.child_count += 1 551031c2a00SAdrian Hunter 552031c2a00SAdrian Hunter# Context-sensitive call graph data model 553031c2a00SAdrian Hunter 554031c2a00SAdrian Hunterclass CallGraphModel(TreeModel): 555031c2a00SAdrian Hunter 556031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 557031c2a00SAdrian Hunter super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) 558031c2a00SAdrian Hunter self.glb = glb 559031c2a00SAdrian Hunter 560031c2a00SAdrian Hunter def columnCount(self, parent=None): 561031c2a00SAdrian Hunter return 7 562031c2a00SAdrian Hunter 563031c2a00SAdrian Hunter def columnHeader(self, column): 564031c2a00SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 565031c2a00SAdrian Hunter return headers[column] 566031c2a00SAdrian Hunter 567031c2a00SAdrian Hunter def columnAlignment(self, column): 568031c2a00SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 569031c2a00SAdrian Hunter return alignment[column] 570031c2a00SAdrian Hunter 571ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 572ebd70c7dSAdrian Hunter if pattern: 573ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 574ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 575ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 576ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 577ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 578ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 579ebd70c7dSAdrian Hunter # Escape % and _ 580ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 581ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 582ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 583ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 584ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 585ebd70c7dSAdrian Hunter else: 586ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 587ebd70c7dSAdrian Hunter else: 588ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 589ebd70c7dSAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 590ebd70c7dSAdrian Hunter " FROM calls" 591ebd70c7dSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 592ebd70c7dSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 593ebd70c7dSAdrian Hunter " WHERE symbols.name" + match + 594ebd70c7dSAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 595ebd70c7dSAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 596ebd70c7dSAdrian Hunter 597ebd70c7dSAdrian Hunter def FindPath(self, query): 598ebd70c7dSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 599ebd70c7dSAdrian Hunter # to open the tree at the right place. 600ebd70c7dSAdrian Hunter ids = [] 601ebd70c7dSAdrian Hunter parent_id = query.value(0) 602ebd70c7dSAdrian Hunter while parent_id: 603ebd70c7dSAdrian Hunter ids.insert(0, parent_id) 604ebd70c7dSAdrian Hunter q2 = QSqlQuery(self.glb.db) 605ebd70c7dSAdrian Hunter QueryExec(q2, "SELECT parent_id" 606ebd70c7dSAdrian Hunter " FROM call_paths" 607ebd70c7dSAdrian Hunter " WHERE id = " + str(parent_id)) 608ebd70c7dSAdrian Hunter if not q2.next(): 609ebd70c7dSAdrian Hunter break 610ebd70c7dSAdrian Hunter parent_id = q2.value(0) 611ebd70c7dSAdrian Hunter # The call path root is not used 612ebd70c7dSAdrian Hunter if ids[0] == 1: 613ebd70c7dSAdrian Hunter del ids[0] 614ebd70c7dSAdrian Hunter ids.insert(0, query.value(2)) 615ebd70c7dSAdrian Hunter ids.insert(0, query.value(1)) 616ebd70c7dSAdrian Hunter return ids 617ebd70c7dSAdrian Hunter 618ebd70c7dSAdrian Hunter def Found(self, query, found): 619ebd70c7dSAdrian Hunter if found: 620ebd70c7dSAdrian Hunter return self.FindPath(query) 621ebd70c7dSAdrian Hunter return [] 622ebd70c7dSAdrian Hunter 623ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 624ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 625ebd70c7dSAdrian Hunter found = query.first() 626ebd70c7dSAdrian Hunter else: 627ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 628ebd70c7dSAdrian Hunter found = query.next() 629ebd70c7dSAdrian Hunter return self.Found(query, found) 630ebd70c7dSAdrian Hunter 631ebd70c7dSAdrian Hunter def FindNext(self, query): 632ebd70c7dSAdrian Hunter found = query.next() 633ebd70c7dSAdrian Hunter if not found: 634ebd70c7dSAdrian Hunter found = query.first() 635ebd70c7dSAdrian Hunter return self.Found(query, found) 636ebd70c7dSAdrian Hunter 637ebd70c7dSAdrian Hunter def FindPrev(self, query): 638ebd70c7dSAdrian Hunter found = query.previous() 639ebd70c7dSAdrian Hunter if not found: 640ebd70c7dSAdrian Hunter found = query.last() 641ebd70c7dSAdrian Hunter return self.Found(query, found) 642ebd70c7dSAdrian Hunter 643ebd70c7dSAdrian Hunter def FindThread(self, c): 644ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 645ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 646ebd70c7dSAdrian Hunter elif c.direction > 0: 647ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 648ebd70c7dSAdrian Hunter else: 649ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 650ebd70c7dSAdrian Hunter return (True, ids) 651ebd70c7dSAdrian Hunter 652ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 653ebd70c7dSAdrian Hunter class Context(): 654ebd70c7dSAdrian Hunter def __init__(self, *x): 655ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 656ebd70c7dSAdrian Hunter def Update(self, *x): 657ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 658ebd70c7dSAdrian Hunter if len(context): 659ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 660ebd70c7dSAdrian Hunter else: 661ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 662ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 663ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 664ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 665ebd70c7dSAdrian Hunter thread.start() 666ebd70c7dSAdrian Hunter 667ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 668ebd70c7dSAdrian Hunter callback(ids) 669ebd70c7dSAdrian Hunter 670ebd70c7dSAdrian Hunter# Vertical widget layout 671ebd70c7dSAdrian Hunter 672ebd70c7dSAdrian Hunterclass VBox(): 673ebd70c7dSAdrian Hunter 674ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 675ebd70c7dSAdrian Hunter self.vbox = QWidget() 676ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 677ebd70c7dSAdrian Hunter 678ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 679ebd70c7dSAdrian Hunter 680ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 681ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 682ebd70c7dSAdrian Hunter if w3: 683ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 684ebd70c7dSAdrian Hunter 685ebd70c7dSAdrian Hunter def Widget(self): 686ebd70c7dSAdrian Hunter return self.vbox 687ebd70c7dSAdrian Hunter 6881beb5c7bSAdrian Hunter# Context-sensitive call graph window 6891beb5c7bSAdrian Hunter 6901beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow): 6911beb5c7bSAdrian Hunter 6921beb5c7bSAdrian Hunter def __init__(self, glb, parent=None): 6931beb5c7bSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 6941beb5c7bSAdrian Hunter 6951beb5c7bSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 6961beb5c7bSAdrian Hunter 6971beb5c7bSAdrian Hunter self.view = QTreeView() 6981beb5c7bSAdrian Hunter self.view.setModel(self.model) 6991beb5c7bSAdrian Hunter 7001beb5c7bSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 7011beb5c7bSAdrian Hunter self.view.setColumnWidth(c, w) 7021beb5c7bSAdrian Hunter 703ebd70c7dSAdrian Hunter self.find_bar = FindBar(self, self) 704ebd70c7dSAdrian Hunter 705ebd70c7dSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 706ebd70c7dSAdrian Hunter 707ebd70c7dSAdrian Hunter self.setWidget(self.vbox.Widget()) 7081beb5c7bSAdrian Hunter 7091beb5c7bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 7101beb5c7bSAdrian Hunter 711ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 712ebd70c7dSAdrian Hunter if not len(ids): 713ebd70c7dSAdrian Hunter return False 714ebd70c7dSAdrian Hunter parent = QModelIndex() 715ebd70c7dSAdrian Hunter for dbid in ids: 716ebd70c7dSAdrian Hunter found = False 717ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 718ebd70c7dSAdrian Hunter for row in xrange(n): 719ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 720ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 721ebd70c7dSAdrian Hunter found = True 722ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 723ebd70c7dSAdrian Hunter parent = child 724ebd70c7dSAdrian Hunter break 725ebd70c7dSAdrian Hunter if not found: 726ebd70c7dSAdrian Hunter break 727ebd70c7dSAdrian Hunter return found 728ebd70c7dSAdrian Hunter 729ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 730ebd70c7dSAdrian Hunter self.view.setFocus() 731ebd70c7dSAdrian Hunter self.find_bar.Busy() 732ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 733ebd70c7dSAdrian Hunter 734ebd70c7dSAdrian Hunter def FindDone(self, ids): 735ebd70c7dSAdrian Hunter found = True 736ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 737ebd70c7dSAdrian Hunter found = False 738ebd70c7dSAdrian Hunter self.find_bar.Idle() 739ebd70c7dSAdrian Hunter if not found: 740ebd70c7dSAdrian Hunter self.find_bar.NotFound() 741ebd70c7dSAdrian Hunter 7428392b74bSAdrian Hunter# Child data item finder 7438392b74bSAdrian Hunter 7448392b74bSAdrian Hunterclass ChildDataItemFinder(): 7458392b74bSAdrian Hunter 7468392b74bSAdrian Hunter def __init__(self, root): 7478392b74bSAdrian Hunter self.root = root 7488392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 7498392b74bSAdrian Hunter self.rows = [] 7508392b74bSAdrian Hunter self.pos = 0 7518392b74bSAdrian Hunter 7528392b74bSAdrian Hunter def FindSelect(self): 7538392b74bSAdrian Hunter self.rows = [] 7548392b74bSAdrian Hunter if self.pattern: 7558392b74bSAdrian Hunter pattern = re.compile(self.value) 7568392b74bSAdrian Hunter for child in self.root.child_items: 7578392b74bSAdrian Hunter for column_data in child.data: 7588392b74bSAdrian Hunter if re.search(pattern, str(column_data)) is not None: 7598392b74bSAdrian Hunter self.rows.append(child.row) 7608392b74bSAdrian Hunter break 7618392b74bSAdrian Hunter else: 7628392b74bSAdrian Hunter for child in self.root.child_items: 7638392b74bSAdrian Hunter for column_data in child.data: 7648392b74bSAdrian Hunter if self.value in str(column_data): 7658392b74bSAdrian Hunter self.rows.append(child.row) 7668392b74bSAdrian Hunter break 7678392b74bSAdrian Hunter 7688392b74bSAdrian Hunter def FindValue(self): 7698392b74bSAdrian Hunter self.pos = 0 7708392b74bSAdrian Hunter if self.last_value != self.value or self.pattern != self.last_pattern: 7718392b74bSAdrian Hunter self.FindSelect() 7728392b74bSAdrian Hunter if not len(self.rows): 7738392b74bSAdrian Hunter return -1 7748392b74bSAdrian Hunter return self.rows[self.pos] 7758392b74bSAdrian Hunter 7768392b74bSAdrian Hunter def FindThread(self): 7778392b74bSAdrian Hunter if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: 7788392b74bSAdrian Hunter row = self.FindValue() 7798392b74bSAdrian Hunter elif len(self.rows): 7808392b74bSAdrian Hunter if self.direction > 0: 7818392b74bSAdrian Hunter self.pos += 1 7828392b74bSAdrian Hunter if self.pos >= len(self.rows): 7838392b74bSAdrian Hunter self.pos = 0 7848392b74bSAdrian Hunter else: 7858392b74bSAdrian Hunter self.pos -= 1 7868392b74bSAdrian Hunter if self.pos < 0: 7878392b74bSAdrian Hunter self.pos = len(self.rows) - 1 7888392b74bSAdrian Hunter row = self.rows[self.pos] 7898392b74bSAdrian Hunter else: 7908392b74bSAdrian Hunter row = -1 7918392b74bSAdrian Hunter return (True, row) 7928392b74bSAdrian Hunter 7938392b74bSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 7948392b74bSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) 7958392b74bSAdrian Hunter # Use a thread so the UI is not blocked 7968392b74bSAdrian Hunter thread = Thread(self.FindThread) 7978392b74bSAdrian Hunter thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) 7988392b74bSAdrian Hunter thread.start() 7998392b74bSAdrian Hunter 8008392b74bSAdrian Hunter def FindDone(self, thread, callback, row): 8018392b74bSAdrian Hunter callback(row) 8028392b74bSAdrian Hunter 8038392b74bSAdrian Hunter# Number of database records to fetch in one go 8048392b74bSAdrian Hunter 8058392b74bSAdrian Hunterglb_chunk_sz = 10000 8068392b74bSAdrian Hunter 8078392b74bSAdrian Hunter# size of pickled integer big enough for record size 8088392b74bSAdrian Hunter 8098392b74bSAdrian Hunterglb_nsz = 8 8108392b74bSAdrian Hunter 8118392b74bSAdrian Hunter# Background process for SQL data fetcher 8128392b74bSAdrian Hunter 8138392b74bSAdrian Hunterclass SQLFetcherProcess(): 8148392b74bSAdrian Hunter 8158392b74bSAdrian Hunter def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): 8168392b74bSAdrian Hunter # Need a unique connection name 8178392b74bSAdrian Hunter conn_name = "SQLFetcher" + str(os.getpid()) 8188392b74bSAdrian Hunter self.db, dbname = dbref.Open(conn_name) 8198392b74bSAdrian Hunter self.sql = sql 8208392b74bSAdrian Hunter self.buffer = buffer 8218392b74bSAdrian Hunter self.head = head 8228392b74bSAdrian Hunter self.tail = tail 8238392b74bSAdrian Hunter self.fetch_count = fetch_count 8248392b74bSAdrian Hunter self.fetching_done = fetching_done 8258392b74bSAdrian Hunter self.process_target = process_target 8268392b74bSAdrian Hunter self.wait_event = wait_event 8278392b74bSAdrian Hunter self.fetched_event = fetched_event 8288392b74bSAdrian Hunter self.prep = prep 8298392b74bSAdrian Hunter self.query = QSqlQuery(self.db) 8308392b74bSAdrian Hunter self.query_limit = 0 if "$$last_id$$" in sql else 2 8318392b74bSAdrian Hunter self.last_id = -1 8328392b74bSAdrian Hunter self.fetched = 0 8338392b74bSAdrian Hunter self.more = True 8348392b74bSAdrian Hunter self.local_head = self.head.value 8358392b74bSAdrian Hunter self.local_tail = self.tail.value 8368392b74bSAdrian Hunter 8378392b74bSAdrian Hunter def Select(self): 8388392b74bSAdrian Hunter if self.query_limit: 8398392b74bSAdrian Hunter if self.query_limit == 1: 8408392b74bSAdrian Hunter return 8418392b74bSAdrian Hunter self.query_limit -= 1 8428392b74bSAdrian Hunter stmt = self.sql.replace("$$last_id$$", str(self.last_id)) 8438392b74bSAdrian Hunter QueryExec(self.query, stmt) 8448392b74bSAdrian Hunter 8458392b74bSAdrian Hunter def Next(self): 8468392b74bSAdrian Hunter if not self.query.next(): 8478392b74bSAdrian Hunter self.Select() 8488392b74bSAdrian Hunter if not self.query.next(): 8498392b74bSAdrian Hunter return None 8508392b74bSAdrian Hunter self.last_id = self.query.value(0) 8518392b74bSAdrian Hunter return self.prep(self.query) 8528392b74bSAdrian Hunter 8538392b74bSAdrian Hunter def WaitForTarget(self): 8548392b74bSAdrian Hunter while True: 8558392b74bSAdrian Hunter self.wait_event.clear() 8568392b74bSAdrian Hunter target = self.process_target.value 8578392b74bSAdrian Hunter if target > self.fetched or target < 0: 8588392b74bSAdrian Hunter break 8598392b74bSAdrian Hunter self.wait_event.wait() 8608392b74bSAdrian Hunter return target 8618392b74bSAdrian Hunter 8628392b74bSAdrian Hunter def HasSpace(self, sz): 8638392b74bSAdrian Hunter if self.local_tail <= self.local_head: 8648392b74bSAdrian Hunter space = len(self.buffer) - self.local_head 8658392b74bSAdrian Hunter if space > sz: 8668392b74bSAdrian Hunter return True 8678392b74bSAdrian Hunter if space >= glb_nsz: 8688392b74bSAdrian Hunter # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer 8698392b74bSAdrian Hunter nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL) 8708392b74bSAdrian Hunter self.buffer[self.local_head : self.local_head + len(nd)] = nd 8718392b74bSAdrian Hunter self.local_head = 0 8728392b74bSAdrian Hunter if self.local_tail - self.local_head > sz: 8738392b74bSAdrian Hunter return True 8748392b74bSAdrian Hunter return False 8758392b74bSAdrian Hunter 8768392b74bSAdrian Hunter def WaitForSpace(self, sz): 8778392b74bSAdrian Hunter if self.HasSpace(sz): 8788392b74bSAdrian Hunter return 8798392b74bSAdrian Hunter while True: 8808392b74bSAdrian Hunter self.wait_event.clear() 8818392b74bSAdrian Hunter self.local_tail = self.tail.value 8828392b74bSAdrian Hunter if self.HasSpace(sz): 8838392b74bSAdrian Hunter return 8848392b74bSAdrian Hunter self.wait_event.wait() 8858392b74bSAdrian Hunter 8868392b74bSAdrian Hunter def AddToBuffer(self, obj): 8878392b74bSAdrian Hunter d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL) 8888392b74bSAdrian Hunter n = len(d) 8898392b74bSAdrian Hunter nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL) 8908392b74bSAdrian Hunter sz = n + glb_nsz 8918392b74bSAdrian Hunter self.WaitForSpace(sz) 8928392b74bSAdrian Hunter pos = self.local_head 8938392b74bSAdrian Hunter self.buffer[pos : pos + len(nd)] = nd 8948392b74bSAdrian Hunter self.buffer[pos + glb_nsz : pos + sz] = d 8958392b74bSAdrian Hunter self.local_head += sz 8968392b74bSAdrian Hunter 8978392b74bSAdrian Hunter def FetchBatch(self, batch_size): 8988392b74bSAdrian Hunter fetched = 0 8998392b74bSAdrian Hunter while batch_size > fetched: 9008392b74bSAdrian Hunter obj = self.Next() 9018392b74bSAdrian Hunter if obj is None: 9028392b74bSAdrian Hunter self.more = False 9038392b74bSAdrian Hunter break 9048392b74bSAdrian Hunter self.AddToBuffer(obj) 9058392b74bSAdrian Hunter fetched += 1 9068392b74bSAdrian Hunter if fetched: 9078392b74bSAdrian Hunter self.fetched += fetched 9088392b74bSAdrian Hunter with self.fetch_count.get_lock(): 9098392b74bSAdrian Hunter self.fetch_count.value += fetched 9108392b74bSAdrian Hunter self.head.value = self.local_head 9118392b74bSAdrian Hunter self.fetched_event.set() 9128392b74bSAdrian Hunter 9138392b74bSAdrian Hunter def Run(self): 9148392b74bSAdrian Hunter while self.more: 9158392b74bSAdrian Hunter target = self.WaitForTarget() 9168392b74bSAdrian Hunter if target < 0: 9178392b74bSAdrian Hunter break 9188392b74bSAdrian Hunter batch_size = min(glb_chunk_sz, target - self.fetched) 9198392b74bSAdrian Hunter self.FetchBatch(batch_size) 9208392b74bSAdrian Hunter self.fetching_done.value = True 9218392b74bSAdrian Hunter self.fetched_event.set() 9228392b74bSAdrian Hunter 9238392b74bSAdrian Hunterdef SQLFetcherFn(*x): 9248392b74bSAdrian Hunter process = SQLFetcherProcess(*x) 9258392b74bSAdrian Hunter process.Run() 9268392b74bSAdrian Hunter 9278392b74bSAdrian Hunter# SQL data fetcher 9288392b74bSAdrian Hunter 9298392b74bSAdrian Hunterclass SQLFetcher(QObject): 9308392b74bSAdrian Hunter 9318392b74bSAdrian Hunter done = Signal(object) 9328392b74bSAdrian Hunter 9338392b74bSAdrian Hunter def __init__(self, glb, sql, prep, process_data, parent=None): 9348392b74bSAdrian Hunter super(SQLFetcher, self).__init__(parent) 9358392b74bSAdrian Hunter self.process_data = process_data 9368392b74bSAdrian Hunter self.more = True 9378392b74bSAdrian Hunter self.target = 0 9388392b74bSAdrian Hunter self.last_target = 0 9398392b74bSAdrian Hunter self.fetched = 0 9408392b74bSAdrian Hunter self.buffer_size = 16 * 1024 * 1024 9418392b74bSAdrian Hunter self.buffer = Array(c_char, self.buffer_size, lock=False) 9428392b74bSAdrian Hunter self.head = Value(c_longlong) 9438392b74bSAdrian Hunter self.tail = Value(c_longlong) 9448392b74bSAdrian Hunter self.local_tail = 0 9458392b74bSAdrian Hunter self.fetch_count = Value(c_longlong) 9468392b74bSAdrian Hunter self.fetching_done = Value(c_bool) 9478392b74bSAdrian Hunter self.last_count = 0 9488392b74bSAdrian Hunter self.process_target = Value(c_longlong) 9498392b74bSAdrian Hunter self.wait_event = Event() 9508392b74bSAdrian Hunter self.fetched_event = Event() 9518392b74bSAdrian Hunter glb.AddInstanceToShutdownOnExit(self) 9528392b74bSAdrian 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)) 9538392b74bSAdrian Hunter self.process.start() 9548392b74bSAdrian Hunter self.thread = Thread(self.Thread) 9558392b74bSAdrian Hunter self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) 9568392b74bSAdrian Hunter self.thread.start() 9578392b74bSAdrian Hunter 9588392b74bSAdrian Hunter def Shutdown(self): 9598392b74bSAdrian Hunter # Tell the thread and process to exit 9608392b74bSAdrian Hunter self.process_target.value = -1 9618392b74bSAdrian Hunter self.wait_event.set() 9628392b74bSAdrian Hunter self.more = False 9638392b74bSAdrian Hunter self.fetching_done.value = True 9648392b74bSAdrian Hunter self.fetched_event.set() 9658392b74bSAdrian Hunter 9668392b74bSAdrian Hunter def Thread(self): 9678392b74bSAdrian Hunter if not self.more: 9688392b74bSAdrian Hunter return True, 0 9698392b74bSAdrian Hunter while True: 9708392b74bSAdrian Hunter self.fetched_event.clear() 9718392b74bSAdrian Hunter fetch_count = self.fetch_count.value 9728392b74bSAdrian Hunter if fetch_count != self.last_count: 9738392b74bSAdrian Hunter break 9748392b74bSAdrian Hunter if self.fetching_done.value: 9758392b74bSAdrian Hunter self.more = False 9768392b74bSAdrian Hunter return True, 0 9778392b74bSAdrian Hunter self.fetched_event.wait() 9788392b74bSAdrian Hunter count = fetch_count - self.last_count 9798392b74bSAdrian Hunter self.last_count = fetch_count 9808392b74bSAdrian Hunter self.fetched += count 9818392b74bSAdrian Hunter return False, count 9828392b74bSAdrian Hunter 9838392b74bSAdrian Hunter def Fetch(self, nr): 9848392b74bSAdrian Hunter if not self.more: 9858392b74bSAdrian Hunter # -1 inidcates there are no more 9868392b74bSAdrian Hunter return -1 9878392b74bSAdrian Hunter result = self.fetched 9888392b74bSAdrian Hunter extra = result + nr - self.target 9898392b74bSAdrian Hunter if extra > 0: 9908392b74bSAdrian Hunter self.target += extra 9918392b74bSAdrian Hunter # process_target < 0 indicates shutting down 9928392b74bSAdrian Hunter if self.process_target.value >= 0: 9938392b74bSAdrian Hunter self.process_target.value = self.target 9948392b74bSAdrian Hunter self.wait_event.set() 9958392b74bSAdrian Hunter return result 9968392b74bSAdrian Hunter 9978392b74bSAdrian Hunter def RemoveFromBuffer(self): 9988392b74bSAdrian Hunter pos = self.local_tail 9998392b74bSAdrian Hunter if len(self.buffer) - pos < glb_nsz: 10008392b74bSAdrian Hunter pos = 0 10018392b74bSAdrian Hunter n = cPickle.loads(self.buffer[pos : pos + glb_nsz]) 10028392b74bSAdrian Hunter if n == 0: 10038392b74bSAdrian Hunter pos = 0 10048392b74bSAdrian Hunter n = cPickle.loads(self.buffer[0 : glb_nsz]) 10058392b74bSAdrian Hunter pos += glb_nsz 10068392b74bSAdrian Hunter obj = cPickle.loads(self.buffer[pos : pos + n]) 10078392b74bSAdrian Hunter self.local_tail = pos + n 10088392b74bSAdrian Hunter return obj 10098392b74bSAdrian Hunter 10108392b74bSAdrian Hunter def ProcessData(self, count): 10118392b74bSAdrian Hunter for i in xrange(count): 10128392b74bSAdrian Hunter obj = self.RemoveFromBuffer() 10138392b74bSAdrian Hunter self.process_data(obj) 10148392b74bSAdrian Hunter self.tail.value = self.local_tail 10158392b74bSAdrian Hunter self.wait_event.set() 10168392b74bSAdrian Hunter self.done.emit(count) 10178392b74bSAdrian Hunter 10188392b74bSAdrian Hunter# Fetch more records bar 10198392b74bSAdrian Hunter 10208392b74bSAdrian Hunterclass FetchMoreRecordsBar(): 10218392b74bSAdrian Hunter 10228392b74bSAdrian Hunter def __init__(self, model, parent): 10238392b74bSAdrian Hunter self.model = model 10248392b74bSAdrian Hunter 10258392b74bSAdrian Hunter self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") 10268392b74bSAdrian Hunter self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10278392b74bSAdrian Hunter 10288392b74bSAdrian Hunter self.fetch_count = QSpinBox() 10298392b74bSAdrian Hunter self.fetch_count.setRange(1, 1000000) 10308392b74bSAdrian Hunter self.fetch_count.setValue(10) 10318392b74bSAdrian Hunter self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10328392b74bSAdrian Hunter 10338392b74bSAdrian Hunter self.fetch = QPushButton("Go!") 10348392b74bSAdrian Hunter self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 10358392b74bSAdrian Hunter self.fetch.released.connect(self.FetchMoreRecords) 10368392b74bSAdrian Hunter 10378392b74bSAdrian Hunter self.progress = QProgressBar() 10388392b74bSAdrian Hunter self.progress.setRange(0, 100) 10398392b74bSAdrian Hunter self.progress.hide() 10408392b74bSAdrian Hunter 10418392b74bSAdrian Hunter self.done_label = QLabel("All records fetched") 10428392b74bSAdrian Hunter self.done_label.hide() 10438392b74bSAdrian Hunter 10448392b74bSAdrian Hunter self.spacer = QLabel("") 10458392b74bSAdrian Hunter 10468392b74bSAdrian Hunter self.close_button = QToolButton() 10478392b74bSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 10488392b74bSAdrian Hunter self.close_button.released.connect(self.Deactivate) 10498392b74bSAdrian Hunter 10508392b74bSAdrian Hunter self.hbox = QHBoxLayout() 10518392b74bSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 10528392b74bSAdrian Hunter 10538392b74bSAdrian Hunter self.hbox.addWidget(self.label) 10548392b74bSAdrian Hunter self.hbox.addWidget(self.fetch_count) 10558392b74bSAdrian Hunter self.hbox.addWidget(self.fetch) 10568392b74bSAdrian Hunter self.hbox.addWidget(self.spacer) 10578392b74bSAdrian Hunter self.hbox.addWidget(self.progress) 10588392b74bSAdrian Hunter self.hbox.addWidget(self.done_label) 10598392b74bSAdrian Hunter self.hbox.addWidget(self.close_button) 10608392b74bSAdrian Hunter 10618392b74bSAdrian Hunter self.bar = QWidget() 10628392b74bSAdrian Hunter self.bar.setLayout(self.hbox); 10638392b74bSAdrian Hunter self.bar.show() 10648392b74bSAdrian Hunter 10658392b74bSAdrian Hunter self.in_progress = False 10668392b74bSAdrian Hunter self.model.progress.connect(self.Progress) 10678392b74bSAdrian Hunter 10688392b74bSAdrian Hunter self.done = False 10698392b74bSAdrian Hunter 10708392b74bSAdrian Hunter if not model.HasMoreRecords(): 10718392b74bSAdrian Hunter self.Done() 10728392b74bSAdrian Hunter 10738392b74bSAdrian Hunter def Widget(self): 10748392b74bSAdrian Hunter return self.bar 10758392b74bSAdrian Hunter 10768392b74bSAdrian Hunter def Activate(self): 10778392b74bSAdrian Hunter self.bar.show() 10788392b74bSAdrian Hunter self.fetch.setFocus() 10798392b74bSAdrian Hunter 10808392b74bSAdrian Hunter def Deactivate(self): 10818392b74bSAdrian Hunter self.bar.hide() 10828392b74bSAdrian Hunter 10838392b74bSAdrian Hunter def Enable(self, enable): 10848392b74bSAdrian Hunter self.fetch.setEnabled(enable) 10858392b74bSAdrian Hunter self.fetch_count.setEnabled(enable) 10868392b74bSAdrian Hunter 10878392b74bSAdrian Hunter def Busy(self): 10888392b74bSAdrian Hunter self.Enable(False) 10898392b74bSAdrian Hunter self.fetch.hide() 10908392b74bSAdrian Hunter self.spacer.hide() 10918392b74bSAdrian Hunter self.progress.show() 10928392b74bSAdrian Hunter 10938392b74bSAdrian Hunter def Idle(self): 10948392b74bSAdrian Hunter self.in_progress = False 10958392b74bSAdrian Hunter self.Enable(True) 10968392b74bSAdrian Hunter self.progress.hide() 10978392b74bSAdrian Hunter self.fetch.show() 10988392b74bSAdrian Hunter self.spacer.show() 10998392b74bSAdrian Hunter 11008392b74bSAdrian Hunter def Target(self): 11018392b74bSAdrian Hunter return self.fetch_count.value() * glb_chunk_sz 11028392b74bSAdrian Hunter 11038392b74bSAdrian Hunter def Done(self): 11048392b74bSAdrian Hunter self.done = True 11058392b74bSAdrian Hunter self.Idle() 11068392b74bSAdrian Hunter self.label.hide() 11078392b74bSAdrian Hunter self.fetch_count.hide() 11088392b74bSAdrian Hunter self.fetch.hide() 11098392b74bSAdrian Hunter self.spacer.hide() 11108392b74bSAdrian Hunter self.done_label.show() 11118392b74bSAdrian Hunter 11128392b74bSAdrian Hunter def Progress(self, count): 11138392b74bSAdrian Hunter if self.in_progress: 11148392b74bSAdrian Hunter if count: 11158392b74bSAdrian Hunter percent = ((count - self.start) * 100) / self.Target() 11168392b74bSAdrian Hunter if percent >= 100: 11178392b74bSAdrian Hunter self.Idle() 11188392b74bSAdrian Hunter else: 11198392b74bSAdrian Hunter self.progress.setValue(percent) 11208392b74bSAdrian Hunter if not count: 11218392b74bSAdrian Hunter # Count value of zero means no more records 11228392b74bSAdrian Hunter self.Done() 11238392b74bSAdrian Hunter 11248392b74bSAdrian Hunter def FetchMoreRecords(self): 11258392b74bSAdrian Hunter if self.done: 11268392b74bSAdrian Hunter return 11278392b74bSAdrian Hunter self.progress.setValue(0) 11288392b74bSAdrian Hunter self.Busy() 11298392b74bSAdrian Hunter self.in_progress = True 11308392b74bSAdrian Hunter self.start = self.model.FetchMoreRecords(self.Target()) 11318392b74bSAdrian Hunter 113276099f98SAdrian Hunter# Brance data model level two item 113376099f98SAdrian Hunter 113476099f98SAdrian Hunterclass BranchLevelTwoItem(): 113576099f98SAdrian Hunter 113676099f98SAdrian Hunter def __init__(self, row, text, parent_item): 113776099f98SAdrian Hunter self.row = row 113876099f98SAdrian Hunter self.parent_item = parent_item 113976099f98SAdrian Hunter self.data = [""] * 8 114076099f98SAdrian Hunter self.data[7] = text 114176099f98SAdrian Hunter self.level = 2 114276099f98SAdrian Hunter 114376099f98SAdrian Hunter def getParentItem(self): 114476099f98SAdrian Hunter return self.parent_item 114576099f98SAdrian Hunter 114676099f98SAdrian Hunter def getRow(self): 114776099f98SAdrian Hunter return self.row 114876099f98SAdrian Hunter 114976099f98SAdrian Hunter def childCount(self): 115076099f98SAdrian Hunter return 0 115176099f98SAdrian Hunter 115276099f98SAdrian Hunter def hasChildren(self): 115376099f98SAdrian Hunter return False 115476099f98SAdrian Hunter 115576099f98SAdrian Hunter def getData(self, column): 115676099f98SAdrian Hunter return self.data[column] 115776099f98SAdrian Hunter 115876099f98SAdrian Hunter# Brance data model level one item 115976099f98SAdrian Hunter 116076099f98SAdrian Hunterclass BranchLevelOneItem(): 116176099f98SAdrian Hunter 116276099f98SAdrian Hunter def __init__(self, glb, row, data, parent_item): 116376099f98SAdrian Hunter self.glb = glb 116476099f98SAdrian Hunter self.row = row 116576099f98SAdrian Hunter self.parent_item = parent_item 116676099f98SAdrian Hunter self.child_count = 0 116776099f98SAdrian Hunter self.child_items = [] 116876099f98SAdrian Hunter self.data = data[1:] 116976099f98SAdrian Hunter self.dbid = data[0] 117076099f98SAdrian Hunter self.level = 1 117176099f98SAdrian Hunter self.query_done = False 117276099f98SAdrian Hunter 117376099f98SAdrian Hunter def getChildItem(self, row): 117476099f98SAdrian Hunter return self.child_items[row] 117576099f98SAdrian Hunter 117676099f98SAdrian Hunter def getParentItem(self): 117776099f98SAdrian Hunter return self.parent_item 117876099f98SAdrian Hunter 117976099f98SAdrian Hunter def getRow(self): 118076099f98SAdrian Hunter return self.row 118176099f98SAdrian Hunter 118276099f98SAdrian Hunter def Select(self): 118376099f98SAdrian Hunter self.query_done = True 118476099f98SAdrian Hunter 118576099f98SAdrian Hunter if not self.glb.have_disassembler: 118676099f98SAdrian Hunter return 118776099f98SAdrian Hunter 118876099f98SAdrian Hunter query = QSqlQuery(self.glb.db) 118976099f98SAdrian Hunter 119076099f98SAdrian Hunter QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" 119176099f98SAdrian Hunter " FROM samples" 119276099f98SAdrian Hunter " INNER JOIN dsos ON samples.to_dso_id = dsos.id" 119376099f98SAdrian Hunter " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" 119476099f98SAdrian Hunter " WHERE samples.id = " + str(self.dbid)) 119576099f98SAdrian Hunter if not query.next(): 119676099f98SAdrian Hunter return 119776099f98SAdrian Hunter cpu = query.value(0) 119876099f98SAdrian Hunter dso = query.value(1) 119976099f98SAdrian Hunter sym = query.value(2) 120076099f98SAdrian Hunter if dso == 0 or sym == 0: 120176099f98SAdrian Hunter return 120276099f98SAdrian Hunter off = query.value(3) 120376099f98SAdrian Hunter short_name = query.value(4) 120476099f98SAdrian Hunter long_name = query.value(5) 120576099f98SAdrian Hunter build_id = query.value(6) 120676099f98SAdrian Hunter sym_start = query.value(7) 120776099f98SAdrian Hunter ip = query.value(8) 120876099f98SAdrian Hunter 120976099f98SAdrian Hunter QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" 121076099f98SAdrian Hunter " FROM samples" 121176099f98SAdrian Hunter " INNER JOIN symbols ON samples.symbol_id = symbols.id" 121276099f98SAdrian Hunter " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + 121376099f98SAdrian Hunter " ORDER BY samples.id" 121476099f98SAdrian Hunter " LIMIT 1") 121576099f98SAdrian Hunter if not query.next(): 121676099f98SAdrian Hunter return 121776099f98SAdrian Hunter if query.value(0) != dso: 121876099f98SAdrian Hunter # Cannot disassemble from one dso to another 121976099f98SAdrian Hunter return 122076099f98SAdrian Hunter bsym = query.value(1) 122176099f98SAdrian Hunter boff = query.value(2) 122276099f98SAdrian Hunter bsym_start = query.value(3) 122376099f98SAdrian Hunter if bsym == 0: 122476099f98SAdrian Hunter return 122576099f98SAdrian Hunter tot = bsym_start + boff + 1 - sym_start - off 122676099f98SAdrian Hunter if tot <= 0 or tot > 16384: 122776099f98SAdrian Hunter return 122876099f98SAdrian Hunter 122976099f98SAdrian Hunter inst = self.glb.disassembler.Instruction() 123076099f98SAdrian Hunter f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) 123176099f98SAdrian Hunter if not f: 123276099f98SAdrian Hunter return 123376099f98SAdrian Hunter mode = 0 if Is64Bit(f) else 1 123476099f98SAdrian Hunter self.glb.disassembler.SetMode(inst, mode) 123576099f98SAdrian Hunter 123676099f98SAdrian Hunter buf_sz = tot + 16 123776099f98SAdrian Hunter buf = create_string_buffer(tot + 16) 123876099f98SAdrian Hunter f.seek(sym_start + off) 123976099f98SAdrian Hunter buf.value = f.read(buf_sz) 124076099f98SAdrian Hunter buf_ptr = addressof(buf) 124176099f98SAdrian Hunter i = 0 124276099f98SAdrian Hunter while tot > 0: 124376099f98SAdrian Hunter cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) 124476099f98SAdrian Hunter if cnt: 124576099f98SAdrian Hunter byte_str = tohex(ip).rjust(16) 124676099f98SAdrian Hunter for k in xrange(cnt): 124776099f98SAdrian Hunter byte_str += " %02x" % ord(buf[i]) 124876099f98SAdrian Hunter i += 1 124976099f98SAdrian Hunter while k < 15: 125076099f98SAdrian Hunter byte_str += " " 125176099f98SAdrian Hunter k += 1 125276099f98SAdrian Hunter self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) 125376099f98SAdrian Hunter self.child_count += 1 125476099f98SAdrian Hunter else: 125576099f98SAdrian Hunter return 125676099f98SAdrian Hunter buf_ptr += cnt 125776099f98SAdrian Hunter tot -= cnt 125876099f98SAdrian Hunter buf_sz -= cnt 125976099f98SAdrian Hunter ip += cnt 126076099f98SAdrian Hunter 126176099f98SAdrian Hunter def childCount(self): 126276099f98SAdrian Hunter if not self.query_done: 126376099f98SAdrian Hunter self.Select() 126476099f98SAdrian Hunter if not self.child_count: 126576099f98SAdrian Hunter return -1 126676099f98SAdrian Hunter return self.child_count 126776099f98SAdrian Hunter 126876099f98SAdrian Hunter def hasChildren(self): 126976099f98SAdrian Hunter if not self.query_done: 127076099f98SAdrian Hunter return True 127176099f98SAdrian Hunter return self.child_count > 0 127276099f98SAdrian Hunter 127376099f98SAdrian Hunter def getData(self, column): 127476099f98SAdrian Hunter return self.data[column] 127576099f98SAdrian Hunter 127676099f98SAdrian Hunter# Brance data model root item 127776099f98SAdrian Hunter 127876099f98SAdrian Hunterclass BranchRootItem(): 127976099f98SAdrian Hunter 128076099f98SAdrian Hunter def __init__(self): 128176099f98SAdrian Hunter self.child_count = 0 128276099f98SAdrian Hunter self.child_items = [] 128376099f98SAdrian Hunter self.level = 0 128476099f98SAdrian Hunter 128576099f98SAdrian Hunter def getChildItem(self, row): 128676099f98SAdrian Hunter return self.child_items[row] 128776099f98SAdrian Hunter 128876099f98SAdrian Hunter def getParentItem(self): 128976099f98SAdrian Hunter return None 129076099f98SAdrian Hunter 129176099f98SAdrian Hunter def getRow(self): 129276099f98SAdrian Hunter return 0 129376099f98SAdrian Hunter 129476099f98SAdrian Hunter def childCount(self): 129576099f98SAdrian Hunter return self.child_count 129676099f98SAdrian Hunter 129776099f98SAdrian Hunter def hasChildren(self): 129876099f98SAdrian Hunter return self.child_count > 0 129976099f98SAdrian Hunter 130076099f98SAdrian Hunter def getData(self, column): 130176099f98SAdrian Hunter return "" 130276099f98SAdrian Hunter 130376099f98SAdrian Hunter# Branch data preparation 130476099f98SAdrian Hunter 130576099f98SAdrian Hunterdef BranchDataPrep(query): 130676099f98SAdrian Hunter data = [] 130776099f98SAdrian Hunter for i in xrange(0, 8): 130876099f98SAdrian Hunter data.append(query.value(i)) 130976099f98SAdrian Hunter data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + 131076099f98SAdrian Hunter " (" + dsoname(query.value(11)) + ")" + " -> " + 131176099f98SAdrian Hunter tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + 131276099f98SAdrian Hunter " (" + dsoname(query.value(15)) + ")") 131376099f98SAdrian Hunter return data 131476099f98SAdrian Hunter 131576099f98SAdrian Hunter# Branch data model 131676099f98SAdrian Hunter 131776099f98SAdrian Hunterclass BranchModel(TreeModel): 131876099f98SAdrian Hunter 131976099f98SAdrian Hunter progress = Signal(object) 132076099f98SAdrian Hunter 132176099f98SAdrian Hunter def __init__(self, glb, event_id, where_clause, parent=None): 132276099f98SAdrian Hunter super(BranchModel, self).__init__(BranchRootItem(), parent) 132376099f98SAdrian Hunter self.glb = glb 132476099f98SAdrian Hunter self.event_id = event_id 132576099f98SAdrian Hunter self.more = True 132676099f98SAdrian Hunter self.populated = 0 132776099f98SAdrian Hunter sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," 132876099f98SAdrian Hunter " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," 132976099f98SAdrian Hunter " ip, symbols.name, sym_offset, dsos.short_name," 133076099f98SAdrian Hunter " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" 133176099f98SAdrian Hunter " FROM samples" 133276099f98SAdrian Hunter " INNER JOIN comms ON comm_id = comms.id" 133376099f98SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 133476099f98SAdrian Hunter " INNER JOIN branch_types ON branch_type = branch_types.id" 133576099f98SAdrian Hunter " INNER JOIN symbols ON symbol_id = symbols.id" 133676099f98SAdrian Hunter " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" 133776099f98SAdrian Hunter " INNER JOIN dsos ON samples.dso_id = dsos.id" 133876099f98SAdrian Hunter " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" 133976099f98SAdrian Hunter " WHERE samples.id > $$last_id$$" + where_clause + 134076099f98SAdrian Hunter " AND evsel_id = " + str(self.event_id) + 134176099f98SAdrian Hunter " ORDER BY samples.id" 134276099f98SAdrian Hunter " LIMIT " + str(glb_chunk_sz)) 134376099f98SAdrian Hunter self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample) 134476099f98SAdrian Hunter self.fetcher.done.connect(self.Update) 134576099f98SAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 134676099f98SAdrian Hunter 134776099f98SAdrian Hunter def columnCount(self, parent=None): 134876099f98SAdrian Hunter return 8 134976099f98SAdrian Hunter 135076099f98SAdrian Hunter def columnHeader(self, column): 135176099f98SAdrian Hunter return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] 135276099f98SAdrian Hunter 135376099f98SAdrian Hunter def columnFont(self, column): 135476099f98SAdrian Hunter if column != 7: 135576099f98SAdrian Hunter return None 135676099f98SAdrian Hunter return QFont("Monospace") 135776099f98SAdrian Hunter 135876099f98SAdrian Hunter def DisplayData(self, item, index): 135976099f98SAdrian Hunter if item.level == 1: 136076099f98SAdrian Hunter self.FetchIfNeeded(item.row) 136176099f98SAdrian Hunter return item.getData(index.column()) 136276099f98SAdrian Hunter 136376099f98SAdrian Hunter def AddSample(self, data): 136476099f98SAdrian Hunter child = BranchLevelOneItem(self.glb, self.populated, data, self.root) 136576099f98SAdrian Hunter self.root.child_items.append(child) 136676099f98SAdrian Hunter self.populated += 1 136776099f98SAdrian Hunter 136876099f98SAdrian Hunter def Update(self, fetched): 136976099f98SAdrian Hunter if not fetched: 137076099f98SAdrian Hunter self.more = False 137176099f98SAdrian Hunter self.progress.emit(0) 137276099f98SAdrian Hunter child_count = self.root.child_count 137376099f98SAdrian Hunter count = self.populated - child_count 137476099f98SAdrian Hunter if count > 0: 137576099f98SAdrian Hunter parent = QModelIndex() 137676099f98SAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 137776099f98SAdrian Hunter self.insertRows(child_count, count, parent) 137876099f98SAdrian Hunter self.root.child_count += count 137976099f98SAdrian Hunter self.endInsertRows() 138076099f98SAdrian Hunter self.progress.emit(self.root.child_count) 138176099f98SAdrian Hunter 138276099f98SAdrian Hunter def FetchMoreRecords(self, count): 138376099f98SAdrian Hunter current = self.root.child_count 138476099f98SAdrian Hunter if self.more: 138576099f98SAdrian Hunter self.fetcher.Fetch(count) 138676099f98SAdrian Hunter else: 138776099f98SAdrian Hunter self.progress.emit(0) 138876099f98SAdrian Hunter return current 138976099f98SAdrian Hunter 139076099f98SAdrian Hunter def HasMoreRecords(self): 139176099f98SAdrian Hunter return self.more 139276099f98SAdrian Hunter 139376099f98SAdrian Hunter# Branch window 139476099f98SAdrian Hunter 139576099f98SAdrian Hunterclass BranchWindow(QMdiSubWindow): 139676099f98SAdrian Hunter 139776099f98SAdrian Hunter def __init__(self, glb, event_id, name, where_clause, parent=None): 139876099f98SAdrian Hunter super(BranchWindow, self).__init__(parent) 139976099f98SAdrian Hunter 140076099f98SAdrian Hunter model_name = "Branch Events " + str(event_id) 140176099f98SAdrian Hunter if len(where_clause): 140276099f98SAdrian Hunter model_name = where_clause + " " + model_name 140376099f98SAdrian Hunter 140476099f98SAdrian Hunter self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) 140576099f98SAdrian Hunter 140676099f98SAdrian Hunter self.view = QTreeView() 140776099f98SAdrian Hunter self.view.setUniformRowHeights(True) 140876099f98SAdrian Hunter self.view.setModel(self.model) 140976099f98SAdrian Hunter 141076099f98SAdrian Hunter self.ResizeColumnsToContents() 141176099f98SAdrian Hunter 141276099f98SAdrian Hunter self.find_bar = FindBar(self, self, True) 141376099f98SAdrian Hunter 141476099f98SAdrian Hunter self.finder = ChildDataItemFinder(self.model.root) 141576099f98SAdrian Hunter 141676099f98SAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.model, self) 141776099f98SAdrian Hunter 141876099f98SAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 141976099f98SAdrian Hunter 142076099f98SAdrian Hunter self.setWidget(self.vbox.Widget()) 142176099f98SAdrian Hunter 142276099f98SAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") 142376099f98SAdrian Hunter 142476099f98SAdrian Hunter def ResizeColumnToContents(self, column, n): 142576099f98SAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 142676099f98SAdrian Hunter # so implement a crude alternative 142776099f98SAdrian Hunter mm = "MM" if column else "MMMM" 142876099f98SAdrian Hunter font = self.view.font() 142976099f98SAdrian Hunter metrics = QFontMetrics(font) 143076099f98SAdrian Hunter max = 0 143176099f98SAdrian Hunter for row in xrange(n): 143276099f98SAdrian Hunter val = self.model.root.child_items[row].data[column] 143376099f98SAdrian Hunter len = metrics.width(str(val) + mm) 143476099f98SAdrian Hunter max = len if len > max else max 143576099f98SAdrian Hunter val = self.model.columnHeader(column) 143676099f98SAdrian Hunter len = metrics.width(str(val) + mm) 143776099f98SAdrian Hunter max = len if len > max else max 143876099f98SAdrian Hunter self.view.setColumnWidth(column, max) 143976099f98SAdrian Hunter 144076099f98SAdrian Hunter def ResizeColumnsToContents(self): 144176099f98SAdrian Hunter n = min(self.model.root.child_count, 100) 144276099f98SAdrian Hunter if n < 1: 144376099f98SAdrian Hunter # No data yet, so connect a signal to notify when there is 144476099f98SAdrian Hunter self.model.rowsInserted.connect(self.UpdateColumnWidths) 144576099f98SAdrian Hunter return 144676099f98SAdrian Hunter columns = self.model.columnCount() 144776099f98SAdrian Hunter for i in xrange(columns): 144876099f98SAdrian Hunter self.ResizeColumnToContents(i, n) 144976099f98SAdrian Hunter 145076099f98SAdrian Hunter def UpdateColumnWidths(self, *x): 145176099f98SAdrian Hunter # This only needs to be done once, so disconnect the signal now 145276099f98SAdrian Hunter self.model.rowsInserted.disconnect(self.UpdateColumnWidths) 145376099f98SAdrian Hunter self.ResizeColumnsToContents() 145476099f98SAdrian Hunter 145576099f98SAdrian Hunter def Find(self, value, direction, pattern, context): 145676099f98SAdrian Hunter self.view.setFocus() 145776099f98SAdrian Hunter self.find_bar.Busy() 145876099f98SAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 145976099f98SAdrian Hunter 146076099f98SAdrian Hunter def FindDone(self, row): 146176099f98SAdrian Hunter self.find_bar.Idle() 146276099f98SAdrian Hunter if row >= 0: 146376099f98SAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 146476099f98SAdrian Hunter else: 146576099f98SAdrian Hunter self.find_bar.NotFound() 146676099f98SAdrian Hunter 146776099f98SAdrian Hunter# Event list 146876099f98SAdrian Hunter 146976099f98SAdrian Hunterdef GetEventList(db): 147076099f98SAdrian Hunter events = [] 147176099f98SAdrian Hunter query = QSqlQuery(db) 147276099f98SAdrian Hunter QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") 147376099f98SAdrian Hunter while query.next(): 147476099f98SAdrian Hunter events.append(query.value(0)) 147576099f98SAdrian Hunter return events 147676099f98SAdrian Hunter 14778392b74bSAdrian Hunter# SQL data preparation 14788392b74bSAdrian Hunter 14798392b74bSAdrian Hunterdef SQLTableDataPrep(query, count): 14808392b74bSAdrian Hunter data = [] 14818392b74bSAdrian Hunter for i in xrange(count): 14828392b74bSAdrian Hunter data.append(query.value(i)) 14838392b74bSAdrian Hunter return data 14848392b74bSAdrian Hunter 14858392b74bSAdrian Hunter# SQL table data model item 14868392b74bSAdrian Hunter 14878392b74bSAdrian Hunterclass SQLTableItem(): 14888392b74bSAdrian Hunter 14898392b74bSAdrian Hunter def __init__(self, row, data): 14908392b74bSAdrian Hunter self.row = row 14918392b74bSAdrian Hunter self.data = data 14928392b74bSAdrian Hunter 14938392b74bSAdrian Hunter def getData(self, column): 14948392b74bSAdrian Hunter return self.data[column] 14958392b74bSAdrian Hunter 14968392b74bSAdrian Hunter# SQL table data model 14978392b74bSAdrian Hunter 14988392b74bSAdrian Hunterclass SQLTableModel(TableModel): 14998392b74bSAdrian Hunter 15008392b74bSAdrian Hunter progress = Signal(object) 15018392b74bSAdrian Hunter 15028392b74bSAdrian Hunter def __init__(self, glb, sql, column_count, parent=None): 15038392b74bSAdrian Hunter super(SQLTableModel, self).__init__(parent) 15048392b74bSAdrian Hunter self.glb = glb 15058392b74bSAdrian Hunter self.more = True 15068392b74bSAdrian Hunter self.populated = 0 15078392b74bSAdrian Hunter self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample) 15088392b74bSAdrian Hunter self.fetcher.done.connect(self.Update) 15098392b74bSAdrian Hunter self.fetcher.Fetch(glb_chunk_sz) 15108392b74bSAdrian Hunter 15118392b74bSAdrian Hunter def DisplayData(self, item, index): 15128392b74bSAdrian Hunter self.FetchIfNeeded(item.row) 15138392b74bSAdrian Hunter return item.getData(index.column()) 15148392b74bSAdrian Hunter 15158392b74bSAdrian Hunter def AddSample(self, data): 15168392b74bSAdrian Hunter child = SQLTableItem(self.populated, data) 15178392b74bSAdrian Hunter self.child_items.append(child) 15188392b74bSAdrian Hunter self.populated += 1 15198392b74bSAdrian Hunter 15208392b74bSAdrian Hunter def Update(self, fetched): 15218392b74bSAdrian Hunter if not fetched: 15228392b74bSAdrian Hunter self.more = False 15238392b74bSAdrian Hunter self.progress.emit(0) 15248392b74bSAdrian Hunter child_count = self.child_count 15258392b74bSAdrian Hunter count = self.populated - child_count 15268392b74bSAdrian Hunter if count > 0: 15278392b74bSAdrian Hunter parent = QModelIndex() 15288392b74bSAdrian Hunter self.beginInsertRows(parent, child_count, child_count + count - 1) 15298392b74bSAdrian Hunter self.insertRows(child_count, count, parent) 15308392b74bSAdrian Hunter self.child_count += count 15318392b74bSAdrian Hunter self.endInsertRows() 15328392b74bSAdrian Hunter self.progress.emit(self.child_count) 15338392b74bSAdrian Hunter 15348392b74bSAdrian Hunter def FetchMoreRecords(self, count): 15358392b74bSAdrian Hunter current = self.child_count 15368392b74bSAdrian Hunter if self.more: 15378392b74bSAdrian Hunter self.fetcher.Fetch(count) 15388392b74bSAdrian Hunter else: 15398392b74bSAdrian Hunter self.progress.emit(0) 15408392b74bSAdrian Hunter return current 15418392b74bSAdrian Hunter 15428392b74bSAdrian Hunter def HasMoreRecords(self): 15438392b74bSAdrian Hunter return self.more 15448392b74bSAdrian Hunter 15458392b74bSAdrian Hunter# SQL automatic table data model 15468392b74bSAdrian Hunter 15478392b74bSAdrian Hunterclass SQLAutoTableModel(SQLTableModel): 15488392b74bSAdrian Hunter 15498392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 15508392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE id > $$last_id$$ ORDER BY id LIMIT " + str(glb_chunk_sz) 15518392b74bSAdrian Hunter if table_name == "comm_threads_view": 15528392b74bSAdrian Hunter # For now, comm_threads_view has no id column 15538392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 15548392b74bSAdrian Hunter self.column_headers = [] 15558392b74bSAdrian Hunter query = QSqlQuery(glb.db) 15568392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 15578392b74bSAdrian Hunter QueryExec(query, "PRAGMA table_info(" + table_name + ")") 15588392b74bSAdrian Hunter while query.next(): 15598392b74bSAdrian Hunter self.column_headers.append(query.value(1)) 15608392b74bSAdrian Hunter if table_name == "sqlite_master": 15618392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 15628392b74bSAdrian Hunter else: 15638392b74bSAdrian Hunter if table_name[:19] == "information_schema.": 15648392b74bSAdrian Hunter sql = "SELECT * FROM " + table_name 15658392b74bSAdrian Hunter select_table_name = table_name[19:] 15668392b74bSAdrian Hunter schema = "information_schema" 15678392b74bSAdrian Hunter else: 15688392b74bSAdrian Hunter select_table_name = table_name 15698392b74bSAdrian Hunter schema = "public" 15708392b74bSAdrian Hunter QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 15718392b74bSAdrian Hunter while query.next(): 15728392b74bSAdrian Hunter self.column_headers.append(query.value(0)) 15738392b74bSAdrian Hunter super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent) 15748392b74bSAdrian Hunter 15758392b74bSAdrian Hunter def columnCount(self, parent=None): 15768392b74bSAdrian Hunter return len(self.column_headers) 15778392b74bSAdrian Hunter 15788392b74bSAdrian Hunter def columnHeader(self, column): 15798392b74bSAdrian Hunter return self.column_headers[column] 15808392b74bSAdrian Hunter 15818392b74bSAdrian Hunter# Base class for custom ResizeColumnsToContents 15828392b74bSAdrian Hunter 15838392b74bSAdrian Hunterclass ResizeColumnsToContentsBase(QObject): 15848392b74bSAdrian Hunter 15858392b74bSAdrian Hunter def __init__(self, parent=None): 15868392b74bSAdrian Hunter super(ResizeColumnsToContentsBase, self).__init__(parent) 15878392b74bSAdrian Hunter 15888392b74bSAdrian Hunter def ResizeColumnToContents(self, column, n): 15898392b74bSAdrian Hunter # Using the view's resizeColumnToContents() here is extrememly slow 15908392b74bSAdrian Hunter # so implement a crude alternative 15918392b74bSAdrian Hunter font = self.view.font() 15928392b74bSAdrian Hunter metrics = QFontMetrics(font) 15938392b74bSAdrian Hunter max = 0 15948392b74bSAdrian Hunter for row in xrange(n): 15958392b74bSAdrian Hunter val = self.data_model.child_items[row].data[column] 15968392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 15978392b74bSAdrian Hunter max = len if len > max else max 15988392b74bSAdrian Hunter val = self.data_model.columnHeader(column) 15998392b74bSAdrian Hunter len = metrics.width(str(val) + "MM") 16008392b74bSAdrian Hunter max = len if len > max else max 16018392b74bSAdrian Hunter self.view.setColumnWidth(column, max) 16028392b74bSAdrian Hunter 16038392b74bSAdrian Hunter def ResizeColumnsToContents(self): 16048392b74bSAdrian Hunter n = min(self.data_model.child_count, 100) 16058392b74bSAdrian Hunter if n < 1: 16068392b74bSAdrian Hunter # No data yet, so connect a signal to notify when there is 16078392b74bSAdrian Hunter self.data_model.rowsInserted.connect(self.UpdateColumnWidths) 16088392b74bSAdrian Hunter return 16098392b74bSAdrian Hunter columns = self.data_model.columnCount() 16108392b74bSAdrian Hunter for i in xrange(columns): 16118392b74bSAdrian Hunter self.ResizeColumnToContents(i, n) 16128392b74bSAdrian Hunter 16138392b74bSAdrian Hunter def UpdateColumnWidths(self, *x): 16148392b74bSAdrian Hunter # This only needs to be done once, so disconnect the signal now 16158392b74bSAdrian Hunter self.data_model.rowsInserted.disconnect(self.UpdateColumnWidths) 16168392b74bSAdrian Hunter self.ResizeColumnsToContents() 16178392b74bSAdrian Hunter 16188392b74bSAdrian Hunter# Table window 16198392b74bSAdrian Hunter 16208392b74bSAdrian Hunterclass TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 16218392b74bSAdrian Hunter 16228392b74bSAdrian Hunter def __init__(self, glb, table_name, parent=None): 16238392b74bSAdrian Hunter super(TableWindow, self).__init__(parent) 16248392b74bSAdrian Hunter 16258392b74bSAdrian Hunter self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 16268392b74bSAdrian Hunter 16278392b74bSAdrian Hunter self.model = QSortFilterProxyModel() 16288392b74bSAdrian Hunter self.model.setSourceModel(self.data_model) 16298392b74bSAdrian Hunter 16308392b74bSAdrian Hunter self.view = QTableView() 16318392b74bSAdrian Hunter self.view.setModel(self.model) 16328392b74bSAdrian Hunter self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) 16338392b74bSAdrian Hunter self.view.verticalHeader().setVisible(False) 16348392b74bSAdrian Hunter self.view.sortByColumn(-1, Qt.AscendingOrder) 16358392b74bSAdrian Hunter self.view.setSortingEnabled(True) 16368392b74bSAdrian Hunter 16378392b74bSAdrian Hunter self.ResizeColumnsToContents() 16388392b74bSAdrian Hunter 16398392b74bSAdrian Hunter self.find_bar = FindBar(self, self, True) 16408392b74bSAdrian Hunter 16418392b74bSAdrian Hunter self.finder = ChildDataItemFinder(self.data_model) 16428392b74bSAdrian Hunter 16438392b74bSAdrian Hunter self.fetch_bar = FetchMoreRecordsBar(self.data_model, self) 16448392b74bSAdrian Hunter 16458392b74bSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 16468392b74bSAdrian Hunter 16478392b74bSAdrian Hunter self.setWidget(self.vbox.Widget()) 16488392b74bSAdrian Hunter 16498392b74bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, table_name + " Table") 16508392b74bSAdrian Hunter 16518392b74bSAdrian Hunter def Find(self, value, direction, pattern, context): 16528392b74bSAdrian Hunter self.view.setFocus() 16538392b74bSAdrian Hunter self.find_bar.Busy() 16548392b74bSAdrian Hunter self.finder.Find(value, direction, pattern, context, self.FindDone) 16558392b74bSAdrian Hunter 16568392b74bSAdrian Hunter def FindDone(self, row): 16578392b74bSAdrian Hunter self.find_bar.Idle() 16588392b74bSAdrian Hunter if row >= 0: 16598392b74bSAdrian Hunter self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 16608392b74bSAdrian Hunter else: 16618392b74bSAdrian Hunter self.find_bar.NotFound() 16628392b74bSAdrian Hunter 16638392b74bSAdrian Hunter# Table list 16648392b74bSAdrian Hunter 16658392b74bSAdrian Hunterdef GetTableList(glb): 16668392b74bSAdrian Hunter tables = [] 16678392b74bSAdrian Hunter query = QSqlQuery(glb.db) 16688392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 16698392b74bSAdrian Hunter QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name") 16708392b74bSAdrian Hunter else: 16718392b74bSAdrian 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") 16728392b74bSAdrian Hunter while query.next(): 16738392b74bSAdrian Hunter tables.append(query.value(0)) 16748392b74bSAdrian Hunter if glb.dbref.is_sqlite3: 16758392b74bSAdrian Hunter tables.append("sqlite_master") 16768392b74bSAdrian Hunter else: 16778392b74bSAdrian Hunter tables.append("information_schema.tables") 16788392b74bSAdrian Hunter tables.append("information_schema.views") 16798392b74bSAdrian Hunter tables.append("information_schema.columns") 16808392b74bSAdrian Hunter return tables 16818392b74bSAdrian Hunter 16821beb5c7bSAdrian Hunter# Action Definition 16831beb5c7bSAdrian Hunter 16841beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 16851beb5c7bSAdrian Hunter action = QAction(label, parent) 16861beb5c7bSAdrian Hunter if shortcut != None: 16871beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 16881beb5c7bSAdrian Hunter action.setStatusTip(tip) 16891beb5c7bSAdrian Hunter action.triggered.connect(callback) 16901beb5c7bSAdrian Hunter return action 16911beb5c7bSAdrian Hunter 16921beb5c7bSAdrian Hunter# Typical application actions 16931beb5c7bSAdrian Hunter 16941beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 16951beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 16961beb5c7bSAdrian Hunter 16971beb5c7bSAdrian Hunter# Typical MDI actions 16981beb5c7bSAdrian Hunter 16991beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 17001beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 17011beb5c7bSAdrian Hunter 17021beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 17031beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 17041beb5c7bSAdrian Hunter 17051beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 17061beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 17071beb5c7bSAdrian Hunter 17081beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 17091beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 17101beb5c7bSAdrian Hunter 17111beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 17121beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 17131beb5c7bSAdrian Hunter 17141beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 17151beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 17161beb5c7bSAdrian Hunter 17171beb5c7bSAdrian Hunter# Typical MDI window menu 17181beb5c7bSAdrian Hunter 17191beb5c7bSAdrian Hunterclass WindowMenu(): 17201beb5c7bSAdrian Hunter 17211beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 17221beb5c7bSAdrian Hunter self.mdi_area = mdi_area 17231beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 17241beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 17251beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 17261beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 17271beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 17281beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 17291beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 17301beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 17311beb5c7bSAdrian Hunter 17321beb5c7bSAdrian Hunter def Update(self): 17331beb5c7bSAdrian Hunter self.window_menu.clear() 17341beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 17351beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 17361beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 17371beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 17381beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 17391beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 17401beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 17411beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 17421beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 17431beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 17441beb5c7bSAdrian Hunter self.window_menu.addSeparator() 17451beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 17461beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 17471beb5c7bSAdrian Hunter self.window_menu.addSeparator() 17481beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 17491beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 17501beb5c7bSAdrian Hunter if sub_window_count == 0: 17511beb5c7bSAdrian Hunter return 17521beb5c7bSAdrian Hunter self.window_menu.addSeparator() 17531beb5c7bSAdrian Hunter nr = 1 17541beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 17551beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 17561beb5c7bSAdrian Hunter if nr < 10: 17571beb5c7bSAdrian Hunter label = "&" + label 17581beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 17591beb5c7bSAdrian Hunter action.setCheckable(True) 17601beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 17611beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 17621beb5c7bSAdrian Hunter self.window_menu.addAction(action) 17631beb5c7bSAdrian Hunter nr += 1 17641beb5c7bSAdrian Hunter 17651beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 17661beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 17671beb5c7bSAdrian Hunter 176882f68e28SAdrian Hunter# Font resize 176982f68e28SAdrian Hunter 177082f68e28SAdrian Hunterdef ResizeFont(widget, diff): 177182f68e28SAdrian Hunter font = widget.font() 177282f68e28SAdrian Hunter sz = font.pointSize() 177382f68e28SAdrian Hunter font.setPointSize(sz + diff) 177482f68e28SAdrian Hunter widget.setFont(font) 177582f68e28SAdrian Hunter 177682f68e28SAdrian Hunterdef ShrinkFont(widget): 177782f68e28SAdrian Hunter ResizeFont(widget, -1) 177882f68e28SAdrian Hunter 177982f68e28SAdrian Hunterdef EnlargeFont(widget): 178082f68e28SAdrian Hunter ResizeFont(widget, 1) 178182f68e28SAdrian Hunter 17821beb5c7bSAdrian Hunter# Unique name for sub-windows 17831beb5c7bSAdrian Hunter 17841beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 17851beb5c7bSAdrian Hunter if nr > 1: 17861beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 17871beb5c7bSAdrian Hunter return name 17881beb5c7bSAdrian Hunter 17891beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 17901beb5c7bSAdrian Hunter nr = 1 17911beb5c7bSAdrian Hunter while True: 17921beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 17931beb5c7bSAdrian Hunter ok = True 17941beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 17951beb5c7bSAdrian Hunter if sub_window.name == unique_name: 17961beb5c7bSAdrian Hunter ok = False 17971beb5c7bSAdrian Hunter break 17981beb5c7bSAdrian Hunter if ok: 17991beb5c7bSAdrian Hunter return unique_name 18001beb5c7bSAdrian Hunter nr += 1 18011beb5c7bSAdrian Hunter 18021beb5c7bSAdrian Hunter# Add a sub-window 18031beb5c7bSAdrian Hunter 18041beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 18051beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 18061beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 18071beb5c7bSAdrian Hunter sub_window.resize(800, 600) 18081beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 18091beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 18101beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 18111beb5c7bSAdrian Hunter sub_window.name = unique_name 18121beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 18131beb5c7bSAdrian Hunter sub_window.show() 18141beb5c7bSAdrian Hunter 1815031c2a00SAdrian Hunter# Main window 1816031c2a00SAdrian Hunter 1817031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 1818031c2a00SAdrian Hunter 1819031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 1820031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 1821031c2a00SAdrian Hunter 1822031c2a00SAdrian Hunter self.glb = glb 1823031c2a00SAdrian Hunter 18241beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 1825031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 1826031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 1827031c2a00SAdrian Hunter 18281beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 18291beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 18301beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 1831031c2a00SAdrian Hunter 18321beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 1833031c2a00SAdrian Hunter 18341beb5c7bSAdrian Hunter menu = self.menuBar() 1835031c2a00SAdrian Hunter 18361beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 18371beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 18381beb5c7bSAdrian Hunter 1839ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 1840ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 18418392b74bSAdrian Hunter edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) 184282f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 184382f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 1844ebd70c7dSAdrian Hunter 18451beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 18461beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 18471beb5c7bSAdrian Hunter 184876099f98SAdrian Hunter self.EventMenu(GetEventList(glb.db), reports_menu) 184976099f98SAdrian Hunter 18508392b74bSAdrian Hunter self.TableMenu(GetTableList(glb), menu) 18518392b74bSAdrian Hunter 18521beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 18531beb5c7bSAdrian Hunter 1854ebd70c7dSAdrian Hunter def Find(self): 1855ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 1856ebd70c7dSAdrian Hunter if win: 1857ebd70c7dSAdrian Hunter try: 1858ebd70c7dSAdrian Hunter win.find_bar.Activate() 1859ebd70c7dSAdrian Hunter except: 1860ebd70c7dSAdrian Hunter pass 1861ebd70c7dSAdrian Hunter 18628392b74bSAdrian Hunter def FetchMoreRecords(self): 18638392b74bSAdrian Hunter win = self.mdi_area.activeSubWindow() 18648392b74bSAdrian Hunter if win: 18658392b74bSAdrian Hunter try: 18668392b74bSAdrian Hunter win.fetch_bar.Activate() 18678392b74bSAdrian Hunter except: 18688392b74bSAdrian Hunter pass 18698392b74bSAdrian Hunter 187082f68e28SAdrian Hunter def ShrinkFont(self): 187182f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 187282f68e28SAdrian Hunter ShrinkFont(win.view) 187382f68e28SAdrian Hunter 187482f68e28SAdrian Hunter def EnlargeFont(self): 187582f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 187682f68e28SAdrian Hunter EnlargeFont(win.view) 187782f68e28SAdrian Hunter 187876099f98SAdrian Hunter def EventMenu(self, events, reports_menu): 187976099f98SAdrian Hunter branches_events = 0 188076099f98SAdrian Hunter for event in events: 188176099f98SAdrian Hunter event = event.split(":")[0] 188276099f98SAdrian Hunter if event == "branches": 188376099f98SAdrian Hunter branches_events += 1 188476099f98SAdrian Hunter dbid = 0 188576099f98SAdrian Hunter for event in events: 188676099f98SAdrian Hunter dbid += 1 188776099f98SAdrian Hunter event = event.split(":")[0] 188876099f98SAdrian Hunter if event == "branches": 188976099f98SAdrian Hunter label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 189076099f98SAdrian Hunter reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 189176099f98SAdrian Hunter 18928392b74bSAdrian Hunter def TableMenu(self, tables, menu): 18938392b74bSAdrian Hunter table_menu = menu.addMenu("&Tables") 18948392b74bSAdrian Hunter for table in tables: 18958392b74bSAdrian Hunter table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) 18968392b74bSAdrian Hunter 18971beb5c7bSAdrian Hunter def NewCallGraph(self): 18981beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 1899031c2a00SAdrian Hunter 190076099f98SAdrian Hunter def NewBranchView(self, event_id): 190176099f98SAdrian Hunter BranchWindow(self.glb, event_id, "", "", self) 190276099f98SAdrian Hunter 19038392b74bSAdrian Hunter def NewTableView(self, table_name): 19048392b74bSAdrian Hunter TableWindow(self.glb, table_name, self) 19058392b74bSAdrian Hunter 190676099f98SAdrian Hunter# XED Disassembler 190776099f98SAdrian Hunter 190876099f98SAdrian Hunterclass xed_state_t(Structure): 190976099f98SAdrian Hunter 191076099f98SAdrian Hunter _fields_ = [ 191176099f98SAdrian Hunter ("mode", c_int), 191276099f98SAdrian Hunter ("width", c_int) 191376099f98SAdrian Hunter ] 191476099f98SAdrian Hunter 191576099f98SAdrian Hunterclass XEDInstruction(): 191676099f98SAdrian Hunter 191776099f98SAdrian Hunter def __init__(self, libxed): 191876099f98SAdrian Hunter # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion 191976099f98SAdrian Hunter xedd_t = c_byte * 512 192076099f98SAdrian Hunter self.xedd = xedd_t() 192176099f98SAdrian Hunter self.xedp = addressof(self.xedd) 192276099f98SAdrian Hunter libxed.xed_decoded_inst_zero(self.xedp) 192376099f98SAdrian Hunter self.state = xed_state_t() 192476099f98SAdrian Hunter self.statep = addressof(self.state) 192576099f98SAdrian Hunter # Buffer for disassembled instruction text 192676099f98SAdrian Hunter self.buffer = create_string_buffer(256) 192776099f98SAdrian Hunter self.bufferp = addressof(self.buffer) 192876099f98SAdrian Hunter 192976099f98SAdrian Hunterclass LibXED(): 193076099f98SAdrian Hunter 193176099f98SAdrian Hunter def __init__(self): 1932*5ed4419dSAdrian Hunter try: 193376099f98SAdrian Hunter self.libxed = CDLL("libxed.so") 1934*5ed4419dSAdrian Hunter except: 1935*5ed4419dSAdrian Hunter self.libxed = None 1936*5ed4419dSAdrian Hunter if not self.libxed: 1937*5ed4419dSAdrian Hunter self.libxed = CDLL("/usr/local/lib/libxed.so") 193876099f98SAdrian Hunter 193976099f98SAdrian Hunter self.xed_tables_init = self.libxed.xed_tables_init 194076099f98SAdrian Hunter self.xed_tables_init.restype = None 194176099f98SAdrian Hunter self.xed_tables_init.argtypes = [] 194276099f98SAdrian Hunter 194376099f98SAdrian Hunter self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero 194476099f98SAdrian Hunter self.xed_decoded_inst_zero.restype = None 194576099f98SAdrian Hunter self.xed_decoded_inst_zero.argtypes = [ c_void_p ] 194676099f98SAdrian Hunter 194776099f98SAdrian Hunter self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode 194876099f98SAdrian Hunter self.xed_operand_values_set_mode.restype = None 194976099f98SAdrian Hunter self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] 195076099f98SAdrian Hunter 195176099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode 195276099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.restype = None 195376099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] 195476099f98SAdrian Hunter 195576099f98SAdrian Hunter self.xed_decode = self.libxed.xed_decode 195676099f98SAdrian Hunter self.xed_decode.restype = c_int 195776099f98SAdrian Hunter self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] 195876099f98SAdrian Hunter 195976099f98SAdrian Hunter self.xed_format_context = self.libxed.xed_format_context 196076099f98SAdrian Hunter self.xed_format_context.restype = c_uint 196176099f98SAdrian Hunter self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] 196276099f98SAdrian Hunter 196376099f98SAdrian Hunter self.xed_tables_init() 196476099f98SAdrian Hunter 196576099f98SAdrian Hunter def Instruction(self): 196676099f98SAdrian Hunter return XEDInstruction(self) 196776099f98SAdrian Hunter 196876099f98SAdrian Hunter def SetMode(self, inst, mode): 196976099f98SAdrian Hunter if mode: 197076099f98SAdrian Hunter inst.state.mode = 4 # 32-bit 197176099f98SAdrian Hunter inst.state.width = 4 # 4 bytes 197276099f98SAdrian Hunter else: 197376099f98SAdrian Hunter inst.state.mode = 1 # 64-bit 197476099f98SAdrian Hunter inst.state.width = 8 # 8 bytes 197576099f98SAdrian Hunter self.xed_operand_values_set_mode(inst.xedp, inst.statep) 197676099f98SAdrian Hunter 197776099f98SAdrian Hunter def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): 197876099f98SAdrian Hunter self.xed_decoded_inst_zero_keep_mode(inst.xedp) 197976099f98SAdrian Hunter err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) 198076099f98SAdrian Hunter if err: 198176099f98SAdrian Hunter return 0, "" 198276099f98SAdrian Hunter # Use AT&T mode (2), alternative is Intel (3) 198376099f98SAdrian Hunter ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) 198476099f98SAdrian Hunter if not ok: 198576099f98SAdrian Hunter return 0, "" 198676099f98SAdrian Hunter # Return instruction length and the disassembled instruction text 198776099f98SAdrian Hunter # For now, assume the length is in byte 166 198876099f98SAdrian Hunter return inst.xedd[166], inst.buffer.value 198976099f98SAdrian Hunter 199076099f98SAdrian Hunterdef TryOpen(file_name): 199176099f98SAdrian Hunter try: 199276099f98SAdrian Hunter return open(file_name, "rb") 199376099f98SAdrian Hunter except: 199476099f98SAdrian Hunter return None 199576099f98SAdrian Hunter 199676099f98SAdrian Hunterdef Is64Bit(f): 199776099f98SAdrian Hunter result = sizeof(c_void_p) 199876099f98SAdrian Hunter # ELF support only 199976099f98SAdrian Hunter pos = f.tell() 200076099f98SAdrian Hunter f.seek(0) 200176099f98SAdrian Hunter header = f.read(7) 200276099f98SAdrian Hunter f.seek(pos) 200376099f98SAdrian Hunter magic = header[0:4] 200476099f98SAdrian Hunter eclass = ord(header[4]) 200576099f98SAdrian Hunter encoding = ord(header[5]) 200676099f98SAdrian Hunter version = ord(header[6]) 200776099f98SAdrian Hunter if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: 200876099f98SAdrian Hunter result = True if eclass == 2 else False 200976099f98SAdrian Hunter return result 201076099f98SAdrian Hunter 2011031c2a00SAdrian Hunter# Global data 2012031c2a00SAdrian Hunter 2013031c2a00SAdrian Hunterclass Glb(): 2014031c2a00SAdrian Hunter 2015031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 2016031c2a00SAdrian Hunter self.dbref = dbref 2017031c2a00SAdrian Hunter self.db = db 2018031c2a00SAdrian Hunter self.dbname = dbname 201976099f98SAdrian Hunter self.home_dir = os.path.expanduser("~") 202076099f98SAdrian Hunter self.buildid_dir = os.getenv("PERF_BUILDID_DIR") 202176099f98SAdrian Hunter if self.buildid_dir: 202276099f98SAdrian Hunter self.buildid_dir += "/.build-id/" 202376099f98SAdrian Hunter else: 202476099f98SAdrian Hunter self.buildid_dir = self.home_dir + "/.debug/.build-id/" 2025031c2a00SAdrian Hunter self.app = None 2026031c2a00SAdrian Hunter self.mainwindow = None 20278392b74bSAdrian Hunter self.instances_to_shutdown_on_exit = weakref.WeakSet() 202876099f98SAdrian Hunter try: 202976099f98SAdrian Hunter self.disassembler = LibXED() 203076099f98SAdrian Hunter self.have_disassembler = True 203176099f98SAdrian Hunter except: 203276099f98SAdrian Hunter self.have_disassembler = False 203376099f98SAdrian Hunter 203476099f98SAdrian Hunter def FileFromBuildId(self, build_id): 203576099f98SAdrian Hunter file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" 203676099f98SAdrian Hunter return TryOpen(file_name) 203776099f98SAdrian Hunter 203876099f98SAdrian Hunter def FileFromNamesAndBuildId(self, short_name, long_name, build_id): 203976099f98SAdrian Hunter # Assume current machine i.e. no support for virtualization 204076099f98SAdrian Hunter if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": 204176099f98SAdrian Hunter file_name = os.getenv("PERF_KCORE") 204276099f98SAdrian Hunter f = TryOpen(file_name) if file_name else None 204376099f98SAdrian Hunter if f: 204476099f98SAdrian Hunter return f 204576099f98SAdrian Hunter # For now, no special handling if long_name is /proc/kcore 204676099f98SAdrian Hunter f = TryOpen(long_name) 204776099f98SAdrian Hunter if f: 204876099f98SAdrian Hunter return f 204976099f98SAdrian Hunter f = self.FileFromBuildId(build_id) 205076099f98SAdrian Hunter if f: 205176099f98SAdrian Hunter return f 205276099f98SAdrian Hunter return None 20538392b74bSAdrian Hunter 20548392b74bSAdrian Hunter def AddInstanceToShutdownOnExit(self, instance): 20558392b74bSAdrian Hunter self.instances_to_shutdown_on_exit.add(instance) 20568392b74bSAdrian Hunter 20578392b74bSAdrian Hunter # Shutdown any background processes or threads 20588392b74bSAdrian Hunter def ShutdownInstances(self): 20598392b74bSAdrian Hunter for x in self.instances_to_shutdown_on_exit: 20608392b74bSAdrian Hunter try: 20618392b74bSAdrian Hunter x.Shutdown() 20628392b74bSAdrian Hunter except: 20638392b74bSAdrian Hunter pass 2064031c2a00SAdrian Hunter 2065031c2a00SAdrian Hunter# Database reference 2066031c2a00SAdrian Hunter 2067031c2a00SAdrian Hunterclass DBRef(): 2068031c2a00SAdrian Hunter 2069031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 2070031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 2071031c2a00SAdrian Hunter self.dbname = dbname 2072031c2a00SAdrian Hunter 2073031c2a00SAdrian Hunter def Open(self, connection_name): 2074031c2a00SAdrian Hunter dbname = self.dbname 2075031c2a00SAdrian Hunter if self.is_sqlite3: 2076031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 2077031c2a00SAdrian Hunter else: 2078031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 2079031c2a00SAdrian Hunter opts = dbname.split() 2080031c2a00SAdrian Hunter for opt in opts: 2081031c2a00SAdrian Hunter if "=" in opt: 2082031c2a00SAdrian Hunter opt = opt.split("=") 2083031c2a00SAdrian Hunter if opt[0] == "hostname": 2084031c2a00SAdrian Hunter db.setHostName(opt[1]) 2085031c2a00SAdrian Hunter elif opt[0] == "port": 2086031c2a00SAdrian Hunter db.setPort(int(opt[1])) 2087031c2a00SAdrian Hunter elif opt[0] == "username": 2088031c2a00SAdrian Hunter db.setUserName(opt[1]) 2089031c2a00SAdrian Hunter elif opt[0] == "password": 2090031c2a00SAdrian Hunter db.setPassword(opt[1]) 2091031c2a00SAdrian Hunter elif opt[0] == "dbname": 2092031c2a00SAdrian Hunter dbname = opt[1] 2093031c2a00SAdrian Hunter else: 2094031c2a00SAdrian Hunter dbname = opt 2095031c2a00SAdrian Hunter 2096031c2a00SAdrian Hunter db.setDatabaseName(dbname) 2097031c2a00SAdrian Hunter if not db.open(): 2098031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 2099031c2a00SAdrian Hunter return db, dbname 2100031c2a00SAdrian Hunter 2101031c2a00SAdrian Hunter# Main 2102031c2a00SAdrian Hunter 2103031c2a00SAdrian Hunterdef Main(): 2104031c2a00SAdrian Hunter if (len(sys.argv) < 2): 2105031c2a00SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 2106031c2a00SAdrian Hunter raise Exception("Too few arguments") 2107031c2a00SAdrian Hunter 2108031c2a00SAdrian Hunter dbname = sys.argv[1] 2109031c2a00SAdrian Hunter 2110031c2a00SAdrian Hunter is_sqlite3 = False 2111031c2a00SAdrian Hunter try: 2112031c2a00SAdrian Hunter f = open(dbname) 2113031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 2114031c2a00SAdrian Hunter is_sqlite3 = True 2115031c2a00SAdrian Hunter f.close() 2116031c2a00SAdrian Hunter except: 2117031c2a00SAdrian Hunter pass 2118031c2a00SAdrian Hunter 2119031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 2120031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 2121031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 2122031c2a00SAdrian Hunter app = QApplication(sys.argv) 2123031c2a00SAdrian Hunter glb.app = app 2124031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 2125031c2a00SAdrian Hunter glb.mainwindow = mainwindow 2126031c2a00SAdrian Hunter mainwindow.show() 2127031c2a00SAdrian Hunter err = app.exec_() 21288392b74bSAdrian Hunter glb.ShutdownInstances() 2129031c2a00SAdrian Hunter db.close() 2130031c2a00SAdrian Hunter sys.exit(err) 2131031c2a00SAdrian Hunter 2132031c2a00SAdrian Hunterif __name__ == "__main__": 2133031c2a00SAdrian Hunter Main() 2134