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