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)) 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><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>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) # 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("&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)) 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): win = self.mdi_area.activeSubWindow() ShrinkFont(win.view) def EnlargeFont(self): win = self.mdi_area.activeSubWindow() EnlargeFont(win.view) 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 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)) 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)) 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) # 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: x.Shutdown() except: pass # Database reference Lines 2501–3000 of 3073. Previous lines Load more
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)) 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-sensitive call-graph. Expanding a couple of levels of the tree and adjusting column widths to suit will display something like: <pre> Call Graph: pt_example Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) v- ls v- 2638:2638 v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 |- unknown unknown 1 13198 0.1 1 0.0 >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 >- __libc_csu_init ls 1 10354 0.1 10 0.0 |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 v- main ls 1 8182043 99.6 180254 99.9 </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> <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>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) # 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("&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)) 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): win = self.mdi_area.activeSubWindow() ShrinkFont(win.view) def EnlargeFont(self): win = self.mdi_area.activeSubWindow() EnlargeFont(win.view) 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 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)) 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)) 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) # 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: x.Shutdown() except: pass # Database reference