aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/exported-sql-viewer.py
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2018-10-01 02:28:47 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2018-10-23 13:27:30 -0400
commit1beb5c7b07040b70975a2ae0e90b87d412fabf06 (patch)
tree325ff512632d483afdc0585cee775e11a37839f5 /tools/perf/scripts/python/exported-sql-viewer.py
parent031c2a004ba75a4f8f2a6d0a7ca6f2fe5912de22 (diff)
perf scripts python: exported-sql-viewer.py: Add support for multiple sub-windows
Use Qt MDI (multiple document interface) to support multiple sub-windows. Put the data model in a cache so that each sub-window can share the same data. This allows mutiple views of the call-graph at the same time and paves the way to add more reports. Committer testing: Starts with a "File Reports Windows" main menu, from the "Reports" I can get what was available up to now, the "Context-Sensitivi Call Graph" option. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/20181001062853.28285-14-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py182
1 files changed, 173 insertions, 9 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index 03e7a1de7f31..c2f44351821e 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -47,6 +47,8 @@
47# functions that it calls 47# functions that it calls
48 48
49import sys 49import sys
50import weakref
51import threading
50from PySide.QtCore import * 52from PySide.QtCore import *
51from PySide.QtGui import * 53from PySide.QtGui import *
52from PySide.QtSql import * 54from PySide.QtSql import *
@@ -138,6 +140,23 @@ class TreeModel(QAbstractItemModel):
138 item = index.internalPointer() 140 item = index.internalPointer()
139 return self.DisplayData(item, index) 141 return self.DisplayData(item, index)
140 142
143# Model cache
144
145model_cache = weakref.WeakValueDictionary()
146model_cache_lock = threading.Lock()
147
148def LookupCreateModel(model_name, create_fn):
149 model_cache_lock.acquire()
150 try:
151 model = model_cache[model_name]
152 except:
153 model = None
154 if model is None:
155 model = create_fn()
156 model_cache[model_name] = model
157 model_cache_lock.release()
158 return model
159
141# Context-sensitive call graph data model item base 160# Context-sensitive call graph data model item base
142 161
143class CallGraphLevelItemBase(object): 162class CallGraphLevelItemBase(object):
@@ -289,6 +308,144 @@ class CallGraphModel(TreeModel):
289 alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 308 alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
290 return alignment[column] 309 return alignment[column]
291 310
311# Context-sensitive call graph window
312
313class CallGraphWindow(QMdiSubWindow):
314
315 def __init__(self, glb, parent=None):
316 super(CallGraphWindow, self).__init__(parent)
317
318 self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x))
319
320 self.view = QTreeView()
321 self.view.setModel(self.model)
322
323 for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)):
324 self.view.setColumnWidth(c, w)
325
326 self.setWidget(self.view)
327
328 AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph")
329
330# Action Definition
331
332def CreateAction(label, tip, callback, parent=None, shortcut=None):
333 action = QAction(label, parent)
334 if shortcut != None:
335 action.setShortcuts(shortcut)
336 action.setStatusTip(tip)
337 action.triggered.connect(callback)
338 return action
339
340# Typical application actions
341
342def CreateExitAction(app, parent=None):
343 return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit)
344
345# Typical MDI actions
346
347def CreateCloseActiveWindowAction(mdi_area):
348 return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area)
349
350def CreateCloseAllWindowsAction(mdi_area):
351 return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area)
352
353def CreateTileWindowsAction(mdi_area):
354 return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area)
355
356def CreateCascadeWindowsAction(mdi_area):
357 return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area)
358
359def CreateNextWindowAction(mdi_area):
360 return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild)
361
362def CreatePreviousWindowAction(mdi_area):
363 return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild)
364
365# Typical MDI window menu
366
367class WindowMenu():
368
369 def __init__(self, mdi_area, menu):
370 self.mdi_area = mdi_area
371 self.window_menu = menu.addMenu("&Windows")
372 self.close_active_window = CreateCloseActiveWindowAction(mdi_area)
373 self.close_all_windows = CreateCloseAllWindowsAction(mdi_area)
374 self.tile_windows = CreateTileWindowsAction(mdi_area)
375 self.cascade_windows = CreateCascadeWindowsAction(mdi_area)
376 self.next_window = CreateNextWindowAction(mdi_area)
377 self.previous_window = CreatePreviousWindowAction(mdi_area)
378 self.window_menu.aboutToShow.connect(self.Update)
379
380 def Update(self):
381 self.window_menu.clear()
382 sub_window_count = len(self.mdi_area.subWindowList())
383 have_sub_windows = sub_window_count != 0
384 self.close_active_window.setEnabled(have_sub_windows)
385 self.close_all_windows.setEnabled(have_sub_windows)
386 self.tile_windows.setEnabled(have_sub_windows)
387 self.cascade_windows.setEnabled(have_sub_windows)
388 self.next_window.setEnabled(have_sub_windows)
389 self.previous_window.setEnabled(have_sub_windows)
390 self.window_menu.addAction(self.close_active_window)
391 self.window_menu.addAction(self.close_all_windows)
392 self.window_menu.addSeparator()
393 self.window_menu.addAction(self.tile_windows)
394 self.window_menu.addAction(self.cascade_windows)
395 self.window_menu.addSeparator()
396 self.window_menu.addAction(self.next_window)
397 self.window_menu.addAction(self.previous_window)
398 if sub_window_count == 0:
399 return
400 self.window_menu.addSeparator()
401 nr = 1
402 for sub_window in self.mdi_area.subWindowList():
403 label = str(nr) + " " + sub_window.name
404 if nr < 10:
405 label = "&" + label
406 action = self.window_menu.addAction(label)
407 action.setCheckable(True)
408 action.setChecked(sub_window == self.mdi_area.activeSubWindow())
409 action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
410 self.window_menu.addAction(action)
411 nr += 1
412
413 def setActiveSubWindow(self, nr):
414 self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1])
415
416# Unique name for sub-windows
417
418def NumberedWindowName(name, nr):
419 if nr > 1:
420 name += " <" + str(nr) + ">"
421 return name
422
423def UniqueSubWindowName(mdi_area, name):
424 nr = 1
425 while True:
426 unique_name = NumberedWindowName(name, nr)
427 ok = True
428 for sub_window in mdi_area.subWindowList():
429 if sub_window.name == unique_name:
430 ok = False
431 break
432 if ok:
433 return unique_name
434 nr += 1
435
436# Add a sub-window
437
438def AddSubWindow(mdi_area, sub_window, name):
439 unique_name = UniqueSubWindowName(mdi_area, name)
440 sub_window.setMinimumSize(200, 100)
441 sub_window.resize(800, 600)
442 sub_window.setWindowTitle(unique_name)
443 sub_window.setAttribute(Qt.WA_DeleteOnClose)
444 sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon))
445 sub_window.name = unique_name
446 mdi_area.addSubWindow(sub_window)
447 sub_window.show()
448
292# Main window 449# Main window
293 450
294class MainWindow(QMainWindow): 451class MainWindow(QMainWindow):
@@ -298,21 +455,28 @@ class MainWindow(QMainWindow):
298 455
299 self.glb = glb 456 self.glb = glb
300 457
301 self.setWindowTitle("Call Graph: " + glb.dbname) 458 self.setWindowTitle("Exported SQL Viewer: " + glb.dbname)
302 self.move(100, 100)
303 self.resize(800, 600)
304 self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 459 self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))
305 self.setMinimumSize(200, 100) 460 self.setMinimumSize(200, 100)
306 461
307 self.model = CallGraphModel(glb) 462 self.mdi_area = QMdiArea()
463 self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
464 self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
308 465
309 self.view = QTreeView() 466 self.setCentralWidget(self.mdi_area)
310 self.view.setModel(self.model)
311 467
312 for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 468 menu = self.menuBar()
313 self.view.setColumnWidth(c, w) 469
470 file_menu = menu.addMenu("&File")
471 file_menu.addAction(CreateExitAction(glb.app, self))
472
473 reports_menu = menu.addMenu("&Reports")
474 reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self))
475
476 self.window_menu = WindowMenu(self.mdi_area, menu)
314 477
315 self.setCentralWidget(self.view) 478 def NewCallGraph(self):
479 CallGraphWindow(self.glb, self)
316 480
317# Global data 481# Global data
318 482