File indexing completed on 2024-04-28 05:41:31
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2003-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 /* 0010 * Callgraph View 0011 */ 0012 0013 #ifndef CALLGRAPHVIEW_H 0014 #define CALLGRAPHVIEW_H 0015 0016 #include <qwidget.h> 0017 #include <qmap.h> 0018 #include <qtimer.h> 0019 0020 #include <QGraphicsView> 0021 #include <QGraphicsScene> 0022 #include <QGraphicsRectItem> 0023 #include <QGraphicsPolygonItem> 0024 #include <QGraphicsPathItem> 0025 #include <QPixmap> 0026 #include <QFocusEvent> 0027 #include <QPolygon> 0028 #include <QList> 0029 #include <QKeyEvent> 0030 #include <QResizeEvent> 0031 #include <QContextMenuEvent> 0032 #include <QMouseEvent> 0033 0034 #include "treemap.h" // for DrawParams 0035 #include "tracedata.h" 0036 #include "traceitemview.h" 0037 0038 class QProcess; 0039 class QTemporaryFile; 0040 class QIODevice; 0041 0042 class CanvasNode; 0043 class CanvasEdge; 0044 class GraphEdge; 0045 class CallGraphView; 0046 0047 0048 // temporary parts of call graph to be shown 0049 class GraphNode 0050 { 0051 public: 0052 GraphNode(); 0053 0054 TraceFunction* function() const 0055 { 0056 return _f; 0057 } 0058 0059 void setFunction(TraceFunction* f) 0060 { 0061 _f = f; 0062 } 0063 0064 CanvasNode* canvasNode() const 0065 { 0066 return _cn; 0067 } 0068 0069 void setCanvasNode(CanvasNode* cn) 0070 { 0071 _cn = cn; 0072 } 0073 0074 bool isVisible() const 0075 { 0076 return _visible; 0077 } 0078 0079 void setVisible(bool v) 0080 { 0081 _visible = v; 0082 } 0083 0084 void clearEdges(); 0085 void sortEdges(); 0086 void addCallee(GraphEdge*); 0087 void addCaller(GraphEdge*); 0088 void addUniqueCallee(GraphEdge*); 0089 void addUniqueCaller(GraphEdge*); 0090 void removeEdge(GraphEdge*); 0091 double calleeCostSum(); 0092 double calleeCountSum(); 0093 double callerCostSum(); 0094 double callerCountSum(); 0095 0096 // keyboard navigation 0097 TraceCall* visibleCaller(); 0098 TraceCall* visibleCallee(); 0099 void setCallee(GraphEdge*); 0100 void setCaller(GraphEdge*); 0101 TraceFunction* nextVisible(); 0102 TraceFunction* priorVisible(); 0103 TraceCall* nextVisibleCaller(GraphEdge* = nullptr); 0104 TraceCall* nextVisibleCallee(GraphEdge* = nullptr); 0105 TraceCall* priorVisibleCaller(GraphEdge* = nullptr); 0106 TraceCall* priorVisibleCallee(GraphEdge* = nullptr); 0107 0108 double self, incl; 0109 0110 private: 0111 TraceFunction* _f; 0112 CanvasNode* _cn; 0113 bool _visible; 0114 0115 QList<GraphEdge*> callers, callees; 0116 0117 // for keyboard navigation 0118 int _lastCallerIndex, _lastCalleeIndex; 0119 bool _lastFromCaller; 0120 }; 0121 0122 0123 class GraphEdge 0124 { 0125 public: 0126 GraphEdge(); 0127 0128 CanvasEdge* canvasEdge() const 0129 { 0130 return _ce; 0131 } 0132 0133 void setCanvasEdge(CanvasEdge* ce) 0134 { 0135 _ce = ce; 0136 } 0137 0138 TraceCall* call() const 0139 { 0140 return _c; 0141 } 0142 0143 void setCall(TraceCall* c) 0144 { 0145 _c = c; 0146 } 0147 0148 bool isVisible() const 0149 { 0150 return _visible; 0151 } 0152 0153 void setVisible(bool v) 0154 { 0155 _visible = v; 0156 } 0157 0158 GraphNode* fromNode() const 0159 { 0160 return _fromNode; 0161 } 0162 0163 GraphNode* toNode() const 0164 { 0165 return _toNode; 0166 } 0167 0168 TraceFunction* from() const 0169 { 0170 return _from; 0171 } 0172 0173 TraceFunction* to() const 0174 { 0175 return _to; 0176 } 0177 0178 // has special cases for collapsed edges 0179 QString prettyName(); 0180 0181 void setCaller(TraceFunction* f) 0182 { 0183 _from = f; 0184 } 0185 0186 void setCallee(TraceFunction* f) 0187 { 0188 _to = f; 0189 } 0190 0191 void setCallerNode(GraphNode* n) 0192 { 0193 _fromNode = n; 0194 } 0195 0196 void setCalleeNode(GraphNode* n) 0197 { 0198 _toNode = n; 0199 } 0200 0201 // keyboard navigation 0202 TraceFunction* visibleCaller(); 0203 TraceFunction* visibleCallee(); 0204 TraceCall* nextVisible(); 0205 TraceCall* priorVisible(); 0206 0207 double cost, count; 0208 0209 private: 0210 // we have a _c *and* _from/_to because for collapsed edges, 0211 // only _to or _from will be unequal nullptr 0212 TraceCall* _c; 0213 TraceFunction * _from, * _to; 0214 GraphNode *_fromNode, *_toNode; 0215 CanvasEdge* _ce; 0216 bool _visible; 0217 // for keyboard navigation: have we last reached this edge via a caller? 0218 bool _lastFromCaller; 0219 }; 0220 0221 0222 typedef QMap<TraceFunction*, GraphNode> GraphNodeMap; 0223 typedef QMap<QPair<TraceFunction*, TraceFunction*>, GraphEdge> GraphEdgeMap; 0224 0225 0226 /* Abstract Interface for graph options */ 0227 class GraphOptions 0228 { 0229 public: 0230 enum Layout {TopDown, LeftRight, Circular}; 0231 virtual ~GraphOptions() {} 0232 virtual double funcLimit() = 0; 0233 virtual double callLimit() = 0; 0234 virtual int maxCallerDepth() = 0; 0235 virtual int maxCalleeDepth() = 0; 0236 virtual bool showSkipped() = 0; 0237 virtual bool expandCycles() = 0; 0238 virtual bool clusterGroups() = 0; 0239 virtual int detailLevel() = 0; 0240 virtual Layout layout() = 0; 0241 0242 static QString layoutString(Layout); 0243 static Layout layout(QString); 0244 }; 0245 0246 /* Graph Options Storage */ 0247 class StorableGraphOptions: public GraphOptions 0248 { 0249 public: 0250 StorableGraphOptions(); 0251 ~StorableGraphOptions() override{} 0252 // implementation of getters 0253 double funcLimit() override { return _funcLimit; } 0254 double callLimit() override { return _callLimit; } 0255 int maxCallerDepth() override { return _maxCallerDepth; } 0256 int maxCalleeDepth() override { return _maxCalleeDepth; } 0257 bool showSkipped() override { return _showSkipped; } 0258 bool expandCycles() override { return _expandCycles; } 0259 bool clusterGroups() override { return _clusterGroups; } 0260 int detailLevel() override { return _detailLevel; } 0261 Layout layout() override { return _layout; } 0262 0263 // setters 0264 void setMaxCallerDepth(int d) { _maxCallerDepth = d; } 0265 void setMaxCalleeDepth(int d) { _maxCalleeDepth = d; } 0266 void setFuncLimit(double l) { _funcLimit = l; } 0267 void setCallLimit(double l) { _callLimit = l; } 0268 void setShowSkipped(bool b) { _showSkipped = b; } 0269 void setExpandCycles(bool b) { _expandCycles = b; } 0270 void setClusterGroups(bool b) { _clusterGroups = b; } 0271 void setDetailLevel(int l) { _detailLevel = l; } 0272 void setLayout(Layout l) { _layout = l; } 0273 0274 protected: 0275 double _funcLimit, _callLimit; 0276 int _maxCallerDepth, _maxCalleeDepth; 0277 bool _showSkipped, _expandCycles, _clusterGroups; 0278 int _detailLevel; 0279 Layout _layout; 0280 }; 0281 0282 /** 0283 * GraphExporter 0284 * 0285 * Generates a graph file for "dot" 0286 * Create an instance and 0287 */ 0288 class GraphExporter : public StorableGraphOptions 0289 { 0290 public: 0291 GraphExporter(); 0292 GraphExporter(TraceData*, TraceFunction*, EventType*, 0293 ProfileContext::Type, 0294 QString filename = QString()); 0295 ~GraphExporter() override; 0296 0297 void reset(TraceData*, CostItem*, EventType*, 0298 ProfileContext::Type, 0299 QString filename = QString()); 0300 0301 QString filename() 0302 { 0303 return _dotName; 0304 } 0305 0306 int edgeCount() 0307 { 0308 return _edgeMap.count(); 0309 } 0310 0311 int nodeCount() 0312 { 0313 return _nodeMap.count(); 0314 } 0315 0316 // Set the object from which to get graph options for creation. 0317 // Default is this object itself (supply 0 for default) 0318 void setGraphOptions(GraphOptions* go = nullptr); 0319 0320 // Create a subgraph with given limits/maxDepths 0321 void createGraph(); 0322 0323 // calls createGraph before dumping of not already created 0324 bool writeDot(QIODevice* = nullptr); 0325 0326 // ephemereal save dialog and exporter 0327 static bool savePrompt(QWidget *, TraceData*, TraceFunction*, 0328 EventType*, ProfileContext::Type, 0329 CallGraphView*); 0330 0331 // to map back to structures when parsing a layouted graph 0332 0333 /* <toFunc> is a helper for node() and edge(). 0334 * Do not use the returned pointer directly, but only with 0335 * node() or edge(), because it could be a dangling pointer. 0336 */ 0337 TraceFunction* toFunc(QString); 0338 GraphNode* node(TraceFunction*); 0339 GraphEdge* edge(TraceFunction*, TraceFunction*); 0340 0341 /* After CanvasEdges are attached to GraphEdges, we can 0342 * sort the incoming and outgoing edges of all nodes 0343 * regarding start/end points for keyboard navigation 0344 */ 0345 void sortEdges(); 0346 0347 private: 0348 void buildGraph(TraceFunction*, int, bool, double); 0349 0350 QString _dotName; 0351 CostItem* _item; 0352 EventType* _eventType; 0353 ProfileContext::Type _groupType; 0354 QTemporaryFile* _tmpFile; 0355 double _realFuncLimit, _realCallLimit; 0356 bool _graphCreated; 0357 0358 GraphOptions* _go; 0359 0360 // optional graph attributes 0361 bool _useBox; 0362 0363 // graph parts written to file 0364 GraphNodeMap _nodeMap; 0365 GraphEdgeMap _edgeMap; 0366 }; 0367 0368 0369 /** 0370 * A panner laid over a QGraphicsScene 0371 */ 0372 class PanningView : public QGraphicsView 0373 { 0374 Q_OBJECT 0375 0376 public: 0377 explicit PanningView(QWidget * parent = nullptr); 0378 0379 void setZoomRect(const QRectF& r); 0380 0381 Q_SIGNALS: 0382 void zoomRectMoved(qreal dx, qreal dy); 0383 void zoomRectMoveFinished(); 0384 0385 protected: 0386 void mousePressEvent(QMouseEvent*) override; 0387 void mouseMoveEvent(QMouseEvent*) override; 0388 void mouseReleaseEvent(QMouseEvent*) override; 0389 void drawForeground(QPainter * p, const QRectF&) override; 0390 0391 QRectF _zoomRect; 0392 bool _movingZoomRect; 0393 QPointF _lastPos; 0394 }; 0395 0396 0397 /* 0398 * Canvas Items: 0399 * - CanvasNode (Rectangular Area) 0400 * - CanvasEdge (Spline curve) 0401 * - CanvasEdgeLabel (Label for edges) 0402 * - CanvasEdgeArrow (Arrows at the end of the edge spline) 0403 * - CanvasFrame (Grey background blending to show active node) 0404 */ 0405 0406 enum { 0407 CANVAS_NODE = 1122, 0408 CANVAS_EDGE, CANVAS_EDGELABEL, CANVAS_EDGEARROW, 0409 CANVAS_FRAME 0410 }; 0411 0412 class CanvasNode : public QGraphicsRectItem, public StoredDrawParams 0413 { 0414 public: 0415 CanvasNode(CallGraphView*, GraphNode*, int, int, int, int); 0416 0417 void updateGroup(); 0418 void setSelected(bool); 0419 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 0420 0421 GraphNode* node() 0422 { 0423 return _node; 0424 } 0425 0426 int type() const override 0427 { 0428 return CANVAS_NODE; 0429 } 0430 0431 private: 0432 GraphNode* _node; 0433 CallGraphView* _view; 0434 }; 0435 0436 0437 class CanvasEdgeLabel : public QGraphicsRectItem, public StoredDrawParams 0438 { 0439 public: 0440 CanvasEdgeLabel(CallGraphView*, CanvasEdge*, int, int, int, int); 0441 0442 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 0443 0444 CanvasEdge* canvasEdge() 0445 { 0446 return _ce; 0447 } 0448 0449 int type() const override 0450 { 0451 return CANVAS_EDGELABEL; 0452 } 0453 0454 double percentage() const 0455 { 0456 return _percentage; 0457 } 0458 0459 private: 0460 CanvasEdge* _ce; 0461 CallGraphView* _view; 0462 0463 double _percentage; 0464 }; 0465 0466 0467 class CanvasEdgeArrow : public QGraphicsPolygonItem 0468 { 0469 public: 0470 explicit CanvasEdgeArrow(CanvasEdge*); 0471 0472 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 0473 0474 CanvasEdge* canvasEdge() 0475 { 0476 return _ce; 0477 } 0478 0479 int type() const override 0480 { 0481 return CANVAS_EDGEARROW; 0482 } 0483 0484 private: 0485 CanvasEdge* _ce; 0486 }; 0487 0488 0489 class CanvasEdge : public QGraphicsPathItem 0490 { 0491 public: 0492 explicit CanvasEdge(GraphEdge*); 0493 0494 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 0495 0496 void setSelected(bool); 0497 0498 CanvasEdgeLabel* label() 0499 { 0500 return _label; 0501 } 0502 0503 void setLabel(CanvasEdgeLabel* l); 0504 0505 CanvasEdgeArrow* arrow() 0506 { 0507 return _arrow; 0508 } 0509 0510 void setArrow(CanvasEdgeArrow* a); 0511 0512 const QPolygon& controlPoints() const 0513 { 0514 return _points; 0515 } 0516 0517 void setControlPoints(const QPolygon& a); 0518 0519 GraphEdge* edge() 0520 { 0521 return _edge; 0522 } 0523 0524 int type() const override 0525 { 0526 return CANVAS_EDGE; 0527 } 0528 0529 private: 0530 GraphEdge* _edge; 0531 CanvasEdgeLabel* _label; 0532 CanvasEdgeArrow* _arrow; 0533 QPolygon _points; 0534 0535 double _thickness; 0536 }; 0537 0538 0539 class CanvasFrame : public QGraphicsRectItem 0540 { 0541 public: 0542 explicit CanvasFrame(CanvasNode*); 0543 0544 int type() const override 0545 { 0546 return CANVAS_FRAME; 0547 } 0548 0549 bool hit(const QPoint&) const 0550 { 0551 return false; 0552 } 0553 0554 void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override; 0555 0556 private: 0557 static QPixmap* _p; 0558 }; 0559 0560 0561 class CallGraphTip; 0562 0563 /** 0564 * A QGraphicsView showing a part of the call graph 0565 * and another zoomed out CanvasView in a border acting as 0566 * a panner to select to visible part (only if needed) 0567 */ 0568 class CallGraphView : public QGraphicsView, public TraceItemView, 0569 public StorableGraphOptions 0570 { 0571 Q_OBJECT 0572 0573 public: 0574 enum ZoomPosition {TopLeft, TopRight, BottomLeft, BottomRight, Auto, Hide}; 0575 0576 explicit CallGraphView(TraceItemView* parentView, QWidget* parent, 0577 const QString& name); 0578 ~CallGraphView() override; 0579 0580 void restoreOptions(const QString& prefix, const QString& postfix) override; 0581 void saveOptions(const QString& prefix, const QString& postfix) override; 0582 0583 QWidget* widget() override 0584 { 0585 return this; 0586 } 0587 0588 QString whatsThis() const override; 0589 0590 ZoomPosition zoomPos() const 0591 { 0592 return _zoomPosition; 0593 } 0594 0595 static ZoomPosition zoomPos(QString); 0596 static QString zoomPosString(ZoomPosition); 0597 0598 public Q_SLOTS: 0599 void zoomRectMoved(qreal, qreal); 0600 void zoomRectMoveFinished(); 0601 0602 void showRenderWarning(); 0603 void showRenderError(QString); 0604 void stopRendering(); 0605 void readDotOutput(); 0606 void dotError(); 0607 void dotExited(); 0608 0609 // context menu trigger handlers 0610 void callerDepthTriggered(QAction*); 0611 void calleeDepthTriggered(QAction*); 0612 void nodeLimitTriggered(QAction*); 0613 void callLimitTriggered(QAction*); 0614 void zoomPosTriggered(QAction*); 0615 void layoutTriggered(QAction*); 0616 0617 protected: 0618 void resizeEvent(QResizeEvent*) override; 0619 void mousePressEvent(QMouseEvent*) override; 0620 void mouseMoveEvent(QMouseEvent*) override; 0621 void mouseReleaseEvent(QMouseEvent*) override; 0622 void mouseDoubleClickEvent(QMouseEvent*) override; 0623 void contextMenuEvent(QContextMenuEvent*) override; 0624 void keyPressEvent(QKeyEvent*) override; 0625 void focusInEvent(QFocusEvent*) override; 0626 void focusOutEvent(QFocusEvent*) override; 0627 void scrollContentsBy(int dx, int dy) override; 0628 void wheelEvent(QWheelEvent*) override; 0629 0630 private: 0631 void updateSizes(QSize s = QSize(0,0)); 0632 CostItem* canShow(CostItem*) override; 0633 void doUpdate(int, bool) override; 0634 void refresh(); 0635 void makeFrame(CanvasNode*, bool active); 0636 void clear(); 0637 void showText(QString); 0638 0639 // context menu builders 0640 QAction* addCallerDepthAction(QMenu*,QString,int); 0641 QMenu* addCallerDepthMenu(QMenu*); 0642 QAction* addCalleeDepthAction(QMenu*,QString,int); 0643 QMenu* addCalleeDepthMenu(QMenu*); 0644 QAction* addNodeLimitAction(QMenu*,QString,double); 0645 QMenu* addNodeLimitMenu(QMenu*); 0646 QAction* addCallLimitAction(QMenu*,QString,double); 0647 QMenu* addCallLimitMenu(QMenu*); 0648 QAction* addZoomPosAction(QMenu*,QString,ZoomPosition); 0649 QMenu* addZoomPosMenu(QMenu*); 0650 QAction* addLayoutAction(QMenu*,QString,Layout); 0651 QMenu* addLayoutMenu(QMenu*); 0652 0653 QGraphicsScene *_scene; 0654 int _xMargin, _yMargin; 0655 PanningView *_panningView; 0656 double _panningZoom; 0657 0658 CallGraphTip* _tip; 0659 0660 bool _isMoving; 0661 QPoint _lastPos; 0662 0663 GraphExporter _exporter; 0664 0665 GraphNode* _selectedNode; 0666 GraphEdge* _selectedEdge; 0667 0668 qreal _zoomLevel = 1; 0669 0670 // widget options 0671 ZoomPosition _zoomPosition, _lastAutoPosition; 0672 0673 // background rendering 0674 QProcess* _renderProcess; 0675 QString _renderProcessCmdLine; 0676 QTimer _renderTimer; 0677 GraphNode* _prevSelectedNode; 0678 QPoint _prevSelectedPos; 0679 QString _unparsedOutput; 0680 }; 0681 0682 0683 #endif