1*031c2a00SAdrian Hunter#!/usr/bin/python2
2*031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0
3*031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database
4*031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation.
5*031c2a00SAdrian Hunter
6*031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the
7*031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script.  Refer to those
8*031c2a00SAdrian Hunter# scripts for details.
9*031c2a00SAdrian Hunter#
10*031c2a00SAdrian Hunter# Following on from the example in the export scripts, a
11*031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this:
12*031c2a00SAdrian Hunter#
13*031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py pt_example
14*031c2a00SAdrian Hunter#
15*031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases
16*031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g.
17*031c2a00SAdrian Hunter#
18*031c2a00SAdrian Hunter#	python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example"
19*031c2a00SAdrian Hunter#
20*031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive
21*031c2a00SAdrian Hunter# call-graph.  Expanding a couple of levels of the tree and adjusting column
22*031c2a00SAdrian Hunter# widths to suit will display something like:
23*031c2a00SAdrian Hunter#
24*031c2a00SAdrian Hunter#                                         Call Graph: pt_example
25*031c2a00SAdrian Hunter# Call Path                          Object      Count   Time(ns)  Time(%)  Branch Count   Branch Count(%)
26*031c2a00SAdrian Hunter# v- ls
27*031c2a00SAdrian Hunter#     v- 2638:2638
28*031c2a00SAdrian Hunter#         v- _start                  ld-2.19.so    1     10074071   100.0         211135            100.0
29*031c2a00SAdrian Hunter#           |- unknown               unknown       1        13198     0.1              1              0.0
30*031c2a00SAdrian Hunter#           >- _dl_start             ld-2.19.so    1      1400980    13.9          19637              9.3
31*031c2a00SAdrian Hunter#           >- _d_linit_internal     ld-2.19.so    1       448152     4.4          11094              5.3
32*031c2a00SAdrian Hunter#           v-__libc_start_main@plt  ls            1      8211741    81.5         180397             85.4
33*031c2a00SAdrian Hunter#              >- _dl_fixup          ld-2.19.so    1         7607     0.1            108              0.1
34*031c2a00SAdrian Hunter#              >- __cxa_atexit       libc-2.19.so  1        11737     0.1             10              0.0
35*031c2a00SAdrian Hunter#              >- __libc_csu_init    ls            1        10354     0.1             10              0.0
36*031c2a00SAdrian Hunter#              |- _setjmp            libc-2.19.so  1            0     0.0              4              0.0
37*031c2a00SAdrian Hunter#              v- main               ls            1      8182043    99.6         180254             99.9
38*031c2a00SAdrian Hunter#
39*031c2a00SAdrian Hunter# Points to note:
40*031c2a00SAdrian Hunter#	The top level is a command name (comm)
41*031c2a00SAdrian Hunter#	The next level is a thread (pid:tid)
42*031c2a00SAdrian Hunter#	Subsequent levels are functions
43*031c2a00SAdrian Hunter#	'Count' is the number of calls
44*031c2a00SAdrian Hunter#	'Time' is the elapsed time until the function returns
45*031c2a00SAdrian Hunter#	Percentages are relative to the level above
46*031c2a00SAdrian Hunter#	'Branch Count' is the total number of branches for that function and all
47*031c2a00SAdrian Hunter#       functions that it calls
48*031c2a00SAdrian Hunter
49*031c2a00SAdrian Hunterimport sys
50*031c2a00SAdrian Hunterfrom PySide.QtCore import *
51*031c2a00SAdrian Hunterfrom PySide.QtGui import *
52*031c2a00SAdrian Hunterfrom PySide.QtSql import *
53*031c2a00SAdrian Hunterfrom decimal import *
54*031c2a00SAdrian Hunter
55*031c2a00SAdrian Hunter# Data formatting helpers
56*031c2a00SAdrian Hunter
57*031c2a00SAdrian Hunterdef dsoname(name):
58*031c2a00SAdrian Hunter	if name == "[kernel.kallsyms]":
59*031c2a00SAdrian Hunter		return "[kernel]"
60*031c2a00SAdrian Hunter	return name
61*031c2a00SAdrian Hunter
62*031c2a00SAdrian Hunter# Percent to one decimal place
63*031c2a00SAdrian Hunter
64*031c2a00SAdrian Hunterdef PercentToOneDP(n, d):
65*031c2a00SAdrian Hunter	if not d:
66*031c2a00SAdrian Hunter		return "0.0"
67*031c2a00SAdrian Hunter	x = (n * Decimal(100)) / d
68*031c2a00SAdrian Hunter	return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP))
69*031c2a00SAdrian Hunter
70*031c2a00SAdrian Hunter# Helper for queries that must not fail
71*031c2a00SAdrian Hunter
72*031c2a00SAdrian Hunterdef QueryExec(query, stmt):
73*031c2a00SAdrian Hunter	ret = query.exec_(stmt)
74*031c2a00SAdrian Hunter	if not ret:
75*031c2a00SAdrian Hunter		raise Exception("Query failed: " + query.lastError().text())
76*031c2a00SAdrian Hunter
77*031c2a00SAdrian Hunter# Tree data model
78*031c2a00SAdrian Hunter
79*031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel):
80*031c2a00SAdrian Hunter
81*031c2a00SAdrian Hunter	def __init__(self, root, parent=None):
82*031c2a00SAdrian Hunter		super(TreeModel, self).__init__(parent)
83*031c2a00SAdrian Hunter		self.root = root
84*031c2a00SAdrian Hunter		self.last_row_read = 0
85*031c2a00SAdrian Hunter
86*031c2a00SAdrian Hunter	def Item(self, parent):
87*031c2a00SAdrian Hunter		if parent.isValid():
88*031c2a00SAdrian Hunter			return parent.internalPointer()
89*031c2a00SAdrian Hunter		else:
90*031c2a00SAdrian Hunter			return self.root
91*031c2a00SAdrian Hunter
92*031c2a00SAdrian Hunter	def rowCount(self, parent):
93*031c2a00SAdrian Hunter		result = self.Item(parent).childCount()
94*031c2a00SAdrian Hunter		if result < 0:
95*031c2a00SAdrian Hunter			result = 0
96*031c2a00SAdrian Hunter			self.dataChanged.emit(parent, parent)
97*031c2a00SAdrian Hunter		return result
98*031c2a00SAdrian Hunter
99*031c2a00SAdrian Hunter	def hasChildren(self, parent):
100*031c2a00SAdrian Hunter		return self.Item(parent).hasChildren()
101*031c2a00SAdrian Hunter
102*031c2a00SAdrian Hunter	def headerData(self, section, orientation, role):
103*031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
104*031c2a00SAdrian Hunter			return self.columnAlignment(section)
105*031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
106*031c2a00SAdrian Hunter			return None
107*031c2a00SAdrian Hunter		if orientation != Qt.Horizontal:
108*031c2a00SAdrian Hunter			return None
109*031c2a00SAdrian Hunter		return self.columnHeader(section)
110*031c2a00SAdrian Hunter
111*031c2a00SAdrian Hunter	def parent(self, child):
112*031c2a00SAdrian Hunter		child_item = child.internalPointer()
113*031c2a00SAdrian Hunter		if child_item is self.root:
114*031c2a00SAdrian Hunter			return QModelIndex()
115*031c2a00SAdrian Hunter		parent_item = child_item.getParentItem()
116*031c2a00SAdrian Hunter		return self.createIndex(parent_item.getRow(), 0, parent_item)
117*031c2a00SAdrian Hunter
118*031c2a00SAdrian Hunter	def index(self, row, column, parent):
119*031c2a00SAdrian Hunter		child_item = self.Item(parent).getChildItem(row)
120*031c2a00SAdrian Hunter		return self.createIndex(row, column, child_item)
121*031c2a00SAdrian Hunter
122*031c2a00SAdrian Hunter	def DisplayData(self, item, index):
123*031c2a00SAdrian Hunter		return item.getData(index.column())
124*031c2a00SAdrian Hunter
125*031c2a00SAdrian Hunter	def columnAlignment(self, column):
126*031c2a00SAdrian Hunter		return Qt.AlignLeft
127*031c2a00SAdrian Hunter
128*031c2a00SAdrian Hunter	def columnFont(self, column):
129*031c2a00SAdrian Hunter		return None
130*031c2a00SAdrian Hunter
131*031c2a00SAdrian Hunter	def data(self, index, role):
132*031c2a00SAdrian Hunter		if role == Qt.TextAlignmentRole:
133*031c2a00SAdrian Hunter			return self.columnAlignment(index.column())
134*031c2a00SAdrian Hunter		if role == Qt.FontRole:
135*031c2a00SAdrian Hunter			return self.columnFont(index.column())
136*031c2a00SAdrian Hunter		if role != Qt.DisplayRole:
137*031c2a00SAdrian Hunter			return None
138*031c2a00SAdrian Hunter		item = index.internalPointer()
139*031c2a00SAdrian Hunter		return self.DisplayData(item, index)
140*031c2a00SAdrian Hunter
141*031c2a00SAdrian Hunter# Context-sensitive call graph data model item base
142*031c2a00SAdrian Hunter
143*031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object):
144*031c2a00SAdrian Hunter
145*031c2a00SAdrian Hunter	def __init__(self, glb, row, parent_item):
146*031c2a00SAdrian Hunter		self.glb = glb
147*031c2a00SAdrian Hunter		self.row = row
148*031c2a00SAdrian Hunter		self.parent_item = parent_item
149*031c2a00SAdrian Hunter		self.query_done = False;
150*031c2a00SAdrian Hunter		self.child_count = 0
151*031c2a00SAdrian Hunter		self.child_items = []
152*031c2a00SAdrian Hunter
153*031c2a00SAdrian Hunter	def getChildItem(self, row):
154*031c2a00SAdrian Hunter		return self.child_items[row]
155*031c2a00SAdrian Hunter
156*031c2a00SAdrian Hunter	def getParentItem(self):
157*031c2a00SAdrian Hunter		return self.parent_item
158*031c2a00SAdrian Hunter
159*031c2a00SAdrian Hunter	def getRow(self):
160*031c2a00SAdrian Hunter		return self.row
161*031c2a00SAdrian Hunter
162*031c2a00SAdrian Hunter	def childCount(self):
163*031c2a00SAdrian Hunter		if not self.query_done:
164*031c2a00SAdrian Hunter			self.Select()
165*031c2a00SAdrian Hunter			if not self.child_count:
166*031c2a00SAdrian Hunter				return -1
167*031c2a00SAdrian Hunter		return self.child_count
168*031c2a00SAdrian Hunter
169*031c2a00SAdrian Hunter	def hasChildren(self):
170*031c2a00SAdrian Hunter		if not self.query_done:
171*031c2a00SAdrian Hunter			return True
172*031c2a00SAdrian Hunter		return self.child_count > 0
173*031c2a00SAdrian Hunter
174*031c2a00SAdrian Hunter	def getData(self, column):
175*031c2a00SAdrian Hunter		return self.data[column]
176*031c2a00SAdrian Hunter
177*031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base
178*031c2a00SAdrian Hunter
179*031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
180*031c2a00SAdrian Hunter
181*031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
182*031c2a00SAdrian Hunter		super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
183*031c2a00SAdrian Hunter		self.comm_id = comm_id
184*031c2a00SAdrian Hunter		self.thread_id = thread_id
185*031c2a00SAdrian Hunter		self.call_path_id = call_path_id
186*031c2a00SAdrian Hunter		self.branch_count = branch_count
187*031c2a00SAdrian Hunter		self.time = time
188*031c2a00SAdrian Hunter
189*031c2a00SAdrian Hunter	def Select(self):
190*031c2a00SAdrian Hunter		self.query_done = True;
191*031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
192*031c2a00SAdrian Hunter		QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
193*031c2a00SAdrian Hunter					" FROM calls"
194*031c2a00SAdrian Hunter					" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
195*031c2a00SAdrian Hunter					" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
196*031c2a00SAdrian Hunter					" INNER JOIN dsos ON symbols.dso_id = dsos.id"
197*031c2a00SAdrian Hunter					" WHERE parent_call_path_id = " + str(self.call_path_id) +
198*031c2a00SAdrian Hunter					" AND comm_id = " + str(self.comm_id) +
199*031c2a00SAdrian Hunter					" AND thread_id = " + str(self.thread_id) +
200*031c2a00SAdrian Hunter					" GROUP BY call_path_id, name, short_name"
201*031c2a00SAdrian Hunter					" ORDER BY call_path_id")
202*031c2a00SAdrian Hunter		while query.next():
203*031c2a00SAdrian Hunter			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)
204*031c2a00SAdrian Hunter			self.child_items.append(child_item)
205*031c2a00SAdrian Hunter			self.child_count += 1
206*031c2a00SAdrian Hunter
207*031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item
208*031c2a00SAdrian Hunter
209*031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
210*031c2a00SAdrian Hunter
211*031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
212*031c2a00SAdrian Hunter		super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
213*031c2a00SAdrian Hunter		dso = dsoname(dso)
214*031c2a00SAdrian Hunter		self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
215*031c2a00SAdrian Hunter		self.dbid = call_path_id
216*031c2a00SAdrian Hunter
217*031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item
218*031c2a00SAdrian Hunter
219*031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
220*031c2a00SAdrian Hunter
221*031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
222*031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
223*031c2a00SAdrian Hunter		self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
224*031c2a00SAdrian Hunter		self.dbid = thread_id
225*031c2a00SAdrian Hunter
226*031c2a00SAdrian Hunter	def Select(self):
227*031c2a00SAdrian Hunter		super(CallGraphLevelTwoItem, self).Select()
228*031c2a00SAdrian Hunter		for child_item in self.child_items:
229*031c2a00SAdrian Hunter			self.time += child_item.time
230*031c2a00SAdrian Hunter			self.branch_count += child_item.branch_count
231*031c2a00SAdrian Hunter		for child_item in self.child_items:
232*031c2a00SAdrian Hunter			child_item.data[4] = PercentToOneDP(child_item.time, self.time)
233*031c2a00SAdrian Hunter			child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
234*031c2a00SAdrian Hunter
235*031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item
236*031c2a00SAdrian Hunter
237*031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase):
238*031c2a00SAdrian Hunter
239*031c2a00SAdrian Hunter	def __init__(self, glb, row, comm_id, comm, parent_item):
240*031c2a00SAdrian Hunter		super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
241*031c2a00SAdrian Hunter		self.data = [comm, "", "", "", "", "", ""]
242*031c2a00SAdrian Hunter		self.dbid = comm_id
243*031c2a00SAdrian Hunter
244*031c2a00SAdrian Hunter	def Select(self):
245*031c2a00SAdrian Hunter		self.query_done = True;
246*031c2a00SAdrian Hunter		query = QSqlQuery(self.glb.db)
247*031c2a00SAdrian Hunter		QueryExec(query, "SELECT thread_id, pid, tid"
248*031c2a00SAdrian Hunter					" FROM comm_threads"
249*031c2a00SAdrian Hunter					" INNER JOIN threads ON thread_id = threads.id"
250*031c2a00SAdrian Hunter					" WHERE comm_id = " + str(self.dbid))
251*031c2a00SAdrian Hunter		while query.next():
252*031c2a00SAdrian Hunter			child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
253*031c2a00SAdrian Hunter			self.child_items.append(child_item)
254*031c2a00SAdrian Hunter			self.child_count += 1
255*031c2a00SAdrian Hunter
256*031c2a00SAdrian Hunter# Context-sensitive call graph data model root item
257*031c2a00SAdrian Hunter
258*031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase):
259*031c2a00SAdrian Hunter
260*031c2a00SAdrian Hunter	def __init__(self, glb):
261*031c2a00SAdrian Hunter		super(CallGraphRootItem, self).__init__(glb, 0, None)
262*031c2a00SAdrian Hunter		self.dbid = 0
263*031c2a00SAdrian Hunter		self.query_done = True;
264*031c2a00SAdrian Hunter		query = QSqlQuery(glb.db)
265*031c2a00SAdrian Hunter		QueryExec(query, "SELECT id, comm FROM comms")
266*031c2a00SAdrian Hunter		while query.next():
267*031c2a00SAdrian Hunter			if not query.value(0):
268*031c2a00SAdrian Hunter				continue
269*031c2a00SAdrian Hunter			child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
270*031c2a00SAdrian Hunter			self.child_items.append(child_item)
271*031c2a00SAdrian Hunter			self.child_count += 1
272*031c2a00SAdrian Hunter
273*031c2a00SAdrian Hunter# Context-sensitive call graph data model
274*031c2a00SAdrian Hunter
275*031c2a00SAdrian Hunterclass CallGraphModel(TreeModel):
276*031c2a00SAdrian Hunter
277*031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
278*031c2a00SAdrian Hunter		super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent)
279*031c2a00SAdrian Hunter		self.glb = glb
280*031c2a00SAdrian Hunter
281*031c2a00SAdrian Hunter	def columnCount(self, parent=None):
282*031c2a00SAdrian Hunter		return 7
283*031c2a00SAdrian Hunter
284*031c2a00SAdrian Hunter	def columnHeader(self, column):
285*031c2a00SAdrian Hunter		headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
286*031c2a00SAdrian Hunter		return headers[column]
287*031c2a00SAdrian Hunter
288*031c2a00SAdrian Hunter	def columnAlignment(self, column):
289*031c2a00SAdrian Hunter		alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
290*031c2a00SAdrian Hunter		return alignment[column]
291*031c2a00SAdrian Hunter
292*031c2a00SAdrian Hunter# Main window
293*031c2a00SAdrian Hunter
294*031c2a00SAdrian Hunterclass MainWindow(QMainWindow):
295*031c2a00SAdrian Hunter
296*031c2a00SAdrian Hunter	def __init__(self, glb, parent=None):
297*031c2a00SAdrian Hunter		super(MainWindow, self).__init__(parent)
298*031c2a00SAdrian Hunter
299*031c2a00SAdrian Hunter		self.glb = glb
300*031c2a00SAdrian Hunter
301*031c2a00SAdrian Hunter		self.setWindowTitle("Call Graph: " + glb.dbname)
302*031c2a00SAdrian Hunter		self.move(100, 100)
303*031c2a00SAdrian Hunter		self.resize(800, 600)
304*031c2a00SAdrian Hunter		self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
305*031c2a00SAdrian Hunter		self.setMinimumSize(200, 100)
306*031c2a00SAdrian Hunter
307*031c2a00SAdrian Hunter		self.model = CallGraphModel(glb)
308*031c2a00SAdrian Hunter
309*031c2a00SAdrian Hunter		self.view = QTreeView()
310*031c2a00SAdrian Hunter		self.view.setModel(self.model)
311*031c2a00SAdrian Hunter
312*031c2a00SAdrian Hunter		for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
313*031c2a00SAdrian Hunter			self.view.setColumnWidth(c, w)
314*031c2a00SAdrian Hunter
315*031c2a00SAdrian Hunter		self.setCentralWidget(self.view)
316*031c2a00SAdrian Hunter
317*031c2a00SAdrian Hunter# Global data
318*031c2a00SAdrian Hunter
319*031c2a00SAdrian Hunterclass Glb():
320*031c2a00SAdrian Hunter
321*031c2a00SAdrian Hunter	def __init__(self, dbref, db, dbname):
322*031c2a00SAdrian Hunter		self.dbref = dbref
323*031c2a00SAdrian Hunter		self.db = db
324*031c2a00SAdrian Hunter		self.dbname = dbname
325*031c2a00SAdrian Hunter		self.app = None
326*031c2a00SAdrian Hunter		self.mainwindow = None
327*031c2a00SAdrian Hunter
328*031c2a00SAdrian Hunter# Database reference
329*031c2a00SAdrian Hunter
330*031c2a00SAdrian Hunterclass DBRef():
331*031c2a00SAdrian Hunter
332*031c2a00SAdrian Hunter	def __init__(self, is_sqlite3, dbname):
333*031c2a00SAdrian Hunter		self.is_sqlite3 = is_sqlite3
334*031c2a00SAdrian Hunter		self.dbname = dbname
335*031c2a00SAdrian Hunter
336*031c2a00SAdrian Hunter	def Open(self, connection_name):
337*031c2a00SAdrian Hunter		dbname = self.dbname
338*031c2a00SAdrian Hunter		if self.is_sqlite3:
339*031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QSQLITE", connection_name)
340*031c2a00SAdrian Hunter		else:
341*031c2a00SAdrian Hunter			db = QSqlDatabase.addDatabase("QPSQL", connection_name)
342*031c2a00SAdrian Hunter			opts = dbname.split()
343*031c2a00SAdrian Hunter			for opt in opts:
344*031c2a00SAdrian Hunter				if "=" in opt:
345*031c2a00SAdrian Hunter					opt = opt.split("=")
346*031c2a00SAdrian Hunter					if opt[0] == "hostname":
347*031c2a00SAdrian Hunter						db.setHostName(opt[1])
348*031c2a00SAdrian Hunter					elif opt[0] == "port":
349*031c2a00SAdrian Hunter						db.setPort(int(opt[1]))
350*031c2a00SAdrian Hunter					elif opt[0] == "username":
351*031c2a00SAdrian Hunter						db.setUserName(opt[1])
352*031c2a00SAdrian Hunter					elif opt[0] == "password":
353*031c2a00SAdrian Hunter						db.setPassword(opt[1])
354*031c2a00SAdrian Hunter					elif opt[0] == "dbname":
355*031c2a00SAdrian Hunter						dbname = opt[1]
356*031c2a00SAdrian Hunter				else:
357*031c2a00SAdrian Hunter					dbname = opt
358*031c2a00SAdrian Hunter
359*031c2a00SAdrian Hunter		db.setDatabaseName(dbname)
360*031c2a00SAdrian Hunter		if not db.open():
361*031c2a00SAdrian Hunter			raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text())
362*031c2a00SAdrian Hunter		return db, dbname
363*031c2a00SAdrian Hunter
364*031c2a00SAdrian Hunter# Main
365*031c2a00SAdrian Hunter
366*031c2a00SAdrian Hunterdef Main():
367*031c2a00SAdrian Hunter	if (len(sys.argv) < 2):
368*031c2a00SAdrian Hunter		print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>"
369*031c2a00SAdrian Hunter		raise Exception("Too few arguments")
370*031c2a00SAdrian Hunter
371*031c2a00SAdrian Hunter	dbname = sys.argv[1]
372*031c2a00SAdrian Hunter
373*031c2a00SAdrian Hunter	is_sqlite3 = False
374*031c2a00SAdrian Hunter	try:
375*031c2a00SAdrian Hunter		f = open(dbname)
376*031c2a00SAdrian Hunter		if f.read(15) == "SQLite format 3":
377*031c2a00SAdrian Hunter			is_sqlite3 = True
378*031c2a00SAdrian Hunter		f.close()
379*031c2a00SAdrian Hunter	except:
380*031c2a00SAdrian Hunter		pass
381*031c2a00SAdrian Hunter
382*031c2a00SAdrian Hunter	dbref = DBRef(is_sqlite3, dbname)
383*031c2a00SAdrian Hunter	db, dbname = dbref.Open("main")
384*031c2a00SAdrian Hunter	glb = Glb(dbref, db, dbname)
385*031c2a00SAdrian Hunter	app = QApplication(sys.argv)
386*031c2a00SAdrian Hunter	glb.app = app
387*031c2a00SAdrian Hunter	mainwindow = MainWindow(glb)
388*031c2a00SAdrian Hunter	glb.mainwindow = mainwindow
389*031c2a00SAdrian Hunter	mainwindow.show()
390*031c2a00SAdrian Hunter	err = app.exec_()
391*031c2a00SAdrian Hunter	db.close()
392*031c2a00SAdrian Hunter	sys.exit(err)
393*031c2a00SAdrian Hunter
394*031c2a00SAdrian Hunterif __name__ == "__main__":
395*031c2a00SAdrian Hunter	Main()
396