aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/exported-sql-viewer.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py510
1 files changed, 351 insertions, 159 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index c3091401df91..09ce73b07d35 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -1,3 +1,4 @@
1#!/usr/bin/env python2
1# SPDX-License-Identifier: GPL-2.0 2# SPDX-License-Identifier: GPL-2.0
2# exported-sql-viewer.py: view data from sql database 3# exported-sql-viewer.py: view data from sql database
3# Copyright (c) 2014-2018, Intel Corporation. 4# Copyright (c) 2014-2018, Intel Corporation.
@@ -1397,18 +1398,28 @@ class BranchModel(TreeModel):
1397 def HasMoreRecords(self): 1398 def HasMoreRecords(self):
1398 return self.more 1399 return self.more
1399 1400
1401# Report Variables
1402
1403class ReportVars():
1404
1405 def __init__(self, name = "", where_clause = "", limit = ""):
1406 self.name = name
1407 self.where_clause = where_clause
1408 self.limit = limit
1409
1410 def UniqueId(self):
1411 return str(self.where_clause + ";" + self.limit)
1412
1400# Branch window 1413# Branch window
1401 1414
1402class BranchWindow(QMdiSubWindow): 1415class BranchWindow(QMdiSubWindow):
1403 1416
1404 def __init__(self, glb, event_id, name, where_clause, parent=None): 1417 def __init__(self, glb, event_id, report_vars, parent=None):
1405 super(BranchWindow, self).__init__(parent) 1418 super(BranchWindow, self).__init__(parent)
1406 1419
1407 model_name = "Branch Events " + str(event_id) 1420 model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId()
1408 if len(where_clause):
1409 model_name = where_clause + " " + model_name
1410 1421
1411 self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) 1422 self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause))
1412 1423
1413 self.view = QTreeView() 1424 self.view = QTreeView()
1414 self.view.setUniformRowHeights(True) 1425 self.view.setUniformRowHeights(True)
@@ -1426,7 +1437,7 @@ class BranchWindow(QMdiSubWindow):
1426 1437
1427 self.setWidget(self.vbox.Widget()) 1438 self.setWidget(self.vbox.Widget())
1428 1439
1429 AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") 1440 AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events")
1430 1441
1431 def ResizeColumnToContents(self, column, n): 1442 def ResizeColumnToContents(self, column, n):
1432 # Using the view's resizeColumnToContents() here is extrememly slow 1443 # Using the view's resizeColumnToContents() here is extrememly slow
@@ -1471,47 +1482,134 @@ class BranchWindow(QMdiSubWindow):
1471 else: 1482 else:
1472 self.find_bar.NotFound() 1483 self.find_bar.NotFound()
1473 1484
1474# Dialog data item converted and validated using a SQL table 1485# Line edit data item
1475 1486
1476class SQLTableDialogDataItem(): 1487class LineEditDataItem(object):
1477 1488
1478 def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent): 1489 def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1479 self.glb = glb 1490 self.glb = glb
1480 self.label = label 1491 self.label = label
1481 self.placeholder_text = placeholder_text 1492 self.placeholder_text = placeholder_text
1482 self.table_name = table_name
1483 self.match_column = match_column
1484 self.column_name1 = column_name1
1485 self.column_name2 = column_name2
1486 self.parent = parent 1493 self.parent = parent
1494 self.id = id
1487 1495
1488 self.value = "" 1496 self.value = default
1489 1497
1490 self.widget = QLineEdit() 1498 self.widget = QLineEdit(default)
1491 self.widget.editingFinished.connect(self.Validate) 1499 self.widget.editingFinished.connect(self.Validate)
1492 self.widget.textChanged.connect(self.Invalidate) 1500 self.widget.textChanged.connect(self.Invalidate)
1493 self.red = False 1501 self.red = False
1494 self.error = "" 1502 self.error = ""
1495 self.validated = True 1503 self.validated = True
1496 1504
1497 self.last_id = 0
1498 self.first_time = 0
1499 self.last_time = 2 ** 64
1500 if self.table_name == "<timeranges>":
1501 query = QSqlQuery(self.glb.db)
1502 QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1503 if query.next():
1504 self.last_id = int(query.value(0))
1505 self.last_time = int(query.value(1))
1506 QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1507 if query.next():
1508 self.first_time = int(query.value(0))
1509 if placeholder_text:
1510 placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1511
1512 if placeholder_text: 1505 if placeholder_text:
1513 self.widget.setPlaceholderText(placeholder_text) 1506 self.widget.setPlaceholderText(placeholder_text)
1514 1507
1508 def TurnTextRed(self):
1509 if not self.red:
1510 palette = QPalette()
1511 palette.setColor(QPalette.Text,Qt.red)
1512 self.widget.setPalette(palette)
1513 self.red = True
1514
1515 def TurnTextNormal(self):
1516 if self.red:
1517 palette = QPalette()
1518 self.widget.setPalette(palette)
1519 self.red = False
1520
1521 def InvalidValue(self, value):
1522 self.value = ""
1523 self.TurnTextRed()
1524 self.error = self.label + " invalid value '" + value + "'"
1525 self.parent.ShowMessage(self.error)
1526
1527 def Invalidate(self):
1528 self.validated = False
1529
1530 def DoValidate(self, input_string):
1531 self.value = input_string.strip()
1532
1533 def Validate(self):
1534 self.validated = True
1535 self.error = ""
1536 self.TurnTextNormal()
1537 self.parent.ClearMessage()
1538 input_string = self.widget.text()
1539 if not len(input_string.strip()):
1540 self.value = ""
1541 return
1542 self.DoValidate(input_string)
1543
1544 def IsValid(self):
1545 if not self.validated:
1546 self.Validate()
1547 if len(self.error):
1548 self.parent.ShowMessage(self.error)
1549 return False
1550 return True
1551
1552 def IsNumber(self, value):
1553 try:
1554 x = int(value)
1555 except:
1556 x = 0
1557 return str(x) == value
1558
1559# Non-negative integer ranges dialog data item
1560
1561class NonNegativeIntegerRangesDataItem(LineEditDataItem):
1562
1563 def __init__(self, glb, label, placeholder_text, column_name, parent):
1564 super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
1565
1566 self.column_name = column_name
1567
1568 def DoValidate(self, input_string):
1569 singles = []
1570 ranges = []
1571 for value in [x.strip() for x in input_string.split(",")]:
1572 if "-" in value:
1573 vrange = value.split("-")
1574 if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1575 return self.InvalidValue(value)
1576 ranges.append(vrange)
1577 else:
1578 if not self.IsNumber(value):
1579 return self.InvalidValue(value)
1580 singles.append(value)
1581 ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
1582 if len(singles):
1583 ranges.append(self.column_name + " IN (" + ",".join(singles) + ")")
1584 self.value = " OR ".join(ranges)
1585
1586# Positive integer dialog data item
1587
1588class PositiveIntegerDataItem(LineEditDataItem):
1589
1590 def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""):
1591 super(PositiveIntegerDataItem, self).__init__(glb, label, placeholder_text, parent, id, default)
1592
1593 def DoValidate(self, input_string):
1594 if not self.IsNumber(input_string.strip()):
1595 return self.InvalidValue(input_string)
1596 value = int(input_string.strip())
1597 if value <= 0:
1598 return self.InvalidValue(input_string)
1599 self.value = str(value)
1600
1601# Dialog data item converted and validated using a SQL table
1602
1603class SQLTableDataItem(LineEditDataItem):
1604
1605 def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
1606 super(SQLTableDataItem, self).__init__(glb, label, placeholder_text, parent)
1607
1608 self.table_name = table_name
1609 self.match_column = match_column
1610 self.column_name1 = column_name1
1611 self.column_name2 = column_name2
1612
1515 def ValueToIds(self, value): 1613 def ValueToIds(self, value):
1516 ids = [] 1614 ids = []
1517 query = QSqlQuery(self.glb.db) 1615 query = QSqlQuery(self.glb.db)
@@ -1522,6 +1620,42 @@ class SQLTableDialogDataItem():
1522 ids.append(str(query.value(0))) 1620 ids.append(str(query.value(0)))
1523 return ids 1621 return ids
1524 1622
1623 def DoValidate(self, input_string):
1624 all_ids = []
1625 for value in [x.strip() for x in input_string.split(",")]:
1626 ids = self.ValueToIds(value)
1627 if len(ids):
1628 all_ids.extend(ids)
1629 else:
1630 return self.InvalidValue(value)
1631 self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1632 if self.column_name2:
1633 self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1634
1635# Sample time ranges dialog data item converted and validated using 'samples' SQL table
1636
1637class SampleTimeRangesDataItem(LineEditDataItem):
1638
1639 def __init__(self, glb, label, placeholder_text, column_name, parent):
1640 self.column_name = column_name
1641
1642 self.last_id = 0
1643 self.first_time = 0
1644 self.last_time = 2 ** 64
1645
1646 query = QSqlQuery(glb.db)
1647 QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1648 if query.next():
1649 self.last_id = int(query.value(0))
1650 self.last_time = int(query.value(1))
1651 QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1652 if query.next():
1653 self.first_time = int(query.value(0))
1654 if placeholder_text:
1655 placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1656
1657 super(SampleTimeRangesDataItem, self).__init__(glb, label, placeholder_text, parent)
1658
1525 def IdBetween(self, query, lower_id, higher_id, order): 1659 def IdBetween(self, query, lower_id, higher_id, order):
1526 QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1") 1660 QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
1527 if query.next(): 1661 if query.next():
@@ -1559,7 +1693,6 @@ class SQLTableDialogDataItem():
1559 return str(lower_id) 1693 return str(lower_id)
1560 1694
1561 def ConvertRelativeTime(self, val): 1695 def ConvertRelativeTime(self, val):
1562 print "val ", val
1563 mult = 1 1696 mult = 1
1564 suffix = val[-2:] 1697 suffix = val[-2:]
1565 if suffix == "ms": 1698 if suffix == "ms":
@@ -1581,29 +1714,23 @@ class SQLTableDialogDataItem():
1581 return str(val) 1714 return str(val)
1582 1715
1583 def ConvertTimeRange(self, vrange): 1716 def ConvertTimeRange(self, vrange):
1584 print "vrange ", vrange
1585 if vrange[0] == "": 1717 if vrange[0] == "":
1586 vrange[0] = str(self.first_time) 1718 vrange[0] = str(self.first_time)
1587 if vrange[1] == "": 1719 if vrange[1] == "":
1588 vrange[1] = str(self.last_time) 1720 vrange[1] = str(self.last_time)
1589 vrange[0] = self.ConvertRelativeTime(vrange[0]) 1721 vrange[0] = self.ConvertRelativeTime(vrange[0])
1590 vrange[1] = self.ConvertRelativeTime(vrange[1]) 1722 vrange[1] = self.ConvertRelativeTime(vrange[1])
1591 print "vrange2 ", vrange
1592 if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): 1723 if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1593 return False 1724 return False
1594 print "ok1"
1595 beg_range = max(int(vrange[0]), self.first_time) 1725 beg_range = max(int(vrange[0]), self.first_time)
1596 end_range = min(int(vrange[1]), self.last_time) 1726 end_range = min(int(vrange[1]), self.last_time)
1597 if beg_range > self.last_time or end_range < self.first_time: 1727 if beg_range > self.last_time or end_range < self.first_time:
1598 return False 1728 return False
1599 print "ok2"
1600 vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True) 1729 vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
1601 vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False) 1730 vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
1602 print "vrange3 ", vrange
1603 return True 1731 return True
1604 1732
1605 def AddTimeRange(self, value, ranges): 1733 def AddTimeRange(self, value, ranges):
1606 print "value ", value
1607 n = value.count("-") 1734 n = value.count("-")
1608 if n == 1: 1735 if n == 1:
1609 pass 1736 pass
@@ -1621,111 +1748,31 @@ class SQLTableDialogDataItem():
1621 return True 1748 return True
1622 return False 1749 return False
1623 1750
1624 def InvalidValue(self, value): 1751 def DoValidate(self, input_string):
1625 self.value = "" 1752 ranges = []
1626 palette = QPalette() 1753 for value in [x.strip() for x in input_string.split(",")]:
1627 palette.setColor(QPalette.Text,Qt.red) 1754 if not self.AddTimeRange(value, ranges):
1628 self.widget.setPalette(palette) 1755 return self.InvalidValue(value)
1629 self.red = True 1756 ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges]
1630 self.error = self.label + " invalid value '" + value + "'" 1757 self.value = " OR ".join(ranges)
1631 self.parent.ShowMessage(self.error)
1632 1758
1633 def IsNumber(self, value): 1759# Report Dialog Base
1634 try:
1635 x = int(value)
1636 except:
1637 x = 0
1638 return str(x) == value
1639 1760
1640 def Invalidate(self): 1761class ReportDialogBase(QDialog):
1641 self.validated = False
1642 1762
1643 def Validate(self): 1763 def __init__(self, glb, title, items, partial, parent=None):
1644 input_string = self.widget.text() 1764 super(ReportDialogBase, self).__init__(parent)
1645 self.validated = True
1646 if self.red:
1647 palette = QPalette()
1648 self.widget.setPalette(palette)
1649 self.red = False
1650 if not len(input_string.strip()):
1651 self.error = ""
1652 self.value = ""
1653 return
1654 if self.table_name == "<timeranges>":
1655 ranges = []
1656 for value in [x.strip() for x in input_string.split(",")]:
1657 if not self.AddTimeRange(value, ranges):
1658 return self.InvalidValue(value)
1659 ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1660 self.value = " OR ".join(ranges)
1661 elif self.table_name == "<ranges>":
1662 singles = []
1663 ranges = []
1664 for value in [x.strip() for x in input_string.split(",")]:
1665 if "-" in value:
1666 vrange = value.split("-")
1667 if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1668 return self.InvalidValue(value)
1669 ranges.append(vrange)
1670 else:
1671 if not self.IsNumber(value):
1672 return self.InvalidValue(value)
1673 singles.append(value)
1674 ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1675 if len(singles):
1676 ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")")
1677 self.value = " OR ".join(ranges)
1678 elif self.table_name:
1679 all_ids = []
1680 for value in [x.strip() for x in input_string.split(",")]:
1681 ids = self.ValueToIds(value)
1682 if len(ids):
1683 all_ids.extend(ids)
1684 else:
1685 return self.InvalidValue(value)
1686 self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1687 if self.column_name2:
1688 self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1689 else:
1690 self.value = input_string.strip()
1691 self.error = ""
1692 self.parent.ClearMessage()
1693
1694 def IsValid(self):
1695 if not self.validated:
1696 self.Validate()
1697 if len(self.error):
1698 self.parent.ShowMessage(self.error)
1699 return False
1700 return True
1701
1702# Selected branch report creation dialog
1703
1704class SelectedBranchDialog(QDialog):
1705
1706 def __init__(self, glb, parent=None):
1707 super(SelectedBranchDialog, self).__init__(parent)
1708 1765
1709 self.glb = glb 1766 self.glb = glb
1710 1767
1711 self.name = "" 1768 self.report_vars = ReportVars()
1712 self.where_clause = ""
1713 1769
1714 self.setWindowTitle("Selected Branches") 1770 self.setWindowTitle(title)
1715 self.setMinimumWidth(600) 1771 self.setMinimumWidth(600)
1716 1772
1717 items = ( 1773 self.data_items = [x(glb, self) for x in items]
1718 ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""), 1774
1719 ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""), 1775 self.partial = partial
1720 ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""),
1721 ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""),
1722 ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""),
1723 ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""),
1724 ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"),
1725 ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"),
1726 ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""),
1727 )
1728 self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items]
1729 1776
1730 self.grid = QGridLayout() 1777 self.grid = QGridLayout()
1731 1778
@@ -1757,23 +1804,28 @@ class SelectedBranchDialog(QDialog):
1757 self.setLayout(self.vbox); 1804 self.setLayout(self.vbox);
1758 1805
1759 def Ok(self): 1806 def Ok(self):
1760 self.name = self.data_items[0].value 1807 vars = self.report_vars
1761 if not self.name: 1808 for d in self.data_items:
1809 if d.id == "REPORTNAME":
1810 vars.name = d.value
1811 if not vars.name:
1762 self.ShowMessage("Report name is required") 1812 self.ShowMessage("Report name is required")
1763 return 1813 return
1764 for d in self.data_items: 1814 for d in self.data_items:
1765 if not d.IsValid(): 1815 if not d.IsValid():
1766 return 1816 return
1767 for d in self.data_items[1:]: 1817 for d in self.data_items[1:]:
1768 if len(d.value): 1818 if d.id == "LIMIT":
1769 if len(self.where_clause): 1819 vars.limit = d.value
1770 self.where_clause += " AND " 1820 elif len(d.value):
1771 self.where_clause += d.value 1821 if len(vars.where_clause):
1772 if len(self.where_clause): 1822 vars.where_clause += " AND "
1773 self.where_clause = " AND ( " + self.where_clause + " ) " 1823 vars.where_clause += d.value
1774 else: 1824 if len(vars.where_clause):
1775 self.ShowMessage("No selection") 1825 if self.partial:
1776 return 1826 vars.where_clause = " AND ( " + vars.where_clause + " ) "
1827 else:
1828 vars.where_clause = " WHERE " + vars.where_clause + " "
1777 self.accept() 1829 self.accept()
1778 1830
1779 def ShowMessage(self, msg): 1831 def ShowMessage(self, msg):
@@ -1782,6 +1834,23 @@ class SelectedBranchDialog(QDialog):
1782 def ClearMessage(self): 1834 def ClearMessage(self):
1783 self.status.setText("") 1835 self.status.setText("")
1784 1836
1837# Selected branch report creation dialog
1838
1839class SelectedBranchDialog(ReportDialogBase):
1840
1841 def __init__(self, glb, parent=None):
1842 title = "Selected Branches"
1843 items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
1844 lambda g, p: SampleTimeRangesDataItem(g, "Time ranges:", "Enter time ranges", "samples.id", p),
1845 lambda g, p: NonNegativeIntegerRangesDataItem(g, "CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "cpu", p),
1846 lambda g, p: SQLTableDataItem(g, "Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", "", p),
1847 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", "", p),
1848 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
1849 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id", p),
1850 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id", p),
1851 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p))
1852 super(SelectedBranchDialog, self).__init__(glb, title, items, True, parent)
1853
1785# Event list 1854# Event list
1786 1855
1787def GetEventList(db): 1856def GetEventList(db):
@@ -1792,6 +1861,16 @@ def GetEventList(db):
1792 events.append(query.value(0)) 1861 events.append(query.value(0))
1793 return events 1862 return events
1794 1863
1864# Is a table selectable
1865
1866def IsSelectable(db, table):
1867 query = QSqlQuery(db)
1868 try:
1869 QueryExec(query, "SELECT * FROM " + table + " LIMIT 1")
1870 except:
1871 return False
1872 return True
1873
1795# SQL data preparation 1874# SQL data preparation
1796 1875
1797def SQLTableDataPrep(query, count): 1876def SQLTableDataPrep(query, count):
@@ -1817,12 +1896,13 @@ class SQLTableModel(TableModel):
1817 1896
1818 progress = Signal(object) 1897 progress = Signal(object)
1819 1898
1820 def __init__(self, glb, sql, column_count, parent=None): 1899 def __init__(self, glb, sql, column_headers, parent=None):
1821 super(SQLTableModel, self).__init__(parent) 1900 super(SQLTableModel, self).__init__(parent)
1822 self.glb = glb 1901 self.glb = glb
1823 self.more = True 1902 self.more = True
1824 self.populated = 0 1903 self.populated = 0
1825 self.fetcher = SQLFetcher(glb, sql, lambda x, y=column_count: SQLTableDataPrep(x, y), self.AddSample) 1904 self.column_headers = column_headers
1905 self.fetcher = SQLFetcher(glb, sql, lambda x, y=len(column_headers): SQLTableDataPrep(x, y), self.AddSample)
1826 self.fetcher.done.connect(self.Update) 1906 self.fetcher.done.connect(self.Update)
1827 self.fetcher.Fetch(glb_chunk_sz) 1907 self.fetcher.Fetch(glb_chunk_sz)
1828 1908
@@ -1860,6 +1940,12 @@ class SQLTableModel(TableModel):
1860 def HasMoreRecords(self): 1940 def HasMoreRecords(self):
1861 return self.more 1941 return self.more
1862 1942
1943 def columnCount(self, parent=None):
1944 return len(self.column_headers)
1945
1946 def columnHeader(self, column):
1947 return self.column_headers[column]
1948
1863# SQL automatic table data model 1949# SQL automatic table data model
1864 1950
1865class SQLAutoTableModel(SQLTableModel): 1951class SQLAutoTableModel(SQLTableModel):
@@ -1869,12 +1955,12 @@ class SQLAutoTableModel(SQLTableModel):
1869 if table_name == "comm_threads_view": 1955 if table_name == "comm_threads_view":
1870 # For now, comm_threads_view has no id column 1956 # For now, comm_threads_view has no id column
1871 sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz) 1957 sql = "SELECT * FROM " + table_name + " WHERE comm_id > $$last_id$$ ORDER BY comm_id LIMIT " + str(glb_chunk_sz)
1872 self.column_headers = [] 1958 column_headers = []
1873 query = QSqlQuery(glb.db) 1959 query = QSqlQuery(glb.db)
1874 if glb.dbref.is_sqlite3: 1960 if glb.dbref.is_sqlite3:
1875 QueryExec(query, "PRAGMA table_info(" + table_name + ")") 1961 QueryExec(query, "PRAGMA table_info(" + table_name + ")")
1876 while query.next(): 1962 while query.next():
1877 self.column_headers.append(query.value(1)) 1963 column_headers.append(query.value(1))
1878 if table_name == "sqlite_master": 1964 if table_name == "sqlite_master":
1879 sql = "SELECT * FROM " + table_name 1965 sql = "SELECT * FROM " + table_name
1880 else: 1966 else:
@@ -1887,14 +1973,8 @@ class SQLAutoTableModel(SQLTableModel):
1887 schema = "public" 1973 schema = "public"
1888 QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'") 1974 QueryExec(query, "SELECT column_name FROM information_schema.columns WHERE table_schema = '" + schema + "' and table_name = '" + select_table_name + "'")
1889 while query.next(): 1975 while query.next():
1890 self.column_headers.append(query.value(0)) 1976 column_headers.append(query.value(0))
1891 super(SQLAutoTableModel, self).__init__(glb, sql, len(self.column_headers), parent) 1977 super(SQLAutoTableModel, self).__init__(glb, sql, column_headers, parent)
1892
1893 def columnCount(self, parent=None):
1894 return len(self.column_headers)
1895
1896 def columnHeader(self, column):
1897 return self.column_headers[column]
1898 1978
1899# Base class for custom ResizeColumnsToContents 1979# Base class for custom ResizeColumnsToContents
1900 1980
@@ -1997,6 +2077,103 @@ def GetTableList(glb):
1997 tables.append("information_schema.columns") 2077 tables.append("information_schema.columns")
1998 return tables 2078 return tables
1999 2079
2080# Top Calls data model
2081
2082class TopCallsModel(SQLTableModel):
2083
2084 def __init__(self, glb, report_vars, parent=None):
2085 text = ""
2086 if not glb.dbref.is_sqlite3:
2087 text = "::text"
2088 limit = ""
2089 if len(report_vars.limit):
2090 limit = " LIMIT " + report_vars.limit
2091 sql = ("SELECT comm, pid, tid, name,"
2092 " CASE"
2093 " WHEN (short_name = '[kernel.kallsyms]') THEN '[kernel]'" + text +
2094 " ELSE short_name"
2095 " END AS dso,"
2096 " call_time, return_time, (return_time - call_time) AS elapsed_time, branch_count, "
2097 " CASE"
2098 " WHEN (calls.flags = 1) THEN 'no call'" + text +
2099 " WHEN (calls.flags = 2) THEN 'no return'" + text +
2100 " WHEN (calls.flags = 3) THEN 'no call/return'" + text +
2101 " ELSE ''" + text +
2102 " END AS flags"
2103 " FROM calls"
2104 " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
2105 " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
2106 " INNER JOIN dsos ON symbols.dso_id = dsos.id"
2107 " INNER JOIN comms ON calls.comm_id = comms.id"
2108 " INNER JOIN threads ON calls.thread_id = threads.id" +
2109 report_vars.where_clause +
2110 " ORDER BY elapsed_time DESC" +
2111 limit
2112 )
2113 column_headers = ("Command", "PID", "TID", "Symbol", "Object", "Call Time", "Return Time", "Elapsed Time (ns)", "Branch Count", "Flags")
2114 self.alignment = (Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignLeft)
2115 super(TopCallsModel, self).__init__(glb, sql, column_headers, parent)
2116
2117 def columnAlignment(self, column):
2118 return self.alignment[column]
2119
2120# Top Calls report creation dialog
2121
2122class TopCallsDialog(ReportDialogBase):
2123
2124 def __init__(self, glb, parent=None):
2125 title = "Top Calls by Elapsed Time"
2126 items = (lambda g, p: LineEditDataItem(g, "Report name:", "Enter a name to appear in the window title bar", p, "REPORTNAME"),
2127 lambda g, p: SQLTableDataItem(g, "Commands:", "Only calls with these commands will be included", "comms", "comm", "comm_id", "", p),
2128 lambda g, p: SQLTableDataItem(g, "PIDs:", "Only calls with these process IDs will be included", "threads", "pid", "thread_id", "", p),
2129 lambda g, p: SQLTableDataItem(g, "TIDs:", "Only calls with these thread IDs will be included", "threads", "tid", "thread_id", "", p),
2130 lambda g, p: SQLTableDataItem(g, "DSOs:", "Only calls with these DSOs will be included", "dsos", "short_name", "dso_id", "", p),
2131 lambda g, p: SQLTableDataItem(g, "Symbols:", "Only calls with these symbols will be included", "symbols", "name", "symbol_id", "", p),
2132 lambda g, p: LineEditDataItem(g, "Raw SQL clause: ", "Enter a raw SQL WHERE clause", p),
2133 lambda g, p: PositiveIntegerDataItem(g, "Record limit:", "Limit selection to this number of records", p, "LIMIT", "100"))
2134 super(TopCallsDialog, self).__init__(glb, title, items, False, parent)
2135
2136# Top Calls window
2137
2138class TopCallsWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
2139
2140 def __init__(self, glb, report_vars, parent=None):
2141 super(TopCallsWindow, self).__init__(parent)
2142
2143 self.data_model = LookupCreateModel("Top Calls " + report_vars.UniqueId(), lambda: TopCallsModel(glb, report_vars))
2144 self.model = self.data_model
2145
2146 self.view = QTableView()
2147 self.view.setModel(self.model)
2148 self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
2149 self.view.verticalHeader().setVisible(False)
2150
2151 self.ResizeColumnsToContents()
2152
2153 self.find_bar = FindBar(self, self, True)
2154
2155 self.finder = ChildDataItemFinder(self.model)
2156
2157 self.fetch_bar = FetchMoreRecordsBar(self.data_model, self)
2158
2159 self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget())
2160
2161 self.setWidget(self.vbox.Widget())
2162
2163 AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name)
2164
2165 def Find(self, value, direction, pattern, context):
2166 self.view.setFocus()
2167 self.find_bar.Busy()
2168 self.finder.Find(value, direction, pattern, context, self.FindDone)
2169
2170 def FindDone(self, row):
2171 self.find_bar.Idle()
2172 if row >= 0:
2173 self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex()))
2174 else:
2175 self.find_bar.NotFound()
2176
2000# Action Definition 2177# Action Definition
2001 2178
2002def CreateAction(label, tip, callback, parent=None, shortcut=None): 2179def CreateAction(label, tip, callback, parent=None, shortcut=None):
@@ -2100,6 +2277,7 @@ p.c2 {
2100<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> 2277<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2101<p class=c2><a href=#allbranches>1.2 All branches</a></p> 2278<p class=c2><a href=#allbranches>1.2 All branches</a></p>
2102<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> 2279<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
2280<p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p>
2103<p class=c1><a href=#tables>2. Tables</a></p> 2281<p class=c1><a href=#tables>2. Tables</a></p>
2104<h1 id=reports>1. Reports</h1> 2282<h1 id=reports>1. Reports</h1>
2105<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> 2283<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
@@ -2175,6 +2353,10 @@ ms, us or ns. Also, negative values are relative to the end of trace. Examples:
2175 -10ms- The last 10ms 2353 -10ms- The last 10ms
2176</pre> 2354</pre>
2177N.B. Due to the granularity of timestamps, there could be no branches in any given time range. 2355N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2356<h2 id=topcallsbyelapsedtime>1.4 Top calls by elapsed time</h2>
2357The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned.
2358The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2359If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar.
2178<h1 id=tables>2. Tables</h1> 2360<h1 id=tables>2. Tables</h1>
2179The Tables menu shows all tables and views in the database. Most tables have an associated view 2361The Tables menu shows all tables and views in the database. Most tables have an associated view
2180which displays the information in a more friendly way. Not all data for large tables is fetched 2362which displays the information in a more friendly way. Not all data for large tables is fetched
@@ -2304,10 +2486,14 @@ class MainWindow(QMainWindow):
2304 edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 2486 edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")]))
2305 2487
2306 reports_menu = menu.addMenu("&Reports") 2488 reports_menu = menu.addMenu("&Reports")
2307 reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 2489 if IsSelectable(glb.db, "calls"):
2490 reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
2308 2491
2309 self.EventMenu(GetEventList(glb.db), reports_menu) 2492 self.EventMenu(GetEventList(glb.db), reports_menu)
2310 2493
2494 if IsSelectable(glb.db, "calls"):
2495 reports_menu.addAction(CreateAction("&Top calls by elapsed time", "Create a new window displaying top calls by elapsed time", self.NewTopCalls, self))
2496
2311 self.TableMenu(GetTableList(glb), menu) 2497 self.TableMenu(GetTableList(glb), menu)
2312 2498
2313 self.window_menu = WindowMenu(self.mdi_area, menu) 2499 self.window_menu = WindowMenu(self.mdi_area, menu)
@@ -2363,14 +2549,20 @@ class MainWindow(QMainWindow):
2363 def NewCallGraph(self): 2549 def NewCallGraph(self):
2364 CallGraphWindow(self.glb, self) 2550 CallGraphWindow(self.glb, self)
2365 2551
2552 def NewTopCalls(self):
2553 dialog = TopCallsDialog(self.glb, self)
2554 ret = dialog.exec_()
2555 if ret:
2556 TopCallsWindow(self.glb, dialog.report_vars, self)
2557
2366 def NewBranchView(self, event_id): 2558 def NewBranchView(self, event_id):
2367 BranchWindow(self.glb, event_id, "", "", self) 2559 BranchWindow(self.glb, event_id, ReportVars(), self)
2368 2560
2369 def NewSelectedBranchView(self, event_id): 2561 def NewSelectedBranchView(self, event_id):
2370 dialog = SelectedBranchDialog(self.glb, self) 2562 dialog = SelectedBranchDialog(self.glb, self)
2371 ret = dialog.exec_() 2563 ret = dialog.exec_()
2372 if ret: 2564 if ret:
2373 BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self) 2565 BranchWindow(self.glb, event_id, dialog.report_vars, self)
2374 2566
2375 def NewTableView(self, table_name): 2567 def NewTableView(self, table_name):
2376 TableWindow(self.glb, table_name, self) 2568 TableWindow(self.glb, table_name, self)