diff options
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 285 |
1 files changed, 272 insertions, 13 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index ed39a0153dd3..63b14b80ebcd 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py | |||
@@ -1702,6 +1702,265 @@ class SQLTableDialogDataItem(): | |||
1702 | return False | 1702 | return False |
1703 | return True | 1703 | return True |
1704 | 1704 | ||
1705 | # Line edit data item | ||
1706 | |||
1707 | class LineEditDataItem(object): | ||
1708 | |||
1709 | def __init__(self, glb, label, placeholder_text, parent, id = ""): | ||
1710 | self.glb = glb | ||
1711 | self.label = label | ||
1712 | self.placeholder_text = placeholder_text | ||
1713 | self.parent = parent | ||
1714 | self.id = id | ||
1715 | |||
1716 | self.value = "" | ||
1717 | |||
1718 | self.widget = QLineEdit() | ||
1719 | self.widget.editingFinished.connect(self.Validate) | ||
1720 | self.widget.textChanged.connect(self.Invalidate) | ||
1721 | self.red = False | ||
1722 | self.error = "" | ||
1723 | self.validated = True | ||
1724 | |||
1725 | if placeholder_text: | ||
1726 | self.widget.setPlaceholderText(placeholder_text) | ||
1727 | |||
1728 | def TurnTextRed(self): | ||
1729 | if not self.red: | ||
1730 | palette = QPalette() | ||
1731 | palette.setColor(QPalette.Text,Qt.red) | ||
1732 | self.widget.setPalette(palette) | ||
1733 | self.red = True | ||
1734 | |||
1735 | def TurnTextNormal(self): | ||
1736 | if self.red: | ||
1737 | palette = QPalette() | ||
1738 | self.widget.setPalette(palette) | ||
1739 | self.red = False | ||
1740 | |||
1741 | def InvalidValue(self, value): | ||
1742 | self.value = "" | ||
1743 | self.TurnTextRed() | ||
1744 | self.error = self.label + " invalid value '" + value + "'" | ||
1745 | self.parent.ShowMessage(self.error) | ||
1746 | |||
1747 | def Invalidate(self): | ||
1748 | self.validated = False | ||
1749 | |||
1750 | def DoValidate(self, input_string): | ||
1751 | self.value = input_string.strip() | ||
1752 | |||
1753 | def Validate(self): | ||
1754 | self.validated = True | ||
1755 | self.error = "" | ||
1756 | self.TurnTextNormal() | ||
1757 | self.parent.ClearMessage() | ||
1758 | input_string = self.widget.text() | ||
1759 | if not len(input_string.strip()): | ||
1760 | self.value = "" | ||
1761 | return | ||
1762 | self.DoValidate(input_string) | ||
1763 | |||
1764 | def IsValid(self): | ||
1765 | if not self.validated: | ||
1766 | self.Validate() | ||
1767 | if len(self.error): | ||
1768 | self.parent.ShowMessage(self.error) | ||
1769 | return False | ||
1770 | return True | ||
1771 | |||
1772 | def IsNumber(self, value): | ||
1773 | try: | ||
1774 | x = int(value) | ||
1775 | except: | ||
1776 | x = 0 | ||
1777 | return str(x) == value | ||
1778 | |||
1779 | # Non-negative integer ranges dialog data item | ||
1780 | |||
1781 | class NonNegativeIntegerRangesDataItem(LineEditDataItem): | ||
1782 | |||
1783 | def __init__(self, glb, label, placeholder_text, column_name, parent): | ||
1784 | super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) | ||
1785 | |||
1786 | self.column_name = column_name | ||
1787 | |||
1788 | def DoValidate(self, input_string): | ||
1789 | singles = [] | ||
1790 | ranges = [] | ||
1791 | for value in [x.strip() for x in input_string.split(",")]: | ||
1792 | if "-" in value: | ||
1793 | vrange = value.split("-") | ||
1794 | if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
1795 | return self.InvalidValue(value) | ||
1796 | ranges.append(vrange) | ||
1797 | else: | ||
1798 | if not self.IsNumber(value): | ||
1799 | return self.InvalidValue(value) | ||
1800 | singles.append(value) | ||
1801 | ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] | ||
1802 | if len(singles): | ||
1803 | ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") | ||
1804 | self.value = " OR ".join(ranges) | ||
1805 | |||
1806 | # Dialog data item converted and validated using a SQL table | ||
1807 | |||
1808 | class SQLTableDataItem(LineEditDataItem): | ||
1809 | |||
1810 | def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): | ||
1811 | super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent) | ||
1812 | |||
1813 | self.table_name = table_name | ||
1814 | self.match_column = match_column | ||
1815 | self.column_name1 = column_name1 | ||
1816 | self.column_name2 = column_name2 | ||
1817 | |||
1818 | def ValueToIds(self, value): | ||
1819 | ids = [] | ||
1820 | query = QSqlQuery(self.glb.db) | ||
1821 | stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'" | ||
1822 | ret = query.exec_(stmt) | ||
1823 | if ret: | ||
1824 | while query.next(): | ||
1825 | ids.append(str(query.value(0))) | ||
1826 | return ids | ||
1827 | |||
1828 | def DoValidate(self, input_string): | ||
1829 | all_ids = [] | ||
1830 | for value in [x.strip() for x in input_string.split(",")]: | ||
1831 | ids = self.ValueToIds(value) | ||
1832 | if len(ids): | ||
1833 | all_ids.extend(ids) | ||
1834 | else: | ||
1835 | return self.InvalidValue(value) | ||
1836 | self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")" | ||
1837 | if self.column_name2: | ||
1838 | self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )" | ||
1839 | |||
1840 | # Sample time ranges dialog data item converted and validated using 'samples' SQL table | ||
1841 | |||
1842 | class SampleTimeRangesDataItem(LineEditDataItem): | ||
1843 | |||
1844 | def __init__(self, glb, label, placeholder_text, column_name, parent): | ||
1845 | self.column_name = column_name | ||
1846 | |||
1847 | self.last_id = 0 | ||
1848 | self.first_time = 0 | ||
1849 | self.last_time = 2 ** 64 | ||
1850 | |||
1851 | query = QSqlQuery(glb.db) | ||
1852 | QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1") | ||
1853 | if query.next(): | ||
1854 | self.last_id = int(query.value(0)) | ||
1855 | self.last_time = int(query.value(1)) | ||
1856 | QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1") | ||
1857 | if query.next(): | ||
1858 | self.first_time = int(query.value(0)) | ||
1859 | if placeholder_text: | ||
1860 | placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time) | ||
1861 | |||
1862 | super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent) | ||
1863 | |||
1864 | def IdBetween(self, query, lower_id, higher_id, order): | ||
1865 | QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") | ||
1866 | if query.next(): | ||
1867 | return True, int(query.value(0)) | ||
1868 | else: | ||
1869 | return False, 0 | ||
1870 | |||
1871 | def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor): | ||
1872 | query = QSqlQuery(self.glb.db) | ||
1873 | while True: | ||
1874 | next_id = int((lower_id + higher_id) / 2) | ||
1875 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
1876 | if not query.next(): | ||
1877 | ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC") | ||
1878 | if not ok: | ||
1879 | ok, dbid = self.IdBetween(query, next_id, higher_id, "") | ||
1880 | if not ok: | ||
1881 | return str(higher_id) | ||
1882 | next_id = dbid | ||
1883 | QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id)) | ||
1884 | next_time = int(query.value(0)) | ||
1885 | if get_floor: | ||
1886 | if target_time > next_time: | ||
1887 | lower_id = next_id | ||
1888 | else: | ||
1889 | higher_id = next_id | ||
1890 | if higher_id <= lower_id + 1: | ||
1891 | return str(higher_id) | ||
1892 | else: | ||
1893 | if target_time >= next_time: | ||
1894 | lower_id = next_id | ||
1895 | else: | ||
1896 | higher_id = next_id | ||
1897 | if higher_id <= lower_id + 1: | ||
1898 | return str(lower_id) | ||
1899 | |||
1900 | def ConvertRelativeTime(self, val): | ||
1901 | mult = 1 | ||
1902 | suffix = val[-2:] | ||
1903 | if suffix == "ms": | ||
1904 | mult = 1000000 | ||
1905 | elif suffix == "us": | ||
1906 | mult = 1000 | ||
1907 | elif suffix == "ns": | ||
1908 | mult = 1 | ||
1909 | else: | ||
1910 | return val | ||
1911 | val = val[:-2].strip() | ||
1912 | if not self.IsNumber(val): | ||
1913 | return val | ||
1914 | val = int(val) * mult | ||
1915 | if val >= 0: | ||
1916 | val += self.first_time | ||
1917 | else: | ||
1918 | val += self.last_time | ||
1919 | return str(val) | ||
1920 | |||
1921 | def ConvertTimeRange(self, vrange): | ||
1922 | if vrange[0] == "": | ||
1923 | vrange[0] = str(self.first_time) | ||
1924 | if vrange[1] == "": | ||
1925 | vrange[1] = str(self.last_time) | ||
1926 | vrange[0] = self.ConvertRelativeTime(vrange[0]) | ||
1927 | vrange[1] = self.ConvertRelativeTime(vrange[1]) | ||
1928 | if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): | ||
1929 | return False | ||
1930 | beg_range = max(int(vrange[0]), self.first_time) | ||
1931 | end_range = min(int(vrange[1]), self.last_time) | ||
1932 | if beg_range > self.last_time or end_range < self.first_time: | ||
1933 | return False | ||
1934 | vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) | ||
1935 | vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) | ||
1936 | return True | ||
1937 | |||
1938 | def AddTimeRange(self, value, ranges): | ||
1939 | n = value.count("-") | ||
1940 | if n == 1: | ||
1941 | pass | ||
1942 | elif n == 2: | ||
1943 | if value.split("-")[1].strip() == "": | ||
1944 | n = 1 | ||
1945 | elif n == 3: | ||
1946 | n = 2 | ||
1947 | else: | ||
1948 | return False | ||
1949 | pos = findnth(value, "-", n) | ||
1950 | vrange = [value[:pos].strip() ,value[pos+1:].strip()] | ||
1951 | if self.ConvertTimeRange(vrange): | ||
1952 | ranges.append(vrange) | ||
1953 | return True | ||
1954 | return False | ||
1955 | |||
1956 | def DoValidate(self, input_string): | ||
1957 | ranges = [] | ||
1958 | for value in [x.strip() for x in input_string.split(",")]: | ||
1959 | if not self.AddTimeRange(value, ranges): | ||
1960 | return self.InvalidValue(value) | ||
1961 | ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] | ||
1962 | self.value = " OR ".join(ranges) | ||
1963 | |||
1705 | # Report Dialog Base | 1964 | # Report Dialog Base |
1706 | 1965 | ||
1707 | class ReportDialogBase(QDialog): | 1966 | class ReportDialogBase(QDialog): |
@@ -1716,7 +1975,7 @@ class ReportDialogBase(QDialog): | |||
1716 | self.setWindowTitle(title) | 1975 | self.setWindowTitle(title) |
1717 | self.setMinimumWidth(600) | 1976 | self.setMinimumWidth(600) |
1718 | 1977 | ||
1719 | self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items] | 1978 | self.data_items = [x(glb, self) for x in items] |
1720 | 1979 | ||
1721 | self.partial = partial | 1980 | self.partial = partial |
1722 | 1981 | ||
@@ -1751,7 +2010,9 @@ class ReportDialogBase(QDialog): | |||
1751 | 2010 | ||
1752 | def Ok(self): | 2011 | def Ok(self): |
1753 | vars = self.report_vars | 2012 | vars = self.report_vars |
1754 | vars.name = self.data_items[0].value | 2013 | for d in self.data_items: |
2014 | if d.id == "REPORTNAME": | ||
2015 | vars.name = d.value | ||
1755 | if not vars.name: | 2016 | if not vars.name: |
1756 | self.ShowMessage("Report name is required") | 2017 | self.ShowMessage("Report name is required") |
1757 | return | 2018 | return |
@@ -1785,17 +2046,15 @@ class SelectedBranchDialog(ReportDialogBase): | |||
1785 | 2046 | ||
1786 | def __init__(self, glb, parent=None): | 2047 | def __init__(self, glb, parent=None): |
1787 | title = "Selected Branches" | 2048 | title = "Selected Branches" |
1788 | items = ( | 2049 | items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"), |
1789 | ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), | 2050 | lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p), |
1790 | ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), | 2051 | lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p), |
1791 | ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""), | 2052 | lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p), |
1792 | ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""), | 2053 | lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p), |
1793 | ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""), | 2054 | lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p), |
1794 | ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""), | 2055 | lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p), |
1795 | ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"), | 2056 | lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p), |
1796 | ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"), | 2057 | lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p)) |
1797 | ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""), | ||
1798 | ) | ||
1799 | super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) | 2058 | super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent) |
1800 | 2059 | ||
1801 | # Event list | 2060 | # Event list |