#!/usr/bin/env python2# SPDX-License-Identifier: GPL-2.0# exported-sql-viewer.py: view data from sql database# Copyright (c) 2014-2018, Intel Corporation. # To use this script you will need to have exported data using either the# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those# scripts for details.## Following on from the example in the export scripts, a# call-graph can be displayed for the pt_example database like this:## python tools/perf/scripts/python/exported-sql-viewer.py pt_example## Note that for PostgreSQL, this script supports connecting to remote databases# by setting hostname, port, username, password, and dbname e.g.## python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"## The result is a GUI window with a tree representing a context-sensitive# call-graph. Expanding a couple of levels of the tree and adjusting column# widths to suit will display something like:## Call Graph: pt_example# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%)# v- ls# v- 2638:2638# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0# |- unknown unknown 1 13198 0.1 1 0.0# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0# >- __libc_csu_init ls 1 10354 0.1 10 0.0# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0# v- main ls 1 8182043 99.6 180254 99.9## Points to note:# The top level is a command name (comm)# The next level is a thread (pid:tid)# Subsequent levels are functions# 'Count' is the number of calls# 'Time' is the elapsed time until the function returns# Percentages are relative to the level above# 'Branch Count' is the total number of branches for that function and all# functions that it calls # There is also a "All branches" report, which displays branches and# possibly disassembly. However, presently, the only supported disassembler is# Intel XED, and additionally the object code must be present in perf build ID# cache. To use Intel XED, libxed.so must be present. To build and install# libxed.so:# git clone https://github.com/intelxed/mbuild.git mbuild# git clone https://github.com/intelxed/xed# cd xed# ./mfile.py --share# sudo ./mfile.py --prefix=/usr/local install# sudo ldconfig## Example report:## Time CPU Command PID TID Branch Type In Tx Branch# 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)# 7fab593ea260 48 89 e7 mov %rsp, %rdi# 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])# 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so)# 7fab593ea260 48 89 e7 mov %rsp, %rdi# 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930# 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so)# 7fab593ea930 55 pushq %rbp# 7fab593ea931 48 89 e5 mov %rsp, %rbp# 7fab593ea934 41 57 pushq %r15# 7fab593ea936 41 56 pushq %r14# 7fab593ea938 41 55 pushq %r13# 7fab593ea93a 41 54 pushq %r12# 7fab593ea93c 53 pushq %rbx# 7fab593ea93d 48 89 fb mov %rdi, %rbx# 7fab593ea940 48 83 ec 68 sub $0x68, %rsp# 7fab593ea944 0f 31 rdtsc# 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx# 7fab593ea94a 89 c0 mov %eax, %eax# 7fab593ea94c 48 09 c2 or %rax, %rdx# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax# 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])# 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so)# 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax# 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip)# 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) from __future__ import print_function import sysimport weakrefimport threadingimport stringtry: # Python2 import cPickle as pickle # size of pickled integer big enough for record size glb_nsz = 8except ImportError: import pickle glb_nsz = 16import reimport osfrom PySide.QtCore import *from PySide.QtGui import *from PySide.QtSql import *pyside_version_1 = Truefrom decimal import *from ctypes import *from multiprocessing import Process, Array, Value, Event # xrange is range in Python3try: xrangeexcept NameError: xrange = range def printerr(*args, **keyword_args): print(*args, file=sys.stderr, **keyword_args) # Data formatting helpers def tohex(ip): if ip < 0: ip += 1 << 64 return "%x" % ip def offstr(offset): if offset: return "+0x%x" % offset return "" def dsoname(name): if name == "[kernel.kallsyms]": return "[kernel]" return name def findnth(s, sub, n, offs=0): pos = s.find(sub) if pos < 0: return pos if n <= 1: return offs + pos return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) # Percent to one decimal place def PercentToOneDP(n, d): if not d: return "0.0" x = (n * Decimal(100)) / d return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) # Helper for queries that must not fail def QueryExec(query, stmt): ret = query.exec_(stmt) if not ret: raise Exception("Query failed: " + query.lastError().text()) # Background thread class Thread(QThread): done = Signal(object) def __init__(self, task, param=None, parent=None): super(Thread, self).__init__(parent) self.task = task self.param = param def run(self): while True: if self.param is None: done, result = self.task() else: done, result = self.task(self.param) self.done.emit(result) if done: break # Tree data model class TreeModel(QAbstractItemModel): def __init__(self, glb, parent=None): super(TreeModel, self).__init__(parent) self.glb = glb self.root = self.GetRoot() self.last_row_read = 0 def Item(self, parent): if parent.isValid(): return parent.internalPointer() else: return self.root def rowCount(self, parent): result = self.Item(parent).childCount() if result < 0: result = 0 self.dataChanged.emit(parent, parent) return result def hasChildren(self, parent): return self.Item(parent).hasChildren() def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(section) if role != Qt.DisplayRole: return None if orientation != Qt.Horizontal: return None return self.columnHeader(section) def parent(self, child): child_item = child.internalPointer() if child_item is self.root: return QModelIndex() parent_item = child_item.getParentItem() return self.createIndex(parent_item.getRow(), 0, parent_item) def index(self, row, column, parent): child_item = self.Item(parent).getChildItem(row) return self.createIndex(row, column, child_item) def DisplayData(self, item, index): return item.getData(index.column()) def FetchIfNeeded(self, row): if row > self.last_row_read: self.last_row_read = row if row + 10 >= self.root.child_count: self.fetcher.Fetch(glb_chunk_sz) def columnAlignment(self, column): return Qt.AlignLeft def columnFont(self, column): return None def data(self, index, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(index.column()) if role == Qt.FontRole: return self.columnFont(index.column()) if role != Qt.DisplayRole: return None item = index.internalPointer() return self.DisplayData(item, index) # Table data model class TableModel(QAbstractTableModel): def __init__(self, parent=None): super(TableModel, self).__init__(parent) self.child_count = 0 self.child_items = [] self.last_row_read = 0 def Item(self, parent): if parent.isValid(): return parent.internalPointer() else: return self def rowCount(self, parent): return self.child_count def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(section) if role != Qt.DisplayRole: return None if orientation != Qt.Horizontal: return None return self.columnHeader(section) def index(self, row, column, parent): return self.createIndex(row, column, self.child_items[row]) def DisplayData(self, item, index): return item.getData(index.column()) def FetchIfNeeded(self, row): if row > self.last_row_read: self.last_row_read = row if row + 10 >= self.child_count: self.fetcher.Fetch(glb_chunk_sz) def columnAlignment(self, column): return Qt.AlignLeft def columnFont(self, column): return None def data(self, index, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(index.column()) if role == Qt.FontRole: return self.columnFont(index.column()) if role != Qt.DisplayRole: return None item = index.internalPointer() return self.DisplayData(item, index) # Model cache model_cache = weakref.WeakValueDictionary()model_cache_lock = threading.Lock() def LookupCreateModel(model_name, create_fn): model_cache_lock.acquire() try: model = model_cache[model_name] except: model = None if model is None: model = create_fn() model_cache[model_name] = model model_cache_lock.release() return model # Find bar class FindBar(): def __init__(self, parent, finder, is_reg_expr=False): self.finder = finder self.context = [] self.last_value = None self.last_pattern = None label = QLabel("Find:") label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.textbox = QComboBox() self.textbox.setEditable(True) self.textbox.currentIndexChanged.connect(self.ValueChanged) self.progress = QProgressBar() self.progress.setRange(0, 0) self.progress.hide() if is_reg_expr: self.pattern = QCheckBox("Regular Expression") else: self.pattern = QCheckBox("Pattern") self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.next_button = QToolButton() self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) self.next_button.released.connect(lambda: self.NextPrev(1)) self.prev_button = QToolButton() self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) self.prev_button.released.connect(lambda: self.NextPrev(-1)) self.close_button = QToolButton() self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.close_button.released.connect(self.Deactivate) self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0, 0, 0, 0) self.hbox.addWidget(label) self.hbox.addWidget(self.textbox) self.hbox.addWidget(self.progress) self.hbox.addWidget(self.pattern) self.hbox.addWidget(self.next_button) self.hbox.addWidget(self.prev_button) self.hbox.addWidget(self.close_button) self.bar = QWidget() self.bar.setLayout(self.hbox); self.bar.hide() def Widget(self): return self.bar def Activate(self): self.bar.show() self.textbox.setFocus() def Deactivate(self): self.bar.hide() def Busy(self): self.textbox.setEnabled(False) self.pattern.hide() self.next_button.hide() self.prev_button.hide() self.progress.show() def Idle(self): self.textbox.setEnabled(True) self.progress.hide() self.pattern.show() self.next_button.show() self.prev_button.show() def Find(self, direction): value = self.textbox.currentText() pattern = self.pattern.isChecked() self.last_value = value self.last_pattern = pattern self.finder.Find(value, direction, pattern, self.context) def ValueChanged(self): value = self.textbox.currentText() pattern = self.pattern.isChecked() index = self.textbox.currentIndex() data = self.textbox.itemData(index) # Store the pattern in the combo box to keep it with the text value if data == None: self.textbox.setItemData(index, pattern) else: self.pattern.setChecked(data) self.Find(0) def NextPrev(self, direction): value = self.textbox.currentText() pattern = self.pattern.isChecked() if value != self.last_value: index = self.textbox.findText(value) # Allow for a button press before the value has been added to the combo box if index < 0: index = self.textbox.count() self.textbox.addItem(value, pattern) self.textbox.setCurrentIndex(index) return else: self.textbox.setItemData(index, pattern) elif pattern != self.last_pattern: # Keep the pattern recorded in the combo box up to date index = self.textbox.currentIndex() self.textbox.setItemData(index, pattern) self.Find(direction) def NotFound(self): QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") # Context-sensitive call graph data model item base class CallGraphLevelItemBase(object): def __init__(self, glb, row, parent_item): self.glb = glb self.row = row self.parent_item = parent_item self.query_done = False; self.child_count = 0 self.child_items = [] def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return self.parent_item def getRow(self): return self.row def childCount(self): if not self.query_done: self.Select() if not self.child_count: return -1 return self.child_count def hasChildren(self): if not self.query_done: return True return self.child_count > 0 def getData(self, column): return self.data[column] # Context-sensitive call graph data model level 2+ item base class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) self.comm_id = comm_id self.thread_id = thread_id self.call_path_id = call_path_id self.branch_count = branch_count self.time = time def Select(self): self.query_done = True; query = QSqlQuery(self.glb.db) QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" " FROM calls"Lines 1–500 of 3073. Load more
#!/usr/bin/env python2 # SPDX-License-Identifier: GPL-2.0 # exported-sql-viewer.py: view data from sql database # Copyright (c) 2014-2018, Intel Corporation. # To use this script you will need to have exported data using either the # export-to-sqlite.py or the export-to-postgresql.py script. Refer to those # scripts for details. # # Following on from the example in the export scripts, a # call-graph can be displayed for the pt_example database like this: # # python tools/perf/scripts/python/exported-sql-viewer.py pt_example # # Note that for PostgreSQL, this script supports connecting to remote databases # by setting hostname, port, username, password, and dbname e.g. # # python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" # # The result is a GUI window with a tree representing a context-sensitive # call-graph. Expanding a couple of levels of the tree and adjusting column # widths to suit will display something like: # # Call Graph: pt_example # Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) # v- ls # v- 2638:2638 # v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 # |- unknown unknown 1 13198 0.1 1 0.0 # >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 # >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 # v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 # >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 # >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 # >- __libc_csu_init ls 1 10354 0.1 10 0.0 # |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 # v- main ls 1 8182043 99.6 180254 99.9 # # Points to note: # The top level is a command name (comm) # The next level is a thread (pid:tid) # Subsequent levels are functions # 'Count' is the number of calls # 'Time' is the elapsed time until the function returns # Percentages are relative to the level above # 'Branch Count' is the total number of branches for that function and all # functions that it calls # There is also a "All branches" report, which displays branches and # possibly disassembly. However, presently, the only supported disassembler is # Intel XED, and additionally the object code must be present in perf build ID # cache. To use Intel XED, libxed.so must be present. To build and install # libxed.so: # git clone https://github.com/intelxed/mbuild.git mbuild # git clone https://github.com/intelxed/xed # cd xed # ./mfile.py --share # sudo ./mfile.py --prefix=/usr/local install # sudo ldconfig # # Example report: # # Time CPU Command PID TID Branch Type In Tx Branch # 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) # 7fab593ea260 48 89 e7 mov %rsp, %rdi # 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) # 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) # 7fab593ea260 48 89 e7 mov %rsp, %rdi # 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930 # 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so) # 7fab593ea930 55 pushq %rbp # 7fab593ea931 48 89 e5 mov %rsp, %rbp # 7fab593ea934 41 57 pushq %r15 # 7fab593ea936 41 56 pushq %r14 # 7fab593ea938 41 55 pushq %r13 # 7fab593ea93a 41 54 pushq %r12 # 7fab593ea93c 53 pushq %rbx # 7fab593ea93d 48 89 fb mov %rdi, %rbx # 7fab593ea940 48 83 ec 68 sub $0x68, %rsp # 7fab593ea944 0f 31 rdtsc # 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx # 7fab593ea94a 89 c0 mov %eax, %eax # 7fab593ea94c 48 09 c2 or %rax, %rdx # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax # 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) # 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so) # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax # 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) # 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) from __future__ import print_function import sys import weakref import threading import string try: # Python2 import cPickle as pickle # size of pickled integer big enough for record size glb_nsz = 8 except ImportError: import pickle glb_nsz = 16 import re import os from PySide.QtCore import * from PySide.QtGui import * from PySide.QtSql import * pyside_version_1 = True from decimal import * from ctypes import * from multiprocessing import Process, Array, Value, Event # xrange is range in Python3 try: xrange except NameError: xrange = range def printerr(*args, **keyword_args): print(*args, file=sys.stderr, **keyword_args) # Data formatting helpers def tohex(ip): if ip < 0: ip += 1 << 64 return "%x" % ip def offstr(offset): if offset: return "+0x%x" % offset return "" def dsoname(name): if name == "[kernel.kallsyms]": return "[kernel]" return name def findnth(s, sub, n, offs=0): pos = s.find(sub) if pos < 0: return pos if n <= 1: return offs + pos return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1) # Percent to one decimal place def PercentToOneDP(n, d): if not d: return "0.0" x = (n * Decimal(100)) / d return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) # Helper for queries that must not fail def QueryExec(query, stmt): ret = query.exec_(stmt) if not ret: raise Exception("Query failed: " + query.lastError().text()) # Background thread class Thread(QThread): done = Signal(object) def __init__(self, task, param=None, parent=None): super(Thread, self).__init__(parent) self.task = task self.param = param def run(self): while True: if self.param is None: done, result = self.task() else: done, result = self.task(self.param) self.done.emit(result) if done: break # Tree data model class TreeModel(QAbstractItemModel): def __init__(self, glb, parent=None): super(TreeModel, self).__init__(parent) self.glb = glb self.root = self.GetRoot() self.last_row_read = 0 def Item(self, parent): if parent.isValid(): return parent.internalPointer() else: return self.root def rowCount(self, parent): result = self.Item(parent).childCount() if result < 0: result = 0 self.dataChanged.emit(parent, parent) return result def hasChildren(self, parent): return self.Item(parent).hasChildren() def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(section) if role != Qt.DisplayRole: return None if orientation != Qt.Horizontal: return None return self.columnHeader(section) def parent(self, child): child_item = child.internalPointer() if child_item is self.root: return QModelIndex() parent_item = child_item.getParentItem() return self.createIndex(parent_item.getRow(), 0, parent_item) def index(self, row, column, parent): child_item = self.Item(parent).getChildItem(row) return self.createIndex(row, column, child_item) def DisplayData(self, item, index): return item.getData(index.column()) def FetchIfNeeded(self, row): if row > self.last_row_read: self.last_row_read = row if row + 10 >= self.root.child_count: self.fetcher.Fetch(glb_chunk_sz) def columnAlignment(self, column): return Qt.AlignLeft def columnFont(self, column): return None def data(self, index, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(index.column()) if role == Qt.FontRole: return self.columnFont(index.column()) if role != Qt.DisplayRole: return None item = index.internalPointer() return self.DisplayData(item, index) # Table data model class TableModel(QAbstractTableModel): def __init__(self, parent=None): super(TableModel, self).__init__(parent) self.child_count = 0 self.child_items = [] self.last_row_read = 0 def Item(self, parent): if parent.isValid(): return parent.internalPointer() else: return self def rowCount(self, parent): return self.child_count def headerData(self, section, orientation, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(section) if role != Qt.DisplayRole: return None if orientation != Qt.Horizontal: return None return self.columnHeader(section) def index(self, row, column, parent): return self.createIndex(row, column, self.child_items[row]) def DisplayData(self, item, index): return item.getData(index.column()) def FetchIfNeeded(self, row): if row > self.last_row_read: self.last_row_read = row if row + 10 >= self.child_count: self.fetcher.Fetch(glb_chunk_sz) def columnAlignment(self, column): return Qt.AlignLeft def columnFont(self, column): return None def data(self, index, role): if role == Qt.TextAlignmentRole: return self.columnAlignment(index.column()) if role == Qt.FontRole: return self.columnFont(index.column()) if role != Qt.DisplayRole: return None item = index.internalPointer() return self.DisplayData(item, index) # Model cache model_cache = weakref.WeakValueDictionary() model_cache_lock = threading.Lock() def LookupCreateModel(model_name, create_fn): model_cache_lock.acquire() try: model = model_cache[model_name] except: model = None if model is None: model = create_fn() model_cache[model_name] = model model_cache_lock.release() return model # Find bar class FindBar(): def __init__(self, parent, finder, is_reg_expr=False): self.finder = finder self.context = [] self.last_value = None self.last_pattern = None label = QLabel("Find:") label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.textbox = QComboBox() self.textbox.setEditable(True) self.textbox.currentIndexChanged.connect(self.ValueChanged) self.progress = QProgressBar() self.progress.setRange(0, 0) self.progress.hide() if is_reg_expr: self.pattern = QCheckBox("Regular Expression") else: self.pattern = QCheckBox("Pattern") self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.next_button = QToolButton() self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) self.next_button.released.connect(lambda: self.NextPrev(1)) self.prev_button = QToolButton() self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) self.prev_button.released.connect(lambda: self.NextPrev(-1)) self.close_button = QToolButton() self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.close_button.released.connect(self.Deactivate) self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0, 0, 0, 0) self.hbox.addWidget(label) self.hbox.addWidget(self.textbox) self.hbox.addWidget(self.progress) self.hbox.addWidget(self.pattern) self.hbox.addWidget(self.next_button) self.hbox.addWidget(self.prev_button) self.hbox.addWidget(self.close_button) self.bar = QWidget() self.bar.setLayout(self.hbox); self.bar.hide() def Widget(self): return self.bar def Activate(self): self.bar.show() self.textbox.setFocus() def Deactivate(self): self.bar.hide() def Busy(self): self.textbox.setEnabled(False) self.pattern.hide() self.next_button.hide() self.prev_button.hide() self.progress.show() def Idle(self): self.textbox.setEnabled(True) self.progress.hide() self.pattern.show() self.next_button.show() self.prev_button.show() def Find(self, direction): value = self.textbox.currentText() pattern = self.pattern.isChecked() self.last_value = value self.last_pattern = pattern self.finder.Find(value, direction, pattern, self.context) def ValueChanged(self): value = self.textbox.currentText() pattern = self.pattern.isChecked() index = self.textbox.currentIndex() data = self.textbox.itemData(index) # Store the pattern in the combo box to keep it with the text value if data == None: self.textbox.setItemData(index, pattern) else: self.pattern.setChecked(data) self.Find(0) def NextPrev(self, direction): value = self.textbox.currentText() pattern = self.pattern.isChecked() if value != self.last_value: index = self.textbox.findText(value) # Allow for a button press before the value has been added to the combo box if index < 0: index = self.textbox.count() self.textbox.addItem(value, pattern) self.textbox.setCurrentIndex(index) return else: self.textbox.setItemData(index, pattern) elif pattern != self.last_pattern: # Keep the pattern recorded in the combo box up to date index = self.textbox.currentIndex() self.textbox.setItemData(index, pattern) self.Find(direction) def NotFound(self): QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") # Context-sensitive call graph data model item base class CallGraphLevelItemBase(object): def __init__(self, glb, row, parent_item): self.glb = glb self.row = row self.parent_item = parent_item self.query_done = False; self.child_count = 0 self.child_items = [] def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return self.parent_item def getRow(self): return self.row def childCount(self): if not self.query_done: self.Select() if not self.child_count: return -1 return self.child_count def hasChildren(self): if not self.query_done: return True return self.child_count > 0 def getData(self, column): return self.data[column] # Context-sensitive call graph data model level 2+ item base class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) self.comm_id = comm_id self.thread_id = thread_id self.call_path_id = call_path_id self.branch_count = branch_count self.time = time def Select(self): self.query_done = True; query = QSqlQuery(self.glb.db) QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" " FROM calls"