diff options
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
| -rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 345 | 
1 files changed, 264 insertions, 81 deletions
| diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index affed7d149be..6e7934f2ac9a 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/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. @@ -91,6 +91,7 @@  from __future__ import print_function  import sys +import argparse  import weakref  import threading  import string @@ -104,10 +105,23 @@ except ImportError:  	glb_nsz = 16  import re  import os -from PySide.QtCore import * -from PySide.QtGui import * -from PySide.QtSql import * +  pyside_version_1 = True +if 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 @@ -186,9 +200,10 @@ class Thread(QThread):  class TreeModel(QAbstractItemModel): -	def __init__(self, glb, parent=None): +	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 @@ -385,6 +400,7 @@ class FindBar():  	def Activate(self):  		self.bar.show() +		self.textbox.lineEdit().selectAll()  		self.textbox.setFocus()  	def Deactivate(self): @@ -449,8 +465,9 @@ class FindBar():  class CallGraphLevelItemBase(object): -	def __init__(self, glb, row, parent_item): +	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; @@ -489,18 +506,24 @@ class CallGraphLevelItemBase(object):  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) +	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): +		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)  		self.comm_id = comm_id  		self.thread_id = thread_id  		self.call_path_id = call_path_id +		self.insn_cnt = insn_cnt +		self.cyc_cnt = cyc_cnt  		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)" +		if self.params.have_ipc: +			ipc_str = ", SUM(insn_count), SUM(cyc_count)" +		else: +			ipc_str = "" +		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"  					" FROM calls"  					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"  					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id" @@ -511,7 +534,15 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):  					" GROUP BY call_path_id, name, short_name"  					" ORDER BY call_path_id")  		while query.next(): -			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) +			if self.params.have_ipc: +				insn_cnt = int(query.value(5)) +				cyc_cnt = int(query.value(6)) +				branch_count = int(query.value(7)) +			else: +				insn_cnt = 0 +				cyc_cnt = 0 +				branch_count = int(query.value(5)) +			child_item = CallGraphLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)  			self.child_items.append(child_item)  			self.child_count += 1 @@ -519,37 +550,57 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):  class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): -	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): -		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) +	def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item): +		super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)  		dso = dsoname(dso) -		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] +		if self.params.have_ipc: +			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) +			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) +			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) +			ipc = CalcIPC(cyc_cnt, insn_cnt) +			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ] +		else: +			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]  		self.dbid = call_path_id  # Context-sensitive call graph data model level two item  class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): -	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): -		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) -		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] +	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): +		super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item) +		if self.params.have_ipc: +			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] +		else: +			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]  		self.dbid = thread_id  	def Select(self):  		super(CallGraphLevelTwoItem, self).Select()  		for child_item in self.child_items:  			self.time += child_item.time +			self.insn_cnt += child_item.insn_cnt +			self.cyc_cnt += child_item.cyc_cnt  			self.branch_count += child_item.branch_count  		for child_item in self.child_items:  			child_item.data[4] = PercentToOneDP(child_item.time, self.time) -			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) +			if self.params.have_ipc: +				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) +				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) +				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) +			else: +				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)  # Context-sensitive call graph data model level one item  class CallGraphLevelOneItem(CallGraphLevelItemBase): -	def __init__(self, glb, row, comm_id, comm, parent_item): -		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) -		self.data = [comm, "", "", "", "", "", ""] +	def __init__(self, glb, params, row, comm_id, comm, parent_item): +		super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item) +		if self.params.have_ipc: +			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] +		else: +			self.data = [comm, "", "", "", "", "", ""]  		self.dbid = comm_id  	def Select(self): @@ -560,7 +611,7 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):  					" INNER JOIN threads ON thread_id = threads.id"  					" WHERE comm_id = " + str(self.dbid))  		while query.next(): -			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) +			child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)  			self.child_items.append(child_item)  			self.child_count += 1 @@ -568,8 +619,8 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):  class CallGraphRootItem(CallGraphLevelItemBase): -	def __init__(self, glb): -		super(CallGraphRootItem, self).__init__(glb, 0, None) +	def __init__(self, glb, params): +		super(CallGraphRootItem, self).__init__(glb, params, 0, None)  		self.dbid = 0  		self.query_done = True;  		query = QSqlQuery(glb.db) @@ -577,16 +628,23 @@ class CallGraphRootItem(CallGraphLevelItemBase):  		while query.next():  			if not query.value(0):  				continue -			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) +			child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)  			self.child_items.append(child_item)  			self.child_count += 1 +# Call graph model parameters + +class CallGraphModelParams(): + +	def __init__(self, glb, parent=None): +		self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count") +  # Context-sensitive call graph data model base  class CallGraphModelBase(TreeModel):  	def __init__(self, glb, parent=None): -		super(CallGraphModelBase, self).__init__(glb, parent) +		super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)  	def FindSelect(self, value, pattern, query):  		if pattern: @@ -668,17 +726,26 @@ class CallGraphModel(CallGraphModelBase):  		super(CallGraphModel, self).__init__(glb, parent)  	def GetRoot(self): -		return CallGraphRootItem(self.glb) +		return CallGraphRootItem(self.glb, self.params)  	def columnCount(self, parent=None): -		return 7 +		if self.params.have_ipc: +			return 12 +		else: +			return 7  	def columnHeader(self, column): -		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] +		if self.params.have_ipc: +			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] +		else: +			headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]  		return headers[column]  	def columnAlignment(self, column): -		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] +		if self.params.have_ipc: +			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] +		else: +			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]  		return alignment[column]  	def DoFindSelect(self, query, match): @@ -715,11 +782,13 @@ class CallGraphModel(CallGraphModelBase):  class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): -	def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): -		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) +	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item): +		super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)  		self.comm_id = comm_id  		self.thread_id = thread_id  		self.calls_id = calls_id +		self.insn_cnt = insn_cnt +		self.cyc_cnt = cyc_cnt  		self.branch_count = branch_count  		self.time = time @@ -729,8 +798,12 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):  			comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)  		else:  			comm_thread = "" +		if self.params.have_ipc: +			ipc_str = ", insn_count, cyc_count" +		else: +			ipc_str = ""  		query = QSqlQuery(self.glb.db) -		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" +		QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"  					" FROM calls"  					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"  					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id" @@ -738,7 +811,15 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):  					" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +  					" ORDER BY call_time, calls.id")  		while query.next(): -			child_item = CallTreeLevelThreeItem(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) +			if self.params.have_ipc: +				insn_cnt = int(query.value(5)) +				cyc_cnt = int(query.value(6)) +				branch_count = int(query.value(7)) +			else: +				insn_cnt = 0 +				cyc_cnt = 0 +				branch_count = int(query.value(5)) +			child_item = CallTreeLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)  			self.child_items.append(child_item)  			self.child_count += 1 @@ -746,37 +827,57 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):  class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): -	def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): -		super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) +	def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item): +		super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)  		dso = dsoname(dso) -		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] +		if self.params.have_ipc: +			insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt) +			cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt) +			br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count) +			ipc = CalcIPC(cyc_cnt, insn_cnt) +			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ] +		else: +			self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]  		self.dbid = calls_id  # Call tree data model level two item  class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): -	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): -		super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) -		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] +	def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item): +		super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item) +		if self.params.have_ipc: +			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""] +		else: +			self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]  		self.dbid = thread_id  	def Select(self):  		super(CallTreeLevelTwoItem, self).Select()  		for child_item in self.child_items:  			self.time += child_item.time +			self.insn_cnt += child_item.insn_cnt +			self.cyc_cnt += child_item.cyc_cnt  			self.branch_count += child_item.branch_count  		for child_item in self.child_items:  			child_item.data[4] = PercentToOneDP(child_item.time, self.time) -			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) +			if self.params.have_ipc: +				child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt) +				child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt) +				child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count) +			else: +				child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)  # Call tree data model level one item  class CallTreeLevelOneItem(CallGraphLevelItemBase): -	def __init__(self, glb, row, comm_id, comm, parent_item): -		super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) -		self.data = [comm, "", "", "", "", "", ""] +	def __init__(self, glb, params, row, comm_id, comm, parent_item): +		super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item) +		if self.params.have_ipc: +			self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""] +		else: +			self.data = [comm, "", "", "", "", "", ""]  		self.dbid = comm_id  	def Select(self): @@ -787,7 +888,7 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):  					" INNER JOIN threads ON thread_id = threads.id"  					" WHERE comm_id = " + str(self.dbid))  		while query.next(): -			child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) +			child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)  			self.child_items.append(child_item)  			self.child_count += 1 @@ -795,8 +896,8 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):  class CallTreeRootItem(CallGraphLevelItemBase): -	def __init__(self, glb): -		super(CallTreeRootItem, self).__init__(glb, 0, None) +	def __init__(self, glb, params): +		super(CallTreeRootItem, self).__init__(glb, params, 0, None)  		self.dbid = 0  		self.query_done = True;  		query = QSqlQuery(glb.db) @@ -804,7 +905,7 @@ class CallTreeRootItem(CallGraphLevelItemBase):  		while query.next():  			if not query.value(0):  				continue -			child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) +			child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)  			self.child_items.append(child_item)  			self.child_count += 1 @@ -816,17 +917,26 @@ class CallTreeModel(CallGraphModelBase):  		super(CallTreeModel, self).__init__(glb, parent)  	def GetRoot(self): -		return CallTreeRootItem(self.glb) +		return CallTreeRootItem(self.glb, self.params)  	def columnCount(self, parent=None): -		return 7 +		if self.params.have_ipc: +			return 12 +		else: +			return 7  	def columnHeader(self, column): -		headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] +		if self.params.have_ipc: +			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "] +		else: +			headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]  		return headers[column]  	def columnAlignment(self, column): -		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] +		if self.params.have_ipc: +			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] +		else: +			alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]  		return alignment[column]  	def DoFindSelect(self, query, match): @@ -1355,11 +1465,11 @@ class FetchMoreRecordsBar():  class BranchLevelTwoItem(): -	def __init__(self, row, text, parent_item): +	def __init__(self, row, col, text, parent_item):  		self.row = row  		self.parent_item = parent_item -		self.data = [""] * 8 -		self.data[7] = text +		self.data = [""] * (col + 1) +		self.data[col] = text  		self.level = 2  	def getParentItem(self): @@ -1391,6 +1501,7 @@ class BranchLevelOneItem():  		self.dbid = data[0]  		self.level = 1  		self.query_done = False +		self.br_col = len(self.data) - 1  	def getChildItem(self, row):  		return self.child_items[row] @@ -1471,7 +1582,7 @@ class BranchLevelOneItem():  				while k < 15:  					byte_str += "   "  					k += 1 -				self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) +				self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))  				self.child_count += 1  			else:  				return @@ -1522,16 +1633,37 @@ class BranchRootItem():  	def getData(self, column):  		return "" +# Calculate instructions per cycle + +def CalcIPC(cyc_cnt, insn_cnt): +	if cyc_cnt and insn_cnt: +		ipc = Decimal(float(insn_cnt) / cyc_cnt) +		ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) +	else: +		ipc = "0" +	return ipc +  # Branch data preparation -def BranchDataPrep(query): -	data = [] -	for i in xrange(0, 8): -		data.append(query.value(i)) +def BranchDataPrepBr(query, data):  	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +  			" (" + dsoname(query.value(11)) + ")" + " -> " +  			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +  			" (" + dsoname(query.value(15)) + ")") + +def BranchDataPrepIPC(query, data): +	insn_cnt = query.value(16) +	cyc_cnt = query.value(17) +	ipc = CalcIPC(cyc_cnt, insn_cnt) +	data.append(insn_cnt) +	data.append(cyc_cnt) +	data.append(ipc) + +def BranchDataPrep(query): +	data = [] +	for i in xrange(0, 8): +		data.append(query.value(i)) +	BranchDataPrepBr(query, data)  	return data  def BranchDataPrepWA(query): @@ -1541,10 +1673,26 @@ def BranchDataPrepWA(query):  	data.append("{:>19}".format(query.value(1)))  	for i in xrange(2, 8):  		data.append(query.value(i)) -	data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + -			" (" + dsoname(query.value(11)) + ")" + " -> " + -			tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + -			" (" + dsoname(query.value(15)) + ")") +	BranchDataPrepBr(query, data) +	return data + +def BranchDataWithIPCPrep(query): +	data = [] +	for i in xrange(0, 8): +		data.append(query.value(i)) +	BranchDataPrepIPC(query, data) +	BranchDataPrepBr(query, data) +	return data + +def BranchDataWithIPCPrepWA(query): +	data = [] +	data.append(query.value(0)) +	# Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string +	data.append("{:>19}".format(query.value(1))) +	for i in xrange(2, 8): +		data.append(query.value(i)) +	BranchDataPrepIPC(query, data) +	BranchDataPrepBr(query, data)  	return data  # Branch data model @@ -1554,14 +1702,24 @@ class BranchModel(TreeModel):  	progress = Signal(object)  	def __init__(self, glb, event_id, where_clause, parent=None): -		super(BranchModel, self).__init__(glb, parent) +		super(BranchModel, self).__init__(glb, None, parent)  		self.event_id = event_id  		self.more = True  		self.populated = 0 +		self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") +		if self.have_ipc: +			select_ipc = ", insn_count, cyc_count" +			prep_fn = BranchDataWithIPCPrep +			prep_wa_fn = BranchDataWithIPCPrepWA +		else: +			select_ipc = "" +			prep_fn = BranchDataPrep +			prep_wa_fn = BranchDataPrepWA  		sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"  			" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"  			" ip, symbols.name, sym_offset, dsos.short_name,"  			" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" +			+ select_ipc +  			" FROM samples"  			" INNER JOIN comms ON comm_id = comms.id"  			" INNER JOIN threads ON thread_id = threads.id" @@ -1575,9 +1733,9 @@ class BranchModel(TreeModel):  			" ORDER BY samples.id"  			" LIMIT " + str(glb_chunk_sz))  		if pyside_version_1 and sys.version_info[0] == 3: -			prep = BranchDataPrepWA +			prep = prep_fn  		else: -			prep = BranchDataPrep +			prep = prep_wa_fn  		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)  		self.fetcher.done.connect(self.Update)  		self.fetcher.Fetch(glb_chunk_sz) @@ -1586,13 +1744,23 @@ class BranchModel(TreeModel):  		return BranchRootItem()  	def columnCount(self, parent=None): -		return 8 +		if self.have_ipc: +			return 11 +		else: +			return 8  	def columnHeader(self, column): -		return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] +		if self.have_ipc: +			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] +		else: +			return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]  	def columnFont(self, column): -		if column != 7: +		if self.have_ipc: +			br_col = 10 +		else: +			br_col = 7 +		if column != br_col:  			return None  		return QFont("Monospace") @@ -2100,10 +2268,10 @@ def GetEventList(db):  # Is a table selectable -def IsSelectable(db, table, sql = ""): +def IsSelectable(db, table, sql = "", columns = "*"):  	query = QSqlQuery(db)  	try: -		QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") +		QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")  	except:  		return False  	return True @@ -2754,7 +2922,7 @@ class WindowMenu():  			action = self.window_menu.addAction(label)  			action.setCheckable(True)  			action.setChecked(sub_window == self.mdi_area.activeSubWindow()) -			action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) +			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))  			self.window_menu.addAction(action)  			nr += 1 @@ -2840,6 +3008,12 @@ cd xed  sudo ./mfile.py --prefix=/usr/local install  sudo ldconfig  </pre> +<h3>Instructions per Cycle (IPC)</h3> +If available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'. +<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch. +Due to the granularity of timing information, the number of cycles for some code blocks will not be known. +In that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period +since the previous displayed 'IPC'.  <h3>Find</h3>  Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.  Refer to Python documentation for the regular expression syntax. @@ -3114,14 +3288,14 @@ class MainWindow(QMainWindow):  			event = event.split(":")[0]  			if event == "branches":  				label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" -				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) +				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))  				label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")" -				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self)) +				reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))  	def TableMenu(self, tables, menu):  		table_menu = menu.addMenu("&Tables")  		for table in tables: -			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self)) +			table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))  	def NewCallGraph(self):  		CallGraphWindow(self.glb, self) @@ -3361,18 +3535,27 @@ class DBRef():  # Main  def Main(): -	if (len(sys.argv) < 2): -		printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}"); -		raise Exception("Too few arguments") - -	dbname = sys.argv[1] -	if dbname == "--help-only": +	usage_str =	"exported-sql-viewer.py [--pyside-version-1] <database name>\n" \ +			"   or: exported-sql-viewer.py --help-only" +	ap = argparse.ArgumentParser(usage = usage_str, add_help = False) +	ap.add_argument("--pyside-version-1", action='store_true') +	ap.add_argument("dbname", nargs="?") +	ap.add_argument("--help-only", action='store_true') +	args = ap.parse_args() + +	if args.help_only:  		app = QApplication(sys.argv)  		mainwindow = HelpOnlyWindow()  		mainwindow.show()  		err = app.exec_()  		sys.exit(err) +	dbname = args.dbname +	if dbname is None: +		ap.print_usage() +		print("Too few arguments") +		sys.exit(1) +  	is_sqlite3 = False  	try:  		f = open(dbname, "rb") | 
