]> Git Repo - linux.git/blobdiff - tools/perf/scripts/python/exported-sql-viewer.py
perf scripts python: exported-sql-viewer.py: Remove SQLTableDialogDataItem
[linux.git] / tools / perf / scripts / python / exported-sql-viewer.py
index f278ce5ebab76640de4eb61ee8661040d0ac092a..e1c2f9e54238d2efc0976f2a1379e5a29e1a1d2d 100755 (executable)
@@ -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,27 @@ class BranchModel(TreeModel):
        def HasMoreRecords(self):
                return self.more
 
+# Report Variables
+
+class ReportVars():
+
+       def __init__(self, name = "", where_clause = ""):
+               self.name = name
+               self.where_clause = where_clause
+
+       def UniqueId(self):
+               return str(self.where_clause)
+
 # 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 +1436,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,19 +1481,16 @@ 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 = ""):
                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 = ""
 
@@ -1495,24 +1501,99 @@ class SQLTableDialogDataItem():
                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)
+
+# 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 +1604,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 +1677,6 @@ class SQLTableDialogDataItem():
                                        return str(lower_id)
 
        def ConvertRelativeTime(self, val):
-               print "val ", val
                mult = 1
                suffix = val[-2:]
                if suffix == "ms":
@@ -1582,29 +1698,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 +1732,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 IsNumber(self, value):
-               try:
-                       x = int(value)
-               except:
-                       x = 0
-               return str(x) == value
-
-       def Invalidate(self):
-               self.validated = False
-
-       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
+       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)
 
-# Selected branch report creation dialog
+# Report Dialog Base
 
-class SelectedBranchDialog(QDialog):
+class ReportDialogBase(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,8 +1788,11 @@ 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:
@@ -1767,11 +1800,14 @@ class SelectedBranchDialog(QDialog):
                                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 + " ) "
+                               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 + " "
                else:
                        self.ShowMessage("No selection")
                        return
@@ -1783,6 +1819,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 +1846,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 +1881,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 +1925,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 +1940,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 +1958,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
 
@@ -2305,7 +2369,8 @@ 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)
 
@@ -2365,13 +2430,13 @@ class MainWindow(QMainWindow):
                CallGraphWindow(self.glb, 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)
This page took 0.055482 seconds and 4 git commands to generate.