diff options
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 511 |
1 files changed, 351 insertions, 160 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index f278ce5ebab7..09ce73b07d35 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python2 # SPDX-License-Identifier: GPL-2.0 # exported-sql-viewer.py: view data from sql database # Copyright (c) 2014-2018, Intel Corporation. @@ -1398,18 +1398,28 @@ class BranchModel(TreeModel): 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, name, where_clause, parent=None): + def __init__(self, glb, event_id, report_vars, parent=None): super(BranchWindow, self).__init__(parent) - model_name = "Branch Events " + str(event_id) - if len(where_clause): - model_name = where_clause + " " + model_name + model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() - self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) + self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) self.view = QTreeView() self.view.setUniformRowHeights(True) @@ -1427,7 +1437,7 @@ class BranchWindow(QMdiSubWindow): self.setWidget(self.vbox.Widget()) - AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") + 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 @@ -1472,47 +1482,134 @@ class BranchWindow(QMdiSubWindow): else: self.find_bar.NotFound() -# Dialog data item converted and validated using a SQL table +# Line edit data item -class SQLTableDialogDataItem(): +class LineEditDataItem(object): - def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): + def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): self.glb = glb self.label = label self.placeholder_text = placeholder_text - self.table_name = table_name - self.match_column = match_column - self.column_name1 = column_name1 - self.column_name2 = column_name2 self.parent = parent + self.id = id - self.value = "" + self.value = default - self.widget = QLineEdit() + self.widget = QLineEdit(default) self.widget.editingFinished.connect(self.Validate) self.widget.textChanged.connect(self.Invalidate) self.red = False self.error = "" self.validated = True - self.last_id = 0 - self.first_time = 0 - self.last_time = 2 ** 64 - if self.table_name == "<timeranges>": - query = QSqlQuery(self.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.last_time = int(query.value(1)) - QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") - if query.next(): - self.first_time = int(query.value(0)) - if placeholder_text: - placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) - 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) @@ -1523,6 +1620,42 @@ class SQLTableDialogDataItem(): 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.last_time = int(query.value(1)) + QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") + if query.next(): + self.first_time = int(query.value(0)) + 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(): @@ -1560,7 +1693,6 @@ class SQLTableDialogDataItem(): return str(lower_id) def ConvertRelativeTime(self, val): - print "val ", val mult = 1 suffix = val[-2:] if suffix == "ms": @@ -1582,29 +1714,23 @@ class SQLTableDialogDataItem(): return str(val) def ConvertTimeRange(self, vrange): - print "vrange ", vrange if vrange[0] == "": vrange[0] = str(self.first_time) if vrange[1] == "": vrange[1] = str(self.last_time) vrange[0] = self.ConvertRelativeTime(vrange[0]) vrange[1] = self.ConvertRelativeTime(vrange[1]) - print "vrange2 ", vrange if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): return False - print "ok1" beg_range = max(int(vrange[0]), self.first_time) end_range = min(int(vrange[1]), self.last_time) if beg_range > self.last_time or end_range < self.first_time: return False - print "ok2" vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) - print "vrange3 ", vrange return True def AddTimeRange(self, value, ranges): - print "value ", value n = value.count("-") if n == 1: pass @@ -1622,111 +1748,31 @@ class SQLTableDialogDataItem(): return True return False - def InvalidValue(self, value): - self.value = "" - palette = QPalette() - palette.setColor(QPalette.Text,Qt.red) - self.widget.setPalette(palette) - self.red = True - self.error = self.label + " invalid value '" + value + "'" - self.parent.ShowMessage(self.error) + def DoValidate(self, input_string): + ranges = [] + for value in [x.strip() for x in input_string.split(",")]: + if not self.AddTimeRange(value, ranges): + return self.InvalidValue(value) + ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] + self.value = " OR ".join(ranges) - def IsNumber(self, value): - try: - x = int(value) - except: - x = 0 - return str(x) == value +# Report Dialog Base - def Invalidate(self): - self.validated = False +class ReportDialogBase(QDialog): - def Validate(self): - input_string = self.widget.text() - self.validated = True - if self.red: - palette = QPalette() - self.widget.setPalette(palette) - self.red = False - if not len(input_string.strip()): - self.error = "" - self.value = "" - return - if self.table_name == "<timeranges>": - ranges = [] - for value in [x.strip() for x in input_string.split(",")]: - if not self.AddTimeRange(value, ranges): - return self.InvalidValue(value) - ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] - self.value = " OR ".join(ranges) - elif self.table_name == "<ranges>": - 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_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges] - if len(singles): - ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")") - self.value = " OR ".join(ranges) - elif self.table_name: - 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) + ") )" - else: - self.value = input_string.strip() - self.error = "" - self.parent.ClearMessage() - - def IsValid(self): - if not self.validated: - self.Validate() - if len(self.error): - self.parent.ShowMessage(self.error) - return False - return True - -# Selected branch report creation dialog - -class SelectedBranchDialog(QDialog): - - def __init__(self, glb, parent=None): - super(SelectedBranchDialog, self).__init__(parent) + def __init__(self, glb, title, items, partial, parent=None): + super(ReportDialogBase, self).__init__(parent) self.glb = glb - self.name = "" - self.where_clause = "" + self.report_vars = ReportVars() - self.setWindowTitle("Selected Branches") + self.setWindowTitle(title) self.setMinimumWidth(600) - items = ( - ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), - ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), - ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""), - ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""), - ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""), - ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""), - ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"), - ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"), - ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""), - ) - self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items] + self.data_items = [x(glb, self) for x in items] + + self.partial = partial self.grid = QGridLayout() @@ -1758,23 +1804,28 @@ class SelectedBranchDialog(QDialog): self.setLayout(self.vbox); def Ok(self): - self.name = self.data_items[0].value - if not self.name: + vars = self.report_vars + for d in self.data_items: + if d.id == "REPORTNAME": + vars.name = d.value + if not vars.name: self.ShowMessage("Report name is required") return for d in self.data_items: if not d.IsValid(): return for d in self.data_items[1:]: - if len(d.value): - if len(self.where_clause): - self.where_clause += " AND " - self.where_clause += d.value - if len(self.where_clause): - self.where_clause = " AND ( " + self.where_clause + " ) " - else: - self.ShowMessage("No selection") - return + if d.id == "LIMIT": + vars.limit = d.value + elif len(d.value): + if len(vars.where_clause): + vars.where_clause += " AND " + vars.where_clause += d.value + if len(vars.where_clause): + if self.partial: + vars.where_clause = " AND ( " + vars.where_clause + " ) " + else: + vars.where_clause = " WHERE " + vars.where_clause + " " self.accept() def ShowMessage(self, msg): @@ -1783,6 +1834,23 @@ class SelectedBranchDialog(QDialog): def ClearMessage(self): self.status.setText("") +# Selected branch report creation dialog + +class SelectedBranchDialog(ReportDialogBase): + + def __init__(self, glb, parent=None): + title = "Selected Branches" + items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), + lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), + lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), + lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), + lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), + lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), + lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p), + lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), + lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) + super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) + # Event list def GetEventList(db): @@ -1793,6 +1861,16 @@ def GetEventList(db): events.append(query.value(0)) return events +# Is a table selectable + +def IsSelectable(db, table): + query = QSqlQuery(db) + try: + QueryExec(query, "SELECT * FROM " + table + " LIMIT 1") + except: + return False + return True + # SQL data preparation def SQLTableDataPrep(query, count): @@ -1818,12 +1896,13 @@ class SQLTableModel(TableModel): progress = Signal(object) - def __init__(self, glb, sql, column_count, parent=None): + def __init__(self, glb, sql, column_headers, parent=None): super(SQLTableModel, self).__init__(parent) self.glb = glb self.more = True self.populated = 0 - self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample) + self.column_headers = column_headers + self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample) self.fetcher.done.connect(self.Update) self.fetcher.Fetch(glb_chunk_sz) @@ -1861,6 +1940,12 @@ class SQLTableModel(TableModel): def HasMoreRecords(self): return self.more + def columnCount(self, parent=None): + return len(self.column_headers) + + def columnHeader(self, column): + return self.column_headers[column] + # SQL automatic table data model class SQLAutoTableModel(SQLTableModel): @@ -1870,12 +1955,12 @@ class SQLAutoTableModel(SQLTableModel): if table_name == "comm_threads_view": # For now, comm_threads_view has no id column sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) - self.column_headers = [] + column_headers = [] query = QSqlQuery(glb.db) if glb.dbref.is_sqlite3: QueryExec(query, "PRAGMA table_info(" + table_name + ")") while query.next(): - self.column_headers.append(query.value(1)) + column_headers.append(query.value(1)) if table_name == "sqlite_master": sql = "SELECT * FROM " + table_name else: @@ -1888,14 +1973,8 @@ class SQLAutoTableModel(SQLTableModel): schema = "public" QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") while query.next(): - self.column_headers.append(query.value(0)) - super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent) - - def columnCount(self, parent=None): - return len(self.column_headers) - - def columnHeader(self, column): - return self.column_headers[column] + column_headers.append(query.value(0)) + super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent) # Base class for custom ResizeColumnsToContents @@ -1998,6 +2077,103 @@ def GetTableList(glb): 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.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): @@ -2101,6 +2277,7 @@ p.c2 { <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> <p class=c2><a href=#allbranches>1.2 All branches</a></p> <p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> +<p class=c2><a href=#topcallsbyelapsedtime>1.4 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> @@ -2176,6 +2353,10 @@ ms, us or ns. Also, negative values are relative to the end of trace. Examples: -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.4 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 @@ -2305,10 +2486,14 @@ class MainWindow(QMainWindow): edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) reports_menu = menu.addMenu("&Reports") - 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"): + reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, 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) @@ -2364,14 +2549,20 @@ class MainWindow(QMainWindow): def NewCallGraph(self): CallGraphWindow(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, "", "", self) + 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.name, dialog.where_clause, self) + BranchWindow(self.glb, event_id, dialog.report_vars, self) def NewTableView(self, table_name): TableWindow(self.glb, table_name, self) |