• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdesdk API Reference
  • KDE Home
  • Contact Us
 

kcachegrind

  • sources
  • kde-4.14
  • kdesdk
  • kcachegrind
  • libviews
callgraphview.cpp
Go to the documentation of this file.
1 /* This file is part of KCachegrind.
2  Copyright (C) 2007 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
3 
4  KCachegrind is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public
6  License as published by the Free Software Foundation, version 2.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; see the file COPYING. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 */
18 
19 /*
20  * Callgraph View
21  */
22 
23 #include "callgraphview.h"
24 
25 #include <stdlib.h>
26 #include <math.h>
27 
28 #include <QApplication>
29 #include <QDebug>
30 #include <QFile>
31 #include <QFileDialog>
32 #include <QTemporaryFile>
33 #include <QTextStream>
34 #include <QMatrix>
35 #include <QPair>
36 #include <QPainter>
37 #include <QStyle>
38 #include <QScrollBar>
39 #include <QResizeEvent>
40 #include <QMouseEvent>
41 #include <QFocusEvent>
42 #include <QKeyEvent>
43 #include <QStyleOptionGraphicsItem>
44 #include <QContextMenuEvent>
45 #include <QList>
46 #include <QPixmap>
47 #include <QDesktopWidget>
48 #include <QProcess>
49 #include <QMenu>
50 
51 
52 #include "config.h"
53 #include "globalguiconfig.h"
54 #include "listutils.h"
55 
56 
57 #define DEBUG_GRAPH 0
58 
59 // CallGraphView defaults
60 
61 #define DEFAULT_FUNCLIMIT .05
62 #define DEFAULT_CALLLIMIT 1.
63 #define DEFAULT_MAXCALLER 2
64 #define DEFAULT_MAXCALLEE -1
65 #define DEFAULT_SHOWSKIPPED false
66 #define DEFAULT_EXPANDCYCLES false
67 #define DEFAULT_CLUSTERGROUPS false
68 #define DEFAULT_DETAILLEVEL 1
69 #define DEFAULT_LAYOUT GraphOptions::TopDown
70 #define DEFAULT_ZOOMPOS Auto
71 
72 
73 // LessThen functors as helpers for sorting of graph edges
74 // for keyboard navigation. Sorting is done according to
75 // the angle at which a edge spline goes out or in of a function.
76 
77 // Sort angles of outgoing edges (edge seen as attached to the caller)
78 class CallerGraphEdgeLessThan
79 {
80 public:
81  bool operator()(const GraphEdge* ge1, const GraphEdge* ge2) const
82  {
83  const CanvasEdge* ce1 = ge1->canvasEdge();
84  const CanvasEdge* ce2 = ge2->canvasEdge();
85 
86  // sort invisible edges (ie. without matching CanvasEdge) in front
87  if (!ce1) return true;
88  if (!ce2) return false;
89 
90  QPolygon p1 = ce1->controlPoints();
91  QPolygon p2 = ce2->controlPoints();
92  QPoint d1 = p1.point(1) - p1.point(0);
93  QPoint d2 = p2.point(1) - p2.point(0);
94  double angle1 = atan2(double(d1.y()), double(d1.x()));
95  double angle2 = atan2(double(d2.y()), double(d2.x()));
96 
97  return (angle1 < angle2);
98  }
99 };
100 
101 // Sort angles of ingoing edges (edge seen as attached to the callee)
102 class CalleeGraphEdgeLessThan
103 {
104 public:
105  bool operator()(const GraphEdge* ge1, const GraphEdge* ge2) const
106  {
107  const CanvasEdge* ce1 = ge1->canvasEdge();
108  const CanvasEdge* ce2 = ge2->canvasEdge();
109 
110  // sort invisible edges (ie. without matching CanvasEdge) in front
111  if (!ce1) return true;
112  if (!ce2) return false;
113 
114  QPolygon p1 = ce1->controlPoints();
115  QPolygon p2 = ce2->controlPoints();
116  QPoint d1 = p1.point(p1.count()-2) - p1.point(p1.count()-1);
117  QPoint d2 = p2.point(p2.count()-2) - p2.point(p2.count()-1);
118  double angle1 = atan2(double(d1.y()), double(d1.x()));
119  double angle2 = atan2(double(d2.y()), double(d2.x()));
120 
121  // for ingoing edges sort according to descending angles
122  return (angle2 < angle1);
123  }
124 };
125 
126 
127 
128 //
129 // GraphNode
130 //
131 
132 GraphNode::GraphNode()
133 {
134  _f=0;
135  self = incl = 0;
136  _cn = 0;
137 
138  _visible = false;
139  _lastCallerIndex = _lastCalleeIndex = -1;
140 
141  _lastFromCaller = true;
142 }
143 
144 void GraphNode::clearEdges()
145 {
146  callees.clear();
147  callers.clear();
148 }
149 
150 CallerGraphEdgeLessThan callerGraphEdgeLessThan;
151 CalleeGraphEdgeLessThan calleeGraphEdgeLessThan;
152 
153 void GraphNode::sortEdges()
154 {
155  qSort(callers.begin(), callers.end(), callerGraphEdgeLessThan);
156  qSort(callees.begin(), callees.end(), calleeGraphEdgeLessThan);
157 }
158 
159 void GraphNode::addCallee(GraphEdge* e)
160 {
161  if (e)
162  callees.append(e);
163 }
164 
165 void GraphNode::addCaller(GraphEdge* e)
166 {
167  if (e)
168  callers.append(e);
169 }
170 
171 void GraphNode::addUniqueCallee(GraphEdge* e)
172 {
173  if (e && (callees.count(e) == 0))
174  callees.append(e);
175 }
176 
177 void GraphNode::addUniqueCaller(GraphEdge* e)
178 {
179  if (e && (callers.count(e) == 0))
180  callers.append(e);
181 }
182 
183 void GraphNode::removeEdge(GraphEdge* e)
184 {
185  callers.removeAll(e);
186  callees.removeAll(e);
187 }
188 
189 double GraphNode::calleeCostSum()
190 {
191  double sum = 0.0;
192 
193  foreach(GraphEdge* e, callees)
194  sum += e->cost;
195 
196  return sum;
197 }
198 
199 double GraphNode::calleeCountSum()
200 {
201  double sum = 0.0;
202 
203  foreach(GraphEdge* e, callees)
204  sum += e->count;
205 
206  return sum;
207 }
208 
209 double GraphNode::callerCostSum()
210 {
211  double sum = 0.0;
212 
213  foreach(GraphEdge* e, callers)
214  sum += e->cost;
215 
216  return sum;
217 }
218 
219 double GraphNode::callerCountSum()
220 {
221  double sum = 0.0;
222 
223  foreach(GraphEdge* e, callers)
224  sum += e->count;
225 
226  return sum;
227 }
228 
229 
230 TraceCall* GraphNode::visibleCaller()
231 {
232  if (0)
233  qDebug("GraphNode::visibleCaller %s: last %d, count %d",
234  qPrintable(_f->prettyName()), _lastCallerIndex, callers.count());
235 
236  // can not use at(): index can be -1 (out of bounds), result is 0 then
237  GraphEdge* e = callers.value(_lastCallerIndex);
238  if (e && !e->isVisible())
239  e = 0;
240  if (!e) {
241  double maxCost = 0.0;
242  GraphEdge* maxEdge = 0;
243  for(int i = 0; i<callers.size(); i++) {
244  e = callers[i];
245  if (e->isVisible() && (e->cost > maxCost)) {
246  maxCost = e->cost;
247  maxEdge = e;
248  _lastCallerIndex = i;
249  }
250  }
251  e = maxEdge;
252  }
253  return e ? e->call() : 0;
254 }
255 
256 TraceCall* GraphNode::visibleCallee()
257 {
258  if (0)
259  qDebug("GraphNode::visibleCallee %s: last %d, count %d",
260  qPrintable(_f->prettyName()), _lastCalleeIndex, callees.count());
261 
262  GraphEdge* e = callees.value(_lastCalleeIndex);
263  if (e && !e->isVisible())
264  e = 0;
265 
266  if (!e) {
267  double maxCost = 0.0;
268  GraphEdge* maxEdge = 0;
269  for(int i = 0; i<callees.size(); i++) {
270  e = callees[i];
271  if (e->isVisible() && (e->cost > maxCost)) {
272  maxCost = e->cost;
273  maxEdge = e;
274  _lastCalleeIndex = i;
275  }
276  }
277  e = maxEdge;
278  }
279  return e ? e->call() : 0;
280 }
281 
282 void GraphNode::setCallee(GraphEdge* e)
283 {
284  _lastCalleeIndex = callees.indexOf(e);
285  _lastFromCaller = false;
286 }
287 
288 void GraphNode::setCaller(GraphEdge* e)
289 {
290  _lastCallerIndex = callers.indexOf(e);
291  _lastFromCaller = true;
292 }
293 
294 TraceFunction* GraphNode::nextVisible()
295 {
296  TraceCall* c;
297 
298  if (_lastFromCaller) {
299  c = nextVisibleCaller();
300  if (c)
301  return c->called(true);
302  c = nextVisibleCallee();
303  if (c)
304  return c->caller(true);
305  } else {
306  c = nextVisibleCallee();
307  if (c)
308  return c->caller(true);
309  c = nextVisibleCaller();
310  if (c)
311  return c->called(true);
312  }
313  return 0;
314 }
315 
316 TraceFunction* GraphNode::priorVisible()
317 {
318  TraceCall* c;
319 
320  if (_lastFromCaller) {
321  c = priorVisibleCaller();
322  if (c)
323  return c->called(true);
324  c = priorVisibleCallee();
325  if (c)
326  return c->caller(true);
327  } else {
328  c = priorVisibleCallee();
329  if (c)
330  return c->caller(true);
331  c = priorVisibleCaller();
332  if (c)
333  return c->called(true);
334  }
335  return 0;
336 }
337 
338 TraceCall* GraphNode::nextVisibleCaller(GraphEdge* e)
339 {
340  int idx = e ? callers.indexOf(e) : _lastCallerIndex;
341  idx++;
342  while(idx < callers.size()) {
343  if (callers[idx]->isVisible()) {
344  _lastCallerIndex = idx;
345  return callers[idx]->call();
346  }
347  idx++;
348  }
349  return 0;
350 }
351 
352 TraceCall* GraphNode::nextVisibleCallee(GraphEdge* e)
353 {
354  int idx = e ? callees.indexOf(e) : _lastCalleeIndex;
355  idx++;
356  while(idx < callees.size()) {
357  if (callees[idx]->isVisible()) {
358  _lastCalleeIndex = idx;
359  return callees[idx]->call();
360  }
361  idx++;
362  }
363  return 0;
364 }
365 
366 TraceCall* GraphNode::priorVisibleCaller(GraphEdge* e)
367 {
368  int idx = e ? callers.indexOf(e) : _lastCallerIndex;
369 
370  idx = (idx<0) ? callers.size()-1 : idx-1;
371  while(idx >= 0) {
372  if (callers[idx]->isVisible()) {
373  _lastCallerIndex = idx;
374  return callers[idx]->call();
375  }
376  idx--;
377  }
378  return 0;
379 }
380 
381 TraceCall* GraphNode::priorVisibleCallee(GraphEdge* e)
382 {
383  int idx = e ? callees.indexOf(e) : _lastCalleeIndex;
384 
385  idx = (idx<0) ? callees.size()-1 : idx-1;
386  while(idx >= 0) {
387  if (callees[idx]->isVisible()) {
388  _lastCalleeIndex = idx;
389  return callees[idx]->call();
390  }
391  idx--;
392  }
393  return 0;
394 }
395 
396 
397 //
398 // GraphEdge
399 //
400 
401 GraphEdge::GraphEdge()
402 {
403  _c=0;
404  _from = _to = 0;
405  _fromNode = _toNode = 0;
406  cost = count = 0;
407  _ce = 0;
408 
409  _visible = false;
410  _lastFromCaller = true;
411 }
412 
413 QString GraphEdge::prettyName()
414 {
415  if (_c)
416  return _c->prettyName();
417 
418  if (_from)
419  return QObject::tr("Call(s) from %1").arg(_from->prettyName());
420 
421  if (_to)
422  return QObject::tr("Call(s) to %1").arg(_to->prettyName());
423 
424  return QObject::tr("(unknown call)");
425 }
426 
427 TraceFunction* GraphEdge::visibleCaller()
428 {
429  if (_from) {
430  _lastFromCaller = true;
431  if (_fromNode)
432  _fromNode->setCallee(this);
433  return _from;
434  }
435  return 0;
436 }
437 
438 TraceFunction* GraphEdge::visibleCallee()
439 {
440  if (_to) {
441  _lastFromCaller = false;
442  if (_toNode)
443  _toNode->setCaller(this);
444  return _to;
445  }
446  return 0;
447 }
448 
449 TraceCall* GraphEdge::nextVisible()
450 {
451  TraceCall* res = 0;
452 
453  if (_lastFromCaller && _fromNode) {
454  res = _fromNode->nextVisibleCallee(this);
455  if (!res && _toNode)
456  res = _toNode->nextVisibleCaller(this);
457  } else if (_toNode) {
458  res = _toNode->nextVisibleCaller(this);
459  if (!res && _fromNode)
460  res = _fromNode->nextVisibleCallee(this);
461  }
462  return res;
463 }
464 
465 TraceCall* GraphEdge::priorVisible()
466 {
467  TraceCall* res = 0;
468 
469  if (_lastFromCaller && _fromNode) {
470  res = _fromNode->priorVisibleCallee(this);
471  if (!res && _toNode)
472  res = _toNode->priorVisibleCaller(this);
473  } else if (_toNode) {
474  res = _toNode->priorVisibleCaller(this);
475  if (!res && _fromNode)
476  res = _fromNode->priorVisibleCallee(this);
477  }
478  return res;
479 }
480 
481 
482 
483 //
484 // GraphOptions
485 //
486 
487 QString GraphOptions::layoutString(Layout l)
488 {
489  if (l == Circular)
490  return QString("Circular");
491  if (l == LeftRight)
492  return QString("LeftRight");
493  return QString("TopDown");
494 }
495 
496 GraphOptions::Layout GraphOptions::layout(QString s)
497 {
498  if (s == QString("Circular"))
499  return Circular;
500  if (s == QString("LeftRight"))
501  return LeftRight;
502  return TopDown;
503 }
504 
505 
506 //
507 // StorableGraphOptions
508 //
509 
510 StorableGraphOptions::StorableGraphOptions()
511 {
512  // default options
513  _funcLimit = DEFAULT_FUNCLIMIT;
514  _callLimit = DEFAULT_CALLLIMIT;
515  _maxCallerDepth = DEFAULT_MAXCALLER;
516  _maxCalleeDepth = DEFAULT_MAXCALLEE;
517  _showSkipped = DEFAULT_SHOWSKIPPED;
518  _expandCycles = DEFAULT_EXPANDCYCLES;
519  _detailLevel = DEFAULT_DETAILLEVEL;
520  _layout = DEFAULT_LAYOUT;
521 }
522 
523 
524 
525 
526 //
527 // GraphExporter
528 //
529 
530 GraphExporter::GraphExporter()
531 {
532  _go = this;
533  _tmpFile = 0;
534  _item = 0;
535  reset(0, 0, 0, ProfileContext::InvalidType, QString());
536 }
537 
538 GraphExporter::GraphExporter(TraceData* d, TraceFunction* f,
539  EventType* ct, ProfileContext::Type gt,
540  QString filename)
541 {
542  _go = this;
543  _tmpFile = 0;
544  _item = 0;
545  reset(d, f, ct, gt, filename);
546 }
547 
548 GraphExporter::~GraphExporter()
549 {
550  if (_item && _tmpFile) {
551 #if DEBUG_GRAPH
552  _tmpFile->setAutoRemove(true);
553 #endif
554  delete _tmpFile;
555  }
556 }
557 
558 
559 void GraphExporter::reset(TraceData*, CostItem* i, EventType* ct,
560  ProfileContext::Type gt, QString filename)
561 {
562  _graphCreated = false;
563  _nodeMap.clear();
564  _edgeMap.clear();
565 
566  if (_item && _tmpFile) {
567  _tmpFile->setAutoRemove(true);
568  delete _tmpFile;
569  }
570 
571  if (i) {
572  switch (i->type()) {
573  case ProfileContext::Function:
574  case ProfileContext::FunctionCycle:
575  case ProfileContext::Call:
576  break;
577  default:
578  i = 0;
579  }
580  }
581 
582  _item = i;
583  _eventType = ct;
584  _groupType = gt;
585  if (!i)
586  return;
587 
588  if (filename.isEmpty()) {
589  _tmpFile = new QTemporaryFile();
590  //_tmpFile->setSuffix(".dot");
591  _tmpFile->setAutoRemove(false);
592  _tmpFile->open();
593  _dotName = _tmpFile->fileName();
594  _useBox = true;
595  } else {
596  _tmpFile = 0;
597  _dotName = filename;
598  _useBox = false;
599  }
600 }
601 
602 
603 
604 void GraphExporter::setGraphOptions(GraphOptions* go)
605 {
606  if (go == 0)
607  go = this;
608  _go = go;
609 }
610 
611 void GraphExporter::createGraph()
612 {
613  if (!_item)
614  return;
615  if (_graphCreated)
616  return;
617  _graphCreated = true;
618 
619  if ((_item->type() == ProfileContext::Function) ||(_item->type()
620  == ProfileContext::FunctionCycle)) {
621  TraceFunction* f = (TraceFunction*) _item;
622 
623  double incl = f->inclusive()->subCost(_eventType);
624  _realFuncLimit = incl * _go->funcLimit();
625  _realCallLimit = _realFuncLimit * _go->callLimit();
626 
627  buildGraph(f, 0, true, 1.0); // down to callees
628 
629  // set costs of function back to 0, as it will be added again
630  GraphNode& n = _nodeMap[f];
631  n.self = n.incl = 0.0;
632 
633  buildGraph(f, 0, false, 1.0); // up to callers
634  } else {
635  TraceCall* c = (TraceCall*) _item;
636 
637  double incl = c->subCost(_eventType);
638  _realFuncLimit = incl * _go->funcLimit();
639  _realCallLimit = _realFuncLimit * _go->callLimit();
640 
641  // create edge
642  TraceFunction *caller, *called;
643  caller = c->caller(false);
644  called = c->called(false);
645  QPair<TraceFunction*,TraceFunction*> p(caller, called);
646  GraphEdge& e = _edgeMap[p];
647  e.setCall(c);
648  e.setCaller(p.first);
649  e.setCallee(p.second);
650  e.cost = c->subCost(_eventType);
651  e.count = c->callCount();
652 
653  SubCost s = called->inclusive()->subCost(_eventType);
654  buildGraph(called, 0, true, e.cost / s); // down to callees
655  s = caller->inclusive()->subCost(_eventType);
656  buildGraph(caller, 0, false, e.cost / s); // up to callers
657  }
658 }
659 
660 
661 void GraphExporter::writeDot(QIODevice* device)
662 {
663  if (!_item)
664  return;
665 
666  QFile* file = 0;
667  QTextStream* stream = 0;
668 
669  if (device)
670  stream = new QTextStream(device);
671  else {
672  if (_tmpFile)
673  stream = new QTextStream(_tmpFile);
674  else {
675  file = new QFile(_dotName);
676  if ( !file->open(QIODevice::WriteOnly ) ) {
677  qDebug() << "Can not write dot file '"<< _dotName << "'";
678  delete file;
679  return;
680  }
681  stream = new QTextStream(file);
682  }
683  }
684 
685  if (!_graphCreated)
686  createGraph();
687 
688  /* Generate dot format...
689  * When used for the CallGraphView (in contrast to "Export Callgraph..."),
690  * the labels are only dummy placeholders to reserve space for our own
691  * drawings.
692  */
693 
694  *stream << "digraph \"callgraph\" {\n";
695 
696  if (_go->layout() == LeftRight) {
697  *stream << QString(" rankdir=LR;\n");
698  } else if (_go->layout() == Circular) {
699  TraceFunction *f = 0;
700  switch (_item->type()) {
701  case ProfileContext::Function:
702  case ProfileContext::FunctionCycle:
703  f = (TraceFunction*) _item;
704  break;
705  case ProfileContext::Call:
706  f = ((TraceCall*)_item)->caller(true);
707  break;
708  default:
709  break;
710  }
711  if (f)
712  *stream << QString(" center=F%1;\n").arg((qptrdiff)f, 0, 16);
713  *stream << QString(" overlap=false;\n splines=true;\n");
714  }
715 
716  // for clustering
717  QMap<TraceCostItem*,QList<GraphNode*> > nLists;
718 
719  GraphNodeMap::Iterator nit;
720  for (nit = _nodeMap.begin(); nit != _nodeMap.end(); ++nit ) {
721  GraphNode& n = *nit;
722 
723  if (n.incl <= _realFuncLimit)
724  continue;
725 
726  // for clustering: get cost item group of function
727  TraceCostItem* g;
728  TraceFunction* f = n.function();
729  switch (_groupType) {
730  case ProfileContext::Object:
731  g = f->object();
732  break;
733  case ProfileContext::Class:
734  g = f->cls();
735  break;
736  case ProfileContext::File:
737  g = f->file();
738  break;
739  case ProfileContext::FunctionCycle:
740  g = f->cycle();
741  break;
742  default:
743  g = 0;
744  break;
745  }
746  nLists[g].append(&n);
747  }
748 
749  QMap<TraceCostItem*,QList<GraphNode*> >::Iterator lit;
750  int cluster = 0;
751  for (lit = nLists.begin(); lit != nLists.end(); ++lit, cluster++) {
752  QList<GraphNode*>& l = lit.value();
753  TraceCostItem* i = lit.key();
754 
755  if (_go->clusterGroups() && i) {
756  QString iabr = GlobalConfig::shortenSymbol(i->prettyName());
757  *stream << QString("subgraph \"cluster%1\" { label=\"%2\";\n")
758  .arg(cluster).arg(iabr);
759  }
760 
761  foreach(GraphNode* np, l) {
762  TraceFunction* f = np->function();
763 
764  QString abr = GlobalConfig::shortenSymbol(f->prettyName());
765  *stream << QString(" F%1 [").arg((qptrdiff)f, 0, 16);
766  if (_useBox) {
767  // we want a minimal size for cost display
768  if ((int)abr.length() < 8) abr = abr + QString(8 - abr.length(),'_');
769 
770  // make label 3 lines for CallGraphView
771  *stream << QString("shape=box,label=\"** %1 **\\n**\\n%2\"];\n")
772  .arg(abr)
773  .arg(SubCost(np->incl).pretty());
774  } else
775  *stream << QString("label=\"%1\\n%2\"];\n")
776  .arg(abr)
777  .arg(SubCost(np->incl).pretty());
778  }
779 
780  if (_go->clusterGroups() && i)
781  *stream << QString("}\n");
782  }
783 
784  GraphEdgeMap::Iterator eit;
785  for (eit = _edgeMap.begin(); eit != _edgeMap.end(); ++eit ) {
786  GraphEdge& e = *eit;
787 
788  if (e.cost < _realCallLimit)
789  continue;
790  if (!_go->expandCycles()) {
791  // do not show inner cycle calls
792  if (e.call()->inCycle()>0)
793  continue;
794  }
795 
796  GraphNode& from = _nodeMap[e.from()];
797  GraphNode& to = _nodeMap[e.to()];
798 
799  e.setCallerNode(&from);
800  e.setCalleeNode(&to);
801 
802  if ((from.incl <= _realFuncLimit) ||(to.incl <= _realFuncLimit))
803  continue;
804 
805  // remove dumped edges from n.callers/n.callees
806  from.removeEdge(&e);
807  to.removeEdge(&e);
808 
809  *stream << QString(" F%1 -> F%2 [weight=%3")
810  .arg((qptrdiff)e.from(), 0, 16)
811  .arg((qptrdiff)e.to(), 0, 16)
812  .arg((long)log(log(e.cost)));
813 
814  if (_go->detailLevel() ==1) {
815  *stream << QString(",label=\"%1 (%2x)\"")
816  .arg(SubCost(e.cost).pretty())
817  .arg(SubCost(e.count).pretty());
818  }
819  else if (_go->detailLevel() ==2)
820  *stream << QString(",label=\"%3\\n%4 x\"")
821  .arg(SubCost(e.cost).pretty())
822  .arg(SubCost(e.count).pretty());
823 
824  *stream << QString("];\n");
825  }
826 
827  if (_go->showSkipped()) {
828 
829  // Create sum-edges for skipped edges
830  GraphEdge* e;
831  double costSum, countSum;
832  for (nit = _nodeMap.begin(); nit != _nodeMap.end(); ++nit ) {
833  GraphNode& n = *nit;
834  if (n.incl <= _realFuncLimit)
835  continue;
836 
837  // add edge for all skipped callers if cost sum is high enough
838  costSum = n.callerCostSum();
839  countSum = n.callerCountSum();
840  if (costSum > _realCallLimit) {
841 
842  QPair<TraceFunction*,TraceFunction*> p(0, n.function());
843  e = &(_edgeMap[p]);
844  e->setCallee(p.second);
845  e->cost = costSum;
846  e->count = countSum;
847 
848  *stream << QString(" R%1 [shape=point,label=\"\"];\n")
849  .arg((qptrdiff)n.function(), 0, 16);
850  *stream << QString(" R%1 -> F%2 [label=\"%3\\n%4 x\",weight=%5];\n")
851  .arg((qptrdiff)n.function(), 0, 16)
852  .arg((qptrdiff)n.function(), 0, 16)
853  .arg(SubCost(costSum).pretty())
854  .arg(SubCost(countSum).pretty())
855  .arg((int)log(costSum));
856  }
857 
858  // add edge for all skipped callees if cost sum is high enough
859  costSum = n.calleeCostSum();
860  countSum = n.calleeCountSum();
861  if (costSum > _realCallLimit) {
862 
863  QPair<TraceFunction*,TraceFunction*> p(n.function(), 0);
864  e = &(_edgeMap[p]);
865  e->setCaller(p.first);
866  e->cost = costSum;
867  e->count = countSum;
868 
869  *stream << QString(" S%1 [shape=point,label=\"\"];\n")
870  .arg((qptrdiff)n.function(), 0, 16);
871  *stream << QString(" F%1 -> S%2 [label=\"%3\\n%4 x\",weight=%5];\n")
872  .arg((qptrdiff)n.function(), 0, 16)
873  .arg((qptrdiff)n.function(), 0, 16)
874  .arg(SubCost(costSum).pretty())
875  .arg(SubCost(countSum).pretty())
876  .arg((int)log(costSum));
877  }
878  }
879  }
880 
881  // clear edges here completely.
882  // Visible edges are inserted again on parsing in CallGraphView::refresh
883  for (nit = _nodeMap.begin(); nit != _nodeMap.end(); ++nit ) {
884  GraphNode& n = *nit;
885  n.clearEdges();
886  }
887 
888  *stream << "}\n";
889 
890  if (!device) {
891  if (_tmpFile) {
892  stream->flush();
893  _tmpFile->seek(0);
894  } else {
895  file->close();
896  delete file;
897  }
898  }
899  delete stream;
900 }
901 
902 void GraphExporter::sortEdges()
903 {
904  GraphNodeMap::Iterator nit;
905  for (nit = _nodeMap.begin(); nit != _nodeMap.end(); ++nit ) {
906  GraphNode& n = *nit;
907  n.sortEdges();
908  }
909 }
910 
911 TraceFunction* GraphExporter::toFunc(QString s)
912 {
913  if (s[0] != 'F')
914  return 0;
915  bool ok;
916  TraceFunction* f = (TraceFunction*) s.mid(1).toULongLong(&ok, 16);
917  if (!ok)
918  return 0;
919 
920  return f;
921 }
922 
923 GraphNode* GraphExporter::node(TraceFunction* f)
924 {
925  if (!f)
926  return 0;
927 
928  GraphNodeMap::Iterator it = _nodeMap.find(f);
929  if (it == _nodeMap.end())
930  return 0;
931 
932  return &(*it);
933 }
934 
935 GraphEdge* GraphExporter::edge(TraceFunction* f1, TraceFunction* f2)
936 {
937  GraphEdgeMap::Iterator it = _edgeMap.find(qMakePair(f1, f2));
938  if (it == _edgeMap.end())
939  return 0;
940 
941  return &(*it);
942 }
943 
953 void GraphExporter::buildGraph(TraceFunction* f, int d, bool toCallees,
954  double factor)
955 {
956 #if DEBUG_GRAPH
957  qDebug() << "buildGraph(" << f->prettyName() << "," << d << "," << factor
958  << ") [to " << (toCallees ? "Callees":"Callers") << "]";
959 #endif
960 
961  double oldIncl = 0.0;
962  GraphNode& n = _nodeMap[f];
963  if (n.function() == 0) {
964  n.setFunction(f);
965  } else
966  oldIncl = n.incl;
967 
968  double incl = f->inclusive()->subCost(_eventType) * factor;
969  n.incl += incl;
970  n.self += f->subCost(_eventType) * factor;
971  if (0)
972  qDebug(" Added Incl. %f, now %f", incl, n.incl);
973 
974  // A negative depth limit means "unlimited"
975  int maxDepth = toCallees ? _go->maxCalleeDepth()
976  : _go->maxCallerDepth();
977  if ((maxDepth>=0) && (d >= maxDepth)) {
978  if (0)
979  qDebug(" Cutoff, max depth reached");
980  return;
981  }
982 
983  // if we just reached the limit by summing, do a DFS
984  // from here with full incl. cost because of previous cutoffs
985  if ((n.incl >= _realFuncLimit) && (oldIncl < _realFuncLimit))
986  incl = n.incl;
987 
988  if (f->cycle()) {
989  // for cycles members, we never stop on first visit, but always on 2nd
990  // note: a 2nd visit never should happen, as we do not follow inner-cycle
991  // calls
992  if (oldIncl > 0.0) {
993  if (0)
994  qDebug(" Cutoff, 2nd visit to Cycle Member");
995  // and takeback cost addition, as it is added twice
996  n.incl = oldIncl;
997  n.self -= f->subCost(_eventType) * factor;
998  return;
999  }
1000  } else if (incl <= _realFuncLimit) {
1001  if (0)
1002  qDebug(" Cutoff, below limit");
1003  return;
1004  }
1005 
1006  TraceFunction* f2;
1007 
1008  // on entering a cycle, only go the FunctionCycle
1009  TraceCallList l = toCallees ? f->callings(false) : f->callers(false);
1010 
1011  foreach(TraceCall* call, l) {
1012 
1013  f2 = toCallees ? call->called(false) : call->caller(false);
1014 
1015  double count = call->callCount() * factor;
1016  double cost = call->subCost(_eventType) * factor;
1017 
1018  // ignore function calls with absolute cost < 3 per call
1019  // No: This would skip a lot of functions e.g. with L2 cache misses
1020  // if (count>0.0 && (cost/count < 3)) continue;
1021 
1022  double oldCost = 0.0;
1023  QPair<TraceFunction*,TraceFunction*> p(toCallees ? f : f2,
1024  toCallees ? f2 : f);
1025  GraphEdge& e = _edgeMap[p];
1026  if (e.call() == 0) {
1027  e.setCall(call);
1028  e.setCaller(p.first);
1029  e.setCallee(p.second);
1030  } else
1031  oldCost = e.cost;
1032 
1033  e.cost += cost;
1034  e.count += count;
1035  if (0)
1036  qDebug(" Edge to %s, added cost %f, now %f",
1037  qPrintable(f2->prettyName()), cost, e.cost);
1038 
1039  // if this call goes into a FunctionCycle, we also show the real call
1040  if (f2->cycle() == f2) {
1041  TraceFunction* realF;
1042  realF = toCallees ? call->called(true) : call->caller(true);
1043  QPair<TraceFunction*,TraceFunction*>
1044  realP(toCallees ? f : realF, toCallees ? realF : f);
1045  GraphEdge& e = _edgeMap[realP];
1046  if (e.call() == 0) {
1047  e.setCall(call);
1048  e.setCaller(realP.first);
1049  e.setCallee(realP.second);
1050  }
1051  e.cost += cost;
1052  e.count += count;
1053  }
1054 
1055  // - do not do a DFS on calls in recursion/cycle
1056  if (call->inCycle()>0)
1057  continue;
1058  if (call->isRecursion())
1059  continue;
1060 
1061  if (toCallees)
1062  n.addUniqueCallee(&e);
1063  else
1064  n.addUniqueCaller(&e);
1065 
1066  // if we just reached the call limit (=func limit by summing, do a DFS
1067  // from here with full incl. cost because of previous cutoffs
1068  if ((e.cost >= _realCallLimit) && (oldCost < _realCallLimit))
1069  cost = e.cost;
1070  if (cost < _realCallLimit) {
1071  if (0)
1072  qDebug(" Edge Cutoff, limit not reached");
1073  continue;
1074  }
1075 
1076  SubCost s;
1077  if (call->inCycle())
1078  s = f2->cycle()->inclusive()->subCost(_eventType);
1079  else
1080  s = f2->inclusive()->subCost(_eventType);
1081  SubCost v = call->subCost(_eventType);
1082  // FIXME: Can s be 0?
1083  buildGraph(f2, d+1, toCallees, factor * v / s);
1084  }
1085 }
1086 
1087 //
1088 // PannerView
1089 //
1090 PanningView::PanningView(QWidget * parent)
1091  : QGraphicsView(parent)
1092 {
1093  _movingZoomRect = false;
1094 
1095  // FIXME: Why does this not work?
1096  viewport()->setFocusPolicy(Qt::NoFocus);
1097 }
1098 
1099 void PanningView::setZoomRect(const QRectF& r)
1100 {
1101  _zoomRect = r;
1102  viewport()->update();
1103 }
1104 
1105 void PanningView::drawForeground(QPainter * p, const QRectF&)
1106 {
1107  if (!_zoomRect.isValid())
1108  return;
1109 
1110  QColor red(Qt::red);
1111  QPen pen(red.dark());
1112  pen.setWidthF(2.0 / matrix().m11());
1113  p->setPen(pen);
1114 
1115  QColor c(red.dark());
1116  c.setAlphaF(0.05);
1117  p->setBrush(QBrush(c));
1118 
1119  p->drawRect(QRectF(_zoomRect.x(), _zoomRect.y(),
1120  _zoomRect.width()-1, _zoomRect.height()-1));
1121 }
1122 
1123 void PanningView::mousePressEvent(QMouseEvent* e)
1124 {
1125  QPointF sPos = mapToScene(e->pos());
1126 
1127  if (_zoomRect.isValid()) {
1128  if (!_zoomRect.contains(sPos))
1129  emit zoomRectMoved(sPos.x() - _zoomRect.center().x(),
1130  sPos.y() - _zoomRect.center().y());
1131 
1132  _movingZoomRect = true;
1133  _lastPos = sPos;
1134  }
1135 }
1136 
1137 void PanningView::mouseMoveEvent(QMouseEvent* e)
1138 {
1139  QPointF sPos = mapToScene(e->pos());
1140  if (_movingZoomRect) {
1141  emit zoomRectMoved(sPos.x() - _lastPos.x(),
1142  sPos.y() - _lastPos.y());
1143  _lastPos = sPos;
1144  }
1145 }
1146 
1147 void PanningView::mouseReleaseEvent(QMouseEvent*)
1148 {
1149  _movingZoomRect = false;
1150  emit zoomRectMoveFinished();
1151 }
1152 
1153 
1154 
1155 
1156 
1157 //
1158 // CanvasNode
1159 //
1160 
1161 CanvasNode::CanvasNode(CallGraphView* v, GraphNode* n, int x, int y, int w,
1162  int h) :
1163  QGraphicsRectItem(QRect(x, y, w, h)), _node(n), _view(v)
1164 {
1165  setPosition(0, DrawParams::TopCenter);
1166  setPosition(1, DrawParams::BottomCenter);
1167 
1168  updateGroup();
1169 
1170  if (!_node || !_view)
1171  return;
1172 
1173  if (_node->function())
1174  setText(0, _node->function()->prettyName());
1175 
1176  ProfileCostArray* totalCost;
1177  if (GlobalConfig::showExpanded()) {
1178  if (_view->activeFunction()) {
1179  if (_view->activeFunction()->cycle())
1180  totalCost = _view->activeFunction()->cycle()->inclusive();
1181  else
1182  totalCost = _view->activeFunction()->inclusive();
1183  } else
1184  totalCost = (ProfileCostArray*) _view->activeItem();
1185  } else
1186  totalCost = ((TraceItemView*)_view)->data();
1187  double total = totalCost->subCost(_view->eventType());
1188  double inclP = 100.0 * n->incl/ total;
1189  if (GlobalConfig::showPercentage())
1190  setText(1, QString("%1 %")
1191  .arg(inclP, 0, 'f', GlobalConfig::percentPrecision()));
1192  else
1193  setText(1, SubCost(n->incl).pretty());
1194  setPixmap(1, percentagePixmap(25, 10, (int)(inclP+.5), Qt::blue, true));
1195 
1196  setToolTip(QString("%1 (%2)").arg(text(0)).arg(text(1)));
1197 }
1198 
1199 void CanvasNode::setSelected(bool s)
1200 {
1201  StoredDrawParams::setSelected(s);
1202  update();
1203 }
1204 
1205 void CanvasNode::updateGroup()
1206 {
1207  if (!_view || !_node)
1208  return;
1209 
1210  QColor c = GlobalGUIConfig::functionColor(_view->groupType(),
1211  _node->function());
1212  setBackColor(c);
1213  update();
1214 }
1215 
1216 void CanvasNode::paint(QPainter* p,
1217  const QStyleOptionGraphicsItem* option,
1218  QWidget*)
1219 {
1220  QRect r = rect().toRect(), origRect = r;
1221 
1222  r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
1223 
1224  RectDrawing d(r);
1225  d.drawBack(p, this);
1226  r.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4);
1227 
1228 #if 0
1229  if (StoredDrawParams::selected() && _view->hasFocus()) {
1230  _view->style().drawPrimitive( QStyle::PE_FocusRect, &p, r,
1231  _view->colorGroup());
1232  }
1233 #endif
1234 
1235  // draw afterwards to always get a frame even when zoomed
1236  p->setPen(StoredDrawParams::selected() ? Qt::red : Qt::black);
1237  p->drawRect(QRect(origRect.x(), origRect.y(), origRect.width()-1,
1238  origRect.height()-1));
1239 
1240 #if QT_VERSION >= 0x040600
1241  if (option->levelOfDetailFromTransform(p->transform()) < .5)
1242  return;
1243 #else
1244  if (option->levelOfDetail < .5)
1245  return;
1246 #endif
1247 
1248  d.setRect(r);
1249  d.drawField(p, 0, this);
1250  d.drawField(p, 1, this);
1251 }
1252 
1253 
1254 //
1255 // CanvasEdgeLabel
1256 //
1257 
1258 CanvasEdgeLabel::CanvasEdgeLabel(CallGraphView* v, CanvasEdge* ce, int x,
1259  int y, int w, int h) :
1260  QGraphicsRectItem(QRect(x, y, w, h)), _ce(ce), _view(v), _percentage(0.0)
1261 {
1262  GraphEdge* e = ce->edge();
1263  if (!e)
1264  return;
1265 
1266  setPosition(1, DrawParams::BottomCenter);
1267  ProfileCostArray* totalCost;
1268  if (GlobalConfig::showExpanded()) {
1269  if (_view->activeFunction()) {
1270  if (_view->activeFunction()->cycle())
1271  totalCost = _view->activeFunction()->cycle()->inclusive();
1272  else
1273  totalCost = _view->activeFunction()->inclusive();
1274  } else
1275  totalCost = (ProfileCostArray*) _view->activeItem();
1276  } else
1277  totalCost = ((TraceItemView*)_view)->data();
1278  double total = totalCost->subCost(_view->eventType());
1279  double inclP = 100.0 * e->cost/ total;
1280  if (GlobalConfig::showPercentage())
1281  setText(1, QString("%1 %")
1282  .arg(inclP, 0, 'f', GlobalConfig::percentPrecision()));
1283  else
1284  setText(1, SubCost(e->cost).pretty());
1285 
1286  setPosition(0, DrawParams::TopCenter);
1287  SubCost count((e->count < 1.0) ? 1.0 : e->count);
1288  setText(0, QString("%1 x").arg(count.pretty()));
1289  setPixmap(0, percentagePixmap(25, 10, (int)(inclP+.5), Qt::blue, true));
1290 
1291  _percentage = inclP;
1292  if (_percentage > 100.0) _percentage = 100.0;
1293 
1294  if (e->call() && (e->call()->isRecursion() || e->call()->inCycle())) {
1295  QString icon = "edit-undo";
1296 #if 0
1297  KIconLoader* loader = KIconLoader::global();
1298  QPixmap p= loader->loadIcon(icon, KIconLoader::Small, 0,
1299  KIconLoader::DefaultState, QStringList(), 0,
1300  true);
1301  setPixmap(0, p);
1302 #endif
1303  }
1304 
1305  setToolTip(QString("%1 (%2)").arg(text(0)).arg(text(1)));
1306 }
1307 
1308 void CanvasEdgeLabel::paint(QPainter* p,
1309  const QStyleOptionGraphicsItem* option, QWidget*)
1310 {
1311  // draw nothing in PanningView
1312 #if QT_VERSION >= 0x040600
1313  if (option->levelOfDetailFromTransform(p->transform()) < .5)
1314  return;
1315 #else
1316  if (option->levelOfDetail < .5)
1317  return;
1318 #endif
1319 
1320  QRect r = rect().toRect();
1321 
1322  RectDrawing d(r);
1323  d.drawField(p, 0, this);
1324  d.drawField(p, 1, this);
1325 }
1326 
1327 
1328 
1329 //
1330 // CanvasEdgeArrow
1331 
1332 CanvasEdgeArrow::CanvasEdgeArrow(CanvasEdge* ce)
1333  : _ce(ce)
1334 {}
1335 
1336 void CanvasEdgeArrow::paint(QPainter* p,
1337  const QStyleOptionGraphicsItem *, QWidget *)
1338 {
1339  p->setRenderHint(QPainter::Antialiasing);
1340  p->setBrush(_ce->isSelected() ? Qt::red : Qt::black);
1341  p->drawPolygon(polygon(), Qt::OddEvenFill);
1342 }
1343 
1344 
1345 //
1346 // CanvasEdge
1347 //
1348 
1349 CanvasEdge::CanvasEdge(GraphEdge* e) :
1350  _edge(e)
1351 {
1352  _label = 0;
1353  _arrow = 0;
1354  _thickness = 0;
1355 
1356  setFlag(QGraphicsItem::ItemIsSelectable);
1357 }
1358 
1359 void CanvasEdge::setLabel(CanvasEdgeLabel* l)
1360 {
1361  _label = l;
1362 
1363  if (l) {
1364  QString tip = QString("%1 (%2)").arg(l->text(0)).arg(l->text(1));
1365 
1366  setToolTip(tip);
1367  if (_arrow) _arrow->setToolTip(tip);
1368 
1369  _thickness = log(l->percentage());
1370  if (_thickness < .9) _thickness = .9;
1371  }
1372 }
1373 
1374 void CanvasEdge::setArrow(CanvasEdgeArrow* a)
1375 {
1376  _arrow = a;
1377 
1378  if (a && _label) a->setToolTip(QString("%1 (%2)")
1379  .arg(_label->text(0)).arg(_label->text(1)));
1380 }
1381 
1382 void CanvasEdge::setSelected(bool s)
1383 {
1384  QGraphicsItem::setSelected(s);
1385  update();
1386 }
1387 
1388 void CanvasEdge::setControlPoints(const QPolygon& pa)
1389 {
1390  _points = pa;
1391 
1392  QPainterPath path;
1393  path.moveTo(pa[0]);
1394  for (int i = 1; i < pa.size(); i += 3)
1395  path.cubicTo(pa[i], pa[(i + 1) % pa.size()], pa[(i + 2) % pa.size()]);
1396 
1397  setPath(path);
1398 }
1399 
1400 
1401 void CanvasEdge::paint(QPainter* p,
1402  const QStyleOptionGraphicsItem* option, QWidget*)
1403 {
1404  p->setRenderHint(QPainter::Antialiasing);
1405 
1406  qreal levelOfDetail;
1407 #if QT_VERSION >= 0x040600
1408  levelOfDetail = option->levelOfDetailFromTransform(p->transform());
1409 #else
1410  levelOfDetail = option->levelOfDetail;
1411 #endif
1412 
1413  QPen mypen = pen();
1414  mypen.setWidthF(1.0/levelOfDetail * _thickness);
1415  p->setPen(mypen);
1416  p->drawPath(path());
1417 
1418  if (isSelected()) {
1419  mypen.setColor(Qt::red);
1420  mypen.setWidthF(1.0/levelOfDetail * _thickness/2.0);
1421  p->setPen(mypen);
1422  p->drawPath(path());
1423  }
1424 }
1425 
1426 
1427 //
1428 // CanvasFrame
1429 //
1430 
1431 QPixmap* CanvasFrame::_p = 0;
1432 
1433 CanvasFrame::CanvasFrame(CanvasNode* n)
1434 {
1435  if (!_p) {
1436 
1437  int d = 5;
1438  float v1 = 130.0f, v2 = 10.0f, v = v1, f = 1.03f;
1439 
1440  // calculate pix size
1441  QRect r(0, 0, 30, 30);
1442  while (v>v2) {
1443  r.setRect(r.x()-d, r.y()-d, r.width()+2*d, r.height()+2*d);
1444  v /= f;
1445  }
1446 
1447  _p = new QPixmap(r.size());
1448  _p->fill(Qt::white);
1449  QPainter p(_p);
1450  p.setPen(Qt::NoPen);
1451 
1452  r.translate(-r.x(), -r.y());
1453 
1454  while (v<v1) {
1455  v *= f;
1456  p.setBrush(QColor(265-(int)v, 265-(int)v, 265-(int)v));
1457 
1458  p.drawRect(QRect(r.x(), r.y(), r.width(), d));
1459  p.drawRect(QRect(r.x(), r.bottom()-d, r.width(), d));
1460  p.drawRect(QRect(r.x(), r.y()+d, d, r.height()-2*d));
1461  p.drawRect(QRect(r.right()-d, r.y()+d, d, r.height()-2*d));
1462 
1463  r.setRect(r.x()+d, r.y()+d, r.width()-2*d, r.height()-2*d);
1464  }
1465  }
1466 
1467  setRect(QRectF(n->rect().center().x() - _p->width()/2,
1468  n->rect().center().y() - _p->height()/2, _p->width(), _p->height()) );
1469 }
1470 
1471 
1472 void CanvasFrame::paint(QPainter* p,
1473  const QStyleOptionGraphicsItem* option, QWidget*)
1474 {
1475  qreal levelOfDetail;
1476 #if QT_VERSION >= 0x040600
1477  levelOfDetail = option->levelOfDetailFromTransform(p->transform());
1478 #else
1479  levelOfDetail = option->levelOfDetail;
1480 #endif
1481  if (levelOfDetail < .5) {
1482  QRadialGradient g(rect().center(), rect().width()/3);
1483  g.setColorAt(0.0, Qt::gray);
1484  g.setColorAt(1.0, Qt::white);
1485 
1486  p->setBrush(QBrush(g));
1487  p->setPen(Qt::NoPen);
1488  p->drawRect(rect());
1489  return;
1490  }
1491 
1492  p->drawPixmap(int( rect().x()),int( rect().y()), *_p );
1493 }
1494 
1495 
1496 
1497 //
1498 // CallGraphView
1499 //
1500 CallGraphView::CallGraphView(TraceItemView* parentView, QWidget* parent,
1501  const char* name) :
1502  QGraphicsView(parent), TraceItemView(parentView)
1503 {
1504  setObjectName(name);
1505  _zoomPosition = DEFAULT_ZOOMPOS;
1506  _lastAutoPosition = TopLeft;
1507 
1508  _scene = 0;
1509  _xMargin = _yMargin = 0;
1510  _panningView = new PanningView(this);
1511  _panningZoom = 1;
1512  _selectedNode = 0;
1513  _selectedEdge = 0;
1514  _isMoving = false;
1515 
1516  _exporter.setGraphOptions(this);
1517 
1518  _panningView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1519  _panningView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1520  _panningView->raise();
1521  _panningView->hide();
1522 
1523  setFocusPolicy(Qt::StrongFocus);
1524  setAttribute(Qt::WA_NoSystemBackground, true);
1525 
1526  connect(_panningView, SIGNAL(zoomRectMoved(qreal,qreal)), this, SLOT(zoomRectMoved(qreal,qreal)));
1527  connect(_panningView, SIGNAL(zoomRectMoveFinished()), this, SLOT(zoomRectMoveFinished()));
1528 
1529  this->setWhatsThis(whatsThis() );
1530 
1531  // tooltips...
1532  //_tip = new CallGraphTip(this);
1533 
1534  _renderProcess = 0;
1535  _prevSelectedNode = 0;
1536  connect(&_renderTimer, SIGNAL(timeout()),
1537  this, SLOT(showRenderWarning()));
1538 }
1539 
1540 CallGraphView::~CallGraphView()
1541 {
1542  clear();
1543  delete _panningView;
1544 }
1545 
1546 QString CallGraphView::whatsThis() const
1547 {
1548  return tr("<b>Call Graph around active Function</b>"
1549  "<p>Depending on configuration, this view shows "
1550  "the call graph environment of the active function. "
1551  "Note: the shown cost is <b>only</b> the cost which is "
1552  "spent while the active function was actually running; "
1553  "i.e. the cost shown for main() - if it is visible - should "
1554  "be the same as the cost of the active function, as that is "
1555  "the part of inclusive cost of main() spent while the active "
1556  "function was running.</p>"
1557  "<p>For cycles, blue call arrows indicate that this is an "
1558  "artificial call added for correct drawing which "
1559  "actually never happened.</p>"
1560  "<p>If the graph is larger than the widget area, an overview "
1561  "panner is shown in one edge. "
1562  "There are similar visualization options to the "
1563  "Call Treemap; the selected function is highlighted.</p>");
1564 }
1565 
1566 void CallGraphView::updateSizes(QSize s)
1567 {
1568  if (!_scene)
1569  return;
1570 
1571  if (s == QSize(0, 0))
1572  s = size();
1573 
1574  // the part of the scene that should be visible
1575  int cWidth = (int)_scene->width() - 2*_xMargin + 100;
1576  int cHeight = (int)_scene->height() - 2*_yMargin + 100;
1577 
1578  // hide birds eye view if no overview needed
1579  if (!_data || !_activeItem ||
1580  ((cWidth < s.width()) && (cHeight < s.height())) ) {
1581  _panningView->hide();
1582  return;
1583  }
1584 
1585  // first, assume use of 1/3 of width/height (possible larger)
1586  double zoom = .33 * s.width() / cWidth;
1587  if (zoom * cHeight < .33 * s.height())
1588  zoom = .33 * s.height() / cHeight;
1589 
1590  // fit to widget size
1591  if (cWidth * zoom > s.width())
1592  zoom = s.width() / (double)cWidth;
1593  if (cHeight * zoom > s.height())
1594  zoom = s.height() / (double)cHeight;
1595 
1596  // scale to never use full height/width
1597  zoom = zoom * 3/4;
1598 
1599  // at most a zoom of 1/3
1600  if (zoom > .33)
1601  zoom = .33;
1602 
1603  if (zoom != _panningZoom) {
1604  _panningZoom = zoom;
1605  if (0)
1606  qDebug("Canvas Size: %fx%f, Content: %dx%d, Zoom: %f",
1607  _scene->width(), _scene->height(), cWidth, cHeight, zoom);
1608 
1609  QMatrix m;
1610  _panningView->setMatrix(m.scale(zoom, zoom));
1611 
1612  // make it a little bigger to compensate for widget frame
1613  _panningView->resize(int(cWidth * zoom) + 4, int(cHeight * zoom) + 4);
1614 
1615  // update ZoomRect in panningView
1616  scrollContentsBy(0, 0);
1617  }
1618 
1619  _panningView->centerOn(_scene->width()/2, _scene->height()/2);
1620 
1621  int cvW = _panningView->width();
1622  int cvH = _panningView->height();
1623  int x = width()- cvW - verticalScrollBar()->width() -2;
1624  int y = height()-cvH - horizontalScrollBar()->height() -2;
1625  QPoint oldZoomPos = _panningView->pos();
1626  QPoint newZoomPos = QPoint(0, 0);
1627  ZoomPosition zp = _zoomPosition;
1628  if (zp == Auto) {
1629  int tlCols = items(QRect(0,0, cvW,cvH)).count();
1630  int trCols = items(QRect(x,0, cvW,cvH)).count();
1631  int blCols = items(QRect(0,y, cvW,cvH)).count();
1632  int brCols = items(QRect(x,y, cvW,cvH)).count();
1633  int minCols = tlCols;
1634 
1635  zp = _lastAutoPosition;
1636  switch (zp) {
1637  case TopRight:
1638  minCols = trCols;
1639  break;
1640  case BottomLeft:
1641  minCols = blCols;
1642  break;
1643  case BottomRight:
1644  minCols = brCols;
1645  break;
1646  default:
1647  case TopLeft:
1648  minCols = tlCols;
1649  break;
1650  }
1651 
1652  if (minCols > tlCols) {
1653  minCols = tlCols;
1654  zp = TopLeft;
1655  }
1656  if (minCols > trCols) {
1657  minCols = trCols;
1658  zp = TopRight;
1659  }
1660  if (minCols > blCols) {
1661  minCols = blCols;
1662  zp = BottomLeft;
1663  }
1664  if (minCols > brCols) {
1665  minCols = brCols;
1666  zp = BottomRight;
1667  }
1668 
1669  _lastAutoPosition = zp;
1670  }
1671 
1672  switch (zp) {
1673  case TopLeft:
1674  newZoomPos = QPoint(0, 0);
1675  break;
1676  case TopRight:
1677  newZoomPos = QPoint(x, 0);
1678  break;
1679  case BottomLeft:
1680  newZoomPos = QPoint(0, y);
1681  break;
1682  case BottomRight:
1683  newZoomPos = QPoint(x, y);
1684  break;
1685  default:
1686  break;
1687  }
1688 
1689  if (newZoomPos != oldZoomPos)
1690  _panningView->move(newZoomPos);
1691 
1692  if (zp == Hide)
1693  _panningView->hide();
1694  else
1695  _panningView->show();
1696 }
1697 
1698 void CallGraphView::focusInEvent(QFocusEvent*)
1699 {
1700  if (!_scene) return;
1701 
1702  if (_selectedNode && _selectedNode->canvasNode()) {
1703  _selectedNode->canvasNode()->setSelected(true); // requests item update
1704  _scene->update();
1705  }
1706 }
1707 
1708 void CallGraphView::focusOutEvent(QFocusEvent* e)
1709 {
1710  // trigger updates as in focusInEvent
1711  focusInEvent(e);
1712 }
1713 
1714 void CallGraphView::keyPressEvent(QKeyEvent* e)
1715 {
1716  if (!_scene) {
1717  e->ignore();
1718  return;
1719  }
1720 
1721  if ((e->key() == Qt::Key_Return) ||(e->key() == Qt::Key_Space)) {
1722  if (_selectedNode)
1723  activated(_selectedNode->function());
1724  else if (_selectedEdge && _selectedEdge->call())
1725  activated(_selectedEdge->call());
1726  return;
1727  }
1728 
1729  // move selected node/edge
1730  if (!(e->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))
1731  &&(_selectedNode || _selectedEdge)&&((e->key() == Qt::Key_Up)
1732  ||(e->key() == Qt::Key_Down)||(e->key() == Qt::Key_Left)||(e->key()
1733  == Qt::Key_Right))) {
1734 
1735  TraceFunction* f = 0;
1736  TraceCall* c = 0;
1737 
1738  // rotate arrow key meaning for LeftRight layout
1739  int key = e->key();
1740  if (_layout == LeftRight) {
1741  switch (key) {
1742  case Qt::Key_Up:
1743  key = Qt::Key_Left;
1744  break;
1745  case Qt::Key_Down:
1746  key = Qt::Key_Right;
1747  break;
1748  case Qt::Key_Left:
1749  key = Qt::Key_Up;
1750  break;
1751  case Qt::Key_Right:
1752  key = Qt::Key_Down;
1753  break;
1754  default:
1755  break;
1756  }
1757  }
1758 
1759  if (_selectedNode) {
1760  if (key == Qt::Key_Up)
1761  c = _selectedNode->visibleCaller();
1762  if (key == Qt::Key_Down)
1763  c = _selectedNode->visibleCallee();
1764  if (key == Qt::Key_Right)
1765  f = _selectedNode->nextVisible();
1766  if (key == Qt::Key_Left)
1767  f = _selectedNode->priorVisible();
1768  } else if (_selectedEdge) {
1769  if (key == Qt::Key_Up)
1770  f = _selectedEdge->visibleCaller();
1771  if (key == Qt::Key_Down)
1772  f = _selectedEdge->visibleCallee();
1773  if (key == Qt::Key_Right)
1774  c = _selectedEdge->nextVisible();
1775  if (key == Qt::Key_Left)
1776  c = _selectedEdge->priorVisible();
1777  }
1778 
1779  if (c)
1780  selected(c);
1781  if (f)
1782  selected(f);
1783  return;
1784  }
1785 
1786  // move canvas...
1787  QPointF center = mapToScene(viewport()->rect().center());
1788  if (e->key() == Qt::Key_Home)
1789  centerOn(center + QPointF(-_scene->width(), 0));
1790  else if (e->key() == Qt::Key_End)
1791  centerOn(center + QPointF(_scene->width(), 0));
1792  else if (e->key() == Qt::Key_PageUp) {
1793  QPointF dy = mapToScene(0, height()) - mapToScene(0, 0);
1794  centerOn(center + QPointF(-dy.x()/2, -dy.y()/2));
1795  } else if (e->key() == Qt::Key_PageDown) {
1796  QPointF dy = mapToScene(0, height()) - mapToScene(0, 0);
1797  centerOn(center + QPointF(dy.x()/2, dy.y()/2));
1798  } else if (e->key() == Qt::Key_Left) {
1799  QPointF dx = mapToScene(width(), 0) - mapToScene(0, 0);
1800  centerOn(center + QPointF(-dx.x()/10, -dx.y()/10));
1801  } else if (e->key() == Qt::Key_Right) {
1802  QPointF dx = mapToScene(width(), 0) - mapToScene(0, 0);
1803  centerOn(center + QPointF(dx.x()/10, dx.y()/10));
1804  } else if (e->key() == Qt::Key_Down) {
1805  QPointF dy = mapToScene(0, height()) - mapToScene(0, 0);
1806  centerOn(center + QPointF(dy.x()/10, dy.y()/10));
1807  } else if (e->key() == Qt::Key_Up) {
1808  QPointF dy = mapToScene(0, height()) - mapToScene(0, 0);
1809  centerOn(center + QPointF(-dy.x()/10, -dy.y()/10));
1810  } else
1811  e->ignore();
1812 }
1813 
1814 void CallGraphView::resizeEvent(QResizeEvent* e)
1815 {
1816  QGraphicsView::resizeEvent(e);
1817  if (_scene)
1818  updateSizes(e->size());
1819 }
1820 
1821 CostItem* CallGraphView::canShow(CostItem* i)
1822 {
1823  if (i) {
1824  switch (i->type()) {
1825  case ProfileContext::Function:
1826  case ProfileContext::FunctionCycle:
1827  case ProfileContext::Call:
1828  return i;
1829  default:
1830  break;
1831  }
1832  }
1833  return 0;
1834 }
1835 
1836 void CallGraphView::doUpdate(int changeType, bool)
1837 {
1838  // Special case ?
1839  if (changeType == eventType2Changed)
1840  return;
1841 
1842  if (changeType == selectedItemChanged) {
1843  if (!_scene)
1844  return;
1845 
1846  if (!_selectedItem)
1847  return;
1848 
1849  GraphNode* n = 0;
1850  GraphEdge* e = 0;
1851  if ((_selectedItem->type() == ProfileContext::Function)
1852  ||(_selectedItem->type() == ProfileContext::FunctionCycle)) {
1853  n = _exporter.node((TraceFunction*)_selectedItem);
1854  if (n == _selectedNode)
1855  return;
1856  } else if (_selectedItem->type() == ProfileContext::Call) {
1857  TraceCall* c = (TraceCall*)_selectedItem;
1858  e = _exporter.edge(c->caller(false), c->called(false));
1859  if (e == _selectedEdge)
1860  return;
1861  }
1862 
1863  // unselect any selected item
1864  if (_selectedNode && _selectedNode->canvasNode()) {
1865  _selectedNode->canvasNode()->setSelected(false);
1866  }
1867  _selectedNode = 0;
1868  if (_selectedEdge && _selectedEdge->canvasEdge()) {
1869  _selectedEdge->canvasEdge()->setSelected(false);
1870  }
1871  _selectedEdge = 0;
1872 
1873  // select
1874  CanvasNode* sNode = 0;
1875  if (n && n->canvasNode()) {
1876  _selectedNode = n;
1877  _selectedNode->canvasNode()->setSelected(true);
1878 
1879  if (!_isMoving)
1880  sNode = _selectedNode->canvasNode();
1881  }
1882  if (e && e->canvasEdge()) {
1883  _selectedEdge = e;
1884  _selectedEdge->canvasEdge()->setSelected(true);
1885 
1886 #if 0 // do not change position when selecting edge
1887  if (!_isMoving) {
1888  if (_selectedEdge->fromNode())
1889  sNode = _selectedEdge->fromNode()->canvasNode();
1890  if (!sNode && _selectedEdge->toNode())
1891  sNode = _selectedEdge->toNode()->canvasNode();
1892  }
1893 #endif
1894  }
1895  if (sNode)
1896  ensureVisible(sNode);
1897 
1898  _scene->update();
1899  return;
1900  }
1901 
1902  if (changeType == groupTypeChanged) {
1903  if (!_scene)
1904  return;
1905 
1906  if (_clusterGroups) {
1907  refresh();
1908  return;
1909  }
1910 
1911  QList<QGraphicsItem *> l = _scene->items();
1912  for (int i = 0; i < l.size(); ++i)
1913  if (l[i]->type() == CANVAS_NODE)
1914  ((CanvasNode*)l[i])->updateGroup();
1915 
1916  _scene->update();
1917  return;
1918  }
1919 
1920  if (changeType & dataChanged) {
1921  // invalidate old selection and graph part
1922  _exporter.reset(_data, _activeItem, _eventType, _groupType);
1923  _selectedNode = 0;
1924  _selectedEdge = 0;
1925  }
1926 
1927  refresh();
1928 }
1929 
1930 void CallGraphView::clear()
1931 {
1932  if (!_scene)
1933  return;
1934 
1935  _panningView->setScene(0);
1936  setScene(0);
1937  delete _scene;
1938  _scene = 0;
1939 }
1940 
1941 void CallGraphView::showText(QString s)
1942 {
1943  clear();
1944  _renderTimer.stop();
1945 
1946  _scene = new QGraphicsScene;
1947 
1948  _scene->addSimpleText(s);
1949  centerOn(0, 0);
1950  setScene(_scene);
1951  _scene->update();
1952  _panningView->hide();
1953 }
1954 
1955 void CallGraphView::showRenderWarning()
1956 {
1957  QString s;
1958 
1959  if (_renderProcess)
1960  s = tr("Warning: a long lasting graph layouting is in progress.\n"
1961  "Reduce node/edge limits for speedup.\n");
1962  else
1963  s = tr("Layouting stopped.\n");
1964 
1965  s.append(tr("The call graph has %1 nodes and %2 edges.\n")
1966  .arg(_exporter.nodeCount()).arg(_exporter.edgeCount()));
1967 
1968  showText(s);
1969 }
1970 
1971 void CallGraphView::showRenderError(QString s)
1972 {
1973  QString err;
1974  err = tr("No graph available because the layouting process failed.\n");
1975  if (_renderProcess)
1976  err += tr("Trying to run the following command did not work:\n"
1977  "'%1'\n").arg(_renderProcessCmdLine);
1978  err += tr("Please check that 'dot' is installed (package GraphViz).");
1979 
1980  if (!s.isEmpty())
1981  err += QString("\n\n%1").arg(s);
1982 
1983  showText(err);
1984 }
1985 
1986 void CallGraphView::stopRendering()
1987 {
1988  if (!_renderProcess)
1989  return;
1990 
1991  qDebug("CallGraphView::stopRendering: Killing QProcess %p",
1992  _renderProcess);
1993 
1994  _renderProcess->kill();
1995 
1996  // forget about this process, not interesting any longer
1997  _renderProcess->deleteLater();
1998  _renderProcess = 0;
1999  _unparsedOutput = QString();
2000 
2001  _renderTimer.setSingleShot(true);
2002  _renderTimer.start(200);
2003 }
2004 
2005 void CallGraphView::refresh()
2006 {
2007  // trigger start of new layouting via 'dot'
2008  if (_renderProcess)
2009  stopRendering();
2010 
2011  // we want to keep a selected node item at the same global position
2012  _prevSelectedNode = _selectedNode;
2013  _prevSelectedPos = QPoint(-1, -1);
2014  if (_selectedNode) {
2015  QPointF center = _selectedNode->canvasNode()->rect().center();
2016  _prevSelectedPos = mapFromScene(center);
2017  }
2018 
2019  if (!_data || !_activeItem) {
2020  showText(tr("No item activated for which to "
2021  "draw the call graph."));
2022  return;
2023  }
2024 
2025  ProfileContext::Type t = _activeItem->type();
2026  switch (t) {
2027  case ProfileContext::Function:
2028  case ProfileContext::FunctionCycle:
2029  case ProfileContext::Call:
2030  break;
2031  default:
2032  showText(tr("No call graph can be drawn for "
2033  "the active item."));
2034  return;
2035  }
2036 
2037  if (1)
2038  qDebug() << "CallGraphView::refresh";
2039 
2040  _selectedNode = 0;
2041  _selectedEdge = 0;
2042 
2043  /*
2044  * Call 'dot' asynchronoulsy in the background with the aim to
2045  * - have responsive GUI while layout task runs (potentially long!)
2046  * - notify user about a long run, using a timer
2047  * - kill long running 'dot' processes when another layout is
2048  * requested, as old data is not needed any more
2049  *
2050  * Even after killing a process, the QProcess needs some time
2051  * to make sure the process is destroyed; also, stdout data
2052  * still can be delivered after killing. Thus, there can/should be
2053  * multiple QProcess's at one time.
2054  * The QProcess we currently wait for data from is <_renderProcess>
2055  * Signals from other QProcesses are ignored with the exception of
2056  * the finished() signal, which triggers QProcess destruction.
2057  */
2058  QString renderProgram;
2059  QStringList renderArgs;
2060  if (_layout == GraphOptions::Circular)
2061  renderProgram = "twopi";
2062  else
2063  renderProgram = "dot";
2064  renderArgs << "-Tplain";
2065 
2066  _unparsedOutput = QString();
2067 
2068  // display warning if layouting takes > 1s
2069  _renderTimer.setSingleShot(true);
2070  _renderTimer.start(1000);
2071 
2072  _renderProcess = new QProcess(this);
2073  connect(_renderProcess, SIGNAL(readyReadStandardOutput()),
2074  this, SLOT(readDotOutput()));
2075  connect(_renderProcess, SIGNAL(error(QProcess::ProcessError)),
2076  this, SLOT(dotError()));
2077  connect(_renderProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
2078  this, SLOT(dotExited()));
2079 
2080  _renderProcessCmdLine = renderProgram + " " + renderArgs.join(" ");
2081  qDebug("CallGraphView::refresh: Starting process %p, '%s'",
2082  _renderProcess, qPrintable(_renderProcessCmdLine));
2083 
2084  // _renderProcess can be set to 0 on error after start().
2085  // thus, we use a local copy afterwards
2086  QProcess* p = _renderProcess;
2087  p->start(renderProgram, renderArgs);
2088  _exporter.reset(_data, _activeItem, _eventType, _groupType);
2089  _exporter.writeDot(p);
2090  p->closeWriteChannel();
2091 }
2092 
2093 void CallGraphView::readDotOutput()
2094 {
2095  QProcess* p = qobject_cast<QProcess*>(sender());
2096  qDebug("CallGraphView::readDotOutput: QProcess %p", p);
2097 
2098  // signal from old/uninteresting process?
2099  if ((_renderProcess == 0) || (p != _renderProcess)) {
2100  p->deleteLater();
2101  return;
2102  }
2103 
2104  _unparsedOutput.append(_renderProcess->readAllStandardOutput());
2105 }
2106 
2107 void CallGraphView::dotError()
2108 {
2109  QProcess* p = qobject_cast<QProcess*>(sender());
2110  qDebug("CallGraphView::dotError: Got %d from QProcess %p",
2111  p->error(), p);
2112 
2113  // signal from old/uninteresting process?
2114  if ((_renderProcess == 0) || (p != _renderProcess)) {
2115  p->deleteLater();
2116  return;
2117  }
2118 
2119  showRenderError(_renderProcess->readAllStandardError());
2120 
2121  // not interesting any longer
2122  _renderProcess->deleteLater();
2123  _renderProcess = 0;
2124 }
2125 
2126 
2127 void CallGraphView::dotExited()
2128 {
2129  QProcess* p = qobject_cast<QProcess*>(sender());
2130  qDebug("CallGraphView::dotExited: QProcess %p", p);
2131 
2132  // signal from old/uninteresting process?
2133  if ((_renderProcess == 0) || (p != _renderProcess)) {
2134  p->deleteLater();
2135  return;
2136  }
2137 
2138  _unparsedOutput.append(_renderProcess->readAllStandardOutput());
2139  _renderProcess->deleteLater();
2140  _renderProcess = 0;
2141 
2142  QString line, cmd;
2143  CanvasNode *rItem;
2144  QGraphicsEllipseItem* eItem;
2145  CanvasEdge* sItem;
2146  CanvasEdgeLabel* lItem;
2147  QTextStream* dotStream;
2148  double scale = 1.0, scaleX = 1.0, scaleY = 1.0;
2149  double dotWidth = 0, dotHeight = 0;
2150  GraphNode* activeNode = 0;
2151  GraphEdge* activeEdge = 0;
2152 
2153  _renderTimer.stop();
2154  viewport()->setUpdatesEnabled(false);
2155  clear();
2156  dotStream = new QTextStream(&_unparsedOutput, QIODevice::ReadOnly);
2157 
2158  // First pass to adjust coordinate scaling by node height given from dot
2159  // Normal detail level (=1) should be 3 lines using general KDE font
2160  double nodeHeight = 0.0;
2161  while(1) {
2162  line = dotStream->readLine();
2163  if (line.isNull()) break;
2164  if (line.isEmpty()) continue;
2165  QTextStream lineStream(&line, QIODevice::ReadOnly);
2166  lineStream >> cmd;
2167  if (cmd != "node") continue;
2168  QString s, h;
2169  lineStream >> s /*name*/ >> s /*x*/ >> s /*y*/ >> s /*width*/ >> h /*height*/;
2170  nodeHeight = h.toDouble();
2171  break;
2172  }
2173  if (nodeHeight > 0.0) {
2174  scaleY = (8 + (1 + 2 * _detailLevel) * fontMetrics().height()) / nodeHeight;
2175  scaleX = 80;
2176  }
2177  dotStream->seek(0);
2178  int lineno = 0;
2179  while (1) {
2180  line = dotStream->readLine();
2181  if (line.isNull())
2182  break;
2183 
2184  lineno++;
2185  if (line.isEmpty())
2186  continue;
2187 
2188  QTextStream lineStream(&line, QIODevice::ReadOnly);
2189  lineStream >> cmd;
2190 
2191  if (0)
2192  qDebug("%s:%d - line '%s', cmd '%s'",
2193  qPrintable(_exporter.filename()),
2194  lineno, qPrintable(line), qPrintable(cmd));
2195 
2196  if (cmd == "stop")
2197  break;
2198 
2199  if (cmd == "graph") {
2200  QString dotWidthString, dotHeightString;
2201  // scale will not be used
2202  lineStream >> scale >> dotWidthString >> dotHeightString;
2203  dotWidth = dotWidthString.toDouble();
2204  dotHeight = dotHeightString.toDouble();
2205 
2206  if (!_scene) {
2207  int w = (int)(scaleX * dotWidth);
2208  int h = (int)(scaleY * dotHeight);
2209 
2210  // We use as minimum canvas size the desktop size.
2211  // Otherwise, the canvas would have to be resized on widget resize.
2212  _xMargin = 50;
2213  if (w < QApplication::desktop()->width())
2214  _xMargin += (QApplication::desktop()->width()-w)/2;
2215 
2216  _yMargin = 50;
2217  if (h < QApplication::desktop()->height())
2218  _yMargin += (QApplication::desktop()->height()-h)/2;
2219 
2220  _scene = new QGraphicsScene( 0.0, 0.0,
2221  qreal(w+2*_xMargin), qreal(h+2*_yMargin));
2222  // Change background color for call graph from default system color to
2223  // white. It has to blend into the gradient for the selected function.
2224  _scene->setBackgroundBrush(Qt::white);
2225 
2226 #if DEBUG_GRAPH
2227  qDebug() << qPrintable(_exporter.filename()) << ":" << lineno
2228  << " - graph (" << dotWidth << " x " << dotHeight
2229  << ") => (" << w << " x " << h << ")";
2230 #endif
2231  } else
2232  qDebug() << "Ignoring 2nd 'graph' from dot ("
2233  << _exporter.filename() << ":"<< lineno << ")";
2234  continue;
2235  }
2236 
2237  if ((cmd != "node") && (cmd != "edge")) {
2238  qDebug() << "Ignoring unknown command '"<< cmd
2239  << "' from dot ("<< _exporter.filename() << ":"<< lineno
2240  << ")";
2241  continue;
2242  }
2243 
2244  if (_scene == 0) {
2245  qDebug() << "Ignoring '"<< cmd
2246  << "' without 'graph' from dot ("<< _exporter.filename()
2247  << ":"<< lineno << ")";
2248  continue;
2249  }
2250 
2251  if (cmd == "node") {
2252  // x, y are centered in node
2253  QString nodeName, label, nodeX, nodeY, nodeWidth, nodeHeight;
2254  double x, y, width, height;
2255  lineStream >> nodeName >> nodeX >> nodeY >> nodeWidth
2256  >> nodeHeight;
2257  x = nodeX.toDouble();
2258  y = nodeY.toDouble();
2259  width = nodeWidth.toDouble();
2260  height = nodeHeight.toDouble();
2261 
2262  GraphNode* n = _exporter.node(_exporter.toFunc(nodeName));
2263 
2264  int xx = (int)(scaleX * x + _xMargin);
2265  int yy = (int)(scaleY * (dotHeight - y)+ _yMargin);
2266  int w = (int)(scaleX * width);
2267  int h = (int)(scaleY * height);
2268 
2269 #if DEBUG_GRAPH
2270  qDebug() << _exporter.filename() << ":" << lineno
2271  << " - node '" << nodeName << "' ( "
2272  << x << "/" << y << " - "
2273  << width << "x" << height << " ) => ("
2274  << xx-w/2 << "/" << yy-h/2 << " - "
2275  << w << "x" << h << ")" << endl;
2276 #endif
2277 
2278  // Unnamed nodes with collapsed edges (with 'R' and 'S')
2279  if (nodeName[0] == 'R'|| nodeName[0] == 'S') {
2280  w = 10, h = 10;
2281  eItem = new QGraphicsEllipseItem( QRectF(xx-w/2, yy-h/2, w, h) );
2282  _scene->addItem(eItem);
2283  eItem->setBrush(Qt::gray);
2284  eItem->setZValue(1.0);
2285  eItem->show();
2286  continue;
2287  }
2288 
2289  if (!n) {
2290  qDebug("Warning: Unknown function '%s' ?!",
2291  qPrintable(nodeName));
2292  continue;
2293  }
2294  n->setVisible(true);
2295 
2296  rItem = new CanvasNode(this, n, xx-w/2, yy-h/2, w, h);
2297  // limit symbol space to a maximal number of lines depending on detail level
2298  if (_detailLevel>0) rItem->setMaxLines(0, 2*_detailLevel);
2299  _scene->addItem(rItem);
2300  n->setCanvasNode(rItem);
2301 
2302  if (n) {
2303  if (n->function() == activeItem())
2304  activeNode = n;
2305  if (n->function() == selectedItem())
2306  _selectedNode = n;
2307  rItem->setSelected(n == _selectedNode);
2308  }
2309 
2310  rItem->setZValue(1.0);
2311  rItem->show();
2312 
2313  continue;
2314  }
2315 
2316  // edge
2317 
2318  QString node1Name, node2Name, label, edgeX, edgeY;
2319  double x, y;
2320  QPolygon poly;
2321  int points, i;
2322  lineStream >> node1Name >> node2Name >> points;
2323 
2324  GraphEdge* e = _exporter.edge(_exporter.toFunc(node1Name),
2325  _exporter.toFunc(node2Name));
2326  if (!e) {
2327  qDebug() << "Unknown edge '"<< node1Name << "'-'"<< node2Name
2328  << "' from dot ("<< _exporter.filename() << ":"<< lineno
2329  << ")";
2330  continue;
2331  }
2332  e->setVisible(true);
2333  if (e->fromNode())
2334  e->fromNode()->addCallee(e);
2335  if (e->toNode())
2336  e->toNode()->addCaller(e);
2337 
2338  if (0)
2339  qDebug(" Edge with %d points:", points);
2340 
2341  poly.resize(points);
2342  for (i=0; i<points; i++) {
2343  if (lineStream.atEnd())
2344  break;
2345  lineStream >> edgeX >> edgeY;
2346  x = edgeX.toDouble();
2347  y = edgeY.toDouble();
2348 
2349  int xx = (int)(scaleX * x + _xMargin);
2350  int yy = (int)(scaleY * (dotHeight - y)+ _yMargin);
2351 
2352  if (0)
2353  qDebug(" P %d: ( %f / %f ) => ( %d / %d)", i, x, y, xx, yy);
2354 
2355  poly.setPoint(i, xx, yy);
2356  }
2357  if (i < points) {
2358  qDebug("CallGraphView: Can not read %d spline points (%s:%d)",
2359  points, qPrintable(_exporter.filename()), lineno);
2360  continue;
2361  }
2362 
2363  // calls into/out of cycles are special: make them blue
2364  QColor arrowColor = Qt::black;
2365  TraceFunction* caller = e->fromNode() ? e->fromNode()->function() : 0;
2366  TraceFunction* called = e->toNode() ? e->toNode()->function() : 0;
2367  if ( (caller && (caller->cycle() == caller)) ||
2368  (called && (called->cycle() == called)) ) arrowColor = Qt::blue;
2369 
2370  sItem = new CanvasEdge(e);
2371  _scene->addItem(sItem);
2372  e->setCanvasEdge(sItem);
2373  sItem->setControlPoints(poly);
2374  // width of pen will be adjusted in CanvasEdge::paint()
2375  sItem->setPen(QPen(arrowColor));
2376  sItem->setZValue(0.5);
2377  sItem->show();
2378 
2379  if (e->call() == selectedItem())
2380  _selectedEdge = e;
2381  if (e->call() == activeItem())
2382  activeEdge = e;
2383  sItem->setSelected(e == _selectedEdge);
2384 
2385  // Arrow head
2386  QPoint arrowDir;
2387  int indexHead = -1;
2388 
2389  // check if head is at start of spline...
2390  // this is needed because dot always gives points from top to bottom
2391  CanvasNode* fromNode = e->fromNode() ? e->fromNode()->canvasNode() : 0;
2392  if (fromNode) {
2393  QPointF toCenter = fromNode->rect().center();
2394  qreal dx0 = poly.point(0).x() - toCenter.x();
2395  qreal dy0 = poly.point(0).y() - toCenter.y();
2396  qreal dx1 = poly.point(points-1).x() - toCenter.x();
2397  qreal dy1 = poly.point(points-1).y() - toCenter.y();
2398  if (dx0*dx0+dy0*dy0 > dx1*dx1+dy1*dy1) {
2399  // start of spline is nearer to call target node
2400  indexHead=-1;
2401  while (arrowDir.isNull() && (indexHead<points-2)) {
2402  indexHead++;
2403  arrowDir = poly.point(indexHead) - poly.point(indexHead+1);
2404  }
2405  }
2406  }
2407 
2408  if (arrowDir.isNull()) {
2409  indexHead = points;
2410  // sometimes the last spline points from dot are the same...
2411  while (arrowDir.isNull() && (indexHead>1)) {
2412  indexHead--;
2413  arrowDir = poly.point(indexHead) - poly.point(indexHead-1);
2414  }
2415  }
2416 
2417  if (!arrowDir.isNull()) {
2418  // arrow around pa.point(indexHead) with direction arrowDir
2419  arrowDir *= 10.0/sqrt(double(arrowDir.x()*arrowDir.x() +
2420  arrowDir.y()*arrowDir.y()));
2421 
2422  QPolygonF a;
2423  a << QPointF(poly.point(indexHead) + arrowDir);
2424  a << QPointF(poly.point(indexHead) + QPoint(arrowDir.y()/2,
2425  -arrowDir.x()/2));
2426  a << QPointF(poly.point(indexHead) + QPoint(-arrowDir.y()/2,
2427  arrowDir.x()/2));
2428 
2429  if (0)
2430  qDebug(" Arrow: ( %f/%f, %f/%f, %f/%f)", a[0].x(), a[0].y(),
2431  a[1].x(), a[1].y(), a[2].x(), a[1].y());
2432 
2433  CanvasEdgeArrow* aItem = new CanvasEdgeArrow(sItem);
2434  _scene->addItem(aItem);
2435  aItem->setPolygon(a);
2436  aItem->setBrush(arrowColor);
2437  aItem->setZValue(1.5);
2438  aItem->show();
2439 
2440  sItem->setArrow(aItem);
2441  }
2442 
2443  if (lineStream.atEnd())
2444  continue;
2445 
2446  // parse quoted label
2447  QChar c;
2448  lineStream >> c;
2449  while (c.isSpace())
2450  lineStream >> c;
2451  if (c != '\"') {
2452  lineStream >> label;
2453  label = c + label;
2454  } else {
2455  lineStream >> c;
2456  while (!c.isNull() && (c != '\"')) {
2457  //if (c == '\\') lineStream >> c;
2458 
2459  label += c;
2460  lineStream >> c;
2461  }
2462  }
2463  lineStream >> edgeX >> edgeY;
2464  x = edgeX.toDouble();
2465  y = edgeY.toDouble();
2466 
2467  int xx = (int)(scaleX * x + _xMargin);
2468  int yy = (int)(scaleY * (dotHeight - y)+ _yMargin);
2469 
2470  if (0)
2471  qDebug(" Label '%s': ( %f / %f ) => ( %d / %d)",
2472  qPrintable(label), x, y, xx, yy);
2473 
2474  // Fixed Dimensions for Label: 100 x 40
2475  int w = 100;
2476  int h = _detailLevel * 20;
2477  lItem = new CanvasEdgeLabel(this, sItem, xx-w/2, yy-h/2, w, h);
2478  _scene->addItem(lItem);
2479  // edge labels above nodes
2480  lItem->setZValue(1.5);
2481  sItem->setLabel(lItem);
2482  if (h>0)
2483  lItem->show();
2484 
2485  }
2486  delete dotStream;
2487 
2488  // for keyboard navigation
2489  _exporter.sortEdges();
2490 
2491  if (!_scene) {
2492  _scene = new QGraphicsScene;
2493 
2494  QString s = tr("Error running the graph layouting tool.\n");
2495  s += tr("Please check that 'dot' is installed (package GraphViz).");
2496  _scene->addSimpleText(s);
2497  centerOn(0, 0);
2498  } else if (!activeNode && !activeEdge) {
2499  QString s = tr("There is no call graph available for function\n"
2500  "\t'%1'\n"
2501  "because it has no cost of the selected event type.")
2502  .arg(_activeItem->name());
2503  _scene->addSimpleText(s);
2504  centerOn(0, 0);
2505  }
2506 
2507  _panningView->setScene(_scene);
2508  setScene(_scene);
2509 
2510  // if we do not have a selection, or the old selection is not
2511  // in visible graph, make active function selected for this view
2512  if ((!_selectedNode || !_selectedNode->canvasNode()) &&
2513  (!_selectedEdge || !_selectedEdge->canvasEdge())) {
2514  if (activeNode) {
2515  _selectedNode = activeNode;
2516  _selectedNode->canvasNode()->setSelected(true);
2517  } else if (activeEdge) {
2518  _selectedEdge = activeEdge;
2519  _selectedEdge->canvasEdge()->setSelected(true);
2520  }
2521  }
2522 
2523  CanvasNode* sNode = 0;
2524  if (_selectedNode)
2525  sNode = _selectedNode->canvasNode();
2526  else if (_selectedEdge) {
2527  if (_selectedEdge->fromNode())
2528  sNode = _selectedEdge->fromNode()->canvasNode();
2529  if (!sNode && _selectedEdge->toNode())
2530  sNode = _selectedEdge->toNode()->canvasNode();
2531  }
2532 
2533  if (sNode) {
2534  if (_prevSelectedNode) {
2535  QPointF prevPos = mapToScene(_prevSelectedPos);
2536  if (rect().contains(_prevSelectedPos)) {
2537  QPointF wCenter = mapToScene(viewport()->rect().center());
2538  centerOn(sNode->rect().center() +
2539  wCenter - prevPos);
2540  }
2541  else
2542  ensureVisible(sNode);
2543  } else
2544  centerOn(sNode);
2545  }
2546 
2547  if (activeNode) {
2548  CanvasNode* cn = activeNode->canvasNode();
2549  CanvasFrame* f = new CanvasFrame(cn);
2550  _scene->addItem(f);
2551  f->setPos(cn->pos());
2552  f->setZValue(-1);
2553  }
2554 
2555  _panningZoom = 0;
2556  updateSizes();
2557 
2558  _scene->update();
2559  viewport()->setUpdatesEnabled(true);
2560 
2561  delete _renderProcess;
2562  _renderProcess = 0;
2563 }
2564 
2565 
2566 // Called by QAbstractScrollArea to notify about scrollbar changes
2567 void CallGraphView::scrollContentsBy(int dx, int dy)
2568 {
2569  // call QGraphicsView implementation
2570  QGraphicsView::scrollContentsBy(dx, dy);
2571 
2572  QPointF topLeft = mapToScene(QPoint(0, 0));
2573  QPointF bottomRight = mapToScene(QPoint(width(), height()));
2574 
2575  QRectF z(topLeft, bottomRight);
2576  if (0)
2577  qDebug("CallGraphView::scrollContentsBy(dx %d, dy %d) - to (%f,%f - %f,%f)",
2578  dx, dy, topLeft.x(), topLeft.y(),
2579  bottomRight.x(), bottomRight.y());
2580  _panningView->setZoomRect(z);
2581 }
2582 
2583 void CallGraphView::zoomRectMoved(qreal dx, qreal dy)
2584 {
2585  //FIXME if (leftMargin()>0) dx = 0;
2586  //FIXME if (topMargin()>0) dy = 0;
2587 
2588  QScrollBar *hBar = horizontalScrollBar();
2589  QScrollBar *vBar = verticalScrollBar();
2590  hBar->setValue(hBar->value() + int(dx));
2591  vBar->setValue(vBar->value() + int(dy));
2592 }
2593 
2594 void CallGraphView::zoomRectMoveFinished()
2595 {
2596  if (_zoomPosition == Auto)
2597  updateSizes();
2598 }
2599 
2600 void CallGraphView::mousePressEvent(QMouseEvent* e)
2601 {
2602  // clicking on the viewport sets focus
2603  setFocus();
2604 
2605  // activate scroll mode on left click
2606  if (e->button() == Qt::LeftButton) _isMoving = true;
2607 
2608  QGraphicsItem* i = itemAt(e->pos());
2609  if (i) {
2610 
2611  if (i->type() == CANVAS_NODE) {
2612  GraphNode* n = ((CanvasNode*)i)->node();
2613  if (0)
2614  qDebug("CallGraphView: Got Node '%s'",
2615  qPrintable(n->function()->prettyName()));
2616 
2617  selected(n->function());
2618  }
2619 
2620  // redirect from label / arrow to edge
2621  if (i->type() == CANVAS_EDGELABEL)
2622  i = ((CanvasEdgeLabel*)i)->canvasEdge();
2623  if (i->type() == CANVAS_EDGEARROW)
2624  i = ((CanvasEdgeArrow*)i)->canvasEdge();
2625 
2626  if (i->type() == CANVAS_EDGE) {
2627  GraphEdge* e = ((CanvasEdge*)i)->edge();
2628  if (0)
2629  qDebug("CallGraphView: Got Edge '%s'",
2630  qPrintable(e->prettyName()));
2631 
2632  if (e->call())
2633  selected(e->call());
2634  }
2635  }
2636  _lastPos = e->pos();
2637 }
2638 
2639 void CallGraphView::mouseMoveEvent(QMouseEvent* e)
2640 {
2641  if (_isMoving) {
2642  QPoint delta = e->pos() - _lastPos;
2643 
2644  QScrollBar *hBar = horizontalScrollBar();
2645  QScrollBar *vBar = verticalScrollBar();
2646  hBar->setValue(hBar->value() - delta.x());
2647  vBar->setValue(vBar->value() - delta.y());
2648 
2649  _lastPos = e->pos();
2650  }
2651 }
2652 
2653 void CallGraphView::mouseReleaseEvent(QMouseEvent*)
2654 {
2655  _isMoving = false;
2656  if (_zoomPosition == Auto)
2657  updateSizes();
2658 }
2659 
2660 void CallGraphView::mouseDoubleClickEvent(QMouseEvent* e)
2661 {
2662  QGraphicsItem* i = itemAt(e->pos());
2663  if (i == 0)
2664  return;
2665 
2666  if (i->type() == CANVAS_NODE) {
2667  GraphNode* n = ((CanvasNode*)i)->node();
2668  if (0)
2669  qDebug("CallGraphView: Double Clicked on Node '%s'",
2670  qPrintable(n->function()->prettyName()));
2671 
2672  activated(n->function());
2673  }
2674 
2675  // redirect from label / arrow to edge
2676  if (i->type() == CANVAS_EDGELABEL)
2677  i = ((CanvasEdgeLabel*)i)->canvasEdge();
2678  if (i->type() == CANVAS_EDGEARROW)
2679  i = ((CanvasEdgeArrow*)i)->canvasEdge();
2680 
2681  if (i->type() == CANVAS_EDGE) {
2682  GraphEdge* e = ((CanvasEdge*)i)->edge();
2683  if (e->call()) {
2684  if (0)
2685  qDebug("CallGraphView: Double Clicked On Edge '%s'",
2686  qPrintable(e->call()->prettyName()));
2687 
2688  activated(e->call());
2689  }
2690  }
2691 }
2692 
2693 // helper functions for context menu:
2694 // submenu builders and trigger handlers
2695 
2696 QAction* CallGraphView::addCallerDepthAction(QMenu* m, QString s, int d)
2697 {
2698  QAction* a;
2699 
2700  a = m->addAction(s);
2701  a->setData(d);
2702  a->setCheckable(true);
2703  a->setChecked(_maxCallerDepth == d);
2704 
2705  return a;
2706 }
2707 
2708 QMenu* CallGraphView::addCallerDepthMenu(QMenu* menu)
2709 {
2710  QAction* a;
2711  QMenu* m;
2712 
2713  m = menu->addMenu(tr("Caller Depth"));
2714  a = addCallerDepthAction(m, tr("Unlimited"), -1);
2715  a->setEnabled(_funcLimit>0.005);
2716  m->addSeparator();
2717  addCallerDepthAction(m, tr("Depth 0", "None"), 0);
2718  addCallerDepthAction(m, tr("max. 2"), 2);
2719  addCallerDepthAction(m, tr("max. 5"), 5);
2720  addCallerDepthAction(m, tr("max. 10"), 10);
2721  addCallerDepthAction(m, tr("max. 15"), 15);
2722 
2723  connect(m, SIGNAL(triggered(QAction*)),
2724  this, SLOT(callerDepthTriggered(QAction*)) );
2725 
2726  return m;
2727 }
2728 
2729 void CallGraphView::callerDepthTriggered(QAction* a)
2730 {
2731  _maxCallerDepth = a->data().toInt(0);
2732  refresh();
2733 }
2734 
2735 QAction* CallGraphView::addCalleeDepthAction(QMenu* m, QString s, int d)
2736 {
2737  QAction* a;
2738 
2739  a = m->addAction(s);
2740  a->setData(d);
2741  a->setCheckable(true);
2742  a->setChecked(_maxCalleeDepth == d);
2743 
2744  return a;
2745 }
2746 
2747 QMenu* CallGraphView::addCalleeDepthMenu(QMenu* menu)
2748 {
2749  QAction* a;
2750  QMenu* m;
2751 
2752  m = menu->addMenu(tr("Callee Depth"));
2753  a = addCalleeDepthAction(m, tr("Unlimited"), -1);
2754  a->setEnabled(_funcLimit>0.005);
2755  m->addSeparator();
2756  addCalleeDepthAction(m, tr("Depth 0", "None"), 0);
2757  addCalleeDepthAction(m, tr("max. 2"), 2);
2758  addCalleeDepthAction(m, tr("max. 5"), 5);
2759  addCalleeDepthAction(m, tr("max. 10"), 10);
2760  addCalleeDepthAction(m, tr("max. 15"), 15);
2761 
2762  connect(m, SIGNAL(triggered(QAction*)),
2763  this, SLOT(calleeDepthTriggered(QAction*)) );
2764 
2765  return m;
2766 }
2767 
2768 void CallGraphView::calleeDepthTriggered(QAction* a)
2769 {
2770  _maxCalleeDepth = a->data().toInt(0);
2771  refresh();
2772 }
2773 
2774 QAction* CallGraphView::addNodeLimitAction(QMenu* m, QString s, double l)
2775 {
2776  QAction* a;
2777 
2778  a = m->addAction(s);
2779  a->setData(l);
2780  a->setCheckable(true);
2781  a->setChecked(_funcLimit == l);
2782 
2783  return a;
2784 }
2785 
2786 QMenu* CallGraphView::addNodeLimitMenu(QMenu* menu)
2787 {
2788  QAction* a;
2789  QMenu* m;
2790 
2791  m = menu->addMenu(tr("Min. Node Cost"));
2792  a = addNodeLimitAction(m, tr("No Minimum"), 0.0);
2793  // Unlimited node cost easily produces huge graphs such that 'dot'
2794  // would need a long time to layout. For responsiveness, we only allow
2795  // for unlimited node cost if a caller and callee depth limit is set.
2796  a->setEnabled((_maxCallerDepth>=0) && (_maxCalleeDepth>=0));
2797  m->addSeparator();
2798  addNodeLimitAction(m, tr("50 %"), .5);
2799  addNodeLimitAction(m, tr("20 %"), .2);
2800  addNodeLimitAction(m, tr("10 %"), .1);
2801  addNodeLimitAction(m, tr("5 %"), .05);
2802  addNodeLimitAction(m, tr("2 %"), .02);
2803  addNodeLimitAction(m, tr("1 %"), .01);
2804 
2805  connect(m, SIGNAL(triggered(QAction*)),
2806  this, SLOT(nodeLimitTriggered(QAction*)) );
2807 
2808  return m;
2809 }
2810 
2811 void CallGraphView::nodeLimitTriggered(QAction* a)
2812 {
2813  _funcLimit = a->data().toDouble(0);
2814  refresh();
2815 }
2816 
2817 QAction* CallGraphView::addCallLimitAction(QMenu* m, QString s, double l)
2818 {
2819  QAction* a;
2820 
2821  a = m->addAction(s);
2822  a->setData(l);
2823  a->setCheckable(true);
2824  a->setChecked(_callLimit == l);
2825 
2826  return a;
2827 }
2828 
2829 QMenu* CallGraphView::addCallLimitMenu(QMenu* menu)
2830 {
2831  QMenu* m;
2832 
2833  m = menu->addMenu(tr("Min. Call Cost"));
2834  addCallLimitAction(m, tr("Same as Node"), 1.0);
2835  // xgettext: no-c-format
2836  addCallLimitAction(m, tr("50 % of Node"), .5);
2837  // xgettext: no-c-format
2838  addCallLimitAction(m, tr("20 % of Node"), .2);
2839  // xgettext: no-c-format
2840  addCallLimitAction(m, tr("10 % of Node"), .1);
2841 
2842  connect(m, SIGNAL(triggered(QAction*)),
2843  this, SLOT(callLimitTriggered(QAction*)) );
2844 
2845  return m;
2846 }
2847 
2848 void CallGraphView::callLimitTriggered(QAction* a)
2849 {
2850  _callLimit = a->data().toDouble(0);
2851  refresh();
2852 }
2853 
2854 QAction* CallGraphView::addZoomPosAction(QMenu* m, QString s,
2855  CallGraphView::ZoomPosition p)
2856 {
2857  QAction* a;
2858 
2859  a = m->addAction(s);
2860  a->setData((int)p);
2861  a->setCheckable(true);
2862  a->setChecked(_zoomPosition == p);
2863 
2864  return a;
2865 }
2866 
2867 QMenu* CallGraphView::addZoomPosMenu(QMenu* menu)
2868 {
2869  QMenu* m = menu->addMenu(tr("Birds-eye View"));
2870  addZoomPosAction(m, tr("Top Left"), TopLeft);
2871  addZoomPosAction(m, tr("Top Right"), TopRight);
2872  addZoomPosAction(m, tr("Bottom Left"), BottomLeft);
2873  addZoomPosAction(m, tr("Bottom Right"), BottomRight);
2874  addZoomPosAction(m, tr("Automatic"), Auto);
2875  addZoomPosAction(m, tr("Hide"), Hide);
2876 
2877  connect(m, SIGNAL(triggered(QAction*)),
2878  this, SLOT(zoomPosTriggered(QAction*)) );
2879 
2880  return m;
2881 }
2882 
2883 
2884 void CallGraphView::zoomPosTriggered(QAction* a)
2885 {
2886  _zoomPosition = (ZoomPosition) a->data().toInt(0);
2887  updateSizes();
2888 }
2889 
2890 QAction* CallGraphView::addLayoutAction(QMenu* m, QString s,
2891  GraphOptions::Layout l)
2892 {
2893  QAction* a;
2894 
2895  a = m->addAction(s);
2896  a->setData((int)l);
2897  a->setCheckable(true);
2898  a->setChecked(_layout == l);
2899 
2900  return a;
2901 }
2902 
2903 QMenu* CallGraphView::addLayoutMenu(QMenu* menu)
2904 {
2905  QMenu* m = menu->addMenu(tr("Layout"));
2906  addLayoutAction(m, tr("Top to Down"), TopDown);
2907  addLayoutAction(m, tr("Left to Right"), LeftRight);
2908  addLayoutAction(m, tr("Circular"), Circular);
2909 
2910  connect(m, SIGNAL(triggered(QAction*)),
2911  this, SLOT(layoutTriggered(QAction*)) );
2912 
2913  return m;
2914 }
2915 
2916 
2917 void CallGraphView::layoutTriggered(QAction* a)
2918 {
2919  _layout = (Layout) a->data().toInt(0);
2920  refresh();
2921 }
2922 
2923 
2924 void CallGraphView::contextMenuEvent(QContextMenuEvent* e)
2925 {
2926  _isMoving = false;
2927 
2928  QGraphicsItem* i = itemAt(e->pos());
2929 
2930  QMenu popup;
2931  TraceFunction *f = 0, *cycle = 0;
2932  TraceCall* c = 0;
2933 
2934  QAction* activateFunction = 0;
2935  QAction* activateCycle = 0;
2936  QAction* activateCall = 0;
2937  if (i) {
2938  if (i->type() == CANVAS_NODE) {
2939  GraphNode* n = ((CanvasNode*)i)->node();
2940  if (0)
2941  qDebug("CallGraphView: Menu on Node '%s'",
2942  qPrintable(n->function()->prettyName()));
2943 
2944  f = n->function();
2945  cycle = f->cycle();
2946 
2947  QString name = f->prettyName();
2948  QString menuStr = tr("Go to '%1'")
2949  .arg(GlobalConfig::shortenSymbol(name));
2950  activateFunction = popup.addAction(menuStr);
2951  if (cycle && (cycle != f)) {
2952  name = GlobalConfig::shortenSymbol(cycle->prettyName());
2953  activateCycle = popup.addAction(tr("Go to '%1'").arg(name));
2954  }
2955  popup.addSeparator();
2956  }
2957 
2958  // redirect from label / arrow to edge
2959  if (i->type() == CANVAS_EDGELABEL)
2960  i = ((CanvasEdgeLabel*)i)->canvasEdge();
2961  if (i->type() == CANVAS_EDGEARROW)
2962  i = ((CanvasEdgeArrow*)i)->canvasEdge();
2963 
2964  if (i->type() == CANVAS_EDGE) {
2965  GraphEdge* e = ((CanvasEdge*)i)->edge();
2966  if (0)
2967  qDebug("CallGraphView: Menu on Edge '%s'",
2968  qPrintable(e->prettyName()));
2969 
2970  c = e->call();
2971  if (c) {
2972  QString name = c->prettyName();
2973  QString menuStr = tr("Go to '%1'")
2974  .arg(GlobalConfig::shortenSymbol(name));
2975  activateCall = popup.addAction(menuStr);
2976 
2977  popup.addSeparator();
2978  }
2979  }
2980  }
2981 
2982  QAction* stopLayout = 0;
2983  if (_renderProcess) {
2984  stopLayout = popup.addAction(tr("Stop Layouting"));
2985  popup.addSeparator();
2986  }
2987 
2988  addGoMenu(&popup);
2989  popup.addSeparator();
2990 
2991  QMenu* epopup = popup.addMenu(tr("Export Graph"));
2992  QAction* exportAsDot = epopup->addAction(tr("As DOT file..."));
2993  QAction* exportAsImage = epopup->addAction(tr("As Image..."));
2994  popup.addSeparator();
2995 
2996  QMenu* gpopup = popup.addMenu(tr("Graph"));
2997  addCallerDepthMenu(gpopup);
2998  addCalleeDepthMenu(gpopup);
2999  addNodeLimitMenu(gpopup);
3000  addCallLimitMenu(gpopup);
3001  gpopup->addSeparator();
3002 
3003  QAction* toggleSkipped;
3004  toggleSkipped = gpopup->addAction(tr("Arrows for Skipped Calls"));
3005  toggleSkipped->setCheckable(true);
3006  toggleSkipped->setChecked(_showSkipped);
3007 
3008  QAction* toggleExpand;
3009  toggleExpand = gpopup->addAction(tr("Inner-cycle Calls"));
3010  toggleExpand->setCheckable(true);
3011  toggleExpand->setChecked(_expandCycles);
3012 
3013  QAction* toggleCluster;
3014  toggleCluster = gpopup->addAction(tr("Cluster Groups"));
3015  toggleCluster->setCheckable(true);
3016  toggleCluster->setChecked(_clusterGroups);
3017 
3018  QMenu* vpopup = popup.addMenu(tr("Visualization"));
3019  QAction* layoutCompact = vpopup->addAction(tr("Compact"));
3020  layoutCompact->setCheckable(true);
3021  layoutCompact->setChecked(_detailLevel == 0);
3022  QAction* layoutNormal = vpopup->addAction(tr("Normal"));
3023  layoutNormal->setCheckable(true);
3024  layoutNormal->setChecked(_detailLevel == 1);
3025  QAction* layoutTall = vpopup->addAction(tr("Tall"));
3026  layoutTall->setCheckable(true);
3027  layoutTall->setChecked(_detailLevel == 2);
3028 
3029  addLayoutMenu(&popup);
3030  addZoomPosMenu(&popup);
3031 
3032  QAction* a = popup.exec(e->globalPos());
3033 
3034  if (a == activateFunction)
3035  activated(f);
3036  else if (a == activateCycle)
3037  activated(cycle);
3038  else if (a == activateCall)
3039  activated(c);
3040 
3041  else if (a == stopLayout)
3042  stopRendering();
3043 
3044  else if (a == exportAsDot) {
3045  TraceFunction* f = activeFunction();
3046  if (!f) return;
3047 
3048  QString n;
3049  n = QFileDialog::getSaveFileName(this,
3050  tr("Export Graph As DOT file"),
3051  QString(), tr("Graphviz (*.dot)"));
3052 
3053  if (!n.isEmpty()) {
3054  GraphExporter ge(TraceItemView::data(), f, eventType(),
3055  groupType(), n);
3056  ge.setGraphOptions(this);
3057  ge.writeDot();
3058  }
3059  }
3060  else if (a == exportAsImage) {
3061  // write current content of canvas as image to file
3062  if (!_scene) return;
3063 
3064  QString n;
3065  n = QFileDialog::getSaveFileName(this,
3066  tr("Export Graph As Image"),
3067  QString(),
3068  tr("Images (*.png *.jpg)"));
3069 
3070  if (!n.isEmpty()) {
3071  QRect r = _scene->sceneRect().toRect();
3072  QPixmap pix(r.width(), r.height());
3073  QPainter p(&pix);
3074  _scene->render( &p );
3075  pix.save(n);
3076  }
3077  }
3078 
3079  else if (a == toggleSkipped) {
3080  _showSkipped = !_showSkipped;
3081  refresh();
3082  }
3083  else if (a == toggleExpand) {
3084  _expandCycles = !_expandCycles;
3085  refresh();
3086  }
3087  else if (a == toggleCluster) {
3088  _clusterGroups = !_clusterGroups;
3089  refresh();
3090  }
3091 
3092  else if (a == layoutCompact) {
3093  _detailLevel = 0;
3094  refresh();
3095  }
3096  else if (a == layoutNormal) {
3097  _detailLevel = 1;
3098  refresh();
3099  }
3100  else if (a == layoutTall) {
3101  _detailLevel = 2;
3102  refresh();
3103  }
3104 }
3105 
3106 
3107 CallGraphView::ZoomPosition CallGraphView::zoomPos(QString s)
3108 {
3109  if (s == QString("TopLeft"))
3110  return TopLeft;
3111  if (s == QString("TopRight"))
3112  return TopRight;
3113  if (s == QString("BottomLeft"))
3114  return BottomLeft;
3115  if (s == QString("BottomRight"))
3116  return BottomRight;
3117  if (s == QString("Automatic"))
3118  return Auto;
3119  if (s == QString("Hide"))
3120  return Hide;
3121 
3122  return DEFAULT_ZOOMPOS;
3123 }
3124 
3125 QString CallGraphView::zoomPosString(ZoomPosition p)
3126 {
3127  if (p == TopLeft)
3128  return QString("TopLeft");
3129  if (p == TopRight)
3130  return QString("TopRight");
3131  if (p == BottomLeft)
3132  return QString("BottomLeft");
3133  if (p == BottomRight)
3134  return QString("BottomRight");
3135  if (p == Auto)
3136  return QString("Automatic");
3137  if (p == Hide)
3138  return QString("Hide");
3139 
3140  return QString();
3141 }
3142 
3143 void CallGraphView::restoreOptions(const QString& prefix, const QString& postfix)
3144 {
3145  ConfigGroup* g = ConfigStorage::group(prefix, postfix);
3146 
3147  _maxCallerDepth = g->value("MaxCaller", DEFAULT_MAXCALLER).toInt();
3148  _maxCalleeDepth = g->value("MaxCallee", DEFAULT_MAXCALLEE).toInt();
3149  _funcLimit = g->value("FuncLimit", DEFAULT_FUNCLIMIT).toDouble();
3150  _callLimit = g->value("CallLimit", DEFAULT_CALLLIMIT).toDouble();
3151  _showSkipped = g->value("ShowSkipped", DEFAULT_SHOWSKIPPED).toBool();
3152  _expandCycles = g->value("ExpandCycles", DEFAULT_EXPANDCYCLES).toBool();
3153  _clusterGroups = g->value("ClusterGroups", DEFAULT_CLUSTERGROUPS).toBool();
3154  _detailLevel = g->value("DetailLevel", DEFAULT_DETAILLEVEL).toInt();
3155  _layout = GraphOptions::layout(g->value("Layout",
3156  layoutString(DEFAULT_LAYOUT)).toString());
3157  _zoomPosition = zoomPos(g->value("ZoomPosition",
3158  zoomPosString(DEFAULT_ZOOMPOS)).toString());
3159 
3160  delete g;
3161 }
3162 
3163 void CallGraphView::saveOptions(const QString& prefix, const QString& postfix)
3164 {
3165  ConfigGroup* g = ConfigStorage::group(prefix + postfix);
3166 
3167  g->setValue("MaxCaller", _maxCallerDepth, DEFAULT_MAXCALLER);
3168  g->setValue("MaxCallee", _maxCalleeDepth, DEFAULT_MAXCALLEE);
3169  g->setValue("FuncLimit", _funcLimit, DEFAULT_FUNCLIMIT);
3170  g->setValue("CallLimit", _callLimit, DEFAULT_CALLLIMIT);
3171  g->setValue("ShowSkipped", _showSkipped, DEFAULT_SHOWSKIPPED);
3172  g->setValue("ExpandCycles", _expandCycles, DEFAULT_EXPANDCYCLES);
3173  g->setValue("ClusterGroups", _clusterGroups, DEFAULT_CLUSTERGROUPS);
3174  g->setValue("DetailLevel", _detailLevel, DEFAULT_DETAILLEVEL);
3175  g->setValue("Layout", layoutString(_layout), layoutString(DEFAULT_LAYOUT));
3176  g->setValue("ZoomPosition", zoomPosString(_zoomPosition),
3177  zoomPosString(DEFAULT_ZOOMPOS));
3178 
3179  delete g;
3180 }
3181 
3182 #include "callgraphview.moc"
3183 
QIODevice
GraphOptions::detailLevel
virtual int detailLevel()=0
StoredDrawParams::setSelected
void setSelected(bool b)
Definition: treemap.h:126
TraceItemView::selectedItemChanged
Definition: traceitemview.h:86
QList::clear
void clear()
TraceCall::isRecursion
bool isRecursion()
Definition: tracedata.h:844
QGraphicsItem::setSelected
void setSelected(bool selected)
RectDrawing::drawField
bool drawField(QPainter *, int f, DrawParams *dp=0)
Definition: treemap.cpp:468
QResizeEvent
QGraphicsItem::x
qreal x() const
QGraphicsItem::y
qreal y() const
QWidget
TraceItemView::_selectedItem
CostItem * _selectedItem
Definition: traceitemview.h:211
GraphOptions::layout
virtual Layout layout()=0
QKeyEvent::modifiers
Qt::KeyboardModifiers modifiers() const
PanningView
A panner laid over a QGraphicsScene.
Definition: callgraphview.h:378
GlobalConfig::showPercentage
static bool showPercentage()
Definition: globalconfig.cpp:328
GraphNode::callerCostSum
double callerCostSum()
Definition: callgraphview.cpp:209
QFile::seek
virtual bool seek(qint64 pos)
QString::append
QString & append(QChar ch)
QRect::size
QSize size() const
DEFAULT_EXPANDCYCLES
#define DEFAULT_EXPANDCYCLES
Definition: callgraphview.cpp:66
GraphEdge::setCallerNode
void setCallerNode(GraphNode *n)
Definition: callgraphview.h:201
CallGraphView::Auto
Definition: callgraphview.h:580
ProfileCostArray::subCost
SubCost subCost(EventType *)
Returns a sub cost.
Definition: costitem.cpp:591
GraphExporter::GraphExporter
GraphExporter()
Definition: callgraphview.cpp:530
QProcess::kill
void kill()
QGraphicsScene
QSize::width
int width() const
CanvasEdge::setArrow
void setArrow(CanvasEdgeArrow *a)
Definition: callgraphview.cpp:1374
StorableGraphOptions::_maxCallerDepth
int _maxCallerDepth
Definition: callgraphview.h:286
ProfileContext::FunctionCycle
Definition: context.h:46
CallGraphView::dotExited
void dotExited()
Definition: callgraphview.cpp:2127
QRectF::toRect
QRect toRect() const
TraceItemView::eventType
EventType * eventType() const
Definition: traceitemview.h:151
GraphExporter::edge
GraphEdge * edge(TraceFunction *, TraceFunction *)
Definition: callgraphview.cpp:935
GraphNode::priorVisibleCaller
TraceCall * priorVisibleCaller(GraphEdge *=0)
Definition: callgraphview.cpp:366
QPixmap::width
int width() const
QGraphicsView::items
QList< QGraphicsItem * > items() const
QGraphicsScene::render
void render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode)
GraphOptions::maxCallerDepth
virtual int maxCallerDepth()=0
QGraphicsView::setMatrix
void setMatrix(const QMatrix &matrix, bool combine)
PanningView::PanningView
PanningView(QWidget *parent=0)
Definition: callgraphview.cpp:1090
QPainter::setRenderHint
void setRenderHint(RenderHint hint, bool on)
GraphEdge::visibleCallee
TraceFunction * visibleCallee()
Definition: callgraphview.cpp:438
GraphNode::addUniqueCaller
void addUniqueCaller(GraphEdge *)
Definition: callgraphview.cpp:177
QGraphicsView::centerOn
void centerOn(const QPointF &pos)
QTextStream::readLine
QString readLine(qint64 maxlen)
CanvasEdge::paint
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
Definition: callgraphview.cpp:1401
QPixmap::fill
void fill(const QColor &color)
CanvasEdge::setControlPoints
void setControlPoints(const QPolygon &a)
Definition: callgraphview.cpp:1388
GraphExporter::~GraphExporter
virtual ~GraphExporter()
Definition: callgraphview.cpp:548
QGraphicsItem::setFlag
void setFlag(GraphicsItemFlag flag, bool enabled)
QRect::right
int right() const
CallGraphView::showRenderWarning
void showRenderWarning()
Definition: callgraphview.cpp:1955
CallGraphView::zoomPosString
static QString zoomPosString(ZoomPosition)
Definition: callgraphview.cpp:3125
CallGraphView::BottomLeft
Definition: callgraphview.h:580
StorableGraphOptions::_expandCycles
bool _expandCycles
Definition: callgraphview.h:287
GraphEdge::call
TraceCall * call() const
Definition: callgraphview.h:148
RectDrawing::drawBack
void drawBack(QPainter *, DrawParams *dp=0)
Definition: treemap.cpp:253
QRectF::x
qreal x() const
QRectF::y
qreal y() const
ProfileContext::File
Definition: context.h:48
GraphEdge::canvasEdge
CanvasEdge * canvasEdge() const
Definition: callgraphview.h:138
TraceFunction::callings
const TraceCallList & callings(bool skipCycle=false) const
Definition: tracedata.cpp:2302
GraphExporter::node
GraphNode * node(TraceFunction *)
Definition: callgraphview.cpp:923
GraphExporter::setGraphOptions
void setGraphOptions(GraphOptions *go=0)
Definition: callgraphview.cpp:604
DEFAULT_CLUSTERGROUPS
#define DEFAULT_CLUSTERGROUPS
Definition: callgraphview.cpp:67
QGraphicsScene::items
QList< QGraphicsItem * > items() const
QProcess::error
QProcess::ProcessError error() const
CanvasEdgeArrow
Definition: callgraphview.h:473
CanvasEdgeLabel::paint
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
Definition: callgraphview.cpp:1308
QWidget::setFocusPolicy
void setFocusPolicy(Qt::FocusPolicy policy)
QGradient::setColorAt
void setColorAt(qreal position, const QColor &color)
CANVAS_NODE
Definition: callgraphview.h:413
QObject::sender
QObject * sender() const
QChar
TraceCall::caller
TraceFunction * caller(bool skipCycle=false) const
Definition: tracedata.cpp:1230
QAction::setChecked
void setChecked(bool)
CostItem::type
ProfileContext::Type type() const
Definition: costitem.h:45
CallGraphView::readDotOutput
void readDotOutput()
Definition: callgraphview.cpp:2093
QAction::data
QVariant data() const
CallGraphView::zoomPos
ZoomPosition zoomPos() const
Definition: callgraphview.h:596
CanvasEdgeLabel::CanvasEdgeLabel
CanvasEdgeLabel(CallGraphView *, CanvasEdge *, int, int, int, int)
Definition: callgraphview.cpp:1258
GlobalGUIConfig::functionColor
static QColor functionColor(ProfileContext::Type gt, TraceFunction *)
Definition: globalguiconfig.cpp:205
QWidget::style
QStyle * style() const
GraphEdge::setCanvasEdge
void setCanvasEdge(CanvasEdge *ce)
Definition: callgraphview.h:143
GraphOptions::clusterGroups
virtual bool clusterGroups()=0
QGraphicsView::mapToScene
QPointF mapToScene(const QPoint &point) const
QMap
GraphNode::addCaller
void addCaller(GraphEdge *)
Definition: callgraphview.cpp:165
TraceFunction
A traced function.
Definition: tracedata.h:1122
TraceItemView::_data
TraceData * _data
Definition: traceitemview.h:209
QGraphicsScene::setBackgroundBrush
void setBackgroundBrush(const QBrush &brush)
TraceCostItem
Definition: tracedata.h:980
QMenu::addAction
void addAction(QAction *action)
CanvasEdgeLabel::percentage
double percentage() const
Definition: callgraphview.h:460
QGraphicsPolygonItem::polygon
QPolygonF polygon() const
GraphNode::callerCountSum
double callerCountSum()
Definition: callgraphview.cpp:219
QWidget::y
int y() const
TraceItemView::addGoMenu
void addGoMenu(QMenu *)
Definition: traceitemview.cpp:471
QRect::translate
void translate(int dx, int dy)
TraceCallCost::callCount
SubCost callCount()
Definition: tracedata.cpp:125
TraceFunction::object
TraceObject * object() const
Definition: tracedata.h:1164
ConfigGroup::setValue
virtual void setValue(const QString &key, const QVariant &value, const QVariant &defaultValue=QVariant())
Definition: config.cpp:57
QGraphicsScene::sceneRect
sceneRect
QScrollBar
PanningView::mousePressEvent
void mousePressEvent(QMouseEvent *)
Definition: callgraphview.cpp:1123
QPainterPath::cubicTo
void cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
QWidget::setAttribute
void setAttribute(Qt::WidgetAttribute attribute, bool on)
QPainter::transform
const QTransform & transform() const
QPainter::drawPolygon
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
TraceFunction::cycle
TraceFunctionCycle * cycle()
Definition: tracedata.h:1200
GraphExporter
GraphExporter.
Definition: callgraphview.h:298
QAbstractScrollArea::viewport
QWidget * viewport() const
QPainterPath::moveTo
void moveTo(const QPointF &point)
ProfileContext::Call
Definition: context.h:44
CostItem
Base class for cost items.
Definition: costitem.h:37
config.h
QRect::height
int height() const
GraphNode::priorVisible
TraceFunction * priorVisible()
Definition: callgraphview.cpp:316
QBrush
StoredDrawParams::setPixmap
void setPixmap(int f, const QPixmap &)
Definition: treemap.cpp:155
QGraphicsItem
PanningView::_movingZoomRect
bool _movingZoomRect
Definition: callgraphview.h:398
QRect::x
int x() const
QRect::y
int y() const
QWidget::hasFocus
bool hasFocus() const
QPoint
QMouseEvent
QStringList::join
QString join(const QString &separator) const
CallGraphView::Hide
Definition: callgraphview.h:580
CallGraphView::zoomPosTriggered
void zoomPosTriggered(QAction *)
Definition: callgraphview.cpp:2884
CallGraphView::contextMenuEvent
void contextMenuEvent(QContextMenuEvent *)
Definition: callgraphview.cpp:2924
QGraphicsScene::height
qreal height() const
GraphEdge::setCallee
void setCallee(TraceFunction *f)
Definition: callgraphview.h:196
CallGraphView::calleeDepthTriggered
void calleeDepthTriggered(QAction *)
Definition: callgraphview.cpp:2768
QMap::clear
void clear()
QString::toDouble
double toDouble(bool *ok) const
CallGraphView::mouseReleaseEvent
void mouseReleaseEvent(QMouseEvent *)
Definition: callgraphview.cpp:2653
CallGraphView::keyPressEvent
void keyPressEvent(QKeyEvent *)
Definition: callgraphview.cpp:1714
TraceItemView::activeFunction
TraceFunction * activeFunction()
Definition: traceitemview.cpp:123
QContextMenuEvent::globalPos
const QPoint & globalPos() const
GraphExporter::sortEdges
void sortEdges()
Definition: callgraphview.cpp:902
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
CanvasFrame
Definition: callgraphview.h:545
StoredDrawParams::setMaxLines
void setMaxLines(int f, int)
Definition: treemap.cpp:171
TraceItemView::_groupType
ProfileContext::Type _groupType
Definition: traceitemview.h:213
GraphNode::visibleCallee
TraceCall * visibleCallee()
Definition: callgraphview.cpp:256
CallGraphView::mousePressEvent
void mousePressEvent(QMouseEvent *)
Definition: callgraphview.cpp:2600
percentagePixmap
QPixmap percentagePixmap(int w, int h, int percent, QColor c, bool framed)
Create a percentage pixmap with a filling rate of p percent (0-100).
Definition: listutils.cpp:72
CallGraphView::callerDepthTriggered
void callerDepthTriggered(QAction *)
Definition: callgraphview.cpp:2729
TraceFunction::callers
TraceCallList callers(bool skipCycle=false) const
Definition: tracedata.cpp:2278
QPolygon::setPoint
void setPoint(int index, int x, int y)
DrawParams::BottomCenter
Definition: treemap.h:68
QFile
QWidget::update
void update()
EventType
A cost type, e.g.
Definition: eventtype.h:43
QGraphicsItem::update
void update(const QRectF &rect)
ProfileContext::Class
Definition: context.h:47
QPoint::x
int x() const
QPoint::y
int y() const
QTextStream
StoredDrawParams::setBackColor
void setBackColor(const QColor &c)
Definition: treemap.h:125
QColor::dark
QColor dark(int factor) const
QPolygon
QList::size
int size() const
QGraphicsRectItem::setRect
void setRect(const QRectF &rectangle)
QGraphicsItem::type
virtual int type() const
ConfigStorage::group
static ConfigGroup * group(const QString &group, const QString &optSuffix=QString())
Definition: config.cpp:80
QString::isNull
bool isNull() const
TraceItemView
Abstract Base Class for KCachegrind Views.
Definition: traceitemview.h:70
CallGraphView::stopRendering
void stopRendering()
Definition: callgraphview.cpp:1986
GraphNode::nextVisibleCaller
TraceCall * nextVisibleCaller(GraphEdge *=0)
Definition: callgraphview.cpp:338
QPointF
GraphNode::calleeCountSum
double calleeCountSum()
Definition: callgraphview.cpp:199
QList::value
T value(int i) const
QList::indexOf
int indexOf(const T &value, int from) const
TraceFunction::cls
TraceClass * cls() const
Definition: tracedata.h:1162
DEFAULT_MAXCALLER
#define DEFAULT_MAXCALLER
Definition: callgraphview.cpp:63
QWidget::width
width
RectDrawing::setRect
void setRect(const QRect &)
Definition: treemap.cpp:214
GraphExporter::createGraph
void createGraph()
Definition: callgraphview.cpp:611
QWidget::size
QSize size() const
StoredDrawParams::selected
bool selected() const
Definition: treemap.h:110
QPainter::drawRect
void drawRect(const QRectF &rectangle)
QGraphicsView::scale
void scale(qreal sx, qreal sy)
GraphEdge::setCaller
void setCaller(TraceFunction *f)
Definition: callgraphview.h:191
CanvasEdge::controlPoints
const QPolygon & controlPoints() const
Definition: callgraphview.h:518
TraceItemView::selectedItem
CostItem * selectedItem() const
Definition: traceitemview.h:150
GraphEdge::from
TraceFunction * from() const
Definition: callgraphview.h:178
TraceItemView::_eventType
EventType * _eventType
Definition: traceitemview.h:212
QObject::name
const char * name() const
PanningView::_lastPos
QPointF _lastPos
Definition: callgraphview.h:399
QRect
GraphOptions::maxCalleeDepth
virtual int maxCalleeDepth()=0
QGraphicsItem::pos
QPointF pos() const
ProfileCostArray
An array of basic cost metrics for a trace item.
Definition: costitem.h:144
StorableGraphOptions::_showSkipped
bool _showSkipped
Definition: callgraphview.h:287
QList::count
int count(const T &value) const
DEFAULT_ZOOMPOS
#define DEFAULT_ZOOMPOS
Definition: callgraphview.cpp:70
GraphOptions::funcLimit
virtual double funcLimit()=0
QPointF::x
qreal x() const
QPointF::y
qreal y() const
QList::append
void append(const T &value)
DEFAULT_DETAILLEVEL
#define DEFAULT_DETAILLEVEL
Definition: callgraphview.cpp:68
GraphNode::visibleCaller
TraceCall * visibleCaller()
Definition: callgraphview.cpp:230
QEvent::ignore
void ignore()
TraceItemView::dataChanged
Definition: traceitemview.h:87
QVector::resize
void resize(int size)
QTextStream::atEnd
bool atEnd() const
GraphEdge::priorVisible
TraceCall * priorVisible()
Definition: callgraphview.cpp:465
CANVAS_EDGEARROW
Definition: callgraphview.h:414
CanvasNode::CanvasNode
CanvasNode(CallGraphView *, GraphNode *, int, int, int, int)
Definition: callgraphview.cpp:1161
QVariant::toInt
int toInt(bool *ok) const
QWidget::x
int x() const
CanvasEdgeArrow::CanvasEdgeArrow
CanvasEdgeArrow(CanvasEdge *)
Definition: callgraphview.cpp:1332
GraphNode::function
TraceFunction * function() const
Definition: callgraphview.h:64
CostItem::name
virtual QString name() const
Returns dynamic name info (without type)
Definition: costitem.cpp:53
QProcess
QContextMenuEvent
QWidget::setUpdatesEnabled
void setUpdatesEnabled(bool enable)
QPainter::setPen
void setPen(const QColor &color)
CallGraphView::nodeLimitTriggered
void nodeLimitTriggered(QAction *)
Definition: callgraphview.cpp:2811
QWidget::setFocus
void setFocus()
QGraphicsItem::isSelected
bool isSelected() const
QGraphicsItem::setPos
void setPos(const QPointF &pos)
listutils.h
QMouseEvent::button
Qt::MouseButton button() const
CANVAS_EDGE
Definition: callgraphview.h:414
QTemporaryFile::setAutoRemove
void setAutoRemove(bool b)
GraphEdge::GraphEdge
GraphEdge()
Definition: callgraphview.cpp:401
QAbstractScrollArea::setHorizontalScrollBarPolicy
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy)
QPainter::drawPixmap
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
callerGraphEdgeLessThan
CallerGraphEdgeLessThan callerGraphEdgeLessThan
Definition: callgraphview.cpp:150
QGraphicsScene::addSimpleText
QGraphicsSimpleTextItem * addSimpleText(const QString &text, const QFont &font)
CallGraphView::whatsThis
QString whatsThis() const
Definition: callgraphview.cpp:1546
QPainter
QGraphicsItem::setToolTip
void setToolTip(const QString &toolTip)
QObject::setObjectName
void setObjectName(const QString &name)
GraphNode::setCallee
void setCallee(GraphEdge *)
Definition: callgraphview.cpp:282
QWidget::raise
void raise()
QString::isEmpty
bool isEmpty() const
GraphNode::GraphNode
GraphNode()
Definition: callgraphview.cpp:132
QList::removeAll
int removeAll(const T &value)
GraphOptions::layoutString
static QString layoutString(Layout)
Definition: callgraphview.cpp:487
GraphEdge::toNode
GraphNode * toNode() const
Definition: callgraphview.h:173
CallGraphView::resizeEvent
void resizeEvent(QResizeEvent *)
Definition: callgraphview.cpp:1814
StoredDrawParams::text
QString text(int) const
Definition: treemap.cpp:79
GraphNode::addCallee
void addCallee(GraphEdge *)
Definition: callgraphview.cpp:159
QGraphicsView::itemAt
QGraphicsItem * itemAt(const QPoint &pos) const
StorableGraphOptions::_clusterGroups
bool _clusterGroups
Definition: callgraphview.h:287
CanvasEdge::setSelected
void setSelected(bool)
Definition: callgraphview.cpp:1382
QPen::setWidthF
void setWidthF(qreal width)
QWidget::pos
pos
GraphNode::calleeCostSum
double calleeCostSum()
Definition: callgraphview.cpp:189
QPainter::setBrush
void setBrush(const QBrush &brush)
QGraphicsView::setScene
void setScene(QGraphicsScene *scene)
GraphEdge::setCalleeNode
void setCalleeNode(GraphNode *n)
Definition: callgraphview.h:206
StorableGraphOptions::_maxCalleeDepth
int _maxCalleeDepth
Definition: callgraphview.h:286
TraceFunction::prettyName
QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
Definition: tracedata.cpp:1889
QGraphicsView::mapFromScene
QPoint mapFromScene(const QPointF &point) const
CostItem::data
virtual TraceData * data()
Definition: costitem.cpp:111
QRectF::center
QPointF center() const
StorableGraphOptions::_detailLevel
int _detailLevel
Definition: callgraphview.h:288
GraphOptions::TopDown
Definition: callgraphview.h:240
GraphNode::priorVisibleCallee
TraceCall * priorVisibleCallee(GraphEdge *=0)
Definition: callgraphview.cpp:381
CallGraphView::CallGraphView
CallGraphView(TraceItemView *parentView, QWidget *parent=0, const char *name=0)
Definition: callgraphview.cpp:1500
GraphNode::nextVisibleCallee
TraceCall * nextVisibleCallee(GraphEdge *=0)
Definition: callgraphview.cpp:352
GraphNode::setFunction
void setFunction(TraceFunction *f)
Definition: callgraphview.h:69
StorableGraphOptions::StorableGraphOptions
StorableGraphOptions()
Definition: callgraphview.cpp:510
QObject::deleteLater
void deleteLater()
TraceItemView::_activeItem
CostItem * _activeItem
Definition: traceitemview.h:211
QMenu::addSeparator
QAction * addSeparator()
QString
QList
QWidget::hide
void hide()
CallGraphView::showRenderError
void showRenderError(QString)
Definition: callgraphview.cpp:1971
QMap::end
iterator end()
QColor
DEFAULT_MAXCALLEE
#define DEFAULT_MAXCALLEE
Definition: callgraphview.cpp:64
GraphEdge::cost
double cost
Definition: callgraphview.h:217
QString::toULongLong
qulonglong toULongLong(bool *ok, int base) const
GraphEdge::count
double count
Definition: callgraphview.h:217
DEFAULT_LAYOUT
#define DEFAULT_LAYOUT
Definition: callgraphview.cpp:69
QPen::setColor
void setColor(const QColor &color)
globalguiconfig.h
QAbstractScrollArea::verticalScrollBar
QScrollBar * verticalScrollBar() const
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
GlobalConfig::shortenSymbol
static QString shortenSymbol(const QString &)
Definition: globalconfig.cpp:395
TraceCall::inCycle
int inCycle()
Definition: tracedata.cpp:1202
ConfigGroup
A group of configuration settings.
Definition: config.h:35
PanningView::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *)
Definition: callgraphview.cpp:1137
QMap::begin
iterator begin()
TraceFunction::file
TraceFile * file() const
Definition: tracedata.h:1163
QStringList
QWidget::rect
QRect rect() const
GraphNode::addUniqueCallee
void addUniqueCallee(GraphEdge *)
Definition: callgraphview.cpp:171
QPair
QPixmap
GraphNode::removeEdge
void removeEdge(GraphEdge *)
Definition: callgraphview.cpp:183
QAction::setData
void setData(const QVariant &userData)
QList::end
iterator end()
TraceItemView::selected
virtual void selected(TraceItemView *sender, CostItem *)
Notification from child views.
Definition: traceitemview.cpp:319
QKeyEvent::key
int key() const
QGraphicsView::matrix
QMatrix matrix() const
TraceItemView::data
TraceData * data() const
Definition: traceitemview.h:148
QPolygon::point
void point(int index, int *x, int *y) const
QMenu
GraphNode::setCanvasNode
void setCanvasNode(CanvasNode *cn)
Definition: callgraphview.h:79
QResizeEvent::size
const QSize & size() const
QSize
CanvasEdge
Definition: callgraphview.h:495
StorableGraphOptions::_funcLimit
double _funcLimit
Definition: callgraphview.h:285
QPixmap::height
int height() const
TraceItemView::activated
virtual void activated(TraceItemView *sender, CostItem *)
Definition: traceitemview.cpp:347
QAbstractSlider::setValue
void setValue(int)
QTimer::stop
void stop()
QFile::close
virtual void close()
QAction::setCheckable
void setCheckable(bool)
GraphNode::sortEdges
void sortEdges()
Definition: callgraphview.cpp:153
GraphExporter::filename
QString filename()
Definition: callgraphview.h:311
GraphOptions
Definition: callgraphview.h:237
QTemporaryFile::fileName
QString fileName() const
CanvasNode
Definition: callgraphview.h:418
CallGraphView::mouseDoubleClickEvent
void mouseDoubleClickEvent(QMouseEvent *)
Definition: callgraphview.cpp:2660
GraphOptions::callLimit
virtual double callLimit()=0
QMap::key
const Key key(const T &value) const
calleeGraphEdgeLessThan
CalleeGraphEdgeLessThan calleeGraphEdgeLessThan
Definition: callgraphview.cpp:151
QRectF::isValid
bool isValid() const
CanvasEdge::setLabel
void setLabel(CanvasEdgeLabel *l)
Definition: callgraphview.cpp:1359
QKeyEvent
GraphNode::nextVisible
TraceFunction * nextVisible()
Definition: callgraphview.cpp:294
QGraphicsView::ensureVisible
void ensureVisible(const QRectF &rect, int xmargin, int ymargin)
GraphOptions::Layout
Layout
Definition: callgraphview.h:240
QWidget::setWhatsThis
void setWhatsThis(const QString &)
QPainterPath
QContextMenuEvent::pos
const QPoint & pos() const
QPoint::isNull
bool isNull() const
GraphExporter::writeDot
void writeDot(QIODevice *=0)
Definition: callgraphview.cpp:661
QRect::width
int width() const
QRectF::width
qreal width() const
GlobalConfig::showExpanded
static bool showExpanded()
Definition: globalconfig.cpp:333
QString::mid
QString mid(int position, int n) const
QWidget::fontMetrics
QFontMetrics fontMetrics() const
QPainter::drawPath
void drawPath(const QPainterPath &path)
CallGraphView::ZoomPosition
ZoomPosition
Definition: callgraphview.h:580
CanvasEdge::edge
GraphEdge * edge()
Definition: callgraphview.h:525
CallGraphView::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *)
Definition: callgraphview.cpp:2639
QAbstractGraphicsShapeItem::setPen
void setPen(const QPen &pen)
TraceItemView::activeItem
CostItem * activeItem() const
Definition: traceitemview.h:149
QTextStream::flush
void flush()
QApplication::desktop
QDesktopWidget * desktop()
QGraphicsRectItem
GlobalConfig::percentPrecision
static int percentPrecision()
Definition: globalconfig.cpp:385
GraphNode::incl
double incl
Definition: callgraphview.h:118
CallGraphView::callLimitTriggered
void callLimitTriggered(QAction *)
Definition: callgraphview.cpp:2848
QRect::setRect
void setRect(int x, int y, int width, int height)
GraphEdge::setCall
void setCall(TraceCall *c)
Definition: callgraphview.h:153
QRectF
ProfileContext::Type
Type
Definition: context.h:36
QGraphicsPathItem::setPath
void setPath(const QPainterPath &path)
QRadialGradient
DEFAULT_FUNCLIMIT
#define DEFAULT_FUNCLIMIT
Definition: callgraphview.cpp:61
GraphExporter::nodeCount
int nodeCount()
Definition: callgraphview.h:321
SubCost
Cost event counter, simple wrapper around a 64bit entity.
Definition: subcost.h:32
GraphExporter::reset
void reset(TraceData *, CostItem *, EventType *, ProfileContext::Type, QString filename=QString())
Definition: callgraphview.cpp:559
QMenu::addMenu
QAction * addMenu(QMenu *menu)
QAction
QMatrix
CallGraphView::TopRight
Definition: callgraphview.h:580
QGraphicsScene::update
void update(qreal x, qreal y, qreal w, qreal h)
DrawParams::TopCenter
Definition: treemap.h:67
QFontMetrics::height
int height() const
QSize::height
int height() const
QVector::count
int count(const T &value) const
CallGraphView::dotError
void dotError()
Definition: callgraphview.cpp:2107
TraceCall::called
TraceFunction * called(bool skipCycle=false) const
Definition: tracedata.cpp:1235
PanningView::drawForeground
void drawForeground(QPainter *p, const QRectF &)
Definition: callgraphview.cpp:1105
CallGraphView::TopLeft
Definition: callgraphview.h:580
GraphNode::self
double self
Definition: callgraphview.h:118
QFileDialog::getSaveFileName
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
QRect::bottom
int bottom() const
TraceItemView::groupTypeChanged
Definition: traceitemview.h:83
RectDrawing
Definition: treemap.h:164
QString::length
int length() const
QVariant::toBool
bool toBool() const
StoredDrawParams::setText
void setText(int f, const QString &)
Definition: treemap.cpp:147
PanningView::mouseReleaseEvent
void mouseReleaseEvent(QMouseEvent *)
Definition: callgraphview.cpp:1147
GraphNode::setVisible
void setVisible(bool v)
Definition: callgraphview.h:89
SubCost::pretty
QString pretty(char sep= ' ') const
Convert SubCost value into a QString, spaced every 3 digits.
Definition: subcost.cpp:46
PanningView::zoomRectMoveFinished
void zoomRectMoveFinished()
QStyle::drawPrimitive
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const =0
GraphOptions::Circular
Definition: callgraphview.h:240
CallGraphView
A QGraphicsView showing a part of the call graph and another zoomed out CanvasView in a border acting...
Definition: callgraphview.h:574
QPen
QAbstractScrollArea::horizontalScrollBar
QScrollBar * horizontalScrollBar() const
GraphExporter::toFunc
TraceFunction * toFunc(QString)
Definition: callgraphview.cpp:911
CallGraphView::focusOutEvent
void focusOutEvent(QFocusEvent *)
Definition: callgraphview.cpp:1708
CallGraphView::~CallGraphView
~CallGraphView()
Definition: callgraphview.cpp:1540
CallGraphView::BottomRight
Definition: callgraphview.h:580
QTimer::start
void start(int msec)
QStyleOptionGraphicsItem
QColor::setAlphaF
void setAlphaF(qreal alpha)
QRectF::height
qreal height() const
GraphOptions::LeftRight
Definition: callgraphview.h:240
QWidget::colorGroup
QColorGroup colorGroup() const
QVariant::toDouble
double toDouble(bool *ok) const
DEFAULT_SHOWSKIPPED
#define DEFAULT_SHOWSKIPPED
Definition: callgraphview.cpp:65
CallGraphView::focusInEvent
void focusInEvent(QFocusEvent *)
Definition: callgraphview.cpp:1698
GraphEdge::to
TraceFunction * to() const
Definition: callgraphview.h:183
QRectF::contains
bool contains(const QPointF &point) const
StoredDrawParams::setPosition
void setPosition(int f, Position)
Definition: treemap.cpp:163
TraceItemView::eventType2Changed
Definition: traceitemview.h:82
GraphNode::setCaller
void setCaller(GraphEdge *)
Definition: callgraphview.cpp:288
QWidget::show
void show()
GraphNode::isVisible
bool isVisible() const
Definition: callgraphview.h:84
GraphOptions::expandCycles
virtual bool expandCycles()=0
QMap< TraceFunction *, GraphNode >::Iterator
typedef Iterator
QMouseEvent::pos
const QPoint & pos() const
QTemporaryFile
QAbstractGraphicsShapeItem::setBrush
void setBrush(const QBrush &brush)
CallGraphView::layoutTriggered
void layoutTriggered(QAction *)
Definition: callgraphview.cpp:2917
ProfileContext::Object
Definition: context.h:49
TraceData
This class holds profiling data of multiple tracefiles generated with cachegrind on one command...
Definition: tracedata.h:1363
QGraphicsItem::show
void show()
QGraphicsPolygonItem::setPolygon
void setPolygon(const QPolygonF &polygon)
CanvasEdge::CanvasEdge
CanvasEdge(GraphEdge *)
Definition: callgraphview.cpp:1349
CostItem::prettyName
virtual QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
Definition: costitem.cpp:65
QGraphicsScene::addItem
void addItem(QGraphicsItem *item)
GraphNode
Definition: callgraphview.h:59
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
TraceCall
A call from one to another function.
Definition: tracedata.h:835
QGraphicsPathItem::path
QPainterPath path() const
PanningView::_zoomRect
QRectF _zoomRect
Definition: callgraphview.h:397
QVector::size
int size() const
callgraphview.h
CallGraphView::zoomRectMoved
void zoomRectMoved(qreal, qreal)
Definition: callgraphview.cpp:2583
QStyleOptionGraphicsItem::levelOfDetailFromTransform
qreal levelOfDetailFromTransform(const QTransform &worldTransform)
QProcess::closeWriteChannel
void closeWriteChannel()
QAbstractGraphicsShapeItem::pen
QPen pen() const
QGraphicsEllipseItem
GraphEdge
Definition: callgraphview.h:133
DEFAULT_CALLLIMIT
#define DEFAULT_CALLLIMIT
Definition: callgraphview.cpp:62
CANVAS_EDGELABEL
Definition: callgraphview.h:414
PanningView::zoomRectMoved
void zoomRectMoved(qreal dx, qreal dy)
QTemporaryFile::open
bool open()
TraceItemView::groupType
ProfileContext::Type groupType() const
Definition: traceitemview.h:153
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QVariant::toString
QString toString() const
QGraphicsItem::setZValue
void setZValue(qreal z)
QAbstractScrollArea::setVerticalScrollBarPolicy
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy)
CallGraphView::saveOptions
void saveOptions(const QString &prefix, const QString &postfix)
Definition: callgraphview.cpp:3163
StorableGraphOptions::_layout
Layout _layout
Definition: callgraphview.h:289
QFocusEvent
QGraphicsView
QProcess::readAllStandardOutput
QByteArray readAllStandardOutput()
PanningView::setZoomRect
void setZoomRect(const QRectF &r)
Definition: callgraphview.cpp:1099
QMap::find
iterator find(const Key &key)
GraphExporter::edgeCount
int edgeCount()
Definition: callgraphview.h:316
QList::begin
iterator begin()
GraphNode::clearEdges
void clearEdges()
Definition: callgraphview.cpp:144
QProcess::start
void start(const QString &program, const QStringList &arguments, QFlags< QIODevice::OpenModeFlag > mode)
QAction::setEnabled
void setEnabled(bool)
CanvasNode::updateGroup
void updateGroup()
Definition: callgraphview.cpp:1205
CallGraphView::restoreOptions
void restoreOptions(const QString &prefix, const QString &postfix)
Definition: callgraphview.cpp:3143
QWidget::height
height
CallGraphView::zoomRectMoveFinished
void zoomRectMoveFinished()
Definition: callgraphview.cpp:2594
QProcess::readAllStandardError
QByteArray readAllStandardError()
QGraphicsView::scrollContentsBy
virtual void scrollContentsBy(int dx, int dy)
CanvasFrame::paint
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
Definition: callgraphview.cpp:1472
TraceInclusiveCost::inclusive
ProfileCostArray * inclusive()
Definition: tracedata.cpp:163
CanvasNode::setSelected
void setSelected(bool)
Definition: callgraphview.cpp:1199
CanvasEdgeLabel
Definition: callgraphview.h:443
GraphEdge::fromNode
GraphNode * fromNode() const
Definition: callgraphview.h:168
StorableGraphOptions::_callLimit
double _callLimit
Definition: callgraphview.h:285
ProfileContext::Function
Definition: context.h:46
QTextStream::seek
bool seek(qint64 pos)
GraphOptions::showSkipped
virtual bool showSkipped()=0
QGraphicsScene::width
qreal width() const
CanvasNode::paint
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
Definition: callgraphview.cpp:1216
CanvasEdgeArrow::paint
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
Definition: callgraphview.cpp:1336
QGraphicsRectItem::rect
QRectF rect() const
CanvasFrame::CanvasFrame
CanvasFrame(CanvasNode *)
Definition: callgraphview.cpp:1433
ProfileContext::InvalidType
Definition: context.h:37
ConfigGroup::value
virtual QVariant value(const QString &key, const QVariant &defaultValue) const
Definition: config.cpp:60
QMap::value
const T value(const Key &key) const
GraphEdge::nextVisible
TraceCall * nextVisible()
Definition: callgraphview.cpp:449
QTimer::setSingleShot
void setSingleShot(bool singleShot)
GraphEdge::prettyName
QString prettyName()
Definition: callgraphview.cpp:413
QPolygonF
GraphEdge::visibleCaller
TraceFunction * visibleCaller()
Definition: callgraphview.cpp:427
QGraphicsView::resizeEvent
virtual void resizeEvent(QResizeEvent *event)
CallGraphView::scrollContentsBy
void scrollContentsBy(int dx, int dy)
Definition: callgraphview.cpp:2567
GraphNode::canvasNode
CanvasNode * canvasNode() const
Definition: callgraphview.h:74
GraphEdge::setVisible
void setVisible(bool v)
Definition: callgraphview.h:163
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:39:50 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kcachegrind

Skip menu "kcachegrind"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdesdk API Reference

Skip menu "kdesdk API Reference"
  • kapptemplate
  • kcachegrind
  • kompare
  • lokalize
  • umbrello
  •   umbrello

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal