diff options
Diffstat (limited to 'tools/perf/scripts/python')
| -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 |
