3588 lines · 112.0 KB · Python
RawDownload
				max_width[c] = max(max_width[c], len(val))				width = max_width[c]				align = model.headerData(col, Qt.Horizontal, Qt.TextAlignmentRole)				if align & Qt.AlignRight:					val = val.rjust(width)				text += pad + sep + val				pad = " " * (width - len(val))				sep = "  "		text += "\n"		pad = ""		sep = ""	last_row = min_row	for i in indexes:		if i.row() > last_row:			last_row = i.row()			text += "\n"			pad = ""			sep = ""		if as_csv:			text += sep + ToCSValue(str(i.data()))			sep = ","		else:			width = max_width[i.column() - min_col]			if i.data(Qt.TextAlignmentRole) & Qt.AlignRight:				val = str(i.data()).rjust(width)			else:				val = str(i.data())			text += pad + sep + val			pad = " " * (width - len(val))			sep = "  "	QApplication.clipboard().setText(text) def CopyTreeCellsToClipboard(view, as_csv=False, with_hdr=False):	indexes = view.selectedIndexes()	if not len(indexes):		return 	selection = view.selectionModel() 	first = None	for i in indexes:		above = view.indexAbove(i)		if not selection.isSelected(above):			first = i			break 	if first is None:		raise RuntimeError("CopyTreeCellsToClipboard internal error") 	model = first.model()	row_cnt = 0	col_cnt = model.columnCount(first)	max_width = [0] * col_cnt 	indent_sz = 2	indent_str = " " * indent_sz 	expanded_mark_sz = 2	if sys.version_info[0] == 3:		expanded_mark = "\u25BC "		not_expanded_mark = "\u25B6 "	else:		expanded_mark = unicode(chr(0xE2) + chr(0x96) + chr(0xBC) + " ", "utf-8")		not_expanded_mark =  unicode(chr(0xE2) + chr(0x96) + chr(0xB6) + " ", "utf-8")	leaf_mark = "  " 	if not as_csv:		pos = first		while True:			row_cnt += 1			row = pos.row()			for c in range(col_cnt):				i = pos.sibling(row, c)				if c:					n = len(str(i.data()))				else:					n = len(str(i.data()).strip())					n += (i.internalPointer().level - 1) * indent_sz					n += expanded_mark_sz				max_width[c] = max(max_width[c], n)			pos = view.indexBelow(pos)			if not selection.isSelected(pos):				break 	text = ""	pad = ""	sep = ""	if with_hdr:		for c in range(col_cnt):			val = model.headerData(c, Qt.Horizontal, Qt.DisplayRole).strip()			if as_csv:				text += sep + ToCSValue(val)				sep = ","			else:				max_width[c] = max(max_width[c], len(val))				width = max_width[c]				align = model.headerData(c, Qt.Horizontal, Qt.TextAlignmentRole)				if align & Qt.AlignRight:					val = val.rjust(width)				text += pad + sep + val				pad = " " * (width - len(val))				sep = "   "		text += "\n"		pad = ""		sep = "" 	pos = first	while True:		row = pos.row()		for c in range(col_cnt):			i = pos.sibling(row, c)			val = str(i.data())			if not c:				if model.hasChildren(i):					if view.isExpanded(i):						mark = expanded_mark					else:						mark = not_expanded_mark				else:					mark = leaf_mark				val = indent_str * (i.internalPointer().level - 1) + mark + val.strip()			if as_csv:				text += sep + ToCSValue(val)				sep = ","			else:				width = max_width[c]				if c and i.data(Qt.TextAlignmentRole) & Qt.AlignRight:					val = val.rjust(width)				text += pad + sep + val				pad = " " * (width - len(val))				sep = "   "		pos = view.indexBelow(pos)		if not selection.isSelected(pos):			break		text = text.rstrip() + "\n"		pad = ""		sep = "" 	QApplication.clipboard().setText(text) def CopyCellsToClipboard(view, as_csv=False, with_hdr=False):	view.CopyCellsToClipboard(view, as_csv, with_hdr) def CopyCellsToClipboardHdr(view):	CopyCellsToClipboard(view, False, True) def CopyCellsToClipboardCSV(view):	CopyCellsToClipboard(view, True, True) # Context menu class ContextMenu(object): 	def __init__(self, view):		self.view = view		self.view.setContextMenuPolicy(Qt.CustomContextMenu)		self.view.customContextMenuRequested.connect(self.ShowContextMenu) 	def ShowContextMenu(self, pos):		menu = QMenu(self.view)		self.AddActions(menu)		menu.exec_(self.view.mapToGlobal(pos)) 	def AddCopy(self, menu):		menu.addAction(CreateAction("&Copy selection", "Copy to clipboard", lambda: CopyCellsToClipboardHdr(self.view), self.view))		menu.addAction(CreateAction("Copy selection as CS&V", "Copy to clipboard as CSV", lambda: CopyCellsToClipboardCSV(self.view), self.view)) 	def AddActions(self, menu):		self.AddCopy(menu) class TreeContextMenu(ContextMenu): 	def __init__(self, view):		super(TreeContextMenu, self).__init__(view) 	def AddActions(self, menu):		i = self.view.currentIndex()		text = str(i.data()).strip()		if len(text):			menu.addAction(CreateAction('Copy "' + text + '"', "Copy to clipboard", lambda: QApplication.clipboard().setText(text), self.view))		self.AddCopy(menu) # Table window class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 	def __init__(self, glb, table_name, parent=None):		super(TableWindow, self).__init__(parent) 		self.data_model = LookupCreateModel(table_name + " Table", lambda: SQLAutoTableModel(glb, table_name)) 		self.model = QSortFilterProxyModel()		self.model.setSourceModel(self.data_model) 		self.view = QTableView()		self.view.setModel(self.model)		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)		self.view.verticalHeader().setVisible(False)		self.view.sortByColumn(-1, Qt.AscendingOrder)		self.view.setSortingEnabled(True)		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 		self.ResizeColumnsToContents() 		self.context_menu = ContextMenu(self.view) 		self.find_bar = FindBar(self, self, True) 		self.finder = ChildDataItemFinder(self.data_model) 		self.fetch_bar = FetchMoreRecordsBar(self.data_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, table_name + " Table") 	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.mapFromSource(self.data_model.index(row, 0, QModelIndex())))		else:			self.find_bar.NotFound() # Table list def GetTableList(glb):	tables = []	query = QSqlQuery(glb.db)	if glb.dbref.is_sqlite3:		QueryExec(query, "SELECT name FROM sqlite_master WHERE type IN ( 'table' , 'view' ) ORDER BY name")	else:		QueryExec(query, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type IN ( 'BASE TABLE' , 'VIEW' ) ORDER BY table_name")	while query.next():		tables.append(query.value(0))	if glb.dbref.is_sqlite3:		tables.append("sqlite_master")	else:		tables.append("information_schema.tables")		tables.append("information_schema.views")		tables.append("information_schema.columns")	return tables # Top Calls data model class TopCallsModel(SQLTableModel): 	def __init__(self, glb, report_vars, parent=None):		text = ""		if not glb.dbref.is_sqlite3:			text = "::text"		limit = ""		if len(report_vars.limit):			limit = " LIMIT " + report_vars.limit		sql = ("SELECT comm, pid, tid, name,"			" CASE"			" WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +			" ELSE short_name"			" END AS dso,"			" call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "			" CASE"			" WHEN (calls.flags = 1) THEN 'no call'" + text +			" WHEN (calls.flags = 2) THEN 'no return'" + text +			" WHEN (calls.flags = 3) THEN 'no call/return'" + text +			" ELSE ''" + text +			" END AS flags"			" FROM calls"			" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"			" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"			" INNER JOIN dsos ON symbols.dso_id = dsos.id"			" INNER JOIN comms ON calls.comm_id = comms.id"			" INNER JOIN threads ON calls.thread_id = threads.id" +			report_vars.where_clause +			" ORDER BY elapsed_time DESC" +			limit			)		column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")		self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)		super(TopCallsModel, self).__init__(glb, sql, column_headers, parent) 	def columnAlignment(self, column):		return self.alignment[column] # Top Calls report creation dialog class TopCallsDialog(ReportDialogBase): 	def __init__(self, glb, parent=None):		title = "Top Calls by Elapsed Time"		items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),			 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),			 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),			 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),			 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),			 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),			 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),			 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))		super(TopCallsDialog, self).__init__(glb, title, items, False, parent) # Top Calls window class TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase): 	def __init__(self, glb, report_vars, parent=None):		super(TopCallsWindow, self).__init__(parent) 		self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))		self.model = self.data_model 		self.view = QTableView()		self.view.setModel(self.model)		self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)		self.view.verticalHeader().setVisible(False)		self.view.setSelectionMode(QAbstractItemView.ContiguousSelection)		self.view.CopyCellsToClipboard = CopyTableCellsToClipboard 		self.context_menu = ContextMenu(self.view) 		self.ResizeColumnsToContents() 		self.find_bar = FindBar(self, self, True) 		self.finder = ChildDataItemFinder(self.model) 		self.fetch_bar = FetchMoreRecordsBar(self.data_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) 	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() # Action Definition def CreateAction(label, tip, callback, parent=None, shortcut=None):	action = QAction(label, parent)	if shortcut != None:		action.setShortcuts(shortcut)	action.setStatusTip(tip)	action.triggered.connect(callback)	return action # Typical application actions def CreateExitAction(app, parent=None):	return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) # Typical MDI actions def CreateCloseActiveWindowAction(mdi_area):	return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) def CreateCloseAllWindowsAction(mdi_area):	return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) def CreateTileWindowsAction(mdi_area):	return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) def CreateCascadeWindowsAction(mdi_area):	return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) def CreateNextWindowAction(mdi_area):	return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) def CreatePreviousWindowAction(mdi_area):	return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) # Typical MDI window menu class WindowMenu(): 	def __init__(self, mdi_area, menu):		self.mdi_area = mdi_area		self.window_menu = menu.addMenu("&Windows")		self.close_active_window = CreateCloseActiveWindowAction(mdi_area)		self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)		self.tile_windows = CreateTileWindowsAction(mdi_area)		self.cascade_windows = CreateCascadeWindowsAction(mdi_area)		self.next_window = CreateNextWindowAction(mdi_area)		self.previous_window = CreatePreviousWindowAction(mdi_area)		self.window_menu.aboutToShow.connect(self.Update) 	def Update(self):		self.window_menu.clear()		sub_window_count = len(self.mdi_area.subWindowList())		have_sub_windows = sub_window_count != 0		self.close_active_window.setEnabled(have_sub_windows)		self.close_all_windows.setEnabled(have_sub_windows)		self.tile_windows.setEnabled(have_sub_windows)		self.cascade_windows.setEnabled(have_sub_windows)		self.next_window.setEnabled(have_sub_windows)		self.previous_window.setEnabled(have_sub_windows)		self.window_menu.addAction(self.close_active_window)		self.window_menu.addAction(self.close_all_windows)		self.window_menu.addSeparator()		self.window_menu.addAction(self.tile_windows)		self.window_menu.addAction(self.cascade_windows)		self.window_menu.addSeparator()		self.window_menu.addAction(self.next_window)		self.window_menu.addAction(self.previous_window)		if sub_window_count == 0:			return		self.window_menu.addSeparator()		nr = 1		for sub_window in self.mdi_area.subWindowList():			label = str(nr) + " " + sub_window.name			if nr < 10:				label = "&" + label			action = self.window_menu.addAction(label)			action.setCheckable(True)			action.setChecked(sub_window == self.mdi_area.activeSubWindow())			action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))			self.window_menu.addAction(action)			nr += 1 	def setActiveSubWindow(self, nr):		self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) # Help text glb_help_text = """<h1>Contents</h1><style>p.c1 {    text-indent: 40px;}p.c2 {    text-indent: 80px;}}</style><p class=c1><a href=#reports>1. Reports</a></p><p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p><p class=c2><a href=#calltree>1.2 Call Tree</a></p><p class=c2><a href=#allbranches>1.3 All branches</a></p><p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p><p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p><p class=c1><a href=#tables>2. Tables</a></p><h1 id=reports>1. Reports</h1><h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>The result is a GUI window with a tree representing a context-sensitivecall-graph. Expanding a couple of levels of the tree and adjusting columnwidths to suit will display something like:<pre>                                         Call Graph: pt_exampleCall 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</pre><h3>Points to note:</h3><ul><li>The top level is a command name (comm)</li><li>The next level is a thread (pid:tid)</li><li>Subsequent levels are functions</li><li>'Count' is the number of calls</li><li>'Time' is the elapsed time until the function returns</li><li>Percentages are relative to the level above</li><li>'Branch Count' is the total number of branches for that function and all functions that it calls</ul><h3>Find</h3>Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match.The pattern matching symbols are ? for any character and * for zero or more characters.<h2 id=calltree>1.2 Call Tree</h2>The Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated.Also the 'Count' column, which would be always 1, is replaced by the 'Call Time'.<h2 id=allbranches>1.3 All branches</h2>The All branches report displays all branches in chronological order.Not all data is fetched immediately. More records can be fetched using the Fetch bar provided.<h3>Disassembly</h3>Open a branch to display disassembly. This only works if:<ol>

Lines 2501–3000 of 3588. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t