5030 lines · 153.7 KB · Python
RawDownload
		return self.data[column] # Brance data model root item class BranchRootItem(): 	def __init__(self):		self.child_count = 0		self.child_items = []		self.level = 0 	def getChildItem(self, row):		return self.child_items[row] 	def getParentItem(self):		return None 	def getRow(self):		return 0 	def childCount(self):		return self.child_count 	def hasChildren(self):		return self.child_count > 0 	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 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):	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))	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 class BranchModel(TreeModel): 	progress = Signal(object) 	def __init__(self, glb, event_id, where_clause, parent=None):		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"			" INNER JOIN branch_types ON branch_type = branch_types.id"			" INNER JOIN symbols ON symbol_id = symbols.id"			" INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id"			" INNER JOIN dsos ON samples.dso_id = dsos.id"			" INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id"			" WHERE samples.id > $$last_id$$" + where_clause +			" AND evsel_id = " + str(self.event_id) +			" ORDER BY samples.id"			" LIMIT " + str(glb_chunk_sz))		if pyside_version_1 and sys.version_info[0] == 3:			prep = prep_fn		else:			prep = prep_wa_fn		self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)		self.fetcher.done.connect(self.Update)		self.fetcher.Fetch(glb_chunk_sz) 	def GetRoot(self):		return BranchRootItem() 	def columnCount(self, parent=None):		if self.have_ipc:			return 11		else:			return 8 	def columnHeader(self, 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 self.have_ipc:			br_col = 10		else:			br_col = 7		if column != br_col:			return None		return QFont("Monospace") 	def DisplayData(self, item, index):		if item.level == 1:			self.FetchIfNeeded(item.row)		return item.getData(index.column()) 	def AddSample(self, data):		child = BranchLevelOneItem(self.glb, self.populated, data, self.root)		self.root.child_items.append(child)		self.populated += 1 	def Update(self, fetched):		if not fetched:			self.more = False			self.progress.emit(0)		child_count = self.root.child_count		count = self.populated - child_count		if count > 0:			parent = QModelIndex()			self.beginInsertRows(parent, child_count, child_count + count - 1)			self.insertRows(child_count, count, parent)			self.root.child_count += count			self.endInsertRows()			self.progress.emit(self.root.child_count) 	def FetchMoreRecords(self, count):		current = self.root.child_count		if self.more:			self.fetcher.Fetch(count)		else:			self.progress.emit(0)		return current 	def HasMoreRecords(self):		return self.more # Report Variables class ReportVars(): 	def __init__(self, name = "", where_clause = "", limit = ""):		self.name = name		self.where_clause = where_clause		self.limit = limit 	def UniqueId(self):		return str(self.where_clause + ";" + self.limit) # Branch window class BranchWindow(QMdiSubWindow): 	def __init__(self, glb, event_id, report_vars, parent=None):		super(BranchWindow, self).__init__(parent) 		model_name = "Branch Events " + str(event_id) +  " " + report_vars.UniqueId() 		self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) 		self.view = QTreeView()		self.view.setUniformRowHeights(True)		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)		self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard		self.view.setModel(self.model) 		self.ResizeColumnsToContents() 		self.context_menu = TreeContextMenu(self.view) 		self.find_bar = FindBar(self, self, True) 		self.finder = ChildDataItemFinder(self.model.root) 		self.fetch_bar = FetchMoreRecordsBar(self.model, self) 		self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) 		self.setWidget(self.vbox.Widget()) 		AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") 	def ResizeColumnToContents(self, column, n):		# Using the view's resizeColumnToContents() here is extrememly slow		# so implement a crude alternative		mm = "MM" if column else "MMMM"		font = self.view.font()		metrics = QFontMetrics(font)		max = 0		for row in xrange(n):			val = self.model.root.child_items[row].data[column]			len = metrics.width(str(val) + mm)			max = len if len > max else max		val = self.model.columnHeader(column)		len = metrics.width(str(val) + mm)		max = len if len > max else max		self.view.setColumnWidth(column, max) 	def ResizeColumnsToContents(self):		n = min(self.model.root.child_count, 100)		if n < 1:			# No data yet, so connect a signal to notify when there is			self.model.rowsInserted.connect(self.UpdateColumnWidths)			return		columns = self.model.columnCount()		for i in xrange(columns):			self.ResizeColumnToContents(i, n) 	def UpdateColumnWidths(self, *x):		# This only needs to be done once, so disconnect the signal now		self.model.rowsInserted.disconnect(self.UpdateColumnWidths)		self.ResizeColumnsToContents() 	def Find(self, value, direction, pattern, context):		self.view.setFocus()		self.find_bar.Busy()		self.finder.Find(value, direction, pattern, context, self.FindDone) 	def FindDone(self, row):		self.find_bar.Idle()		if row >= 0:			self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))		else:			self.find_bar.NotFound() # Line edit data item class LineEditDataItem(object): 	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):		self.glb = glb		self.label = label		self.placeholder_text = placeholder_text		self.parent = parent		self.id = id 		self.value = default 		self.widget = QLineEdit(default)		self.widget.editingFinished.connect(self.Validate)		self.widget.textChanged.connect(self.Invalidate)		self.red = False		self.error = ""		self.validated = True 		if placeholder_text:			self.widget.setPlaceholderText(placeholder_text) 	def TurnTextRed(self):		if not self.red:			palette = QPalette()			palette.setColor(QPalette.Text,Qt.red)			self.widget.setPalette(palette)			self.red = True 	def TurnTextNormal(self):		if self.red:			palette = QPalette()			self.widget.setPalette(palette)			self.red = False 	def InvalidValue(self, value):		self.value = ""		self.TurnTextRed()		self.error = self.label + " invalid value '" + value + "'"		self.parent.ShowMessage(self.error) 	def Invalidate(self):		self.validated = False 	def DoValidate(self, input_string):		self.value = input_string.strip() 	def Validate(self):		self.validated = True		self.error = ""		self.TurnTextNormal()		self.parent.ClearMessage()		input_string = self.widget.text()		if not len(input_string.strip()):			self.value = ""			return		self.DoValidate(input_string) 	def IsValid(self):		if not self.validated:			self.Validate()		if len(self.error):			self.parent.ShowMessage(self.error)			return False		return True 	def IsNumber(self, value):		try:			x = int(value)		except:			x = 0		return str(x) == value # Non-negative integer ranges dialog data item class NonNegativeIntegerRangesDataItem(LineEditDataItem): 	def __init__(self, glb, label, placeholder_text, column_name, parent):		super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 		self.column_name = column_name 	def DoValidate(self, input_string):		singles = []		ranges = []		for value in [x.strip() for x in input_string.split(",")]:			if "-" in value:				vrange = value.split("-")				if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):					return self.InvalidValue(value)				ranges.append(vrange)			else:				if not self.IsNumber(value):					return self.InvalidValue(value)				singles.append(value)		ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]		if len(singles):			ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")		self.value = " OR ".join(ranges) # Positive integer dialog data item class PositiveIntegerDataItem(LineEditDataItem): 	def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):		super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default) 	def DoValidate(self, input_string):		if not self.IsNumber(input_string.strip()):			return self.InvalidValue(input_string)		value = int(input_string.strip())		if value <= 0:			return self.InvalidValue(input_string)		self.value = str(value) # Dialog data item converted and validated using a SQL table class SQLTableDataItem(LineEditDataItem): 	def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):		super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) 		self.table_name = table_name		self.match_column = match_column		self.column_name1 = column_name1		self.column_name2 = column_name2 	def ValueToIds(self, value):		ids = []		query = QSqlQuery(self.glb.db)		stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"		ret = query.exec_(stmt)		if ret:			while query.next():				ids.append(str(query.value(0)))		return ids 	def DoValidate(self, input_string):		all_ids = []		for value in [x.strip() for x in input_string.split(",")]:			ids = self.ValueToIds(value)			if len(ids):				all_ids.extend(ids)			else:				return self.InvalidValue(value)		self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"		if self.column_name2:			self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" # Sample time ranges dialog data item converted and validated using 'samples' SQL table class SampleTimeRangesDataItem(LineEditDataItem): 	def __init__(self, glb, label, placeholder_text, column_name, parent):		self.column_name = column_name 		self.last_id = 0		self.first_time = 0		self.last_time = 2 ** 64 		query = QSqlQuery(glb.db)		QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")		if query.next():			self.last_id = int(query.value(0))		self.first_time = int(glb.HostStartTime())		self.last_time = int(glb.HostFinishTime())		if placeholder_text:			placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) 		super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) 	def IdBetween(self, query, lower_id, higher_id, order):		QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")		if query.next():			return True, int(query.value(0))		else:			return False, 0 	def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):		query = QSqlQuery(self.glb.db)		while True:			next_id = int((lower_id + higher_id) / 2)			QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))			if not query.next():				ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")				if not ok:					ok, dbid = self.IdBetween(query, next_id, higher_id, "")					if not ok:						return str(higher_id)				next_id = dbid				QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))			next_time = int(query.value(0))			if get_floor:				if target_time > next_time:					lower_id = next_id				else:					higher_id = next_id				if higher_id <= lower_id + 1:					return str(higher_id)			else:				if target_time >= next_time:					lower_id = next_id				else:					higher_id = next_id				if higher_id <= lower_id + 1:					return str(lower_id) 	def ConvertRelativeTime(self, val):		mult = 1		suffix = val[-2:]		if suffix == "ms":			mult = 1000000		elif suffix == "us":			mult = 1000		elif suffix == "ns":			mult = 1

Lines 3001–3500 of 5030. Previous lines