<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li><li>The object code is available. Currently, only the perf build ID cache is searched for object code.The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),or alternatively, set environment variable PERF_KCORE to the kcore file name.</li></ol><h4 id=xed>Intel XED Setup</h4>To use Intel XED, libxed.so must be present. To build and install libxed.so:<pre>git clone https://github.com/intelxed/mbuild.git mbuildgit clone https://github.com/intelxed/xedcd xed./mfile.py --sharesudo ./mfile.py --prefix=/usr/local installsudo 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 periodsince 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.All columns are searched, but only currently fetched rows are searched.<h2 id=selectedbranches>1.4 Selected branches</h2>This is the same as the <a href=#allbranches>All branches</a> report but with the data reducedby various selection criteria. A dialog box displays available criteria which are AND'ed together.<h3>1.4.1 Time ranges</h3>The time ranges hint text shows the total time range. Relative time ranges can also be entered inms, us or ns. Also, negative values are relative to the end of trace. Examples:<pre> 81073085947329-81073085958238 From 81073085947329 to 81073085958238 100us-200us From 100us to 200us 10ms- From 10ms to the end -100ns The first 100ns -10ms- The last 10ms</pre>N.B. Due to the granularity of timestamps, there could be no branches in any given time range.<h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2>The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.<h1 id=tables>2. Tables</h1>The Tables menu shows all tables and views in the database. Most tables have an associated viewwhich displays the information in a more friendly way. Not all data for large tables is fetchedimmediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,but that can be slow for large tables.<p>There are also tables of database meta-information.For SQLite3 databases, the sqlite_master table is included.For PostgreSQL databases, information_schema.tables/views/columns are included.<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.All columns are searched, but only currently fetched rows are searched.<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previouswill go to the next/previous result in id order, instead of display order.""" # Help window class HelpWindow(QMdiSubWindow): def __init__(self, glb, parent=None): super(HelpWindow, self).__init__(parent) self.text = QTextBrowser() self.text.setHtml(glb_help_text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.setWidget(self.text) AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") # Main window that only displays the help text class HelpOnlyWindow(QMainWindow): def __init__(self, parent=None): super(HelpOnlyWindow, self).__init__(parent) self.setMinimumSize(200, 100) self.resize(800, 600) self.setWindowTitle("Exported SQL Viewer Help") self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) self.text = QTextBrowser() self.text.setHtml(glb_help_text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.setCentralWidget(self.text) # PostqreSQL server version def PostqreSQLServerVersion(db): query = QSqlQuery(db) QueryExec(query, "SELECT VERSION()") if query.next(): v_str = query.value(0) v_list = v_str.strip().split(" ") if v_list[0] == "PostgreSQL" and v_list[2] == "on": return v_list[1] return v_str return "Unknown" # SQLite version def SQLiteVersion(db): query = QSqlQuery(db) QueryExec(query, "SELECT sqlite_version()") if query.next(): return query.value(0) return "Unknown" # About dialog class AboutDialog(QDialog): def __init__(self, glb, parent=None): super(AboutDialog, self).__init__(parent) self.setWindowTitle("About Exported SQL Viewer") self.setMinimumWidth(300) pyside_version = "1" if pyside_version_1 else "2" text = "<pre>" text += "Python version: " + sys.version.split(" ")[0] + "\n" text += "PySide version: " + pyside_version + "\n" text += "Qt version: " + qVersion() + "\n" if glb.dbref.is_sqlite3: text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" else: text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" text += "</pre>" self.text = QTextBrowser() self.text.setHtml(text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.vbox = QVBoxLayout() self.vbox.addWidget(self.text) self.setLayout(self.vbox) # Font resize def ResizeFont(widget, diff): font = widget.font() sz = font.pointSize() font.setPointSize(sz + diff) widget.setFont(font) def ShrinkFont(widget): ResizeFont(widget, -1) def EnlargeFont(widget): ResizeFont(widget, 1) # Unique name for sub-windows def NumberedWindowName(name, nr): if nr > 1: name += " <" + str(nr) + ">" return name def UniqueSubWindowName(mdi_area, name): nr = 1 while True: unique_name = NumberedWindowName(name, nr) ok = True for sub_window in mdi_area.subWindowList(): if sub_window.name == unique_name: ok = False break if ok: return unique_name nr += 1 # Add a sub-window def AddSubWindow(mdi_area, sub_window, name): unique_name = UniqueSubWindowName(mdi_area, name) sub_window.setMinimumSize(200, 100) sub_window.resize(800, 600) sub_window.setWindowTitle(unique_name) sub_window.setAttribute(Qt.WA_DeleteOnClose) sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) sub_window.name = unique_name mdi_area.addSubWindow(sub_window) sub_window.show() # Main window class MainWindow(QMainWindow): def __init__(self, glb, parent=None): super(MainWindow, self).__init__(parent) self.glb = glb self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.setMinimumSize(200, 100) self.mdi_area = QMdiArea() self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdi_area) menu = self.menuBar() file_menu = menu.addMenu("&File") file_menu.addAction(CreateExitAction(glb.app, self)) edit_menu = menu.addMenu("&Edit") edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) reports_menu = menu.addMenu("&Reports") if IsSelectable(glb.db, "calls"): reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) self.EventMenu(GetEventList(glb.db), reports_menu) if IsSelectable(glb.db, "calls"): reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) self.TableMenu(GetTableList(glb), menu) self.window_menu = WindowMenu(self.mdi_area, menu) help_menu = menu.addMenu("&Help") help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) def Try(self, fn): win = self.mdi_area.activeSubWindow() if win: try: fn(win.view) except: pass def CopyToClipboard(self): self.Try(CopyCellsToClipboardHdr) def CopyToClipboardCSV(self): self.Try(CopyCellsToClipboardCSV) def Find(self): win = self.mdi_area.activeSubWindow() if win: try: win.find_bar.Activate() except: pass def FetchMoreRecords(self): win = self.mdi_area.activeSubWindow() if win: try: win.fetch_bar.Activate() except: pass def ShrinkFont(self): self.Try(ShrinkFont) def EnlargeFont(self): self.Try(EnlargeFont) def EventMenu(self, events, reports_menu): branches_events = 0 for event in events: event = event.split(":")[0] if event == "branches": branches_events += 1 dbid = 0 for event in events: dbid += 1 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 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 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 a=None,t=table: self.NewTableView(t), self)) def NewCallGraph(self): CallGraphWindow(self.glb, self) def NewCallTree(self): CallTreeWindow(self.glb, self) def NewTopCalls(self): dialog = TopCallsDialog(self.glb, self) ret = dialog.exec_() if ret: TopCallsWindow(self.glb, dialog.report_vars, self) def NewBranchView(self, event_id): BranchWindow(self.glb, event_id, ReportVars(), self) def NewSelectedBranchView(self, event_id): dialog = SelectedBranchDialog(self.glb, self) ret = dialog.exec_() if ret: BranchWindow(self.glb, event_id, dialog.report_vars, self) def NewTableView(self, table_name): TableWindow(self.glb, table_name, self) def Help(self): HelpWindow(self.glb, self) def About(self): dialog = AboutDialog(self.glb, self) dialog.exec_() # XED Disassembler class xed_state_t(Structure): _fields_ = [ ("mode", c_int), ("width", c_int) ] class XEDInstruction(): def __init__(self, libxed): # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion xedd_t = c_byte * 512 self.xedd = xedd_t() self.xedp = addressof(self.xedd) libxed.xed_decoded_inst_zero(self.xedp) self.state = xed_state_t() self.statep = addressof(self.state) # Buffer for disassembled instruction text self.buffer = create_string_buffer(256) self.bufferp = addressof(self.buffer) class LibXED(): def __init__(self): try: self.libxed = CDLL("libxed.so") except: self.libxed = None if not self.libxed: self.libxed = CDLL("/usr/local/lib/libxed.so") self.xed_tables_init = self.libxed.xed_tables_init self.xed_tables_init.restype = None self.xed_tables_init.argtypes = [] self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero self.xed_decoded_inst_zero.restype = None self.xed_decoded_inst_zero.argtypes = [ c_void_p ] self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode self.xed_operand_values_set_mode.restype = None self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode self.xed_decoded_inst_zero_keep_mode.restype = None self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] self.xed_decode = self.libxed.xed_decode self.xed_decode.restype = c_int self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] self.xed_format_context = self.libxed.xed_format_context self.xed_format_context.restype = c_uint self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] self.xed_tables_init() def Instruction(self): return XEDInstruction(self) def SetMode(self, inst, mode): if mode: inst.state.mode = 4 # 32-bit inst.state.width = 4 # 4 bytes else: inst.state.mode = 1 # 64-bit inst.state.width = 8 # 8 bytes self.xed_operand_values_set_mode(inst.xedp, inst.statep) def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): self.xed_decoded_inst_zero_keep_mode(inst.xedp) err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) if err: return 0, "" # Use AT&T mode (2), alternative is Intel (3) ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) if not ok: return 0, "" if sys.version_info[0] == 2: result = inst.buffer.value else: result = inst.buffer.value.decode() # Return instruction length and the disassembled instruction text # For now, assume the length is in byte 166 return inst.xedd[166], result def TryOpen(file_name): try: return open(file_name, "rb") except: return None def Is64Bit(f): result = sizeof(c_void_p) # ELF support only pos = f.tell() f.seek(0) header = f.read(7) f.seek(pos) magic = header[0:4] if sys.version_info[0] == 2: eclass = ord(header[4]) encoding = ord(header[5]) version = ord(header[6]) else: eclass = header[4] encoding = header[5] version = header[6] if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: result = True if eclass == 2 else False return result # Global data class Glb(): def __init__(self, dbref, db, dbname): self.dbref = dbref self.db = db self.dbname = dbname self.home_dir = os.path.expanduser("~") self.buildid_dir = os.getenv("PERF_BUILDID_DIR") if self.buildid_dir: self.buildid_dir += "/.build-id/" else: self.buildid_dir = self.home_dir + "/.debug/.build-id/" self.app = None self.mainwindow = None self.instances_to_shutdown_on_exit = weakref.WeakSet() try: self.disassembler = LibXED() self.have_disassembler = True except: self.have_disassembler = False def FileFromBuildId(self, build_id): file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" return TryOpen(file_name) def FileFromNamesAndBuildId(self, short_name, long_name, build_id): # Assume current machine i.e. no support for virtualization if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": file_name = os.getenv("PERF_KCORE") f = TryOpen(file_name) if file_name else None if f: return f # For now, no special handling if long_name is /proc/kcore f = TryOpen(long_name) if f: return f f = self.FileFromBuildId(build_id) if f: return f return None def AddInstanceToShutdownOnExit(self, instance): self.instances_to_shutdown_on_exit.add(instance) # Shutdown any background processes or threads def ShutdownInstances(self): for x in self.instances_to_shutdown_on_exit: try:Lines 3001–3500 of 3588. Previous lines Load more
<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li> <li>The object code is available. Currently, only the perf build ID cache is searched for object code. The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR. One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu), or alternatively, set environment variable PERF_KCORE to the kcore file name.</li> </ol> <h4 id=xed>Intel XED Setup</h4> To use Intel XED, libxed.so must be present. To build and install libxed.so: <pre> 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 </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. All columns are searched, but only currently fetched rows are searched. <h2 id=selectedbranches>1.4 Selected branches</h2> This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. <h3>1.4.1 Time ranges</h3> The time ranges hint text shows the total time range. Relative time ranges can also be entered in ms, us or ns. Also, negative values are relative to the end of trace. Examples: <pre> 81073085947329-81073085958238 From 81073085947329 to 81073085958238 100us-200us From 100us to 200us 10ms- From 10ms to the end -100ns The first 100ns -10ms- The last 10ms </pre> N.B. Due to the granularity of timestamps, there could be no branches in any given time range. <h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned. The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. <h1 id=tables>2. Tables</h1> The Tables menu shows all tables and views in the database. Most tables have an associated view which displays the information in a more friendly way. Not all data for large tables is fetched immediately. More records can be fetched using the Fetch bar provided. Columns can be sorted, but that can be slow for large tables. <p>There are also tables of database meta-information. For SQLite3 databases, the sqlite_master table is included. For PostgreSQL databases, information_schema.tables/views/columns are included. <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. All columns are searched, but only currently fetched rows are searched. <p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous will go to the next/previous result in id order, instead of display order. """ # Help window class HelpWindow(QMdiSubWindow): def __init__(self, glb, parent=None): super(HelpWindow, self).__init__(parent) self.text = QTextBrowser() self.text.setHtml(glb_help_text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.setWidget(self.text) AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help") # Main window that only displays the help text class HelpOnlyWindow(QMainWindow): def __init__(self, parent=None): super(HelpOnlyWindow, self).__init__(parent) self.setMinimumSize(200, 100) self.resize(800, 600) self.setWindowTitle("Exported SQL Viewer Help") self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation)) self.text = QTextBrowser() self.text.setHtml(glb_help_text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.setCentralWidget(self.text) # PostqreSQL server version def PostqreSQLServerVersion(db): query = QSqlQuery(db) QueryExec(query, "SELECT VERSION()") if query.next(): v_str = query.value(0) v_list = v_str.strip().split(" ") if v_list[0] == "PostgreSQL" and v_list[2] == "on": return v_list[1] return v_str return "Unknown" # SQLite version def SQLiteVersion(db): query = QSqlQuery(db) QueryExec(query, "SELECT sqlite_version()") if query.next(): return query.value(0) return "Unknown" # About dialog class AboutDialog(QDialog): def __init__(self, glb, parent=None): super(AboutDialog, self).__init__(parent) self.setWindowTitle("About Exported SQL Viewer") self.setMinimumWidth(300) pyside_version = "1" if pyside_version_1 else "2" text = "<pre>" text += "Python version: " + sys.version.split(" ")[0] + "\n" text += "PySide version: " + pyside_version + "\n" text += "Qt version: " + qVersion() + "\n" if glb.dbref.is_sqlite3: text += "SQLite version: " + SQLiteVersion(glb.db) + "\n" else: text += "PostqreSQL version: " + PostqreSQLServerVersion(glb.db) + "\n" text += "</pre>" self.text = QTextBrowser() self.text.setHtml(text) self.text.setReadOnly(True) self.text.setOpenExternalLinks(True) self.vbox = QVBoxLayout() self.vbox.addWidget(self.text) self.setLayout(self.vbox) # Font resize def ResizeFont(widget, diff): font = widget.font() sz = font.pointSize() font.setPointSize(sz + diff) widget.setFont(font) def ShrinkFont(widget): ResizeFont(widget, -1) def EnlargeFont(widget): ResizeFont(widget, 1) # Unique name for sub-windows def NumberedWindowName(name, nr): if nr > 1: name += " <" + str(nr) + ">" return name def UniqueSubWindowName(mdi_area, name): nr = 1 while True: unique_name = NumberedWindowName(name, nr) ok = True for sub_window in mdi_area.subWindowList(): if sub_window.name == unique_name: ok = False break if ok: return unique_name nr += 1 # Add a sub-window def AddSubWindow(mdi_area, sub_window, name): unique_name = UniqueSubWindowName(mdi_area, name) sub_window.setMinimumSize(200, 100) sub_window.resize(800, 600) sub_window.setWindowTitle(unique_name) sub_window.setAttribute(Qt.WA_DeleteOnClose) sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) sub_window.name = unique_name mdi_area.addSubWindow(sub_window) sub_window.show() # Main window class MainWindow(QMainWindow): def __init__(self, glb, parent=None): super(MainWindow, self).__init__(parent) self.glb = glb self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.setMinimumSize(200, 100) self.mdi_area = QMdiArea() self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdi_area) menu = self.menuBar() file_menu = menu.addMenu("&File") file_menu.addAction(CreateExitAction(glb.app, self)) edit_menu = menu.addMenu("&Edit") edit_menu.addAction(CreateAction("&Copy", "Copy to clipboard", self.CopyToClipboard, self, QKeySequence.Copy)) edit_menu.addAction(CreateAction("Copy as CS&V", "Copy to clipboard as CSV", self.CopyToClipboardCSV, self)) edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) edit_menu.addAction(CreateAction("Fetch &more records...", "Fetch more records", self.FetchMoreRecords, self, [QKeySequence(Qt.Key_F8)])) edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) reports_menu = menu.addMenu("&Reports") if IsSelectable(glb.db, "calls"): reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) self.EventMenu(GetEventList(glb.db), reports_menu) if IsSelectable(glb.db, "calls"): reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self)) self.TableMenu(GetTableList(glb), menu) self.window_menu = WindowMenu(self.mdi_area, menu) help_menu = menu.addMenu("&Help") help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents)) help_menu.addAction(CreateAction("&About Exported SQL Viewer", "About this application", self.About, self)) def Try(self, fn): win = self.mdi_area.activeSubWindow() if win: try: fn(win.view) except: pass def CopyToClipboard(self): self.Try(CopyCellsToClipboardHdr) def CopyToClipboardCSV(self): self.Try(CopyCellsToClipboardCSV) def Find(self): win = self.mdi_area.activeSubWindow() if win: try: win.find_bar.Activate() except: pass def FetchMoreRecords(self): win = self.mdi_area.activeSubWindow() if win: try: win.fetch_bar.Activate() except: pass def ShrinkFont(self): self.Try(ShrinkFont) def EnlargeFont(self): self.Try(EnlargeFont) def EventMenu(self, events, reports_menu): branches_events = 0 for event in events: event = event.split(":")[0] if event == "branches": branches_events += 1 dbid = 0 for event in events: dbid += 1 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 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 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 a=None,t=table: self.NewTableView(t), self)) def NewCallGraph(self): CallGraphWindow(self.glb, self) def NewCallTree(self): CallTreeWindow(self.glb, self) def NewTopCalls(self): dialog = TopCallsDialog(self.glb, self) ret = dialog.exec_() if ret: TopCallsWindow(self.glb, dialog.report_vars, self) def NewBranchView(self, event_id): BranchWindow(self.glb, event_id, ReportVars(), self) def NewSelectedBranchView(self, event_id): dialog = SelectedBranchDialog(self.glb, self) ret = dialog.exec_() if ret: BranchWindow(self.glb, event_id, dialog.report_vars, self) def NewTableView(self, table_name): TableWindow(self.glb, table_name, self) def Help(self): HelpWindow(self.glb, self) def About(self): dialog = AboutDialog(self.glb, self) dialog.exec_() # XED Disassembler class xed_state_t(Structure): _fields_ = [ ("mode", c_int), ("width", c_int) ] class XEDInstruction(): def __init__(self, libxed): # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion xedd_t = c_byte * 512 self.xedd = xedd_t() self.xedp = addressof(self.xedd) libxed.xed_decoded_inst_zero(self.xedp) self.state = xed_state_t() self.statep = addressof(self.state) # Buffer for disassembled instruction text self.buffer = create_string_buffer(256) self.bufferp = addressof(self.buffer) class LibXED(): def __init__(self): try: self.libxed = CDLL("libxed.so") except: self.libxed = None if not self.libxed: self.libxed = CDLL("/usr/local/lib/libxed.so") self.xed_tables_init = self.libxed.xed_tables_init self.xed_tables_init.restype = None self.xed_tables_init.argtypes = [] self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero self.xed_decoded_inst_zero.restype = None self.xed_decoded_inst_zero.argtypes = [ c_void_p ] self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode self.xed_operand_values_set_mode.restype = None self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode self.xed_decoded_inst_zero_keep_mode.restype = None self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] self.xed_decode = self.libxed.xed_decode self.xed_decode.restype = c_int self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] self.xed_format_context = self.libxed.xed_format_context self.xed_format_context.restype = c_uint self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] self.xed_tables_init() def Instruction(self): return XEDInstruction(self) def SetMode(self, inst, mode): if mode: inst.state.mode = 4 # 32-bit inst.state.width = 4 # 4 bytes else: inst.state.mode = 1 # 64-bit inst.state.width = 8 # 8 bytes self.xed_operand_values_set_mode(inst.xedp, inst.statep) def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): self.xed_decoded_inst_zero_keep_mode(inst.xedp) err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) if err: return 0, "" # Use AT&T mode (2), alternative is Intel (3) ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) if not ok: return 0, "" if sys.version_info[0] == 2: result = inst.buffer.value else: result = inst.buffer.value.decode() # Return instruction length and the disassembled instruction text # For now, assume the length is in byte 166 return inst.xedd[166], result def TryOpen(file_name): try: return open(file_name, "rb") except: return None def Is64Bit(f): result = sizeof(c_void_p) # ELF support only pos = f.tell() f.seek(0) header = f.read(7) f.seek(pos) magic = header[0:4] if sys.version_info[0] == 2: eclass = ord(header[4]) encoding = ord(header[5]) version = ord(header[6]) else: eclass = header[4] encoding = header[5] version = header[6] if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: result = True if eclass == 2 else False return result # Global data class Glb(): def __init__(self, dbref, db, dbname): self.dbref = dbref self.db = db self.dbname = dbname self.home_dir = os.path.expanduser("~") self.buildid_dir = os.getenv("PERF_BUILDID_DIR") if self.buildid_dir: self.buildid_dir += "/.build-id/" else: self.buildid_dir = self.home_dir + "/.debug/.build-id/" self.app = None self.mainwindow = None self.instances_to_shutdown_on_exit = weakref.WeakSet() try: self.disassembler = LibXED() self.have_disassembler = True except: self.have_disassembler = False def FileFromBuildId(self, build_id): file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" return TryOpen(file_name) def FileFromNamesAndBuildId(self, short_name, long_name, build_id): # Assume current machine i.e. no support for virtualization if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": file_name = os.getenv("PERF_KCORE") f = TryOpen(file_name) if file_name else None if f: return f # For now, no special handling if long_name is /proc/kcore f = TryOpen(long_name) if f: return f f = self.FileFromBuildId(build_id) if f: return f return None def AddInstanceToShutdownOnExit(self, instance): self.instances_to_shutdown_on_exit.add(instance) # Shutdown any background processes or threads def ShutdownInstances(self): for x in self.instances_to_shutdown_on_exit: try: