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