diff options
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
| -rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 354 |
1 files changed, 279 insertions, 75 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index 09ce73b07d35..afec9479ca7f 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py | |||
| @@ -167,9 +167,10 @@ class Thread(QThread): | |||
| 167 | 167 | ||
| 168 | class TreeModel(QAbstractItemModel): | 168 | class TreeModel(QAbstractItemModel): |
| 169 | 169 | ||
| 170 | def __init__(self, root, parent=None): | 170 | def __init__(self, glb, parent=None): |
| 171 | super(TreeModel, self).__init__(parent) | 171 | super(TreeModel, self).__init__(parent) |
| 172 | self.root = root | 172 | self.glb = glb |
| 173 | self.root = self.GetRoot() | ||
| 173 | self.last_row_read = 0 | 174 | self.last_row_read = 0 |
| 174 | 175 | ||
| 175 | def Item(self, parent): | 176 | def Item(self, parent): |
| @@ -557,24 +558,12 @@ class CallGraphRootItem(CallGraphLevelItemBase): | |||
| 557 | self.child_items.append(child_item) | 558 | self.child_items.append(child_item) |
| 558 | self.child_count += 1 | 559 | self.child_count += 1 |
| 559 | 560 | ||
| 560 | # Context-sensitive call graph data model | 561 | # Context-sensitive call graph data model base |
| 561 | 562 | ||
| 562 | class CallGraphModel(TreeModel): | 563 | class CallGraphModelBase(TreeModel): |
| 563 | 564 | ||
| 564 | def __init__(self, glb, parent=None): | 565 | def __init__(self, glb, parent=None): |
| 565 | super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) | 566 | super(CallGraphModelBase, self).__init__(glb, parent) |
| 566 | self.glb = glb | ||
| 567 | |||
| 568 | def columnCount(self, parent=None): | ||
| 569 | return 7 | ||
| 570 | |||
| 571 | def columnHeader(self, column): | ||
| 572 | headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] | ||
| 573 | return headers[column] | ||
| 574 | |||
| 575 | def columnAlignment(self, column): | ||
| 576 | alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] | ||
| 577 | return alignment[column] | ||
| 578 | 567 | ||
| 579 | def FindSelect(self, value, pattern, query): | 568 | def FindSelect(self, value, pattern, query): |
| 580 | if pattern: | 569 | if pattern: |
| @@ -594,34 +583,7 @@ class CallGraphModel(TreeModel): | |||
| 594 | match = " GLOB '" + str(value) + "'" | 583 | match = " GLOB '" + str(value) + "'" |
| 595 | else: | 584 | else: |
| 596 | match = " = '" + str(value) + "'" | 585 | match = " = '" + str(value) + "'" |
| 597 | QueryExec(query, "SELECT call_path_id, comm_id, thread_id" | 586 | self.DoFindSelect(query, match) |
| 598 | " FROM calls" | ||
| 599 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
| 600 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
| 601 | " WHERE symbols.name" + match + | ||
| 602 | " GROUP BY comm_id, thread_id, call_path_id" | ||
| 603 | " ORDER BY comm_id, thread_id, call_path_id") | ||
| 604 | |||
| 605 | def FindPath(self, query): | ||
| 606 | # Turn the query result into a list of ids that the tree view can walk | ||
| 607 | # to open the tree at the right place. | ||
| 608 | ids = [] | ||
| 609 | parent_id = query.value(0) | ||
| 610 | while parent_id: | ||
| 611 | ids.insert(0, parent_id) | ||
| 612 | q2 = QSqlQuery(self.glb.db) | ||
| 613 | QueryExec(q2, "SELECT parent_id" | ||
| 614 | " FROM call_paths" | ||
| 615 | " WHERE id = " + str(parent_id)) | ||
| 616 | if not q2.next(): | ||
| 617 | break | ||
| 618 | parent_id = q2.value(0) | ||
| 619 | # The call path root is not used | ||
| 620 | if ids[0] == 1: | ||
| 621 | del ids[0] | ||
| 622 | ids.insert(0, query.value(2)) | ||
| 623 | ids.insert(0, query.value(1)) | ||
| 624 | return ids | ||
| 625 | 587 | ||
| 626 | def Found(self, query, found): | 588 | def Found(self, query, found): |
| 627 | if found: | 589 | if found: |
| @@ -675,6 +637,201 @@ class CallGraphModel(TreeModel): | |||
| 675 | def FindDone(self, thread, callback, ids): | 637 | def FindDone(self, thread, callback, ids): |
| 676 | callback(ids) | 638 | callback(ids) |
| 677 | 639 | ||
| 640 | # Context-sensitive call graph data model | ||
| 641 | |||
| 642 | class CallGraphModel(CallGraphModelBase): | ||
| 643 | |||
| 644 | def __init__(self, glb, parent=None): | ||
| 645 | super(CallGraphModel, self).__init__(glb, parent) | ||
| 646 | |||
| 647 | def GetRoot(self): | ||
| 648 | return CallGraphRootItem(self.glb) | ||
| 649 | |||
| 650 | def columnCount(self, parent=None): | ||
| 651 | return 7 | ||
| 652 | |||
| 653 | def columnHeader(self, column): | ||
| 654 | headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] | ||
| 655 | return headers[column] | ||
| 656 | |||
| 657 | def columnAlignment(self, column): | ||
| 658 | alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] | ||
| 659 | return alignment[column] | ||
| 660 | |||
| 661 | def DoFindSelect(self, query, match): | ||
| 662 | QueryExec(query, "SELECT call_path_id, comm_id, thread_id" | ||
| 663 | " FROM calls" | ||
| 664 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
| 665 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
| 666 | " WHERE symbols.name" + match + | ||
| 667 | " GROUP BY comm_id, thread_id, call_path_id" | ||
| 668 | " ORDER BY comm_id, thread_id, call_path_id") | ||
| 669 | |||
| 670 | def FindPath(self, query): | ||
| 671 | # Turn the query result into a list of ids that the tree view can walk | ||
| 672 | # to open the tree at the right place. | ||
| 673 | ids = [] | ||
| 674 | parent_id = query.value(0) | ||
| 675 | while parent_id: | ||
| 676 | ids.insert(0, parent_id) | ||
| 677 | q2 = QSqlQuery(self.glb.db) | ||
| 678 | QueryExec(q2, "SELECT parent_id" | ||
| 679 | " FROM call_paths" | ||
| 680 | " WHERE id = " + str(parent_id)) | ||
| 681 | if not q2.next(): | ||
| 682 | break | ||
| 683 | parent_id = q2.value(0) | ||
| 684 | # The call path root is not used | ||
| 685 | if ids[0] == 1: | ||
| 686 | del ids[0] | ||
| 687 | ids.insert(0, query.value(2)) | ||
| 688 | ids.insert(0, query.value(1)) | ||
| 689 | return ids | ||
| 690 | |||
| 691 | # Call tree data model level 2+ item base | ||
| 692 | |||
| 693 | class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): | ||
| 694 | |||
| 695 | def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item): | ||
| 696 | super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) | ||
| 697 | self.comm_id = comm_id | ||
| 698 | self.thread_id = thread_id | ||
| 699 | self.calls_id = calls_id | ||
| 700 | self.branch_count = branch_count | ||
| 701 | self.time = time | ||
| 702 | |||
| 703 | def Select(self): | ||
| 704 | self.query_done = True; | ||
| 705 | if self.calls_id == 0: | ||
| 706 | comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) | ||
| 707 | else: | ||
| 708 | comm_thread = "" | ||
| 709 | query = QSqlQuery(self.glb.db) | ||
| 710 | QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count" | ||
| 711 | " FROM calls" | ||
| 712 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
| 713 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
| 714 | " INNER JOIN dsos ON symbols.dso_id = dsos.id" | ||
| 715 | " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread + | ||
| 716 | " ORDER BY call_time, calls.id") | ||
| 717 | while query.next(): | ||
| 718 | child_item = CallTreeLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) | ||
| 719 | self.child_items.append(child_item) | ||
| 720 | self.child_count += 1 | ||
| 721 | |||
| 722 | # Call tree data model level three item | ||
| 723 | |||
| 724 | class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase): | ||
| 725 | |||
| 726 | def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item): | ||
| 727 | super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item) | ||
| 728 | dso = dsoname(dso) | ||
| 729 | self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] | ||
| 730 | self.dbid = calls_id | ||
| 731 | |||
| 732 | # Call tree data model level two item | ||
| 733 | |||
| 734 | class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase): | ||
| 735 | |||
| 736 | def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): | ||
| 737 | super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item) | ||
| 738 | self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] | ||
| 739 | self.dbid = thread_id | ||
| 740 | |||
| 741 | def Select(self): | ||
| 742 | super(CallTreeLevelTwoItem, self).Select() | ||
| 743 | for child_item in self.child_items: | ||
| 744 | self.time += child_item.time | ||
| 745 | self.branch_count += child_item.branch_count | ||
| 746 | for child_item in self.child_items: | ||
| 747 | child_item.data[4] = PercentToOneDP(child_item.time, self.time) | ||
| 748 | child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) | ||
| 749 | |||
| 750 | # Call tree data model level one item | ||
| 751 | |||
| 752 | class CallTreeLevelOneItem(CallGraphLevelItemBase): | ||
| 753 | |||
| 754 | def __init__(self, glb, row, comm_id, comm, parent_item): | ||
| 755 | super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item) | ||
| 756 | self.data = [comm, "", "", "", "", "", ""] | ||
| 757 | self.dbid = comm_id | ||
| 758 | |||
| 759 | def Select(self): | ||
| 760 | self.query_done = True; | ||
| 761 | query = QSqlQuery(self.glb.db) | ||
| 762 | QueryExec(query, "SELECT thread_id, pid, tid" | ||
| 763 | " FROM comm_threads" | ||
| 764 | " INNER JOIN threads ON thread_id = threads.id" | ||
| 765 | " WHERE comm_id = " + str(self.dbid)) | ||
| 766 | while query.next(): | ||
| 767 | child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) | ||
| 768 | self.child_items.append(child_item) | ||
| 769 | self.child_count += 1 | ||
| 770 | |||
| 771 | # Call tree data model root item | ||
| 772 | |||
| 773 | class CallTreeRootItem(CallGraphLevelItemBase): | ||
| 774 | |||
| 775 | def __init__(self, glb): | ||
| 776 | super(CallTreeRootItem, self).__init__(glb, 0, None) | ||
| 777 | self.dbid = 0 | ||
| 778 | self.query_done = True; | ||
| 779 | query = QSqlQuery(glb.db) | ||
| 780 | QueryExec(query, "SELECT id, comm FROM comms") | ||
| 781 | while query.next(): | ||
| 782 | if not query.value(0): | ||
| 783 | continue | ||
| 784 | child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) | ||
| 785 | self.child_items.append(child_item) | ||
| 786 | self.child_count += 1 | ||
| 787 | |||
| 788 | # Call Tree data model | ||
| 789 | |||
| 790 | class CallTreeModel(CallGraphModelBase): | ||
| 791 | |||
| 792 | def __init__(self, glb, parent=None): | ||
| 793 | super(CallTreeModel, self).__init__(glb, parent) | ||
| 794 | |||
| 795 | def GetRoot(self): | ||
| 796 | return CallTreeRootItem(self.glb) | ||
| 797 | |||
| 798 | def columnCount(self, parent=None): | ||
| 799 | return 7 | ||
| 800 | |||
| 801 | def columnHeader(self, column): | ||
| 802 | headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] | ||
| 803 | return headers[column] | ||
| 804 | |||
| 805 | def columnAlignment(self, column): | ||
| 806 | alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] | ||
| 807 | return alignment[column] | ||
| 808 | |||
| 809 | def DoFindSelect(self, query, match): | ||
| 810 | QueryExec(query, "SELECT calls.id, comm_id, thread_id" | ||
| 811 | " FROM calls" | ||
| 812 | " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" | ||
| 813 | " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" | ||
| 814 | " WHERE symbols.name" + match + | ||
| 815 | " ORDER BY comm_id, thread_id, call_time, calls.id") | ||
| 816 | |||
| 817 | def FindPath(self, query): | ||
| 818 | # Turn the query result into a list of ids that the tree view can walk | ||
| 819 | # to open the tree at the right place. | ||
| 820 | ids = [] | ||
| 821 | parent_id = query.value(0) | ||
| 822 | while parent_id: | ||
| 823 | ids.insert(0, parent_id) | ||
| 824 | q2 = QSqlQuery(self.glb.db) | ||
| 825 | QueryExec(q2, "SELECT parent_id" | ||
| 826 | " FROM calls" | ||
| 827 | " WHERE id = " + str(parent_id)) | ||
| 828 | if not q2.next(): | ||
| 829 | break | ||
| 830 | parent_id = q2.value(0) | ||
| 831 | ids.insert(0, query.value(2)) | ||
| 832 | ids.insert(0, query.value(1)) | ||
| 833 | return ids | ||
| 834 | |||
| 678 | # Vertical widget layout | 835 | # Vertical widget layout |
| 679 | 836 | ||
| 680 | class VBox(): | 837 | class VBox(): |
| @@ -693,28 +850,16 @@ class VBox(): | |||
| 693 | def Widget(self): | 850 | def Widget(self): |
| 694 | return self.vbox | 851 | return self.vbox |
| 695 | 852 | ||
| 696 | # Context-sensitive call graph window | 853 | # Tree window base |
| 697 | |||
| 698 | class CallGraphWindow(QMdiSubWindow): | ||
| 699 | |||
| 700 | def __init__(self, glb, parent=None): | ||
| 701 | super(CallGraphWindow, self).__init__(parent) | ||
| 702 | |||
| 703 | self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) | ||
| 704 | |||
| 705 | self.view = QTreeView() | ||
| 706 | self.view.setModel(self.model) | ||
| 707 | |||
| 708 | for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): | ||
| 709 | self.view.setColumnWidth(c, w) | ||
| 710 | |||
| 711 | self.find_bar = FindBar(self, self) | ||
| 712 | 854 | ||
| 713 | self.vbox = VBox(self.view, self.find_bar.Widget()) | 855 | class TreeWindowBase(QMdiSubWindow): |
| 714 | 856 | ||
| 715 | self.setWidget(self.vbox.Widget()) | 857 | def __init__(self, parent=None): |
| 858 | super(TreeWindowBase, self).__init__(parent) | ||
| 716 | 859 | ||
| 717 | AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") | 860 | self.model = None |
| 861 | self.view = None | ||
| 862 | self.find_bar = None | ||
| 718 | 863 | ||
| 719 | def DisplayFound(self, ids): | 864 | def DisplayFound(self, ids): |
| 720 | if not len(ids): | 865 | if not len(ids): |
| @@ -747,6 +892,53 @@ class CallGraphWindow(QMdiSubWindow): | |||
| 747 | if not found: | 892 | if not found: |
| 748 | self.find_bar.NotFound() | 893 | self.find_bar.NotFound() |
| 749 | 894 | ||
| 895 | |||
| 896 | # Context-sensitive call graph window | ||
| 897 | |||
| 898 | class CallGraphWindow(TreeWindowBase): | ||
| 899 | |||
| 900 | def __init__(self, glb, parent=None): | ||
| 901 | super(CallGraphWindow, self).__init__(parent) | ||
| 902 | |||
| 903 | self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) | ||
| 904 | |||
| 905 | self.view = QTreeView() | ||
| 906 | self.view.setModel(self.model) | ||
| 907 | |||
| 908 | for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): | ||
| 909 | self.view.setColumnWidth(c, w) | ||
| 910 | |||
| 911 | self.find_bar = FindBar(self, self) | ||
| 912 | |||
| 913 | self.vbox = VBox(self.view, self.find_bar.Widget()) | ||
| 914 | |||
| 915 | self.setWidget(self.vbox.Widget()) | ||
| 916 | |||
| 917 | AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") | ||
| 918 | |||
| 919 | # Call tree window | ||
| 920 | |||
| 921 | class CallTreeWindow(TreeWindowBase): | ||
| 922 | |||
| 923 | def __init__(self, glb, parent=None): | ||
| 924 | super(CallTreeWindow, self).__init__(parent) | ||
| 925 | |||
| 926 | self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) | ||
| 927 | |||
| 928 | self.view = QTreeView() | ||
| 929 | self.view.setModel(self.model) | ||
| 930 | |||
| 931 | for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): | ||
| 932 | self.view.setColumnWidth(c, w) | ||
| 933 | |||
| 934 | self.find_bar = FindBar(self, self) | ||
| 935 | |||
| 936 | self.vbox = VBox(self.view, self.find_bar.Widget()) | ||
| 937 | |||
| 938 | self.setWidget(self.vbox.Widget()) | ||
| 939 | |||
| 940 | AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") | ||
| 941 | |||
| 750 | # Child data item finder | 942 | # Child data item finder |
| 751 | 943 | ||
| 752 | class ChildDataItemFinder(): | 944 | class ChildDataItemFinder(): |
| @@ -1327,8 +1519,7 @@ class BranchModel(TreeModel): | |||
| 1327 | progress = Signal(object) | 1519 | progress = Signal(object) |
| 1328 | 1520 | ||
| 1329 | def __init__(self, glb, event_id, where_clause, parent=None): | 1521 | def __init__(self, glb, event_id, where_clause, parent=None): |
| 1330 | super(BranchModel, self).__init__(BranchRootItem(), parent) | 1522 | super(BranchModel, self).__init__(glb, parent) |
| 1331 | self.glb = glb | ||
| 1332 | self.event_id = event_id | 1523 | self.event_id = event_id |
| 1333 | self.more = True | 1524 | self.more = True |
| 1334 | self.populated = 0 | 1525 | self.populated = 0 |
| @@ -1352,6 +1543,9 @@ class BranchModel(TreeModel): | |||
| 1352 | self.fetcher.done.connect(self.Update) | 1543 | self.fetcher.done.connect(self.Update) |
| 1353 | self.fetcher.Fetch(glb_chunk_sz) | 1544 | self.fetcher.Fetch(glb_chunk_sz) |
| 1354 | 1545 | ||
| 1546 | def GetRoot(self): | ||
| 1547 | return BranchRootItem() | ||
| 1548 | |||
| 1355 | def columnCount(self, parent=None): | 1549 | def columnCount(self, parent=None): |
| 1356 | return 8 | 1550 | return 8 |
| 1357 | 1551 | ||
| @@ -1863,10 +2057,10 @@ def GetEventList(db): | |||
| 1863 | 2057 | ||
| 1864 | # Is a table selectable | 2058 | # Is a table selectable |
| 1865 | 2059 | ||
| 1866 | def IsSelectable(db, table): | 2060 | def IsSelectable(db, table, sql = ""): |
| 1867 | query = QSqlQuery(db) | 2061 | query = QSqlQuery(db) |
| 1868 | try: | 2062 | try: |
| 1869 | QueryExec(query, "SELECT * FROM " + table + " LIMIT 1") | 2063 | QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1") |
| 1870 | except: | 2064 | except: |
| 1871 | return False | 2065 | return False |
| 1872 | return True | 2066 | return True |
| @@ -2275,9 +2469,10 @@ p.c2 { | |||
| 2275 | </style> | 2469 | </style> |
| 2276 | <p class=c1><a href=#reports>1. Reports</a></p> | 2470 | <p class=c1><a href=#reports>1. Reports</a></p> |
| 2277 | <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> | 2471 | <p class=c2><a href=#callgraph>1.1 Context-Sensitive Call Graph</a></p> |
| 2278 | <p class=c2><a href=#allbranches>1.2 All branches</a></p> | 2472 | <p class=c2><a href=#calltree>1.2 Call Tree</a></p> |
| 2279 | <p class=c2><a href=#selectedbranches>1.3 Selected branches</a></p> | 2473 | <p class=c2><a href=#allbranches>1.3 All branches</a></p> |
| 2280 | <p class=c2><a href=#topcallsbyelapsedtime>1.4 Top calls by elapsed time</a></p> | 2474 | <p class=c2><a href=#selectedbranches>1.4 Selected branches</a></p> |
| 2475 | <p class=c2><a href=#topcallsbyelapsedtime>1.5 Top calls by elapsed time</a></p> | ||
| 2281 | <p class=c1><a href=#tables>2. Tables</a></p> | 2476 | <p class=c1><a href=#tables>2. Tables</a></p> |
| 2282 | <h1 id=reports>1. Reports</h1> | 2477 | <h1 id=reports>1. Reports</h1> |
| 2283 | <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> | 2478 | <h2 id=callgraph>1.1 Context-Sensitive Call Graph</h2> |
| @@ -2313,7 +2508,10 @@ v- ls | |||
| 2313 | <h3>Find</h3> | 2508 | <h3>Find</h3> |
| 2314 | Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match. | 2509 | Ctrl-F displays a Find bar which finds function names by either an exact match or a pattern match. |
| 2315 | The pattern matching symbols are ? for any character and * for zero or more characters. | 2510 | The pattern matching symbols are ? for any character and * for zero or more characters. |
| 2316 | <h2 id=allbranches>1.2 All branches</h2> | 2511 | <h2 id=calltree>1.2 Call Tree</h2> |
| 2512 | The Call Tree report is very similar to the Context-Sensitive Call Graph, but the data is not aggregated. | ||
| 2513 | Also the 'Count' column, which would be always 1, is replaced by the 'Call Time'. | ||
| 2514 | <h2 id=allbranches>1.3 All branches</h2> | ||
| 2317 | The All branches report displays all branches in chronological order. | 2515 | The All branches report displays all branches in chronological order. |
| 2318 | Not all data is fetched immediately. More records can be fetched using the Fetch bar provided. | 2516 | Not all data is fetched immediately. More records can be fetched using the Fetch bar provided. |
| 2319 | <h3>Disassembly</h3> | 2517 | <h3>Disassembly</h3> |
| @@ -2339,10 +2537,10 @@ sudo ldconfig | |||
| 2339 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. | 2537 | Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match. |
| 2340 | Refer to Python documentation for the regular expression syntax. | 2538 | Refer to Python documentation for the regular expression syntax. |
| 2341 | All columns are searched, but only currently fetched rows are searched. | 2539 | All columns are searched, but only currently fetched rows are searched. |
| 2342 | <h2 id=selectedbranches>1.3 Selected branches</h2> | 2540 | <h2 id=selectedbranches>1.4 Selected branches</h2> |
| 2343 | This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced | 2541 | This is the same as the <a href=#allbranches>All branches</a> report but with the data reduced |
| 2344 | by various selection criteria. A dialog box displays available criteria which are AND'ed together. | 2542 | by various selection criteria. A dialog box displays available criteria which are AND'ed together. |
| 2345 | <h3>1.3.1 Time ranges</h3> | 2543 | <h3>1.4.1 Time ranges</h3> |
| 2346 | The time ranges hint text shows the total time range. Relative time ranges can also be entered in | 2544 | The time ranges hint text shows the total time range. Relative time ranges can also be entered in |
| 2347 | ms, us or ns. Also, negative values are relative to the end of trace. Examples: | 2545 | ms, us or ns. Also, negative values are relative to the end of trace. Examples: |
| 2348 | <pre> | 2546 | <pre> |
| @@ -2353,7 +2551,7 @@ ms, us or ns. Also, negative values are relative to the end of trace. Examples: | |||
| 2353 | -10ms- The last 10ms | 2551 | -10ms- The last 10ms |
| 2354 | </pre> | 2552 | </pre> |
| 2355 | N.B. Due to the granularity of timestamps, there could be no branches in any given time range. | 2553 | N.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> | 2554 | <h2 id=topcallsbyelapsedtime>1.5 Top calls by elapsed time</h2> |
| 2357 | The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned. | 2555 | The Top calls by elapsed time report displays calls in descending order of time elapsed between when the function was called and when it returned. |
| 2358 | The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. | 2556 | The data is reduced by various selection criteria. A dialog box displays available criteria which are AND'ed together. |
| 2359 | If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. | 2557 | If not all data is fetched, a Fetch bar is provided. Ctrl-F displays a Find bar. |
| @@ -2489,6 +2687,9 @@ class MainWindow(QMainWindow): | |||
| 2489 | if IsSelectable(glb.db, "calls"): | 2687 | 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)) | 2688 | reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) |
| 2491 | 2689 | ||
| 2690 | if IsSelectable(glb.db, "calls", "WHERE parent_id >= 0"): | ||
| 2691 | reports_menu.addAction(CreateAction("Call &Tree", "Create a new window containing a call tree", self.NewCallTree, self)) | ||
| 2692 | |||
| 2492 | self.EventMenu(GetEventList(glb.db), reports_menu) | 2693 | self.EventMenu(GetEventList(glb.db), reports_menu) |
| 2493 | 2694 | ||
| 2494 | if IsSelectable(glb.db, "calls"): | 2695 | if IsSelectable(glb.db, "calls"): |
| @@ -2549,6 +2750,9 @@ class MainWindow(QMainWindow): | |||
| 2549 | def NewCallGraph(self): | 2750 | def NewCallGraph(self): |
| 2550 | CallGraphWindow(self.glb, self) | 2751 | CallGraphWindow(self.glb, self) |
| 2551 | 2752 | ||
| 2753 | def NewCallTree(self): | ||
| 2754 | CallTreeWindow(self.glb, self) | ||
| 2755 | |||
| 2552 | def NewTopCalls(self): | 2756 | def NewTopCalls(self): |
| 2553 | dialog = TopCallsDialog(self.glb, self) | 2757 | dialog = TopCallsDialog(self.glb, self) |
| 2554 | ret = dialog.exec_() | 2758 | ret = dialog.exec_() |
