]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - tools/perf/scripts/python/exported-sql-viewer.py
perf scripts python: export-to-sqlite.py: Export Intel PT power and ptwrite events
[linux.git] / tools / perf / scripts / python / exported-sql-viewer.py
index affed7d149be116d032275628fc8bbe834730f97..6e7934f2ac9a31549cddfd1a669e6cc7f61b613e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python
 # SPDX-License-Identifier: GPL-2.0
 # exported-sql-viewer.py: view data from sql database
 # Copyright (c) 2014-2018, Intel Corporation.
@@ -91,6 +91,7 @@
 from __future__ import print_function
 
 import sys
+import argparse
 import weakref
 import threading
 import string
@@ -104,10 +105,23 @@ except ImportError:
        glb_nsz = 16
 import re
 import os
-from PySide.QtCore import *
-from PySide.QtGui import *
-from PySide.QtSql import *
+
 pyside_version_1 = True
+if not "--pyside-version-1" in sys.argv:
+       try:
+               from PySide2.QtCore import *
+               from PySide2.QtGui import *
+               from PySide2.QtSql import *
+               from PySide2.QtWidgets import *
+               pyside_version_1 = False
+       except:
+               pass
+
+if pyside_version_1:
+       from PySide.QtCore import *
+       from PySide.QtGui import *
+       from PySide.QtSql import *
+
 from decimal import *
 from ctypes import *
 from multiprocessing import Process, Array, Value, Event
@@ -186,9 +200,10 @@ class Thread(QThread):
 
 class TreeModel(QAbstractItemModel):
 
-       def __init__(self, glb, parent=None):
+       def __init__(self, glb, params, parent=None):
                super(TreeModel, self).__init__(parent)
                self.glb = glb
+               self.params = params
                self.root = self.GetRoot()
                self.last_row_read = 0
 
@@ -385,6 +400,7 @@ class FindBar():
 
        def Activate(self):
                self.bar.show()
+               self.textbox.lineEdit().selectAll()
                self.textbox.setFocus()
 
        def Deactivate(self):
@@ -449,8 +465,9 @@ class FindBar():
 
 class CallGraphLevelItemBase(object):
 
-       def __init__(self, glb, row, parent_item):
+       def __init__(self, glb, params, row, parent_item):
                self.glb = glb
+               self.params = params
                self.row = row
                self.parent_item = parent_item
                self.query_done = False;
@@ -489,18 +506,24 @@ class CallGraphLevelItemBase(object):
 
 class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
-               super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
+       def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+               super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
                self.comm_id = comm_id
                self.thread_id = thread_id
                self.call_path_id = call_path_id
+               self.insn_cnt = insn_cnt
+               self.cyc_cnt = cyc_cnt
                self.branch_count = branch_count
                self.time = time
 
        def Select(self):
                self.query_done = True;
                query = QSqlQuery(self.glb.db)
-               QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
+               if self.params.have_ipc:
+                       ipc_str = ", SUM(insn_count), SUM(cyc_count)"
+               else:
+                       ipc_str = ""
+               QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
                                        " FROM calls"
                                        " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
                                        " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
@@ -511,7 +534,15 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
                                        " GROUP BY call_path_id, name, short_name"
                                        " ORDER BY call_path_id")
                while query.next():
-                       child_item = CallGraphLevelThreeItem(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)
+                       if self.params.have_ipc:
+                               insn_cnt = int(query.value(5))
+                               cyc_cnt = int(query.value(6))
+                               branch_count = int(query.value(7))
+                       else:
+                               insn_cnt = 0
+                               cyc_cnt = 0
+                               branch_count = int(query.value(5))
+                       child_item = CallGraphLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
@@ -519,37 +550,57 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
 
 class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
-               super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
+       def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+               super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
                dso = dsoname(dso)
-               self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
+               if self.params.have_ipc:
+                       insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
+                       cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
+                       br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
+                       ipc = CalcIPC(cyc_cnt, insn_cnt)
+                       self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
+               else:
+                       self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
                self.dbid = call_path_id
 
 # Context-sensitive call graph data model level two item
 
 class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
-               super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
-               self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
+       def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
+               super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
+               if self.params.have_ipc:
+                       self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
+               else:
+                       self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
                self.dbid = thread_id
 
        def Select(self):
                super(CallGraphLevelTwoItem, self).Select()
                for child_item in self.child_items:
                        self.time += child_item.time
+                       self.insn_cnt += child_item.insn_cnt
+                       self.cyc_cnt += child_item.cyc_cnt
                        self.branch_count += child_item.branch_count
                for child_item in self.child_items:
                        child_item.data[4] = PercentToOneDP(child_item.time, self.time)
-                       child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
+                       if self.params.have_ipc:
+                               child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
+                               child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
+                               child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
+                       else:
+                               child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
 
 # Context-sensitive call graph data model level one item
 
 class CallGraphLevelOneItem(CallGraphLevelItemBase):
 
-       def __init__(self, glb, row, comm_id, comm, parent_item):
-               super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
-               self.data = [comm, "", "", "", "", "", ""]
+       def __init__(self, glb, params, row, comm_id, comm, parent_item):
+               super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
+               if self.params.have_ipc:
+                       self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
+               else:
+                       self.data = [comm, "", "", "", "", "", ""]
                self.dbid = comm_id
 
        def Select(self):
@@ -560,7 +611,7 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):
                                        " INNER JOIN threads ON thread_id = threads.id"
                                        " WHERE comm_id = " + str(self.dbid))
                while query.next():
-                       child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
+                       child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
@@ -568,8 +619,8 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):
 
 class CallGraphRootItem(CallGraphLevelItemBase):
 
-       def __init__(self, glb):
-               super(CallGraphRootItem, self).__init__(glb, 0, None)
+       def __init__(self, glb, params):
+               super(CallGraphRootItem, self).__init__(glb, params, 0, None)
                self.dbid = 0
                self.query_done = True;
                query = QSqlQuery(glb.db)
@@ -577,16 +628,23 @@ class CallGraphRootItem(CallGraphLevelItemBase):
                while query.next():
                        if not query.value(0):
                                continue
-                       child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
+                       child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
+# Call graph model parameters
+
+class CallGraphModelParams():
+
+       def __init__(self, glb, parent=None):
+               self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
+
 # Context-sensitive call graph data model base
 
 class CallGraphModelBase(TreeModel):
 
        def __init__(self, glb, parent=None):
-               super(CallGraphModelBase, self).__init__(glb, parent)
+               super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
 
        def FindSelect(self, value, pattern, query):
                if pattern:
@@ -668,17 +726,26 @@ class CallGraphModel(CallGraphModelBase):
                super(CallGraphModel, self).__init__(glb, parent)
 
        def GetRoot(self):
-               return CallGraphRootItem(self.glb)
+               return CallGraphRootItem(self.glb, self.params)
 
        def columnCount(self, parent=None):
-               return 7
+               if self.params.have_ipc:
+                       return 12
+               else:
+                       return 7
 
        def columnHeader(self, column):
-               headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
+               if self.params.have_ipc:
+                       headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
+               else:
+                       headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
                return headers[column]
 
        def columnAlignment(self, column):
-               alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+               if self.params.have_ipc:
+                       alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+               else:
+                       alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
                return alignment[column]
 
        def DoFindSelect(self, query, match):
@@ -715,11 +782,13 @@ class CallGraphModel(CallGraphModelBase):
 
 class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item):
-               super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
+       def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+               super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
                self.comm_id = comm_id
                self.thread_id = thread_id
                self.calls_id = calls_id
+               self.insn_cnt = insn_cnt
+               self.cyc_cnt = cyc_cnt
                self.branch_count = branch_count
                self.time = time
 
@@ -729,8 +798,12 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
                        comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
                else:
                        comm_thread = ""
+               if self.params.have_ipc:
+                       ipc_str = ", insn_count, cyc_count"
+               else:
+                       ipc_str = ""
                query = QSqlQuery(self.glb.db)
-               QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count"
+               QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
                                        " FROM calls"
                                        " INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
                                        " INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
@@ -738,7 +811,15 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
                                        " WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
                                        " ORDER BY call_time, calls.id")
                while query.next():
-                       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)
+                       if self.params.have_ipc:
+                               insn_cnt = int(query.value(5))
+                               cyc_cnt = int(query.value(6))
+                               branch_count = int(query.value(7))
+                       else:
+                               insn_cnt = 0
+                               cyc_cnt = 0
+                               branch_count = int(query.value(5))
+                       child_item = CallTreeLevelThreeItem(self.glb, self.params, 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)), insn_cnt, cyc_cnt, branch_count, self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
@@ -746,37 +827,57 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
 
 class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item):
-               super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item)
+       def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+               super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
                dso = dsoname(dso)
-               self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
+               if self.params.have_ipc:
+                       insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
+                       cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
+                       br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
+                       ipc = CalcIPC(cyc_cnt, insn_cnt)
+                       self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
+               else:
+                       self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
                self.dbid = calls_id
 
 # Call tree data model level two item
 
 class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
 
-       def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
-               super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item)
-               self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
+       def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
+               super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item)
+               if self.params.have_ipc:
+                       self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
+               else:
+                       self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
                self.dbid = thread_id
 
        def Select(self):
                super(CallTreeLevelTwoItem, self).Select()
                for child_item in self.child_items:
                        self.time += child_item.time
+                       self.insn_cnt += child_item.insn_cnt
+                       self.cyc_cnt += child_item.cyc_cnt
                        self.branch_count += child_item.branch_count
                for child_item in self.child_items:
                        child_item.data[4] = PercentToOneDP(child_item.time, self.time)
-                       child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
+                       if self.params.have_ipc:
+                               child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
+                               child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
+                               child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
+                       else:
+                               child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
 
 # Call tree data model level one item
 
 class CallTreeLevelOneItem(CallGraphLevelItemBase):
 
-       def __init__(self, glb, row, comm_id, comm, parent_item):
-               super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item)
-               self.data = [comm, "", "", "", "", "", ""]
+       def __init__(self, glb, params, row, comm_id, comm, parent_item):
+               super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
+               if self.params.have_ipc:
+                       self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
+               else:
+                       self.data = [comm, "", "", "", "", "", ""]
                self.dbid = comm_id
 
        def Select(self):
@@ -787,7 +888,7 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):
                                        " INNER JOIN threads ON thread_id = threads.id"
                                        " WHERE comm_id = " + str(self.dbid))
                while query.next():
-                       child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
+                       child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
@@ -795,8 +896,8 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):
 
 class CallTreeRootItem(CallGraphLevelItemBase):
 
-       def __init__(self, glb):
-               super(CallTreeRootItem, self).__init__(glb, 0, None)
+       def __init__(self, glb, params):
+               super(CallTreeRootItem, self).__init__(glb, params, 0, None)
                self.dbid = 0
                self.query_done = True;
                query = QSqlQuery(glb.db)
@@ -804,7 +905,7 @@ class CallTreeRootItem(CallGraphLevelItemBase):
                while query.next():
                        if not query.value(0):
                                continue
-                       child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
+                       child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
                        self.child_items.append(child_item)
                        self.child_count += 1
 
@@ -816,17 +917,26 @@ class CallTreeModel(CallGraphModelBase):
                super(CallTreeModel, self).__init__(glb, parent)
 
        def GetRoot(self):
-               return CallTreeRootItem(self.glb)
+               return CallTreeRootItem(self.glb, self.params)
 
        def columnCount(self, parent=None):
-               return 7
+               if self.params.have_ipc:
+                       return 12
+               else:
+                       return 7
 
        def columnHeader(self, column):
-               headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
+               if self.params.have_ipc:
+                       headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
+               else:
+                       headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
                return headers[column]
 
        def columnAlignment(self, column):
-               alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+               if self.params.have_ipc:
+                       alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+               else:
+                       alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
                return alignment[column]
 
        def DoFindSelect(self, query, match):
@@ -1355,11 +1465,11 @@ class FetchMoreRecordsBar():
 
 class BranchLevelTwoItem():
 
-       def __init__(self, row, text, parent_item):
+       def __init__(self, row, col, text, parent_item):
                self.row = row
                self.parent_item = parent_item
-               self.data = [""] * 8
-               self.data[7] = text
+               self.data = [""] * (col + 1)
+               self.data[col] = text
                self.level = 2
 
        def getParentItem(self):
@@ -1391,6 +1501,7 @@ class BranchLevelOneItem():
                self.dbid = data[0]
                self.level = 1
                self.query_done = False
+               self.br_col = len(self.data) - 1
 
        def getChildItem(self, row):
                return self.child_items[row]
@@ -1471,7 +1582,7 @@ class BranchLevelOneItem():
                                while k < 15:
                                        byte_str += "   "
                                        k += 1
-                               self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
+                               self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
                                self.child_count += 1
                        else:
                                return
@@ -1522,16 +1633,37 @@ class BranchRootItem():
        def getData(self, column):
                return ""
 
+# Calculate instructions per cycle
+
+def CalcIPC(cyc_cnt, insn_cnt):
+       if cyc_cnt and insn_cnt:
+               ipc = Decimal(float(insn_cnt) / cyc_cnt)
+               ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
+       else:
+               ipc = "0"
+       return ipc
+
 # Branch data preparation
 
-def BranchDataPrep(query):
-       data = []
-       for i in xrange(0, 8):
-               data.append(query.value(i))
+def BranchDataPrepBr(query, data):
        data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
                        " (" + dsoname(query.value(11)) + ")" + " -> " +
                        tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
                        " (" + dsoname(query.value(15)) + ")")
+
+def BranchDataPrepIPC(query, data):
+       insn_cnt = query.value(16)
+       cyc_cnt = query.value(17)
+       ipc = CalcIPC(cyc_cnt, insn_cnt)
+       data.append(insn_cnt)
+       data.append(cyc_cnt)
+       data.append(ipc)
+
+def BranchDataPrep(query):
+       data = []
+       for i in xrange(0, 8):
+               data.append(query.value(i))
+       BranchDataPrepBr(query, data)
        return data
 
 def BranchDataPrepWA(query):
@@ -1541,10 +1673,26 @@ def BranchDataPrepWA(query):
        data.append("{:>19}".format(query.value(1)))
        for i in xrange(2, 8):
                data.append(query.value(i))
-       data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
-                       " (" + dsoname(query.value(11)) + ")" + " -> " +
-                       tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
-                       " (" + dsoname(query.value(15)) + ")")
+       BranchDataPrepBr(query, data)
+       return data
+
+def BranchDataWithIPCPrep(query):
+       data = []
+       for i in xrange(0, 8):
+               data.append(query.value(i))
+       BranchDataPrepIPC(query, data)
+       BranchDataPrepBr(query, data)
+       return data
+
+def BranchDataWithIPCPrepWA(query):
+       data = []
+       data.append(query.value(0))
+       # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
+       data.append("{:>19}".format(query.value(1)))
+       for i in xrange(2, 8):
+               data.append(query.value(i))
+       BranchDataPrepIPC(query, data)
+       BranchDataPrepBr(query, data)
        return data
 
 # Branch data model
@@ -1554,14 +1702,24 @@ class BranchModel(TreeModel):
        progress = Signal(object)
 
        def __init__(self, glb, event_id, where_clause, parent=None):
-               super(BranchModel, self).__init__(glb, parent)
+               super(BranchModel, self).__init__(glb, None, parent)
                self.event_id = event_id
                self.more = True
                self.populated = 0
+               self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
+               if self.have_ipc:
+                       select_ipc = ", insn_count, cyc_count"
+                       prep_fn = BranchDataWithIPCPrep
+                       prep_wa_fn = BranchDataWithIPCPrepWA
+               else:
+                       select_ipc = ""
+                       prep_fn = BranchDataPrep
+                       prep_wa_fn = BranchDataPrepWA
                sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
                        " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
                        " ip, symbols.name, sym_offset, dsos.short_name,"
                        " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
+                       + select_ipc +
                        " FROM samples"
                        " INNER JOIN comms ON comm_id = comms.id"
                        " INNER JOIN threads ON thread_id = threads.id"
@@ -1575,9 +1733,9 @@ class BranchModel(TreeModel):
                        " ORDER BY samples.id"
                        " LIMIT " + str(glb_chunk_sz))
                if pyside_version_1 and sys.version_info[0] == 3:
-                       prep = BranchDataPrepWA
+                       prep = prep_fn
                else:
-                       prep = BranchDataPrep
+                       prep = prep_wa_fn
                self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
                self.fetcher.done.connect(self.Update)
                self.fetcher.Fetch(glb_chunk_sz)
@@ -1586,13 +1744,23 @@ class BranchModel(TreeModel):
                return BranchRootItem()
 
        def columnCount(self, parent=None):
-               return 8
+               if self.have_ipc:
+                       return 11
+               else:
+                       return 8
 
        def columnHeader(self, column):
-               return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
+               if self.have_ipc:
+                       return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
+               else:
+                       return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
 
        def columnFont(self, column):
-               if column != 7:
+               if self.have_ipc:
+                       br_col = 10
+               else:
+                       br_col = 7
+               if column != br_col:
                        return None
                return QFont("Monospace")
 
@@ -2100,10 +2268,10 @@ def GetEventList(db):
 
 # Is a table selectable
 
-def IsSelectable(db, table, sql = ""):
+def IsSelectable(db, table, sql = "", columns = "*"):
        query = QSqlQuery(db)
        try:
-               QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1")
+               QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
        except:
                return False
        return True
@@ -2754,7 +2922,7 @@ class WindowMenu():
                        action = self.window_menu.addAction(label)
                        action.setCheckable(True)
                        action.setChecked(sub_window == self.mdi_area.activeSubWindow())
-                       action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
+                       action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
                        self.window_menu.addAction(action)
                        nr += 1
 
@@ -2840,6 +3008,12 @@ cd xed
 sudo ./mfile.py --prefix=/usr/local install
 sudo ldconfig
 </pre>
+<h3>Instructions per Cycle (IPC)</h3>
+If available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
+<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
+Due to the granularity of timing information, the number of cycles for some code blocks will not be known.
+In that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
+since the previous displayed 'IPC'.
 <h3>Find</h3>
 Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
 Refer to Python documentation for the regular expression syntax.
@@ -3114,14 +3288,14 @@ class MainWindow(QMainWindow):
                        event = event.split(":")[0]
                        if event == "branches":
                                label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
-                               reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
+                               reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
                                label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
-                               reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
+                               reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
 
        def TableMenu(self, tables, menu):
                table_menu = menu.addMenu("&Tables")
                for table in tables:
-                       table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
+                       table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
 
        def NewCallGraph(self):
                CallGraphWindow(self.glb, self)
@@ -3361,18 +3535,27 @@ class DBRef():
 # Main
 
 def Main():
-       if (len(sys.argv) < 2):
-               printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}");
-               raise Exception("Too few arguments")
-
-       dbname = sys.argv[1]
-       if dbname == "--help-only":
+       usage_str =     "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
+                       "   or: exported-sql-viewer.py --help-only"
+       ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
+       ap.add_argument("--pyside-version-1", action='store_true')
+       ap.add_argument("dbname", nargs="?")
+       ap.add_argument("--help-only", action='store_true')
+       args = ap.parse_args()
+
+       if args.help_only:
                app = QApplication(sys.argv)
                mainwindow = HelpOnlyWindow()
                mainwindow.show()
                err = app.exec_()
                sys.exit(err)
 
+       dbname = args.dbname
+       if dbname is None:
+               ap.print_usage()
+               print("Too few arguments")
+               sys.exit(1)
+
        is_sqlite3 = False
        try:
                f = open(dbname, "rb")