1 /* 2 This file is part of KCachegrind. 3 4 SPDX-FileCopyrightText: 2003-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 5 6 SPDX-License-Identifier: GPL-2.0-only 7 */ 8 9 /* 10 * Callgraph View 11 */ 12 13 #ifndef CALLGRAPHVIEW_H 14 #define CALLGRAPHVIEW_H 15 16 #include <qwidget.h> 17 #include <qmap.h> 18 #include <qtimer.h> 19 20 #include <QGraphicsView> 21 #include <QGraphicsScene> 22 #include <QGraphicsRectItem> 23 #include <QGraphicsPolygonItem> 24 #include <QGraphicsPathItem> 25 #include <QPixmap> 26 #include <QFocusEvent> 27 #include <QPolygon> 28 #include <QList> 29 #include <QKeyEvent> 30 #include <QResizeEvent> 31 #include <QContextMenuEvent> 32 #include <QMouseEvent> 33 34 #include "treemap.h" // for DrawParams 35 #include "tracedata.h" 36 #include "traceitemview.h" 37 38 class QProcess; 39 class QTemporaryFile; 40 class QIODevice; 41 42 class CanvasNode; 43 class CanvasEdge; 44 class GraphEdge; 45 class CallGraphView; 46 47 48 // temporary parts of call graph to be shown 49 class GraphNode 50 { 51 public: 52 GraphNode(); 53 function()54 TraceFunction* function() const 55 { 56 return _f; 57 } 58 setFunction(TraceFunction * f)59 void setFunction(TraceFunction* f) 60 { 61 _f = f; 62 } 63 canvasNode()64 CanvasNode* canvasNode() const 65 { 66 return _cn; 67 } 68 setCanvasNode(CanvasNode * cn)69 void setCanvasNode(CanvasNode* cn) 70 { 71 _cn = cn; 72 } 73 isVisible()74 bool isVisible() const 75 { 76 return _visible; 77 } 78 setVisible(bool v)79 void setVisible(bool v) 80 { 81 _visible = v; 82 } 83 84 void clearEdges(); 85 void sortEdges(); 86 void addCallee(GraphEdge*); 87 void addCaller(GraphEdge*); 88 void addUniqueCallee(GraphEdge*); 89 void addUniqueCaller(GraphEdge*); 90 void removeEdge(GraphEdge*); 91 double calleeCostSum(); 92 double calleeCountSum(); 93 double callerCostSum(); 94 double callerCountSum(); 95 96 // keyboard navigation 97 TraceCall* visibleCaller(); 98 TraceCall* visibleCallee(); 99 void setCallee(GraphEdge*); 100 void setCaller(GraphEdge*); 101 TraceFunction* nextVisible(); 102 TraceFunction* priorVisible(); 103 TraceCall* nextVisibleCaller(GraphEdge* = nullptr); 104 TraceCall* nextVisibleCallee(GraphEdge* = nullptr); 105 TraceCall* priorVisibleCaller(GraphEdge* = nullptr); 106 TraceCall* priorVisibleCallee(GraphEdge* = nullptr); 107 108 double self, incl; 109 110 private: 111 TraceFunction* _f; 112 CanvasNode* _cn; 113 bool _visible; 114 115 QList<GraphEdge*> callers, callees; 116 117 // for keyboard navigation 118 int _lastCallerIndex, _lastCalleeIndex; 119 bool _lastFromCaller; 120 }; 121 122 123 class GraphEdge 124 { 125 public: 126 GraphEdge(); 127 canvasEdge()128 CanvasEdge* canvasEdge() const 129 { 130 return _ce; 131 } 132 setCanvasEdge(CanvasEdge * ce)133 void setCanvasEdge(CanvasEdge* ce) 134 { 135 _ce = ce; 136 } 137 call()138 TraceCall* call() const 139 { 140 return _c; 141 } 142 setCall(TraceCall * c)143 void setCall(TraceCall* c) 144 { 145 _c = c; 146 } 147 isVisible()148 bool isVisible() const 149 { 150 return _visible; 151 } 152 setVisible(bool v)153 void setVisible(bool v) 154 { 155 _visible = v; 156 } 157 fromNode()158 GraphNode* fromNode() const 159 { 160 return _fromNode; 161 } 162 toNode()163 GraphNode* toNode() const 164 { 165 return _toNode; 166 } 167 from()168 TraceFunction* from() const 169 { 170 return _from; 171 } 172 to()173 TraceFunction* to() const 174 { 175 return _to; 176 } 177 178 // has special cases for collapsed edges 179 QString prettyName(); 180 setCaller(TraceFunction * f)181 void setCaller(TraceFunction* f) 182 { 183 _from = f; 184 } 185 setCallee(TraceFunction * f)186 void setCallee(TraceFunction* f) 187 { 188 _to = f; 189 } 190 setCallerNode(GraphNode * n)191 void setCallerNode(GraphNode* n) 192 { 193 _fromNode = n; 194 } 195 setCalleeNode(GraphNode * n)196 void setCalleeNode(GraphNode* n) 197 { 198 _toNode = n; 199 } 200 201 // keyboard navigation 202 TraceFunction* visibleCaller(); 203 TraceFunction* visibleCallee(); 204 TraceCall* nextVisible(); 205 TraceCall* priorVisible(); 206 207 double cost, count; 208 209 private: 210 // we have a _c *and* _from/_to because for collapsed edges, 211 // only _to or _from will be unequal nullptr 212 TraceCall* _c; 213 TraceFunction * _from, * _to; 214 GraphNode *_fromNode, *_toNode; 215 CanvasEdge* _ce; 216 bool _visible; 217 // for keyboard navigation: have we last reached this edge via a caller? 218 bool _lastFromCaller; 219 }; 220 221 222 typedef QMap<TraceFunction*, GraphNode> GraphNodeMap; 223 typedef QMap<QPair<TraceFunction*, TraceFunction*>, GraphEdge> GraphEdgeMap; 224 225 226 /* Abstract Interface for graph options */ 227 class GraphOptions 228 { 229 public: 230 enum Layout {TopDown, LeftRight, Circular}; ~GraphOptions()231 virtual ~GraphOptions() {} 232 virtual double funcLimit() = 0; 233 virtual double callLimit() = 0; 234 virtual int maxCallerDepth() = 0; 235 virtual int maxCalleeDepth() = 0; 236 virtual bool showSkipped() = 0; 237 virtual bool expandCycles() = 0; 238 virtual bool clusterGroups() = 0; 239 virtual int detailLevel() = 0; 240 virtual Layout layout() = 0; 241 242 static QString layoutString(Layout); 243 static Layout layout(QString); 244 }; 245 246 /* Graph Options Storage */ 247 class StorableGraphOptions: public GraphOptions 248 { 249 public: 250 StorableGraphOptions(); ~StorableGraphOptions()251 ~StorableGraphOptions() override{} 252 // implementation of getters funcLimit()253 double funcLimit() override { return _funcLimit; } callLimit()254 double callLimit() override { return _callLimit; } maxCallerDepth()255 int maxCallerDepth() override { return _maxCallerDepth; } maxCalleeDepth()256 int maxCalleeDepth() override { return _maxCalleeDepth; } showSkipped()257 bool showSkipped() override { return _showSkipped; } expandCycles()258 bool expandCycles() override { return _expandCycles; } clusterGroups()259 bool clusterGroups() override { return _clusterGroups; } detailLevel()260 int detailLevel() override { return _detailLevel; } layout()261 Layout layout() override { return _layout; } 262 263 // setters setMaxCallerDepth(int d)264 void setMaxCallerDepth(int d) { _maxCallerDepth = d; } setMaxCalleeDepth(int d)265 void setMaxCalleeDepth(int d) { _maxCalleeDepth = d; } setFuncLimit(double l)266 void setFuncLimit(double l) { _funcLimit = l; } setCallLimit(double l)267 void setCallLimit(double l) { _callLimit = l; } setShowSkipped(bool b)268 void setShowSkipped(bool b) { _showSkipped = b; } setExpandCycles(bool b)269 void setExpandCycles(bool b) { _expandCycles = b; } setClusterGroups(bool b)270 void setClusterGroups(bool b) { _clusterGroups = b; } setDetailLevel(int l)271 void setDetailLevel(int l) { _detailLevel = l; } setLayout(Layout l)272 void setLayout(Layout l) { _layout = l; } 273 274 protected: 275 double _funcLimit, _callLimit; 276 int _maxCallerDepth, _maxCalleeDepth; 277 bool _showSkipped, _expandCycles, _clusterGroups; 278 int _detailLevel; 279 Layout _layout; 280 }; 281 282 /** 283 * GraphExporter 284 * 285 * Generates a graph file for "dot" 286 * Create an instance and 287 */ 288 class GraphExporter : public StorableGraphOptions 289 { 290 public: 291 GraphExporter(); 292 GraphExporter(TraceData*, TraceFunction*, EventType*, 293 ProfileContext::Type, 294 QString filename = QString()); 295 ~GraphExporter() override; 296 297 void reset(TraceData*, CostItem*, EventType*, 298 ProfileContext::Type, 299 QString filename = QString()); 300 filename()301 QString filename() 302 { 303 return _dotName; 304 } 305 edgeCount()306 int edgeCount() 307 { 308 return _edgeMap.count(); 309 } 310 nodeCount()311 int nodeCount() 312 { 313 return _nodeMap.count(); 314 } 315 316 // Set the object from which to get graph options for creation. 317 // Default is this object itself (supply 0 for default) 318 void setGraphOptions(GraphOptions* go = nullptr); 319 320 // Create a subgraph with given limits/maxDepths 321 void createGraph(); 322 323 // calls createGraph before dumping of not already created 324 bool writeDot(QIODevice* = nullptr); 325 326 // ephemereal save dialog and exporter 327 static bool savePrompt(QWidget *, TraceData*, TraceFunction*, 328 EventType*, ProfileContext::Type, 329 CallGraphView*); 330 331 // to map back to structures when parsing a layouted graph 332 333 /* <toFunc> is a helper for node() and edge(). 334 * Do not use the returned pointer directly, but only with 335 * node() or edge(), because it could be a dangling pointer. 336 */ 337 TraceFunction* toFunc(QString); 338 GraphNode* node(TraceFunction*); 339 GraphEdge* edge(TraceFunction*, TraceFunction*); 340 341 /* After CanvasEdges are attached to GraphEdges, we can 342 * sort the incoming and outgoing edges of all nodes 343 * regarding start/end points for keyboard navigation 344 */ 345 void sortEdges(); 346 347 private: 348 void buildGraph(TraceFunction*, int, bool, double); 349 350 QString _dotName; 351 CostItem* _item; 352 EventType* _eventType; 353 ProfileContext::Type _groupType; 354 QTemporaryFile* _tmpFile; 355 double _realFuncLimit, _realCallLimit; 356 bool _graphCreated; 357 358 GraphOptions* _go; 359 360 // optional graph attributes 361 bool _useBox; 362 363 // graph parts written to file 364 GraphNodeMap _nodeMap; 365 GraphEdgeMap _edgeMap; 366 }; 367 368 369 /** 370 * A panner laid over a QGraphicsScene 371 */ 372 class PanningView : public QGraphicsView 373 { 374 Q_OBJECT 375 376 public: 377 explicit PanningView(QWidget * parent = nullptr); 378 379 void setZoomRect(const QRectF& r); 380 381 Q_SIGNALS: 382 void zoomRectMoved(qreal dx, qreal dy); 383 void zoomRectMoveFinished(); 384 385 protected: 386 void mousePressEvent(QMouseEvent*) override; 387 void mouseMoveEvent(QMouseEvent*) override; 388 void mouseReleaseEvent(QMouseEvent*) override; 389 void drawForeground(QPainter * p, const QRectF&) override; 390 391 QRectF _zoomRect; 392 bool _movingZoomRect; 393 QPointF _lastPos; 394 }; 395 396 397 /* 398 * Canvas Items: 399 * - CanvasNode (Rectangular Area) 400 * - CanvasEdge (Spline curve) 401 * - CanvasEdgeLabel (Label for edges) 402 * - CanvasEdgeArrow (Arrows at the end of the edge spline) 403 * - CanvasFrame (Grey background blending to show active node) 404 */ 405 406 enum { 407 CANVAS_NODE = 1122, 408 CANVAS_EDGE, CANVAS_EDGELABEL, CANVAS_EDGEARROW, 409 CANVAS_FRAME 410 }; 411 412 class CanvasNode : public QGraphicsRectItem, public StoredDrawParams 413 { 414 public: 415 CanvasNode(CallGraphView*, GraphNode*, int, int, int, int); 416 417 void updateGroup(); 418 void setSelected(bool); 419 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 420 node()421 GraphNode* node() 422 { 423 return _node; 424 } 425 type()426 int type() const override 427 { 428 return CANVAS_NODE; 429 } 430 431 private: 432 GraphNode* _node; 433 CallGraphView* _view; 434 }; 435 436 437 class CanvasEdgeLabel : public QGraphicsRectItem, public StoredDrawParams 438 { 439 public: 440 CanvasEdgeLabel(CallGraphView*, CanvasEdge*, int, int, int, int); 441 442 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 443 canvasEdge()444 CanvasEdge* canvasEdge() 445 { 446 return _ce; 447 } 448 type()449 int type() const override 450 { 451 return CANVAS_EDGELABEL; 452 } 453 percentage()454 double percentage() const 455 { 456 return _percentage; 457 } 458 459 private: 460 CanvasEdge* _ce; 461 CallGraphView* _view; 462 463 double _percentage; 464 }; 465 466 467 class CanvasEdgeArrow : public QGraphicsPolygonItem 468 { 469 public: 470 explicit CanvasEdgeArrow(CanvasEdge*); 471 472 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 473 canvasEdge()474 CanvasEdge* canvasEdge() 475 { 476 return _ce; 477 } 478 type()479 int type() const override 480 { 481 return CANVAS_EDGEARROW; 482 } 483 484 private: 485 CanvasEdge* _ce; 486 }; 487 488 489 class CanvasEdge : public QGraphicsPathItem 490 { 491 public: 492 explicit CanvasEdge(GraphEdge*); 493 494 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 495 496 void setSelected(bool); 497 label()498 CanvasEdgeLabel* label() 499 { 500 return _label; 501 } 502 503 void setLabel(CanvasEdgeLabel* l); 504 arrow()505 CanvasEdgeArrow* arrow() 506 { 507 return _arrow; 508 } 509 510 void setArrow(CanvasEdgeArrow* a); 511 controlPoints()512 const QPolygon& controlPoints() const 513 { 514 return _points; 515 } 516 517 void setControlPoints(const QPolygon& a); 518 edge()519 GraphEdge* edge() 520 { 521 return _edge; 522 } 523 type()524 int type() const override 525 { 526 return CANVAS_EDGE; 527 } 528 529 private: 530 GraphEdge* _edge; 531 CanvasEdgeLabel* _label; 532 CanvasEdgeArrow* _arrow; 533 QPolygon _points; 534 535 double _thickness; 536 }; 537 538 539 class CanvasFrame : public QGraphicsRectItem 540 { 541 public: 542 explicit CanvasFrame(CanvasNode*); 543 type()544 int type() const override 545 { 546 return CANVAS_FRAME; 547 } 548 hit(const QPoint &)549 bool hit(const QPoint&) const 550 { 551 return false; 552 } 553 554 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 555 556 private: 557 static QPixmap* _p; 558 }; 559 560 561 class CallGraphTip; 562 563 /** 564 * A QGraphicsView showing a part of the call graph 565 * and another zoomed out CanvasView in a border acting as 566 * a panner to select to visible part (only if needed) 567 */ 568 class CallGraphView : public QGraphicsView, public TraceItemView, 569 public StorableGraphOptions 570 { 571 Q_OBJECT 572 573 public: 574 enum ZoomPosition {TopLeft, TopRight, BottomLeft, BottomRight, Auto, Hide}; 575 576 explicit CallGraphView(TraceItemView* parentView, QWidget* parent, 577 const QString& name); 578 ~CallGraphView() override; 579 580 void restoreOptions(const QString& prefix, const QString& postfix) override; 581 void saveOptions(const QString& prefix, const QString& postfix) override; 582 widget()583 QWidget* widget() override 584 { 585 return this; 586 } 587 588 QString whatsThis() const override; 589 zoomPos()590 ZoomPosition zoomPos() const 591 { 592 return _zoomPosition; 593 } 594 595 static ZoomPosition zoomPos(QString); 596 static QString zoomPosString(ZoomPosition); 597 598 public Q_SLOTS: 599 void zoomRectMoved(qreal, qreal); 600 void zoomRectMoveFinished(); 601 602 void showRenderWarning(); 603 void showRenderError(QString); 604 void stopRendering(); 605 void readDotOutput(); 606 void dotError(); 607 void dotExited(); 608 609 // context menu trigger handlers 610 void callerDepthTriggered(QAction*); 611 void calleeDepthTriggered(QAction*); 612 void nodeLimitTriggered(QAction*); 613 void callLimitTriggered(QAction*); 614 void zoomPosTriggered(QAction*); 615 void layoutTriggered(QAction*); 616 617 protected: 618 void resizeEvent(QResizeEvent*) override; 619 void mousePressEvent(QMouseEvent*) override; 620 void mouseMoveEvent(QMouseEvent*) override; 621 void mouseReleaseEvent(QMouseEvent*) override; 622 void mouseDoubleClickEvent(QMouseEvent*) override; 623 void contextMenuEvent(QContextMenuEvent*) override; 624 void keyPressEvent(QKeyEvent*) override; 625 void focusInEvent(QFocusEvent*) override; 626 void focusOutEvent(QFocusEvent*) override; 627 void scrollContentsBy(int dx, int dy) override; 628 629 private: 630 void updateSizes(QSize s = QSize(0,0)); 631 CostItem* canShow(CostItem*) override; 632 void doUpdate(int, bool) override; 633 void refresh(); 634 void makeFrame(CanvasNode*, bool active); 635 void clear(); 636 void showText(QString); 637 638 // context menu builders 639 QAction* addCallerDepthAction(QMenu*,QString,int); 640 QMenu* addCallerDepthMenu(QMenu*); 641 QAction* addCalleeDepthAction(QMenu*,QString,int); 642 QMenu* addCalleeDepthMenu(QMenu*); 643 QAction* addNodeLimitAction(QMenu*,QString,double); 644 QMenu* addNodeLimitMenu(QMenu*); 645 QAction* addCallLimitAction(QMenu*,QString,double); 646 QMenu* addCallLimitMenu(QMenu*); 647 QAction* addZoomPosAction(QMenu*,QString,ZoomPosition); 648 QMenu* addZoomPosMenu(QMenu*); 649 QAction* addLayoutAction(QMenu*,QString,Layout); 650 QMenu* addLayoutMenu(QMenu*); 651 652 QGraphicsScene *_scene; 653 int _xMargin, _yMargin; 654 PanningView *_panningView; 655 double _panningZoom; 656 657 CallGraphTip* _tip; 658 659 bool _isMoving; 660 QPoint _lastPos; 661 662 GraphExporter _exporter; 663 664 GraphNode* _selectedNode; 665 GraphEdge* _selectedEdge; 666 667 // widget options 668 ZoomPosition _zoomPosition, _lastAutoPosition; 669 670 // background rendering 671 QProcess* _renderProcess; 672 QString _renderProcessCmdLine; 673 QTimer _renderTimer; 674 GraphNode* _prevSelectedNode; 675 QPoint _prevSelectedPos; 676 QString _unparsedOutput; 677 }; 678 679 680 #endif 681