aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/scripts/python')
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py493
1 files changed, 490 insertions, 3 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index 24cb0bd56afa..f278ce5ebab7 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -119,6 +119,14 @@ def dsoname(name):
119 return "[kernel]" 119 return "[kernel]"
120 return name 120 return name
121 121
122def findnth(s, sub, n, offs=0):
123 pos = s.find(sub)
124 if pos < 0:
125 return pos
126 if n <= 1:
127 return offs + pos
128 return findnth(s[pos + 1:], sub, n - 1, offs + pos + 1)
129
122# Percent to one decimal place 130# Percent to one decimal place
123 131
124def PercentToOneDP(n, d): 132def PercentToOneDP(n, d):
@@ -1464,6 +1472,317 @@ class BranchWindow(QMdiSubWindow):
1464 else: 1472 else:
1465 self.find_bar.NotFound() 1473 self.find_bar.NotFound()
1466 1474
1475# Dialog data item converted and validated using a SQL table
1476
1477class SQLTableDialogDataItem():
1478
1479 def __init__(self, glb, label, placeholder_text, table_name, match_column, column_name1, column_name2, parent):
1480 self.glb = glb
1481 self.label = label
1482 self.placeholder_text = placeholder_text
1483 self.table_name = table_name
1484 self.match_column = match_column
1485 self.column_name1 = column_name1
1486 self.column_name2 = column_name2
1487 self.parent = parent
1488
1489 self.value = ""
1490
1491 self.widget = QLineEdit()
1492 self.widget.editingFinished.connect(self.Validate)
1493 self.widget.textChanged.connect(self.Invalidate)
1494 self.red = False
1495 self.error = ""
1496 self.validated = True
1497
1498 self.last_id = 0
1499 self.first_time = 0
1500 self.last_time = 2 ** 64
1501 if self.table_name == "<timeranges>":
1502 query = QSqlQuery(self.glb.db)
1503 QueryExec(query, "SELECT id, time FROM samples ORDER BY id DESC LIMIT 1")
1504 if query.next():
1505 self.last_id = int(query.value(0))
1506 self.last_time = int(query.value(1))
1507 QueryExec(query, "SELECT time FROM samples WHERE time != 0 ORDER BY id LIMIT 1")
1508 if query.next():
1509 self.first_time = int(query.value(0))
1510 if placeholder_text:
1511 placeholder_text += ", between " + str(self.first_time) + " and " + str(self.last_time)
1512
1513 if placeholder_text:
1514 self.widget.setPlaceholderText(placeholder_text)
1515
1516 def ValueToIds(self, value):
1517 ids = []
1518 query = QSqlQuery(self.glb.db)
1519 stmt = "SELECT id FROM " + self.table_name + " WHERE " + self.match_column + " = '" + value + "'"
1520 ret = query.exec_(stmt)
1521 if ret:
1522 while query.next():
1523 ids.append(str(query.value(0)))
1524 return ids
1525
1526 def IdBetween(self, query, lower_id, higher_id, order):
1527 QueryExec(query, "SELECT id FROM samples WHERE id > " + str(lower_id) + " AND id < " + str(higher_id) + " ORDER BY id " + order + " LIMIT 1")
1528 if query.next():
1529 return True, int(query.value(0))
1530 else:
1531 return False, 0
1532
1533 def BinarySearchTime(self, lower_id, higher_id, target_time, get_floor):
1534 query = QSqlQuery(self.glb.db)
1535 while True:
1536 next_id = int((lower_id + higher_id) / 2)
1537 QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1538 if not query.next():
1539 ok, dbid = self.IdBetween(query, lower_id, next_id, "DESC")
1540 if not ok:
1541 ok, dbid = self.IdBetween(query, next_id, higher_id, "")
1542 if not ok:
1543 return str(higher_id)
1544 next_id = dbid
1545 QueryExec(query, "SELECT time FROM samples WHERE id = " + str(next_id))
1546 next_time = int(query.value(0))
1547 if get_floor:
1548 if target_time > next_time:
1549 lower_id = next_id
1550 else:
1551 higher_id = next_id
1552 if higher_id <= lower_id + 1:
1553 return str(higher_id)
1554 else:
1555 if target_time >= next_time:
1556 lower_id = next_id
1557 else:
1558 higher_id = next_id
1559 if higher_id <= lower_id + 1:
1560 return str(lower_id)
1561
1562 def ConvertRelativeTime(self, val):
1563 print "val ", val
1564 mult = 1
1565 suffix = val[-2:]
1566 if suffix == "ms":
1567 mult = 1000000
1568 elif suffix == "us":
1569 mult = 1000
1570 elif suffix == "ns":
1571 mult = 1
1572 else:
1573 return val
1574 val = val[:-2].strip()
1575 if not self.IsNumber(val):
1576 return val
1577 val = int(val) * mult
1578 if val >= 0:
1579 val += self.first_time
1580 else:
1581 val += self.last_time
1582 return str(val)
1583
1584 def ConvertTimeRange(self, vrange):
1585 print "vrange ", vrange
1586 if vrange[0] == "":
1587 vrange[0] = str(self.first_time)
1588 if vrange[1] == "":
1589 vrange[1] = str(self.last_time)
1590 vrange[0] = self.ConvertRelativeTime(vrange[0])
1591 vrange[1] = self.ConvertRelativeTime(vrange[1])
1592 print "vrange2 ", vrange
1593 if not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1594 return False
1595 print "ok1"
1596 beg_range = max(int(vrange[0]), self.first_time)
1597 end_range = min(int(vrange[1]), self.last_time)
1598 if beg_range > self.last_time or end_range < self.first_time:
1599 return False
1600 print "ok2"
1601 vrange[0] = self.BinarySearchTime(0, self.last_id, beg_range, True)
1602 vrange[1] = self.BinarySearchTime(1, self.last_id + 1, end_range, False)
1603 print "vrange3 ", vrange
1604 return True
1605
1606 def AddTimeRange(self, value, ranges):
1607 print "value ", value
1608 n = value.count("-")
1609 if n == 1:
1610 pass
1611 elif n == 2:
1612 if value.split("-")[1].strip() == "":
1613 n = 1
1614 elif n == 3:
1615 n = 2
1616 else:
1617 return False
1618 pos = findnth(value, "-", n)
1619 vrange = [value[:pos].strip() ,value[pos+1:].strip()]
1620 if self.ConvertTimeRange(vrange):
1621 ranges.append(vrange)
1622 return True
1623 return False
1624
1625 def InvalidValue(self, value):
1626 self.value = ""
1627 palette = QPalette()
1628 palette.setColor(QPalette.Text,Qt.red)
1629 self.widget.setPalette(palette)
1630 self.red = True
1631 self.error = self.label + " invalid value '" + value + "'"
1632 self.parent.ShowMessage(self.error)
1633
1634 def IsNumber(self, value):
1635 try:
1636 x = int(value)
1637 except:
1638 x = 0
1639 return str(x) == value
1640
1641 def Invalidate(self):
1642 self.validated = False
1643
1644 def Validate(self):
1645 input_string = self.widget.text()
1646 self.validated = True
1647 if self.red:
1648 palette = QPalette()
1649 self.widget.setPalette(palette)
1650 self.red = False
1651 if not len(input_string.strip()):
1652 self.error = ""
1653 self.value = ""
1654 return
1655 if self.table_name == "<timeranges>":
1656 ranges = []
1657 for value in [x.strip() for x in input_string.split(",")]:
1658 if not self.AddTimeRange(value, ranges):
1659 return self.InvalidValue(value)
1660 ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1661 self.value = " OR ".join(ranges)
1662 elif self.table_name == "<ranges>":
1663 singles = []
1664 ranges = []
1665 for value in [x.strip() for x in input_string.split(",")]:
1666 if "-" in value:
1667 vrange = value.split("-")
1668 if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]):
1669 return self.InvalidValue(value)
1670 ranges.append(vrange)
1671 else:
1672 if not self.IsNumber(value):
1673 return self.InvalidValue(value)
1674 singles.append(value)
1675 ranges = [("(" + self.column_name1 + " >= " + r[0] + " AND " + self.column_name1 + " <= " + r[1] + ")") for r in ranges]
1676 if len(singles):
1677 ranges.append(self.column_name1 + " IN (" + ",".join(singles) + ")")
1678 self.value = " OR ".join(ranges)
1679 elif self.table_name:
1680 all_ids = []
1681 for value in [x.strip() for x in input_string.split(",")]:
1682 ids = self.ValueToIds(value)
1683 if len(ids):
1684 all_ids.extend(ids)
1685 else:
1686 return self.InvalidValue(value)
1687 self.value = self.column_name1 + " IN (" + ",".join(all_ids) + ")"
1688 if self.column_name2:
1689 self.value = "( " + self.value + " OR " + self.column_name2 + " IN (" + ",".join(all_ids) + ") )"
1690 else:
1691 self.value = input_string.strip()
1692 self.error = ""
1693 self.parent.ClearMessage()
1694
1695 def IsValid(self):
1696 if not self.validated:
1697 self.Validate()
1698 if len(self.error):
1699 self.parent.ShowMessage(self.error)
1700 return False
1701 return True
1702
1703# Selected branch report creation dialog
1704
1705class SelectedBranchDialog(QDialog):
1706
1707 def __init__(self, glb, parent=None):
1708 super(SelectedBranchDialog, self).__init__(parent)
1709
1710 self.glb = glb
1711
1712 self.name = ""
1713 self.where_clause = ""
1714
1715 self.setWindowTitle("Selected Branches")
1716 self.setMinimumWidth(600)
1717
1718 items = (
1719 ("Report name:", "Enter a name to appear in the window title bar", "", "", "", ""),
1720 ("Time ranges:", "Enter time ranges", "<timeranges>", "", "samples.id", ""),
1721 ("CPUs:", "Enter CPUs or ranges e.g. 0,5-6", "<ranges>", "", "cpu", ""),
1722 ("Commands:", "Only branches with these commands will be included", "comms", "comm", "comm_id", ""),
1723 ("PIDs:", "Only branches with these process IDs will be included", "threads", "pid", "thread_id", ""),
1724 ("TIDs:", "Only branches with these thread IDs will be included", "threads", "tid", "thread_id", ""),
1725 ("DSOs:", "Only branches with these DSOs will be included", "dsos", "short_name", "samples.dso_id", "to_dso_id"),
1726 ("Symbols:", "Only branches with these symbols will be included", "symbols", "name", "symbol_id", "to_symbol_id"),
1727 ("Raw SQL clause: ", "Enter a raw SQL WHERE clause", "", "", "", ""),
1728 )
1729 self.data_items = [SQLTableDialogDataItem(glb, *x, parent=self) for x in items]
1730
1731 self.grid = QGridLayout()
1732
1733 for row in xrange(len(self.data_items)):
1734 self.grid.addWidget(QLabel(self.data_items[row].label), row, 0)
1735 self.grid.addWidget(self.data_items[row].widget, row, 1)
1736
1737 self.status = QLabel()
1738
1739 self.ok_button = QPushButton("Ok", self)
1740 self.ok_button.setDefault(True)
1741 self.ok_button.released.connect(self.Ok)
1742 self.ok_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1743
1744 self.cancel_button = QPushButton("Cancel", self)
1745 self.cancel_button.released.connect(self.reject)
1746 self.cancel_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1747
1748 self.hbox = QHBoxLayout()
1749 #self.hbox.addStretch()
1750 self.hbox.addWidget(self.status)
1751 self.hbox.addWidget(self.ok_button)
1752 self.hbox.addWidget(self.cancel_button)
1753
1754 self.vbox = QVBoxLayout()
1755 self.vbox.addLayout(self.grid)
1756 self.vbox.addLayout(self.hbox)
1757
1758 self.setLayout(self.vbox);
1759
1760 def Ok(self):
1761 self.name = self.data_items[0].value
1762 if not self.name:
1763 self.ShowMessage("Report name is required")
1764 return
1765 for d in self.data_items:
1766 if not d.IsValid():
1767 return
1768 for d in self.data_items[1:]:
1769 if len(d.value):
1770 if len(self.where_clause):
1771 self.where_clause += " AND "
1772 self.where_clause += d.value
1773 if len(self.where_clause):
1774 self.where_clause = " AND ( " + self.where_clause + " ) "
1775 else:
1776 self.ShowMessage("No selection")
1777 return
1778 self.accept()
1779
1780 def ShowMessage(self, msg):
1781 self.status.setText("<font color=#FF0000>" + msg)
1782
1783 def ClearMessage(self):
1784 self.status.setText("")
1785
1467# Event list 1786# Event list
1468 1787
1469def GetEventList(db): 1788def GetEventList(db):
@@ -1656,7 +1975,7 @@ class TableWindow(QMdiSubWindow, ResizeColumnsToContentsBase):
1656 def FindDone(self, row): 1975 def FindDone(self, row):
1657 self.find_bar.Idle() 1976 self.find_bar.Idle()
1658 if row >= 0: 1977 if row >= 0:
1659 self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) 1978 self.view.setCurrentIndex(self.model.mapFromSource(self.data_model.index(row, 0, QModelIndex())))
1660 else: 1979 else:
1661 self.find_bar.NotFound() 1980 self.find_bar.NotFound()
1662 1981
@@ -1765,6 +2084,149 @@ class WindowMenu():
1765 def setActiveSubWindow(self, nr): 2084 def setActiveSubWindow(self, nr):
1766 self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 2085 self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
1767 2086
2087# Help text
2088
2089glb_help_text = """
2090<h1>Contents</h1>
2091<style>
2092p.c1 {
2093 text-indent: 40px;
2094}
2095p.c2 {
2096 text-indent: 80px;
2097}
2098}
2099</style>
2100<p class=c1><a href=#reports>1. Reports</a></p>
2101<p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p>
2102<p class=c2><a href=#allbranches>1.2 All branches</a></p>
2103<p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p>
2104<p class=c1><a href=#tables>2. Tables</a></p>
2105<h1 id=reports>1. Reports</h1>
2106<h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2>
2107The result is a GUI window with a tree representing a context-sensitive
2108call-graph. Expanding a couple of levels of the tree and adjusting column
2109widths to suit will display something like:
2110<pre>
2111 Call Graph: pt_example
2112Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%)
2113v- ls
2114 v- 2638:2638
2115 v- _start ld-2.19.so 1 10074071 100.0 211135 100.0
2116 |- unknown unknown 1 13198 0.1 1 0.0
2117 >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3
2118 >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3
2119 v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4
2120 >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1
2121 >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0
2122 >- __libc_csu_init ls 1 10354 0.1 10 0.0
2123 |- _setjmp libc-2.19.so 1 0 0.0 4 0.0
2124 v- main ls 1 8182043 99.6 180254 99.9
2125</pre>
2126<h3>Points to note:</h3>
2127<ul>
2128<li>The top level is a command name (comm)</li>
2129<li>The next level is a thread (pid:tid)</li>
2130<li>Subsequent levels are functions</li>
2131<li>'Count' is the number of calls</li>
2132<li>'Time' is the elapsed time until the function returns</li>
2133<li>Percentages are relative to the level above</li>
2134<li>'Branch Count' is the total number of branches for that function and all functions that it calls
2135</ul>
2136<h3>Find</h3>
2137Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match.
2138The pattern matching symbols are ? for any character and * for zero or more characters.
2139<h2 id=allbranches>1.2 All branches</h2>
2140The All branches report displays all branches in chronological order.
2141Not all data is fetched immediately. More records can be fetched using the Fetch bar provided.
2142<h3>Disassembly</h3>
2143Open a branch to display disassembly. This only works if:
2144<ol>
2145<li>The disassembler is available. Currently, only Intel XED is supported - see <a href=#xed>Intel XED Setup</a></li>
2146<li>The object code is available. Currently, only the perf build ID cache is searched for object code.
2147The default directory ~/.debug can be overridden by setting environment variable PERF_BUILDID_DIR.
2148One exception is kcore where the DSO long name is used (refer dsos_view on the Tables menu),
2149or alternatively, set environment variable PERF_KCORE to the kcore file name.</li>
2150</ol>
2151<h4 id=xed>Intel XED Setup</h4>
2152To use Intel XED, libxed.so must be present. To build and install libxed.so:
2153<pre>
2154git clone https://github.com/intelxed/mbuild.git mbuild
2155git clone https://github.com/intelxed/xed
2156cd xed
2157./mfile.py --share
2158sudo ./mfile.py --prefix=/usr/local install
2159sudo ldconfig
2160</pre>
2161<h3>Find</h3>
2162Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
2163Refer to Python documentation for the regular expression syntax.
2164All columns are searched, but only currently fetched rows are searched.
2165<h2 id=selectedbranches>1.3 Selected branches</h2>
2166This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced
2167by various selection criteria. A dialog box displays available criteria which are AND'ed together.
2168<h3>1.3.1 Time ranges</h3>
2169The time ranges hint text shows the total time range. Relative time ranges can also be entered in
2170ms, us or ns. Also, negative values are relative to the end of trace. Examples:
2171<pre>
2172 81073085947329-81073085958238 From 81073085947329 to 81073085958238
2173 100us-200us From 100us to 200us
2174 10ms- From 10ms to the end
2175 -100ns The first 100ns
2176 -10ms- The last 10ms
2177</pre>
2178N.B. Due to the granularity of timestamps, there could be no branches in any given time range.
2179<h1 id=tables>2. Tables</h1>
2180The Tables menu shows all tables and views in the database. Most tables have an associated view
2181which displays the information in a more friendly way. Not all data for large tables is fetched
2182immediately. More records can be fetched using the Fetch bar provided. Columns can be sorted,
2183but that can be slow for large tables.
2184<p>There are also tables of database meta-information.
2185For SQLite3 databases, the sqlite_master table is included.
2186For PostgreSQL databases, information_schema.tables/views/columns are included.
2187<h3>Find</h3>
2188Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
2189Refer to Python documentation for the regular expression syntax.
2190All columns are searched, but only currently fetched rows are searched.
2191<p>N.B. Results are found in id order, so if the table is re-ordered, find-next and find-previous
2192will go to the next/previous result in id order, instead of display order.
2193"""
2194
2195# Help window
2196
2197class HelpWindow(QMdiSubWindow):
2198
2199 def __init__(self, glb, parent=None):
2200 super(HelpWindow, self).__init__(parent)
2201
2202 self.text = QTextBrowser()
2203 self.text.setHtml(glb_help_text)
2204 self.text.setReadOnly(True)
2205 self.text.setOpenExternalLinks(True)
2206
2207 self.setWidget(self.text)
2208
2209 AddSubWindow(glb.mainwindow.mdi_area, self, "Exported SQL Viewer Help")
2210
2211# Main window that only displays the help text
2212
2213class HelpOnlyWindow(QMainWindow):
2214
2215 def __init__(self, parent=None):
2216 super(HelpOnlyWindow, self).__init__(parent)
2217
2218 self.setMinimumSize(200, 100)
2219 self.resize(800, 600)
2220 self.setWindowTitle("Exported SQL Viewer Help")
2221 self.setWindowIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
2222
2223 self.text = QTextBrowser()
2224 self.text.setHtml(glb_help_text)
2225 self.text.setReadOnly(True)
2226 self.text.setOpenExternalLinks(True)
2227
2228 self.setCentralWidget(self.text)
2229
1768# Font resize 2230# Font resize
1769 2231
1770def ResizeFont(widget, diff): 2232def ResizeFont(widget, diff):
@@ -1851,6 +2313,9 @@ class MainWindow(QMainWindow):
1851 2313
1852 self.window_menu = WindowMenu(self.mdi_area, menu) 2314 self.window_menu = WindowMenu(self.mdi_area, menu)
1853 2315
2316 help_menu = menu.addMenu("&Help")
2317 help_menu.addAction(CreateAction("&Exported SQL Viewer Help", "Helpful information", self.Help, self, QKeySequence.HelpContents))
2318
1854 def Find(self): 2319 def Find(self):
1855 win = self.mdi_area.activeSubWindow() 2320 win = self.mdi_area.activeSubWindow()
1856 if win: 2321 if win:
@@ -1888,6 +2353,8 @@ class MainWindow(QMainWindow):
1888 if event == "branches": 2353 if event == "branches":
1889 label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" 2354 label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
1890 reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) 2355 reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
2356 label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
2357 reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
1891 2358
1892 def TableMenu(self, tables, menu): 2359 def TableMenu(self, tables, menu):
1893 table_menu = menu.addMenu("&Tables") 2360 table_menu = menu.addMenu("&Tables")
@@ -1900,9 +2367,18 @@ class MainWindow(QMainWindow):
1900 def NewBranchView(self, event_id): 2367 def NewBranchView(self, event_id):
1901 BranchWindow(self.glb, event_id, "", "", self) 2368 BranchWindow(self.glb, event_id, "", "", self)
1902 2369
2370 def NewSelectedBranchView(self, event_id):
2371 dialog = SelectedBranchDialog(self.glb, self)
2372 ret = dialog.exec_()
2373 if ret:
2374 BranchWindow(self.glb, event_id, dialog.name, dialog.where_clause, self)
2375
1903 def NewTableView(self, table_name): 2376 def NewTableView(self, table_name):
1904 TableWindow(self.glb, table_name, self) 2377 TableWindow(self.glb, table_name, self)
1905 2378
2379 def Help(self):
2380 HelpWindow(self.glb, self)
2381
1906# XED Disassembler 2382# XED Disassembler
1907 2383
1908class xed_state_t(Structure): 2384class xed_state_t(Structure):
@@ -1929,7 +2405,12 @@ class XEDInstruction():
1929class LibXED(): 2405class LibXED():
1930 2406
1931 def __init__(self): 2407 def __init__(self):
1932 self.libxed = CDLL("libxed.so") 2408 try:
2409 self.libxed = CDLL("libxed.so")
2410 except:
2411 self.libxed = None
2412 if not self.libxed:
2413 self.libxed = CDLL("/usr/local/lib/libxed.so")
1933 2414
1934 self.xed_tables_init = self.libxed.xed_tables_init 2415 self.xed_tables_init = self.libxed.xed_tables_init
1935 self.xed_tables_init.restype = None 2416 self.xed_tables_init.restype = None
@@ -2097,10 +2578,16 @@ class DBRef():
2097 2578
2098def Main(): 2579def Main():
2099 if (len(sys.argv) < 2): 2580 if (len(sys.argv) < 2):
2100 print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 2581 print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}"
2101 raise Exception("Too few arguments") 2582 raise Exception("Too few arguments")
2102 2583
2103 dbname = sys.argv[1] 2584 dbname = sys.argv[1]
2585 if dbname == "--help-only":
2586 app = QApplication(sys.argv)
2587 mainwindow = HelpOnlyWindow()
2588 mainwindow.show()
2589 err = app.exec_()
2590 sys.exit(err)
2104 2591
2105 is_sqlite3 = False 2592 is_sqlite3 = False
2106 try: 2593 try: