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 49031c2a00SAdrian Hunterimport sys 501beb5c7bSAdrian Hunterimport weakref 511beb5c7bSAdrian Hunterimport threading 52*ebd70c7dSAdrian Hunterimport string 53031c2a00SAdrian Hunterfrom PySide.QtCore import * 54031c2a00SAdrian Hunterfrom PySide.QtGui import * 55031c2a00SAdrian Hunterfrom PySide.QtSql import * 56031c2a00SAdrian Hunterfrom decimal import * 57031c2a00SAdrian Hunter 58031c2a00SAdrian Hunter# Data formatting helpers 59031c2a00SAdrian Hunter 60031c2a00SAdrian Hunterdef dsoname(name): 61031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 62031c2a00SAdrian Hunter return "[kernel]" 63031c2a00SAdrian Hunter return name 64031c2a00SAdrian Hunter 65031c2a00SAdrian Hunter# Percent to one decimal place 66031c2a00SAdrian Hunter 67031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 68031c2a00SAdrian Hunter if not d: 69031c2a00SAdrian Hunter return "0.0" 70031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 71031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 72031c2a00SAdrian Hunter 73031c2a00SAdrian Hunter# Helper for queries that must not fail 74031c2a00SAdrian Hunter 75031c2a00SAdrian Hunterdef QueryExec(query, stmt): 76031c2a00SAdrian Hunter ret = query.exec_(stmt) 77031c2a00SAdrian Hunter if not ret: 78031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 79031c2a00SAdrian Hunter 80*ebd70c7dSAdrian Hunter# Background thread 81*ebd70c7dSAdrian Hunter 82*ebd70c7dSAdrian Hunterclass Thread(QThread): 83*ebd70c7dSAdrian Hunter 84*ebd70c7dSAdrian Hunter done = Signal(object) 85*ebd70c7dSAdrian Hunter 86*ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 87*ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 88*ebd70c7dSAdrian Hunter self.task = task 89*ebd70c7dSAdrian Hunter self.param = param 90*ebd70c7dSAdrian Hunter 91*ebd70c7dSAdrian Hunter def run(self): 92*ebd70c7dSAdrian Hunter while True: 93*ebd70c7dSAdrian Hunter if self.param is None: 94*ebd70c7dSAdrian Hunter done, result = self.task() 95*ebd70c7dSAdrian Hunter else: 96*ebd70c7dSAdrian Hunter done, result = self.task(self.param) 97*ebd70c7dSAdrian Hunter self.done.emit(result) 98*ebd70c7dSAdrian Hunter if done: 99*ebd70c7dSAdrian Hunter break 100*ebd70c7dSAdrian Hunter 101031c2a00SAdrian Hunter# Tree data model 102031c2a00SAdrian Hunter 103031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 104031c2a00SAdrian Hunter 105031c2a00SAdrian Hunter def __init__(self, root, parent=None): 106031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 107031c2a00SAdrian Hunter self.root = root 108031c2a00SAdrian Hunter self.last_row_read = 0 109031c2a00SAdrian Hunter 110031c2a00SAdrian Hunter def Item(self, parent): 111031c2a00SAdrian Hunter if parent.isValid(): 112031c2a00SAdrian Hunter return parent.internalPointer() 113031c2a00SAdrian Hunter else: 114031c2a00SAdrian Hunter return self.root 115031c2a00SAdrian Hunter 116031c2a00SAdrian Hunter def rowCount(self, parent): 117031c2a00SAdrian Hunter result = self.Item(parent).childCount() 118031c2a00SAdrian Hunter if result < 0: 119031c2a00SAdrian Hunter result = 0 120031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 121031c2a00SAdrian Hunter return result 122031c2a00SAdrian Hunter 123031c2a00SAdrian Hunter def hasChildren(self, parent): 124031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 125031c2a00SAdrian Hunter 126031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 127031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 128031c2a00SAdrian Hunter return self.columnAlignment(section) 129031c2a00SAdrian Hunter if role != Qt.DisplayRole: 130031c2a00SAdrian Hunter return None 131031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 132031c2a00SAdrian Hunter return None 133031c2a00SAdrian Hunter return self.columnHeader(section) 134031c2a00SAdrian Hunter 135031c2a00SAdrian Hunter def parent(self, child): 136031c2a00SAdrian Hunter child_item = child.internalPointer() 137031c2a00SAdrian Hunter if child_item is self.root: 138031c2a00SAdrian Hunter return QModelIndex() 139031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 140031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 141031c2a00SAdrian Hunter 142031c2a00SAdrian Hunter def index(self, row, column, parent): 143031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 144031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 145031c2a00SAdrian Hunter 146031c2a00SAdrian Hunter def DisplayData(self, item, index): 147031c2a00SAdrian Hunter return item.getData(index.column()) 148031c2a00SAdrian Hunter 149031c2a00SAdrian Hunter def columnAlignment(self, column): 150031c2a00SAdrian Hunter return Qt.AlignLeft 151031c2a00SAdrian Hunter 152031c2a00SAdrian Hunter def columnFont(self, column): 153031c2a00SAdrian Hunter return None 154031c2a00SAdrian Hunter 155031c2a00SAdrian Hunter def data(self, index, role): 156031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 157031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 158031c2a00SAdrian Hunter if role == Qt.FontRole: 159031c2a00SAdrian Hunter return self.columnFont(index.column()) 160031c2a00SAdrian Hunter if role != Qt.DisplayRole: 161031c2a00SAdrian Hunter return None 162031c2a00SAdrian Hunter item = index.internalPointer() 163031c2a00SAdrian Hunter return self.DisplayData(item, index) 164031c2a00SAdrian Hunter 1651beb5c7bSAdrian Hunter# Model cache 1661beb5c7bSAdrian Hunter 1671beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 1681beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 1691beb5c7bSAdrian Hunter 1701beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 1711beb5c7bSAdrian Hunter model_cache_lock.acquire() 1721beb5c7bSAdrian Hunter try: 1731beb5c7bSAdrian Hunter model = model_cache[model_name] 1741beb5c7bSAdrian Hunter except: 1751beb5c7bSAdrian Hunter model = None 1761beb5c7bSAdrian Hunter if model is None: 1771beb5c7bSAdrian Hunter model = create_fn() 1781beb5c7bSAdrian Hunter model_cache[model_name] = model 1791beb5c7bSAdrian Hunter model_cache_lock.release() 1801beb5c7bSAdrian Hunter return model 1811beb5c7bSAdrian Hunter 182*ebd70c7dSAdrian Hunter# Find bar 183*ebd70c7dSAdrian Hunter 184*ebd70c7dSAdrian Hunterclass FindBar(): 185*ebd70c7dSAdrian Hunter 186*ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 187*ebd70c7dSAdrian Hunter self.finder = finder 188*ebd70c7dSAdrian Hunter self.context = [] 189*ebd70c7dSAdrian Hunter self.last_value = None 190*ebd70c7dSAdrian Hunter self.last_pattern = None 191*ebd70c7dSAdrian Hunter 192*ebd70c7dSAdrian Hunter label = QLabel("Find:") 193*ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 194*ebd70c7dSAdrian Hunter 195*ebd70c7dSAdrian Hunter self.textbox = QComboBox() 196*ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 197*ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 198*ebd70c7dSAdrian Hunter 199*ebd70c7dSAdrian Hunter self.progress = QProgressBar() 200*ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 201*ebd70c7dSAdrian Hunter self.progress.hide() 202*ebd70c7dSAdrian Hunter 203*ebd70c7dSAdrian Hunter if is_reg_expr: 204*ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 205*ebd70c7dSAdrian Hunter else: 206*ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 207*ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 208*ebd70c7dSAdrian Hunter 209*ebd70c7dSAdrian Hunter self.next_button = QToolButton() 210*ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 211*ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 212*ebd70c7dSAdrian Hunter 213*ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 214*ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 215*ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 216*ebd70c7dSAdrian Hunter 217*ebd70c7dSAdrian Hunter self.close_button = QToolButton() 218*ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 219*ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 220*ebd70c7dSAdrian Hunter 221*ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 222*ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 223*ebd70c7dSAdrian Hunter 224*ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 225*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 226*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 227*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 228*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 229*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 230*ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 231*ebd70c7dSAdrian Hunter 232*ebd70c7dSAdrian Hunter self.bar = QWidget() 233*ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 234*ebd70c7dSAdrian Hunter self.bar.hide() 235*ebd70c7dSAdrian Hunter 236*ebd70c7dSAdrian Hunter def Widget(self): 237*ebd70c7dSAdrian Hunter return self.bar 238*ebd70c7dSAdrian Hunter 239*ebd70c7dSAdrian Hunter def Activate(self): 240*ebd70c7dSAdrian Hunter self.bar.show() 241*ebd70c7dSAdrian Hunter self.textbox.setFocus() 242*ebd70c7dSAdrian Hunter 243*ebd70c7dSAdrian Hunter def Deactivate(self): 244*ebd70c7dSAdrian Hunter self.bar.hide() 245*ebd70c7dSAdrian Hunter 246*ebd70c7dSAdrian Hunter def Busy(self): 247*ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 248*ebd70c7dSAdrian Hunter self.pattern.hide() 249*ebd70c7dSAdrian Hunter self.next_button.hide() 250*ebd70c7dSAdrian Hunter self.prev_button.hide() 251*ebd70c7dSAdrian Hunter self.progress.show() 252*ebd70c7dSAdrian Hunter 253*ebd70c7dSAdrian Hunter def Idle(self): 254*ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 255*ebd70c7dSAdrian Hunter self.progress.hide() 256*ebd70c7dSAdrian Hunter self.pattern.show() 257*ebd70c7dSAdrian Hunter self.next_button.show() 258*ebd70c7dSAdrian Hunter self.prev_button.show() 259*ebd70c7dSAdrian Hunter 260*ebd70c7dSAdrian Hunter def Find(self, direction): 261*ebd70c7dSAdrian Hunter value = self.textbox.currentText() 262*ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 263*ebd70c7dSAdrian Hunter self.last_value = value 264*ebd70c7dSAdrian Hunter self.last_pattern = pattern 265*ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 266*ebd70c7dSAdrian Hunter 267*ebd70c7dSAdrian Hunter def ValueChanged(self): 268*ebd70c7dSAdrian Hunter value = self.textbox.currentText() 269*ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 270*ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 271*ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 272*ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 273*ebd70c7dSAdrian Hunter if data == None: 274*ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 275*ebd70c7dSAdrian Hunter else: 276*ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 277*ebd70c7dSAdrian Hunter self.Find(0) 278*ebd70c7dSAdrian Hunter 279*ebd70c7dSAdrian Hunter def NextPrev(self, direction): 280*ebd70c7dSAdrian Hunter value = self.textbox.currentText() 281*ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 282*ebd70c7dSAdrian Hunter if value != self.last_value: 283*ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 284*ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 285*ebd70c7dSAdrian Hunter if index < 0: 286*ebd70c7dSAdrian Hunter index = self.textbox.count() 287*ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 288*ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 289*ebd70c7dSAdrian Hunter return 290*ebd70c7dSAdrian Hunter else: 291*ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 292*ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 293*ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 294*ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 295*ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 296*ebd70c7dSAdrian Hunter self.Find(direction) 297*ebd70c7dSAdrian Hunter 298*ebd70c7dSAdrian Hunter def NotFound(self): 299*ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 300*ebd70c7dSAdrian Hunter 301031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 302031c2a00SAdrian Hunter 303031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 304031c2a00SAdrian Hunter 305031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 306031c2a00SAdrian Hunter self.glb = glb 307031c2a00SAdrian Hunter self.row = row 308031c2a00SAdrian Hunter self.parent_item = parent_item 309031c2a00SAdrian Hunter self.query_done = False; 310031c2a00SAdrian Hunter self.child_count = 0 311031c2a00SAdrian Hunter self.child_items = [] 312031c2a00SAdrian Hunter 313031c2a00SAdrian Hunter def getChildItem(self, row): 314031c2a00SAdrian Hunter return self.child_items[row] 315031c2a00SAdrian Hunter 316031c2a00SAdrian Hunter def getParentItem(self): 317031c2a00SAdrian Hunter return self.parent_item 318031c2a00SAdrian Hunter 319031c2a00SAdrian Hunter def getRow(self): 320031c2a00SAdrian Hunter return self.row 321031c2a00SAdrian Hunter 322031c2a00SAdrian Hunter def childCount(self): 323031c2a00SAdrian Hunter if not self.query_done: 324031c2a00SAdrian Hunter self.Select() 325031c2a00SAdrian Hunter if not self.child_count: 326031c2a00SAdrian Hunter return -1 327031c2a00SAdrian Hunter return self.child_count 328031c2a00SAdrian Hunter 329031c2a00SAdrian Hunter def hasChildren(self): 330031c2a00SAdrian Hunter if not self.query_done: 331031c2a00SAdrian Hunter return True 332031c2a00SAdrian Hunter return self.child_count > 0 333031c2a00SAdrian Hunter 334031c2a00SAdrian Hunter def getData(self, column): 335031c2a00SAdrian Hunter return self.data[column] 336031c2a00SAdrian Hunter 337031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 338031c2a00SAdrian Hunter 339031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 340031c2a00SAdrian Hunter 341031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 342031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 343031c2a00SAdrian Hunter self.comm_id = comm_id 344031c2a00SAdrian Hunter self.thread_id = thread_id 345031c2a00SAdrian Hunter self.call_path_id = call_path_id 346031c2a00SAdrian Hunter self.branch_count = branch_count 347031c2a00SAdrian Hunter self.time = time 348031c2a00SAdrian Hunter 349031c2a00SAdrian Hunter def Select(self): 350031c2a00SAdrian Hunter self.query_done = True; 351031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 352031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 353031c2a00SAdrian Hunter " FROM calls" 354031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 355031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 356031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 357031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 358031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 359031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 360031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 361031c2a00SAdrian Hunter " ORDER BY call_path_id") 362031c2a00SAdrian Hunter while query.next(): 363031c2a00SAdrian 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) 364031c2a00SAdrian Hunter self.child_items.append(child_item) 365031c2a00SAdrian Hunter self.child_count += 1 366031c2a00SAdrian Hunter 367031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 368031c2a00SAdrian Hunter 369031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 370031c2a00SAdrian Hunter 371031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 372031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 373031c2a00SAdrian Hunter dso = dsoname(dso) 374031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 375031c2a00SAdrian Hunter self.dbid = call_path_id 376031c2a00SAdrian Hunter 377031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 378031c2a00SAdrian Hunter 379031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 380031c2a00SAdrian Hunter 381031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 382031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 383031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 384031c2a00SAdrian Hunter self.dbid = thread_id 385031c2a00SAdrian Hunter 386031c2a00SAdrian Hunter def Select(self): 387031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 388031c2a00SAdrian Hunter for child_item in self.child_items: 389031c2a00SAdrian Hunter self.time += child_item.time 390031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 391031c2a00SAdrian Hunter for child_item in self.child_items: 392031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 393031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 394031c2a00SAdrian Hunter 395031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 396031c2a00SAdrian Hunter 397031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 398031c2a00SAdrian Hunter 399031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 400031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 401031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 402031c2a00SAdrian Hunter self.dbid = comm_id 403031c2a00SAdrian Hunter 404031c2a00SAdrian Hunter def Select(self): 405031c2a00SAdrian Hunter self.query_done = True; 406031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 407031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 408031c2a00SAdrian Hunter " FROM comm_threads" 409031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 410031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 411031c2a00SAdrian Hunter while query.next(): 412031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 413031c2a00SAdrian Hunter self.child_items.append(child_item) 414031c2a00SAdrian Hunter self.child_count += 1 415031c2a00SAdrian Hunter 416031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 417031c2a00SAdrian Hunter 418031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 419031c2a00SAdrian Hunter 420031c2a00SAdrian Hunter def __init__(self, glb): 421031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 422031c2a00SAdrian Hunter self.dbid = 0 423031c2a00SAdrian Hunter self.query_done = True; 424031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 425031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 426031c2a00SAdrian Hunter while query.next(): 427031c2a00SAdrian Hunter if not query.value(0): 428031c2a00SAdrian Hunter continue 429031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 430031c2a00SAdrian Hunter self.child_items.append(child_item) 431031c2a00SAdrian Hunter self.child_count += 1 432031c2a00SAdrian Hunter 433031c2a00SAdrian Hunter# Context-sensitive call graph data model 434031c2a00SAdrian Hunter 435031c2a00SAdrian Hunterclass CallGraphModel(TreeModel): 436031c2a00SAdrian Hunter 437031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 438031c2a00SAdrian Hunter super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) 439031c2a00SAdrian Hunter self.glb = glb 440031c2a00SAdrian Hunter 441031c2a00SAdrian Hunter def columnCount(self, parent=None): 442031c2a00SAdrian Hunter return 7 443031c2a00SAdrian Hunter 444031c2a00SAdrian Hunter def columnHeader(self, column): 445031c2a00SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 446031c2a00SAdrian Hunter return headers[column] 447031c2a00SAdrian Hunter 448031c2a00SAdrian Hunter def columnAlignment(self, column): 449031c2a00SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 450031c2a00SAdrian Hunter return alignment[column] 451031c2a00SAdrian Hunter 452*ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 453*ebd70c7dSAdrian Hunter if pattern: 454*ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 455*ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 456*ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 457*ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 458*ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 459*ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 460*ebd70c7dSAdrian Hunter # Escape % and _ 461*ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 462*ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 463*ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 464*ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 465*ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 466*ebd70c7dSAdrian Hunter else: 467*ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 468*ebd70c7dSAdrian Hunter else: 469*ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 470*ebd70c7dSAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 471*ebd70c7dSAdrian Hunter " FROM calls" 472*ebd70c7dSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 473*ebd70c7dSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 474*ebd70c7dSAdrian Hunter " WHERE symbols.name" + match + 475*ebd70c7dSAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 476*ebd70c7dSAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 477*ebd70c7dSAdrian Hunter 478*ebd70c7dSAdrian Hunter def FindPath(self, query): 479*ebd70c7dSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 480*ebd70c7dSAdrian Hunter # to open the tree at the right place. 481*ebd70c7dSAdrian Hunter ids = [] 482*ebd70c7dSAdrian Hunter parent_id = query.value(0) 483*ebd70c7dSAdrian Hunter while parent_id: 484*ebd70c7dSAdrian Hunter ids.insert(0, parent_id) 485*ebd70c7dSAdrian Hunter q2 = QSqlQuery(self.glb.db) 486*ebd70c7dSAdrian Hunter QueryExec(q2, "SELECT parent_id" 487*ebd70c7dSAdrian Hunter " FROM call_paths" 488*ebd70c7dSAdrian Hunter " WHERE id = " + str(parent_id)) 489*ebd70c7dSAdrian Hunter if not q2.next(): 490*ebd70c7dSAdrian Hunter break 491*ebd70c7dSAdrian Hunter parent_id = q2.value(0) 492*ebd70c7dSAdrian Hunter # The call path root is not used 493*ebd70c7dSAdrian Hunter if ids[0] == 1: 494*ebd70c7dSAdrian Hunter del ids[0] 495*ebd70c7dSAdrian Hunter ids.insert(0, query.value(2)) 496*ebd70c7dSAdrian Hunter ids.insert(0, query.value(1)) 497*ebd70c7dSAdrian Hunter return ids 498*ebd70c7dSAdrian Hunter 499*ebd70c7dSAdrian Hunter def Found(self, query, found): 500*ebd70c7dSAdrian Hunter if found: 501*ebd70c7dSAdrian Hunter return self.FindPath(query) 502*ebd70c7dSAdrian Hunter return [] 503*ebd70c7dSAdrian Hunter 504*ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 505*ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 506*ebd70c7dSAdrian Hunter found = query.first() 507*ebd70c7dSAdrian Hunter else: 508*ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 509*ebd70c7dSAdrian Hunter found = query.next() 510*ebd70c7dSAdrian Hunter return self.Found(query, found) 511*ebd70c7dSAdrian Hunter 512*ebd70c7dSAdrian Hunter def FindNext(self, query): 513*ebd70c7dSAdrian Hunter found = query.next() 514*ebd70c7dSAdrian Hunter if not found: 515*ebd70c7dSAdrian Hunter found = query.first() 516*ebd70c7dSAdrian Hunter return self.Found(query, found) 517*ebd70c7dSAdrian Hunter 518*ebd70c7dSAdrian Hunter def FindPrev(self, query): 519*ebd70c7dSAdrian Hunter found = query.previous() 520*ebd70c7dSAdrian Hunter if not found: 521*ebd70c7dSAdrian Hunter found = query.last() 522*ebd70c7dSAdrian Hunter return self.Found(query, found) 523*ebd70c7dSAdrian Hunter 524*ebd70c7dSAdrian Hunter def FindThread(self, c): 525*ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 526*ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 527*ebd70c7dSAdrian Hunter elif c.direction > 0: 528*ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 529*ebd70c7dSAdrian Hunter else: 530*ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 531*ebd70c7dSAdrian Hunter return (True, ids) 532*ebd70c7dSAdrian Hunter 533*ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 534*ebd70c7dSAdrian Hunter class Context(): 535*ebd70c7dSAdrian Hunter def __init__(self, *x): 536*ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 537*ebd70c7dSAdrian Hunter def Update(self, *x): 538*ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 539*ebd70c7dSAdrian Hunter if len(context): 540*ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 541*ebd70c7dSAdrian Hunter else: 542*ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 543*ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 544*ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 545*ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 546*ebd70c7dSAdrian Hunter thread.start() 547*ebd70c7dSAdrian Hunter 548*ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 549*ebd70c7dSAdrian Hunter callback(ids) 550*ebd70c7dSAdrian Hunter 551*ebd70c7dSAdrian Hunter# Vertical widget layout 552*ebd70c7dSAdrian Hunter 553*ebd70c7dSAdrian Hunterclass VBox(): 554*ebd70c7dSAdrian Hunter 555*ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 556*ebd70c7dSAdrian Hunter self.vbox = QWidget() 557*ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 558*ebd70c7dSAdrian Hunter 559*ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 560*ebd70c7dSAdrian Hunter 561*ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 562*ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 563*ebd70c7dSAdrian Hunter if w3: 564*ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 565*ebd70c7dSAdrian Hunter 566*ebd70c7dSAdrian Hunter def Widget(self): 567*ebd70c7dSAdrian Hunter return self.vbox 568*ebd70c7dSAdrian Hunter 5691beb5c7bSAdrian Hunter# Context-sensitive call graph window 5701beb5c7bSAdrian Hunter 5711beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow): 5721beb5c7bSAdrian Hunter 5731beb5c7bSAdrian Hunter def __init__(self, glb, parent=None): 5741beb5c7bSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 5751beb5c7bSAdrian Hunter 5761beb5c7bSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 5771beb5c7bSAdrian Hunter 5781beb5c7bSAdrian Hunter self.view = QTreeView() 5791beb5c7bSAdrian Hunter self.view.setModel(self.model) 5801beb5c7bSAdrian Hunter 5811beb5c7bSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 5821beb5c7bSAdrian Hunter self.view.setColumnWidth(c, w) 5831beb5c7bSAdrian Hunter 584*ebd70c7dSAdrian Hunter self.find_bar = FindBar(self, self) 585*ebd70c7dSAdrian Hunter 586*ebd70c7dSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 587*ebd70c7dSAdrian Hunter 588*ebd70c7dSAdrian Hunter self.setWidget(self.vbox.Widget()) 5891beb5c7bSAdrian Hunter 5901beb5c7bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 5911beb5c7bSAdrian Hunter 592*ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 593*ebd70c7dSAdrian Hunter if not len(ids): 594*ebd70c7dSAdrian Hunter return False 595*ebd70c7dSAdrian Hunter parent = QModelIndex() 596*ebd70c7dSAdrian Hunter for dbid in ids: 597*ebd70c7dSAdrian Hunter found = False 598*ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 599*ebd70c7dSAdrian Hunter for row in xrange(n): 600*ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 601*ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 602*ebd70c7dSAdrian Hunter found = True 603*ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 604*ebd70c7dSAdrian Hunter parent = child 605*ebd70c7dSAdrian Hunter break 606*ebd70c7dSAdrian Hunter if not found: 607*ebd70c7dSAdrian Hunter break 608*ebd70c7dSAdrian Hunter return found 609*ebd70c7dSAdrian Hunter 610*ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 611*ebd70c7dSAdrian Hunter self.view.setFocus() 612*ebd70c7dSAdrian Hunter self.find_bar.Busy() 613*ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 614*ebd70c7dSAdrian Hunter 615*ebd70c7dSAdrian Hunter def FindDone(self, ids): 616*ebd70c7dSAdrian Hunter found = True 617*ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 618*ebd70c7dSAdrian Hunter found = False 619*ebd70c7dSAdrian Hunter self.find_bar.Idle() 620*ebd70c7dSAdrian Hunter if not found: 621*ebd70c7dSAdrian Hunter self.find_bar.NotFound() 622*ebd70c7dSAdrian Hunter 6231beb5c7bSAdrian Hunter# Action Definition 6241beb5c7bSAdrian Hunter 6251beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 6261beb5c7bSAdrian Hunter action = QAction(label, parent) 6271beb5c7bSAdrian Hunter if shortcut != None: 6281beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 6291beb5c7bSAdrian Hunter action.setStatusTip(tip) 6301beb5c7bSAdrian Hunter action.triggered.connect(callback) 6311beb5c7bSAdrian Hunter return action 6321beb5c7bSAdrian Hunter 6331beb5c7bSAdrian Hunter# Typical application actions 6341beb5c7bSAdrian Hunter 6351beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 6361beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 6371beb5c7bSAdrian Hunter 6381beb5c7bSAdrian Hunter# Typical MDI actions 6391beb5c7bSAdrian Hunter 6401beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 6411beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 6421beb5c7bSAdrian Hunter 6431beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 6441beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 6451beb5c7bSAdrian Hunter 6461beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 6471beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 6481beb5c7bSAdrian Hunter 6491beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 6501beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 6511beb5c7bSAdrian Hunter 6521beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 6531beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 6541beb5c7bSAdrian Hunter 6551beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 6561beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 6571beb5c7bSAdrian Hunter 6581beb5c7bSAdrian Hunter# Typical MDI window menu 6591beb5c7bSAdrian Hunter 6601beb5c7bSAdrian Hunterclass WindowMenu(): 6611beb5c7bSAdrian Hunter 6621beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 6631beb5c7bSAdrian Hunter self.mdi_area = mdi_area 6641beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 6651beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 6661beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 6671beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 6681beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 6691beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 6701beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 6711beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 6721beb5c7bSAdrian Hunter 6731beb5c7bSAdrian Hunter def Update(self): 6741beb5c7bSAdrian Hunter self.window_menu.clear() 6751beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 6761beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 6771beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 6781beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 6791beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 6801beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 6811beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 6821beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 6831beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 6841beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 6851beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6861beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 6871beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 6881beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6891beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 6901beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 6911beb5c7bSAdrian Hunter if sub_window_count == 0: 6921beb5c7bSAdrian Hunter return 6931beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6941beb5c7bSAdrian Hunter nr = 1 6951beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 6961beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 6971beb5c7bSAdrian Hunter if nr < 10: 6981beb5c7bSAdrian Hunter label = "&" + label 6991beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 7001beb5c7bSAdrian Hunter action.setCheckable(True) 7011beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 7021beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 7031beb5c7bSAdrian Hunter self.window_menu.addAction(action) 7041beb5c7bSAdrian Hunter nr += 1 7051beb5c7bSAdrian Hunter 7061beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 7071beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 7081beb5c7bSAdrian Hunter 7091beb5c7bSAdrian Hunter# Unique name for sub-windows 7101beb5c7bSAdrian Hunter 7111beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 7121beb5c7bSAdrian Hunter if nr > 1: 7131beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 7141beb5c7bSAdrian Hunter return name 7151beb5c7bSAdrian Hunter 7161beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 7171beb5c7bSAdrian Hunter nr = 1 7181beb5c7bSAdrian Hunter while True: 7191beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 7201beb5c7bSAdrian Hunter ok = True 7211beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 7221beb5c7bSAdrian Hunter if sub_window.name == unique_name: 7231beb5c7bSAdrian Hunter ok = False 7241beb5c7bSAdrian Hunter break 7251beb5c7bSAdrian Hunter if ok: 7261beb5c7bSAdrian Hunter return unique_name 7271beb5c7bSAdrian Hunter nr += 1 7281beb5c7bSAdrian Hunter 7291beb5c7bSAdrian Hunter# Add a sub-window 7301beb5c7bSAdrian Hunter 7311beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 7321beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 7331beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 7341beb5c7bSAdrian Hunter sub_window.resize(800, 600) 7351beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 7361beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 7371beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 7381beb5c7bSAdrian Hunter sub_window.name = unique_name 7391beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 7401beb5c7bSAdrian Hunter sub_window.show() 7411beb5c7bSAdrian Hunter 742031c2a00SAdrian Hunter# Main window 743031c2a00SAdrian Hunter 744031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 745031c2a00SAdrian Hunter 746031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 747031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 748031c2a00SAdrian Hunter 749031c2a00SAdrian Hunter self.glb = glb 750031c2a00SAdrian Hunter 7511beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 752031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 753031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 754031c2a00SAdrian Hunter 7551beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 7561beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 7571beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 758031c2a00SAdrian Hunter 7591beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 760031c2a00SAdrian Hunter 7611beb5c7bSAdrian Hunter menu = self.menuBar() 762031c2a00SAdrian Hunter 7631beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 7641beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 7651beb5c7bSAdrian Hunter 766*ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 767*ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 768*ebd70c7dSAdrian Hunter 7691beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 7701beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 7711beb5c7bSAdrian Hunter 7721beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 7731beb5c7bSAdrian Hunter 774*ebd70c7dSAdrian Hunter def Find(self): 775*ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 776*ebd70c7dSAdrian Hunter if win: 777*ebd70c7dSAdrian Hunter try: 778*ebd70c7dSAdrian Hunter win.find_bar.Activate() 779*ebd70c7dSAdrian Hunter except: 780*ebd70c7dSAdrian Hunter pass 781*ebd70c7dSAdrian Hunter 7821beb5c7bSAdrian Hunter def NewCallGraph(self): 7831beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 784031c2a00SAdrian Hunter 785031c2a00SAdrian Hunter# Global data 786031c2a00SAdrian Hunter 787031c2a00SAdrian Hunterclass Glb(): 788031c2a00SAdrian Hunter 789031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 790031c2a00SAdrian Hunter self.dbref = dbref 791031c2a00SAdrian Hunter self.db = db 792031c2a00SAdrian Hunter self.dbname = dbname 793031c2a00SAdrian Hunter self.app = None 794031c2a00SAdrian Hunter self.mainwindow = None 795031c2a00SAdrian Hunter 796031c2a00SAdrian Hunter# Database reference 797031c2a00SAdrian Hunter 798031c2a00SAdrian Hunterclass DBRef(): 799031c2a00SAdrian Hunter 800031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 801031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 802031c2a00SAdrian Hunter self.dbname = dbname 803031c2a00SAdrian Hunter 804031c2a00SAdrian Hunter def Open(self, connection_name): 805031c2a00SAdrian Hunter dbname = self.dbname 806031c2a00SAdrian Hunter if self.is_sqlite3: 807031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 808031c2a00SAdrian Hunter else: 809031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 810031c2a00SAdrian Hunter opts = dbname.split() 811031c2a00SAdrian Hunter for opt in opts: 812031c2a00SAdrian Hunter if "=" in opt: 813031c2a00SAdrian Hunter opt = opt.split("=") 814031c2a00SAdrian Hunter if opt[0] == "hostname": 815031c2a00SAdrian Hunter db.setHostName(opt[1]) 816031c2a00SAdrian Hunter elif opt[0] == "port": 817031c2a00SAdrian Hunter db.setPort(int(opt[1])) 818031c2a00SAdrian Hunter elif opt[0] == "username": 819031c2a00SAdrian Hunter db.setUserName(opt[1]) 820031c2a00SAdrian Hunter elif opt[0] == "password": 821031c2a00SAdrian Hunter db.setPassword(opt[1]) 822031c2a00SAdrian Hunter elif opt[0] == "dbname": 823031c2a00SAdrian Hunter dbname = opt[1] 824031c2a00SAdrian Hunter else: 825031c2a00SAdrian Hunter dbname = opt 826031c2a00SAdrian Hunter 827031c2a00SAdrian Hunter db.setDatabaseName(dbname) 828031c2a00SAdrian Hunter if not db.open(): 829031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 830031c2a00SAdrian Hunter return db, dbname 831031c2a00SAdrian Hunter 832031c2a00SAdrian Hunter# Main 833031c2a00SAdrian Hunter 834031c2a00SAdrian Hunterdef Main(): 835031c2a00SAdrian Hunter if (len(sys.argv) < 2): 836031c2a00SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 837031c2a00SAdrian Hunter raise Exception("Too few arguments") 838031c2a00SAdrian Hunter 839031c2a00SAdrian Hunter dbname = sys.argv[1] 840031c2a00SAdrian Hunter 841031c2a00SAdrian Hunter is_sqlite3 = False 842031c2a00SAdrian Hunter try: 843031c2a00SAdrian Hunter f = open(dbname) 844031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 845031c2a00SAdrian Hunter is_sqlite3 = True 846031c2a00SAdrian Hunter f.close() 847031c2a00SAdrian Hunter except: 848031c2a00SAdrian Hunter pass 849031c2a00SAdrian Hunter 850031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 851031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 852031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 853031c2a00SAdrian Hunter app = QApplication(sys.argv) 854031c2a00SAdrian Hunter glb.app = app 855031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 856031c2a00SAdrian Hunter glb.mainwindow = mainwindow 857031c2a00SAdrian Hunter mainwindow.show() 858031c2a00SAdrian Hunter err = app.exec_() 859031c2a00SAdrian Hunter db.close() 860031c2a00SAdrian Hunter sys.exit(err) 861031c2a00SAdrian Hunter 862031c2a00SAdrian Hunterif __name__ == "__main__": 863031c2a00SAdrian Hunter Main() 864