28 #include <QApplication>
31 #include <QFileDialog>
32 #include <QTemporaryFile>
33 #include <QTextStream>
39 #include <QResizeEvent>
40 #include <QMouseEvent>
41 #include <QFocusEvent>
43 #include <QStyleOptionGraphicsItem>
44 #include <QContextMenuEvent>
47 #include <QDesktopWidget>
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
78 class CallerGraphEdgeLessThan
87 if (!ce1)
return true;
88 if (!ce2)
return false;
94 double angle1 = atan2(
double(d1.
y()),
double(d1.
x()));
95 double angle2 = atan2(
double(d2.
y()),
double(d2.
x()));
97 return (angle1 < angle2);
102 class CalleeGraphEdgeLessThan
111 if (!ce1)
return true;
112 if (!ce2)
return false;
118 double angle1 = atan2(
double(d1.
y()),
double(d1.
x()));
119 double angle2 = atan2(
double(d2.
y()),
double(d2.
x()));
122 return (angle2 < angle1);
139 _lastCallerIndex = _lastCalleeIndex = -1;
141 _lastFromCaller =
true;
173 if (e && (callees.
count(e) == 0))
179 if (e && (callers.
count(e) == 0))
233 qDebug(
"GraphNode::visibleCaller %s: last %d, count %d",
238 if (e && !e->isVisible())
241 double maxCost = 0.0;
243 for(
int i = 0; i<callers.
size(); i++) {
245 if (e->isVisible() && (e->cost > maxCost)) {
248 _lastCallerIndex = i;
253 return e ? e->
call() : 0;
259 qDebug(
"GraphNode::visibleCallee %s: last %d, count %d",
263 if (e && !e->isVisible())
267 double maxCost = 0.0;
269 for(
int i = 0; i<callees.
size(); i++) {
271 if (e->isVisible() && (e->cost > maxCost)) {
274 _lastCalleeIndex = i;
279 return e ? e->
call() : 0;
284 _lastCalleeIndex = callees.
indexOf(e);
285 _lastFromCaller =
false;
290 _lastCallerIndex = callers.
indexOf(e);
291 _lastFromCaller =
true;
298 if (_lastFromCaller) {
320 if (_lastFromCaller) {
340 int idx = e ? callers.
indexOf(e) : _lastCallerIndex;
342 while(idx < callers.
size()) {
344 _lastCallerIndex = idx;
345 return callers[idx]->call();
354 int idx = e ? callees.
indexOf(e) : _lastCalleeIndex;
356 while(idx < callees.
size()) {
358 _lastCalleeIndex = idx;
359 return callees[idx]->call();
368 int idx = e ? callers.
indexOf(e) : _lastCallerIndex;
370 idx = (idx<0) ? callers.
size()-1 : idx-1;
373 _lastCallerIndex = idx;
374 return callers[idx]->call();
383 int idx = e ? callees.
indexOf(e) : _lastCalleeIndex;
385 idx = (idx<0) ? callees.
size()-1 : idx-1;
388 _lastCalleeIndex = idx;
389 return callees[idx]->call();
405 _fromNode = _toNode = 0;
410 _lastFromCaller =
true;
430 _lastFromCaller =
true;
441 _lastFromCaller =
false;
453 if (_lastFromCaller && _fromNode) {
457 }
else if (_toNode) {
459 if (!res && _fromNode)
469 if (_lastFromCaller && _fromNode) {
473 }
else if (_toNode) {
475 if (!res && _fromNode)
545 reset(d, f, ct, gt, filename);
550 if (_item && _tmpFile) {
562 _graphCreated =
false;
566 if (_item && _tmpFile) {
617 _graphCreated =
true;
624 _realFuncLimit = incl * _go->
funcLimit();
625 _realCallLimit = _realFuncLimit * _go->
callLimit();
627 buildGraph(f, 0,
true, 1.0);
633 buildGraph(f, 0,
false, 1.0);
637 double incl = c->
subCost(_eventType);
638 _realFuncLimit = incl * _go->
funcLimit();
639 _realCallLimit = _realFuncLimit * _go->
callLimit();
643 caller = c->
caller(
false);
644 called = c->
called(
false);
654 buildGraph(called, 0,
true, e.
cost / s);
656 buildGraph(caller, 0,
false, e.
cost / s);
675 file =
new QFile(_dotName);
676 if ( !file->
open(QIODevice::WriteOnly ) ) {
677 qDebug() <<
"Can not write dot file '"<< _dotName <<
"'";
694 *stream <<
"digraph \"callgraph\" {\n";
697 *stream <<
QString(
" rankdir=LR;\n");
700 switch (_item->
type()) {
712 *stream <<
QString(
" center=F%1;\n").
arg((qptrdiff)f, 0, 16);
713 *stream <<
QString(
" overlap=false;\n splines=true;\n");
720 for (nit = _nodeMap.
begin(); nit != _nodeMap.
end(); ++nit ) {
723 if (n.
incl <= _realFuncLimit)
729 switch (_groupType) {
746 nLists[g].append(&n);
751 for (lit = nLists.
begin(); lit != nLists.
end(); ++lit, cluster++) {
757 *stream <<
QString(
"subgraph \"cluster%1\" { label=\"%2\";\n")
765 *stream <<
QString(
" F%1 [").
arg((qptrdiff)f, 0, 16);
771 *stream <<
QString(
"shape=box,label=\"** %1 **\\n**\\n%2\"];\n")
775 *stream <<
QString(
"label=\"%1\\n%2\"];\n")
785 for (eit = _edgeMap.
begin(); eit != _edgeMap.
end(); ++eit ) {
788 if (e.
cost < _realCallLimit)
802 if ((from.
incl <= _realFuncLimit) ||(to.
incl <= _realFuncLimit))
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)));
815 *stream <<
QString(
",label=\"%1 (%2x)\"")
820 *stream <<
QString(
",label=\"%3\\n%4 x\"")
831 double costSum, countSum;
832 for (nit = _nodeMap.
begin(); nit != _nodeMap.
end(); ++nit ) {
834 if (n.
incl <= _realFuncLimit)
840 if (costSum > _realCallLimit) {
844 e->setCallee(p.second);
848 *stream <<
QString(
" R%1 [shape=point,label=\"\"];\n")
850 *stream <<
QString(
" R%1 -> F%2 [label=\"%3\\n%4 x\",weight=%5];\n")
855 .arg((
int)log(costSum));
861 if (costSum > _realCallLimit) {
865 e->setCaller(p.first);
869 *stream <<
QString(
" S%1 [shape=point,label=\"\"];\n")
871 *stream <<
QString(
" F%1 -> S%2 [label=\"%3\\n%4 x\",weight=%5];\n")
876 .arg((
int)log(costSum));
883 for (nit = _nodeMap.
begin(); nit != _nodeMap.
end(); ++nit ) {
905 for (nit = _nodeMap.
begin(); nit != _nodeMap.
end(); ++nit ) {
929 if (it == _nodeMap.
end())
938 if (it == _edgeMap.
end())
953 void GraphExporter::buildGraph(
TraceFunction* f,
int d,
bool toCallees,
957 qDebug() <<
"buildGraph(" << f->
prettyName() <<
"," << d <<
"," << factor
958 <<
") [to " << (toCallees ?
"Callees":
"Callers") <<
"]";
961 double oldIncl = 0.0;
972 qDebug(
" Added Incl. %f, now %f", incl, n.
incl);
977 if ((maxDepth>=0) && (d >= maxDepth)) {
979 qDebug(
" Cutoff, max depth reached");
985 if ((n.
incl >= _realFuncLimit) && (oldIncl < _realFuncLimit))
994 qDebug(
" Cutoff, 2nd visit to Cycle Member");
1000 }
else if (incl <= _realFuncLimit) {
1002 qDebug(
" Cutoff, below limit");
1013 f2 = toCallees ? call->
called(
false) : call->
caller(
false);
1015 double count = call->
callCount() * factor;
1016 double cost = call->
subCost(_eventType) * factor;
1022 double oldCost = 0.0;
1024 toCallees ? f2 : f);
1026 if (e.
call() == 0) {
1036 qDebug(
" Edge to %s, added cost %f, now %f",
1040 if (f2->
cycle() == f2) {
1042 realF = toCallees ? call->
called(
true) : call->
caller(
true);
1044 realP(toCallees ? f : realF, toCallees ? realF : f);
1046 if (e.
call() == 0) {
1068 if ((e.
cost >= _realCallLimit) && (oldCost < _realCallLimit))
1070 if (cost < _realCallLimit) {
1072 qDebug(
" Edge Cutoff, limit not reached");
1083 buildGraph(f2, d+1, toCallees, factor * v / s);
1170 if (!_node || !_view)
1188 double inclP = 100.0 * n->
incl/ total;
1207 if (!_view || !_node)
1237 p->
drawRect(
QRect(origRect.x(), origRect.y(), origRect.width()-1,
1238 origRect.height()-1));
1240 #if QT_VERSION >= 0x040600
1244 if (option->levelOfDetail < .5)
1259 int y,
int w,
int h) :
1279 double inclP = 100.0 * e->
cost/ total;
1291 _percentage = inclP;
1292 if (_percentage > 100.0) _percentage = 100.0;
1297 KIconLoader* loader = KIconLoader::global();
1298 QPixmap p= loader->loadIcon(icon, KIconLoader::Small, 0,
1312 #if QT_VERSION >= 0x040600
1316 if (option->levelOfDetail < .5)
1356 setFlag(QGraphicsItem::ItemIsSelectable);
1370 if (_thickness < .9) _thickness = .9;
1379 .arg(_label->
text(0)).arg(_label->
text(1)));
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()]);
1406 qreal levelOfDetail;
1407 #if QT_VERSION >= 0x040600
1410 levelOfDetail = option->levelOfDetail;
1414 mypen.
setWidthF(1.0/levelOfDetail * _thickness);
1420 mypen.
setWidthF(1.0/levelOfDetail * _thickness/2.0);
1438 float v1 = 130.0f, v2 = 10.0f, v = v1, f = 1.03f;
1441 QRect r(0, 0, 30, 30);
1448 _p->
fill(Qt::white);
1475 qreal levelOfDetail;
1476 #if QT_VERSION >= 0x040600
1479 levelOfDetail = option->levelOfDetail;
1481 if (levelOfDetail < .5) {
1509 _xMargin = _yMargin = 0;
1520 _panningView->
raise();
1521 _panningView->
hide();
1535 _prevSelectedNode = 0;
1536 connect(&_renderTimer, SIGNAL(timeout()),
1543 delete _panningView;
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>");
1566 void CallGraphView::updateSizes(
QSize s)
1571 if (s ==
QSize(0, 0))
1575 int cWidth = (int)_scene->
width() - 2*_xMargin + 100;
1576 int cHeight = (int)_scene->
height() - 2*_yMargin + 100;
1580 ((cWidth < s.
width()) && (cHeight < s.
height())) ) {
1581 _panningView->
hide();
1586 double zoom = .33 * s.
width() / cWidth;
1587 if (zoom * cHeight < .33 * s.
height())
1588 zoom = .33 * s.
height() / cHeight;
1591 if (cWidth * zoom > s.
width())
1592 zoom = s.
width() / (double)cWidth;
1593 if (cHeight * zoom > s.
height())
1594 zoom = s.
height() / (double)cHeight;
1603 if (zoom != _panningZoom) {
1604 _panningZoom = zoom;
1606 qDebug(
"Canvas Size: %fx%f, Content: %dx%d, Zoom: %f",
1607 _scene->
width(), _scene->
height(), cWidth, cHeight, zoom);
1610 _panningView->
setMatrix(m.scale(zoom, zoom));
1613 _panningView->
resize(
int(cWidth * zoom) + 4,
int(cHeight * zoom) + 4);
1621 int cvW = _panningView->
width();
1622 int cvH = _panningView->
height();
1625 QPoint oldZoomPos = _panningView->
pos();
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;
1635 zp = _lastAutoPosition;
1652 if (minCols > tlCols) {
1656 if (minCols > trCols) {
1660 if (minCols > blCols) {
1664 if (minCols > brCols) {
1669 _lastAutoPosition = zp;
1674 newZoomPos =
QPoint(0, 0);
1677 newZoomPos =
QPoint(x, 0);
1680 newZoomPos =
QPoint(0, y);
1683 newZoomPos =
QPoint(x, y);
1689 if (newZoomPos != oldZoomPos)
1690 _panningView->
move(newZoomPos);
1693 _panningView->
hide();
1695 _panningView->
show();
1700 if (!_scene)
return;
1702 if (_selectedNode && _selectedNode->
canvasNode()) {
1721 if ((e->
key() == Qt::Key_Return) ||(e->
key() == Qt::Key_Space)) {
1724 else if (_selectedEdge && _selectedEdge->
call())
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))) {
1746 key = Qt::Key_Right;
1759 if (_selectedNode) {
1760 if (key == Qt::Key_Up)
1762 if (key == Qt::Key_Down)
1764 if (key == Qt::Key_Right)
1766 if (key == Qt::Key_Left)
1768 }
else if (_selectedEdge) {
1769 if (key == Qt::Key_Up)
1771 if (key == Qt::Key_Down)
1773 if (key == Qt::Key_Right)
1775 if (key == Qt::Key_Left)
1788 if (e->
key() == Qt::Key_Home)
1790 else if (e->
key() == Qt::Key_End)
1792 else if (e->
key() == Qt::Key_PageUp) {
1795 }
else if (e->
key() == Qt::Key_PageDown) {
1798 }
else if (e->
key() == Qt::Key_Left) {
1801 }
else if (e->
key() == Qt::Key_Right) {
1804 }
else if (e->
key() == Qt::Key_Down) {
1807 }
else if (e->
key() == Qt::Key_Up) {
1818 updateSizes(e->
size());
1824 switch (i->
type()) {
1836 void CallGraphView::doUpdate(
int changeType,
bool)
1854 if (n == _selectedNode)
1859 if (e == _selectedEdge)
1864 if (_selectedNode && _selectedNode->
canvasNode()) {
1868 if (_selectedEdge && _selectedEdge->
canvasEdge()) {
1886 #if 0 // do not change position when selecting edge
1890 if (!sNode && _selectedEdge->
toNode())
1912 for (
int i = 0; i < l.
size(); ++i)
1930 void CallGraphView::clear()
1941 void CallGraphView::showText(
QString s)
1944 _renderTimer.
stop();
1952 _panningView->
hide();
1960 s =
tr(
"Warning: a long lasting graph layouting is in progress.\n"
1961 "Reduce node/edge limits for speedup.\n");
1963 s =
tr(
"Layouting stopped.\n");
1965 s.
append(
tr(
"The call graph has %1 nodes and %2 edges.\n")
1974 err =
tr(
"No graph available because the layouting process failed.\n");
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).");
1988 if (!_renderProcess)
1991 qDebug(
"CallGraphView::stopRendering: Killing QProcess %p",
1994 _renderProcess->
kill();
2002 _renderTimer.
start(200);
2005 void CallGraphView::refresh()
2012 _prevSelectedNode = _selectedNode;
2013 _prevSelectedPos =
QPoint(-1, -1);
2014 if (_selectedNode) {
2020 showText(
tr(
"No item activated for which to "
2021 "draw the call graph."));
2032 showText(
tr(
"No call graph can be drawn for "
2033 "the active item."));
2038 qDebug() <<
"CallGraphView::refresh";
2061 renderProgram =
"twopi";
2063 renderProgram =
"dot";
2064 renderArgs <<
"-Tplain";
2070 _renderTimer.
start(1000);
2072 _renderProcess =
new QProcess(
this);
2073 connect(_renderProcess, SIGNAL(readyReadStandardOutput()),
2075 connect(_renderProcess, SIGNAL(error(QProcess::ProcessError)),
2077 connect(_renderProcess, SIGNAL(finished(
int,QProcess::ExitStatus)),
2080 _renderProcessCmdLine = renderProgram +
" " + renderArgs.
join(
" ");
2081 qDebug(
"CallGraphView::refresh: Starting process %p, '%s'",
2082 _renderProcess, qPrintable(_renderProcessCmdLine));
2087 p->
start(renderProgram, renderArgs);
2096 qDebug(
"CallGraphView::readDotOutput: QProcess %p", p);
2099 if ((_renderProcess == 0) || (p != _renderProcess)) {
2110 qDebug(
"CallGraphView::dotError: Got %d from QProcess %p",
2114 if ((_renderProcess == 0) || (p != _renderProcess)) {
2130 qDebug(
"CallGraphView::dotExited: QProcess %p", p);
2133 if ((_renderProcess == 0) || (p != _renderProcess)) {
2148 double scale = 1.0, scaleX = 1.0, scaleY = 1.0;
2149 double dotWidth = 0, dotHeight = 0;
2153 _renderTimer.
stop();
2156 dotStream =
new QTextStream(&_unparsedOutput, QIODevice::ReadOnly);
2160 double nodeHeight = 0.0;
2163 if (line.
isNull())
break;
2165 QTextStream lineStream(&line, QIODevice::ReadOnly);
2167 if (cmd !=
"node")
continue;
2169 lineStream >> s >> s >> s >> s >> h ;
2173 if (nodeHeight > 0.0) {
2188 QTextStream lineStream(&line, QIODevice::ReadOnly);
2192 qDebug(
"%s:%d - line '%s', cmd '%s'",
2194 lineno, qPrintable(line), qPrintable(cmd));
2199 if (cmd ==
"graph") {
2200 QString dotWidthString, dotHeightString;
2202 lineStream >> scale >> dotWidthString >> dotHeightString;
2203 dotWidth = dotWidthString.
toDouble();
2204 dotHeight = dotHeightString.toDouble();
2207 int w = (int)(scaleX * dotWidth);
2208 int h = (int)(scaleY * dotHeight);
2221 qreal(w+2*_xMargin), qreal(h+2*_yMargin));
2227 qDebug() << qPrintable(_exporter.
filename()) <<
":" << lineno
2228 <<
" - graph (" << dotWidth <<
" x " << dotHeight
2229 <<
") => (" << w <<
" x " << h <<
")";
2232 qDebug() <<
"Ignoring 2nd 'graph' from dot ("
2233 << _exporter.
filename() <<
":"<< lineno <<
")";
2237 if ((cmd !=
"node") && (cmd !=
"edge")) {
2238 qDebug() <<
"Ignoring unknown command '"<< cmd
2239 <<
"' from dot ("<< _exporter.
filename() <<
":"<< lineno
2245 qDebug() <<
"Ignoring '"<< cmd
2246 <<
"' without 'graph' from dot ("<< _exporter.
filename()
2247 <<
":"<< lineno <<
")";
2251 if (cmd ==
"node") {
2253 QString nodeName, label, nodeX, nodeY, nodeWidth, nodeHeight;
2255 lineStream >> nodeName >> nodeX >> nodeY >> nodeWidth
2260 height = nodeHeight.toDouble();
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);
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;
2279 if (nodeName[0] ==
'R'|| nodeName[0] ==
'S') {
2290 qDebug(
"Warning: Unknown function '%s' ?!",
2291 qPrintable(nodeName));
2296 rItem =
new CanvasNode(
this, n, xx-w/2, yy-h/2, w, h);
2318 QString node1Name, node2Name, label, edgeX, edgeY;
2322 lineStream >> node1Name >> node2Name >> points;
2325 _exporter.
toFunc(node2Name));
2327 qDebug() <<
"Unknown edge '"<< node1Name <<
"'-'"<< node2Name
2328 <<
"' from dot ("<< _exporter.
filename() <<
":"<< lineno
2339 qDebug(
" Edge with %d points:", points);
2342 for (i=0; i<points; i++) {
2343 if (lineStream.
atEnd())
2345 lineStream >> edgeX >> edgeY;
2347 y = edgeY.toDouble();
2349 int xx = (int)(scaleX * x + _xMargin);
2350 int yy = (int)(scaleY * (dotHeight - y)+ _yMargin);
2353 qDebug(
" P %d: ( %f / %f ) => ( %d / %d)", i, x, y, xx, yy);
2358 qDebug(
"CallGraphView: Can not read %d spline points (%s:%d)",
2359 points, qPrintable(_exporter.
filename()), lineno);
2364 QColor arrowColor = Qt::black;
2367 if ( (caller && (caller->
cycle() == caller)) ||
2368 (called && (called->
cycle() == called)) ) arrowColor = Qt::blue;
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) {
2401 while (arrowDir.
isNull() && (indexHead<points-2)) {
2403 arrowDir = poly.
point(indexHead) - poly.
point(indexHead+1);
2411 while (arrowDir.
isNull() && (indexHead>1)) {
2413 arrowDir = poly.
point(indexHead) - poly.
point(indexHead-1);
2417 if (!arrowDir.
isNull()) {
2419 arrowDir *= 10.0/sqrt(
double(arrowDir.
x()*arrowDir.
x() +
2420 arrowDir.
y()*arrowDir.
y()));
2426 a << QPointF(poly.
point(indexHead) +
QPoint(-arrowDir.
y()/2,
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());
2443 if (lineStream.
atEnd())
2452 lineStream >> label;
2456 while (!c.isNull() && (c !=
'\"')) {
2463 lineStream >> edgeX >> edgeY;
2465 y = edgeY.toDouble();
2467 int xx = (int)(scaleX * x + _xMargin);
2468 int yy = (int)(scaleY * (dotHeight - y)+ _yMargin);
2471 qDebug(
" Label '%s': ( %f / %f ) => ( %d / %d)",
2472 qPrintable(label), x, y, xx, yy);
2494 QString s =
tr(
"Error running the graph layouting tool.\n");
2495 s +=
tr(
"Please check that 'dot' is installed (package GraphViz).");
2498 }
else if (!activeNode && !activeEdge) {
2499 QString s =
tr(
"There is no call graph available for function\n"
2501 "because it has no cost of the selected event type.")
2512 if ((!_selectedNode || !_selectedNode->
canvasNode()) &&
2513 (!_selectedEdge || !_selectedEdge->
canvasEdge())) {
2515 _selectedNode = activeNode;
2517 }
else if (activeEdge) {
2518 _selectedEdge = activeEdge;
2526 else if (_selectedEdge) {
2529 if (!sNode && _selectedEdge->
toNode())
2534 if (_prevSelectedNode) {
2536 if (
rect().contains(_prevSelectedPos)) {
2561 delete _renderProcess;
2575 QRectF z(topLeft, bottomRight);
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());
2596 if (_zoomPosition ==
Auto)
2606 if (e->
button() == Qt::LeftButton) _isMoving =
true;
2614 qDebug(
"CallGraphView: Got Node '%s'",
2629 qDebug(
"CallGraphView: Got Edge '%s'",
2636 _lastPos = e->
pos();
2649 _lastPos = e->
pos();
2656 if (_zoomPosition ==
Auto)
2669 qDebug(
"CallGraphView: Double Clicked on Node '%s'",
2685 qDebug(
"CallGraphView: Double Clicked On Edge '%s'",
2708 QMenu* CallGraphView::addCallerDepthMenu(
QMenu* menu)
2714 a = addCallerDepthAction(m,
tr(
"Unlimited"), -1);
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);
2747 QMenu* CallGraphView::addCalleeDepthMenu(
QMenu* menu)
2753 a = addCalleeDepthAction(m,
tr(
"Unlimited"), -1);
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);
2786 QMenu* CallGraphView::addNodeLimitMenu(
QMenu* menu)
2791 m = menu->
addMenu(
tr(
"Min. Node Cost"));
2792 a = addNodeLimitAction(m,
tr(
"No Minimum"), 0.0);
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);
2829 QMenu* CallGraphView::addCallLimitMenu(
QMenu* menu)
2833 m = menu->
addMenu(
tr(
"Min. Call Cost"));
2834 addCallLimitAction(m,
tr(
"Same as Node"), 1.0);
2836 addCallLimitAction(m,
tr(
"50 % of Node"), .5);
2838 addCallLimitAction(m,
tr(
"20 % of Node"), .2);
2840 addCallLimitAction(m,
tr(
"10 % of Node"), .1);
2867 QMenu* CallGraphView::addZoomPosMenu(
QMenu* menu)
2870 addZoomPosAction(m,
tr(
"Top Left"),
TopLeft);
2871 addZoomPosAction(m,
tr(
"Top Right"),
TopRight);
2874 addZoomPosAction(m,
tr(
"Automatic"),
Auto);
2875 addZoomPosAction(m,
tr(
"Hide"),
Hide);
2903 QMenu* CallGraphView::addLayoutMenu(
QMenu* menu)
2906 addLayoutAction(m,
tr(
"Top to Down"),
TopDown);
2907 addLayoutAction(m,
tr(
"Left to Right"),
LeftRight);
2908 addLayoutAction(m,
tr(
"Circular"),
Circular);
2934 QAction* activateFunction = 0;
2941 qDebug(
"CallGraphView: Menu on Node '%s'",
2950 activateFunction = popup.addAction(menuStr);
2951 if (cycle && (cycle != f)) {
2953 activateCycle = popup.addAction(
tr(
"Go to '%1'").arg(name));
2955 popup.addSeparator();
2967 qDebug(
"CallGraphView: Menu on Edge '%s'",
2975 activateCall = popup.addAction(menuStr);
2977 popup.addSeparator();
2983 if (_renderProcess) {
2984 stopLayout = popup.addAction(
tr(
"Stop Layouting"));
2985 popup.addSeparator();
2989 popup.addSeparator();
2994 popup.addSeparator();
2997 addCallerDepthMenu(gpopup);
2998 addCalleeDepthMenu(gpopup);
2999 addNodeLimitMenu(gpopup);
3000 addCallLimitMenu(gpopup);
3004 toggleSkipped = gpopup->
addAction(
tr(
"Arrows for Skipped Calls"));
3009 toggleExpand = gpopup->
addAction(
tr(
"Inner-cycle Calls"));
3014 toggleCluster = gpopup->
addAction(
tr(
"Cluster Groups"));
3029 addLayoutMenu(&popup);
3030 addZoomPosMenu(&popup);
3034 if (a == activateFunction)
3036 else if (a == activateCycle)
3038 else if (a == activateCall)
3041 else if (a == stopLayout)
3044 else if (a == exportAsDot) {
3050 tr(
"Export Graph As DOT file"),
3060 else if (a == exportAsImage) {
3062 if (!_scene)
return;
3066 tr(
"Export Graph As Image"),
3068 tr(
"Images (*.png *.jpg)"));
3079 else if (a == toggleSkipped) {
3083 else if (a == toggleExpand) {
3087 else if (a == toggleCluster) {
3092 else if (a == layoutCompact) {
3096 else if (a == layoutNormal) {
3100 else if (a == layoutTall) {
3113 if (s ==
QString(
"BottomLeft"))
3115 if (s ==
QString(
"BottomRight"))
3117 if (s ==
QString(
"Automatic"))
3134 return QString(
"BottomRight");
3182 #include "callgraphview.moc"
virtual int detailLevel()=0
void setSelected(bool selected)
bool drawField(QPainter *, int f, DrawParams *dp=0)
virtual Layout layout()=0
Qt::KeyboardModifiers modifiers() const
A panner laid over a QGraphicsScene.
static bool showPercentage()
virtual bool seek(qint64 pos)
QString & append(QChar ch)
#define DEFAULT_EXPANDCYCLES
void setCallerNode(GraphNode *n)
SubCost subCost(EventType *)
Returns a sub cost.
void setArrow(CanvasEdgeArrow *a)
EventType * eventType() const
GraphEdge * edge(TraceFunction *, TraceFunction *)
TraceCall * priorVisibleCaller(GraphEdge *=0)
QList< QGraphicsItem * > items() const
void render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode)
virtual int maxCallerDepth()=0
void setMatrix(const QMatrix &matrix, bool combine)
PanningView(QWidget *parent=0)
void setRenderHint(RenderHint hint, bool on)
TraceFunction * visibleCallee()
void addUniqueCaller(GraphEdge *)
void centerOn(const QPointF &pos)
QString readLine(qint64 maxlen)
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
void fill(const QColor &color)
void setControlPoints(const QPolygon &a)
void setFlag(GraphicsItemFlag flag, bool enabled)
static QString zoomPosString(ZoomPosition)
void drawBack(QPainter *, DrawParams *dp=0)
CanvasEdge * canvasEdge() const
const TraceCallList & callings(bool skipCycle=false) const
GraphNode * node(TraceFunction *)
void setGraphOptions(GraphOptions *go=0)
#define DEFAULT_CLUSTERGROUPS
QList< QGraphicsItem * > items() const
QProcess::ProcessError error() const
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
void setColorAt(qreal position, const QColor &color)
TraceFunction * caller(bool skipCycle=false) const
ProfileContext::Type type() const
ZoomPosition zoomPos() const
CanvasEdgeLabel(CallGraphView *, CanvasEdge *, int, int, int, int)
static QColor functionColor(ProfileContext::Type gt, TraceFunction *)
void setCanvasEdge(CanvasEdge *ce)
virtual bool clusterGroups()=0
QPointF mapToScene(const QPoint &point) const
void addCaller(GraphEdge *)
void setBackgroundBrush(const QBrush &brush)
double percentage() const
QPolygonF polygon() const
void translate(int dx, int dy)
TraceObject * object() const
virtual void setValue(const QString &key, const QVariant &value, const QVariant &defaultValue=QVariant())
void mousePressEvent(QMouseEvent *)
void cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
TraceFunctionCycle * cycle()
void moveTo(const QPointF &point)
Base class for cost items.
TraceFunction * priorVisible()
void setPixmap(int f, const QPixmap &)
QString join(const QString &separator) const
void zoomPosTriggered(QAction *)
void contextMenuEvent(QContextMenuEvent *)
void setCallee(TraceFunction *f)
void calleeDepthTriggered(QAction *)
double toDouble(bool *ok) const
void mouseReleaseEvent(QMouseEvent *)
void keyPressEvent(QKeyEvent *)
TraceFunction * activeFunction()
QString tr(const char *sourceText, const char *disambiguation, int n)
void setMaxLines(int f, int)
ProfileContext::Type _groupType
TraceCall * visibleCallee()
void mousePressEvent(QMouseEvent *)
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).
void callerDepthTriggered(QAction *)
TraceCallList callers(bool skipCycle=false) const
void setPoint(int index, int x, int y)
void update(const QRectF &rect)
void setBackColor(const QColor &c)
QColor dark(int factor) const
void setRect(const QRectF &rectangle)
static ConfigGroup * group(const QString &group, const QString &optSuffix=QString())
Abstract Base Class for KCachegrind Views.
TraceCall * nextVisibleCaller(GraphEdge *=0)
int indexOf(const T &value, int from) const
#define DEFAULT_MAXCALLER
void setRect(const QRect &)
void drawRect(const QRectF &rectangle)
void scale(qreal sx, qreal sy)
void setCaller(TraceFunction *f)
const QPolygon & controlPoints() const
CostItem * selectedItem() const
TraceFunction * from() const
const char * name() const
virtual int maxCalleeDepth()=0
An array of basic cost metrics for a trace item.
int count(const T &value) const
virtual double funcLimit()=0
void append(const T &value)
#define DEFAULT_DETAILLEVEL
TraceCall * visibleCaller()
TraceCall * priorVisible()
CanvasNode(CallGraphView *, GraphNode *, int, int, int, int)
int toInt(bool *ok) const
CanvasEdgeArrow(CanvasEdge *)
TraceFunction * function() const
virtual QString name() const
Returns dynamic name info (without type)
void setPen(const QColor &color)
void nodeLimitTriggered(QAction *)
void setPos(const QPointF &pos)
void setAutoRemove(bool b)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
CallerGraphEdgeLessThan callerGraphEdgeLessThan
QGraphicsSimpleTextItem * addSimpleText(const QString &text, const QFont &font)
QString whatsThis() const
void setObjectName(const QString &name)
void setCallee(GraphEdge *)
int removeAll(const T &value)
static QString layoutString(Layout)
GraphNode * toNode() const
void resizeEvent(QResizeEvent *)
void addCallee(GraphEdge *)
QGraphicsItem * itemAt(const QPoint &pos) const
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
void setScene(QGraphicsScene *scene)
void setCalleeNode(GraphNode *n)
QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
QPoint mapFromScene(const QPointF &point) const
virtual TraceData * data()
TraceCall * priorVisibleCallee(GraphEdge *=0)
CallGraphView(TraceItemView *parentView, QWidget *parent=0, const char *name=0)
TraceCall * nextVisibleCallee(GraphEdge *=0)
void setFunction(TraceFunction *f)
void showRenderError(QString)
#define DEFAULT_MAXCALLEE
qulonglong toULongLong(bool *ok, int base) const
void setColor(const QColor &color)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
static QString shortenSymbol(const QString &)
A group of configuration settings.
void mouseMoveEvent(QMouseEvent *)
void addUniqueCallee(GraphEdge *)
void removeEdge(GraphEdge *)
void setData(const QVariant &userData)
virtual void selected(TraceItemView *sender, CostItem *)
Notification from child views.
void point(int index, int *x, int *y) const
void setCanvasNode(CanvasNode *cn)
const QSize & size() const
virtual void activated(TraceItemView *sender, CostItem *)
void mouseDoubleClickEvent(QMouseEvent *)
virtual double callLimit()=0
const Key key(const T &value) const
CalleeGraphEdgeLessThan calleeGraphEdgeLessThan
void setLabel(CanvasEdgeLabel *l)
TraceFunction * nextVisible()
void ensureVisible(const QRectF &rect, int xmargin, int ymargin)
void writeDot(QIODevice *=0)
static bool showExpanded()
QString mid(int position, int n) const
void drawPath(const QPainterPath &path)
void mouseMoveEvent(QMouseEvent *)
void setPen(const QPen &pen)
CostItem * activeItem() const
QDesktopWidget * desktop()
static int percentPrecision()
void callLimitTriggered(QAction *)
void setRect(int x, int y, int width, int height)
void setCall(TraceCall *c)
void setPath(const QPainterPath &path)
#define DEFAULT_FUNCLIMIT
Cost event counter, simple wrapper around a 64bit entity.
void reset(TraceData *, CostItem *, EventType *, ProfileContext::Type, QString filename=QString())
void update(qreal x, qreal y, qreal w, qreal h)
int count(const T &value) const
TraceFunction * called(bool skipCycle=false) const
void drawForeground(QPainter *p, const QRectF &)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
void setText(int f, const QString &)
void mouseReleaseEvent(QMouseEvent *)
QString pretty(char sep= ' ') const
Convert SubCost value into a QString, spaced every 3 digits.
void zoomRectMoveFinished()
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const =0
A QGraphicsView showing a part of the call graph and another zoomed out CanvasView in a border acting...
TraceFunction * toFunc(QString)
void focusOutEvent(QFocusEvent *)
void setAlphaF(qreal alpha)
double toDouble(bool *ok) const
#define DEFAULT_SHOWSKIPPED
void focusInEvent(QFocusEvent *)
TraceFunction * to() const
bool contains(const QPointF &point) const
void setPosition(int f, Position)
void setCaller(GraphEdge *)
virtual bool expandCycles()=0
const QPoint & pos() const
void setBrush(const QBrush &brush)
void layoutTriggered(QAction *)
This class holds profiling data of multiple tracefiles generated with cachegrind on one command...
void setPolygon(const QPolygonF &polygon)
virtual QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
void addItem(QGraphicsItem *item)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
A call from one to another function.
QPainterPath path() const
void zoomRectMoved(qreal, qreal)
#define DEFAULT_CALLLIMIT
void zoomRectMoved(qreal dx, qreal dy)
ProfileContext::Type groupType() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
void saveOptions(const QString &prefix, const QString &postfix)
QByteArray readAllStandardOutput()
void setZoomRect(const QRectF &r)
iterator find(const Key &key)
void start(const QString &program, const QStringList &arguments, QFlags< QIODevice::OpenModeFlag > mode)
void restoreOptions(const QString &prefix, const QString &postfix)
void zoomRectMoveFinished()
QByteArray readAllStandardError()
virtual void scrollContentsBy(int dx, int dy)
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
ProfileCostArray * inclusive()
GraphNode * fromNode() const
virtual bool showSkipped()=0
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
CanvasFrame(CanvasNode *)
virtual QVariant value(const QString &key, const QVariant &defaultValue) const
const T value(const Key &key) const
TraceCall * nextVisible()
void setSingleShot(bool singleShot)
TraceFunction * visibleCaller()
virtual void resizeEvent(QResizeEvent *event)
void scrollContentsBy(int dx, int dy)
CanvasNode * canvasNode() const