5108 lines · 155.9 KB · Python
RawDownload
#!/usr/bin/env python# 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 argparseimport 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 osimport randomimport copyimport math pyside_version_1 = Trueif not "--pyside-version-1" in sys.argv:	try:		from PySide2.QtCore import *		from PySide2.QtGui import *		from PySide2.QtSql import *		from PySide2.QtWidgets import *		pyside_version_1 = False	except:		pass if pyside_version_1:	from PySide.QtCore import *	from PySide.QtGui import *	from PySide.QtSql import * from 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, params, parent=None):		super(TreeModel, self).__init__(parent)		self.glb = glb		self.params = params		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 def LookupModel(model_name):	model_cache_lock.acquire()	try:		model = model_cache[model_name]	except:		model = None	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.lineEdit().selectAll()		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, params, row, parent_item):		self.glb = glb		self.params = params		self.row = row		self.parent_item = parent_item		self.query_done = False		self.child_count = 0		self.child_items = []		if parent_item:			self.level = parent_item.level + 1		else:			self.level = 0 	def getChildItem(self, row):		return self.child_items[row] 	def getParentItem(self):		return self.parent_item 	def getRow(self):		return self.row

Lines 1–500 of 5108.