File indexing completed on 2024-04-28 05:41:36
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2011-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 /* 0010 * Items of source view. 0011 */ 0012 0013 #include "sourceitem.h" 0014 0015 #include <QFontDatabase> 0016 #include <QPixmap> 0017 #include <QRegularExpression> 0018 #include <QPainter> 0019 #include <QPolygon> 0020 #include <QTextDocument> 0021 #include <QLineEdit> 0022 0023 #include "globalguiconfig.h" 0024 #include "listutils.h" 0025 #include "sourceview.h" 0026 0027 0028 // 0029 // SourceItem 0030 // 0031 0032 // for source lines 0033 SourceItem::SourceItem(SourceView* sv, QTreeWidget* parent, 0034 int fileno, unsigned int lineno, 0035 bool inside, const QString& src, 0036 TraceLine* line) 0037 : QTreeWidgetItem(parent) 0038 { 0039 _view = sv; 0040 _lineno = lineno; 0041 _fileno = fileno; 0042 _inside = inside; 0043 _line = line; 0044 _lineCall = nullptr; 0045 _lineJump = nullptr; 0046 0047 setTextAlignment(0, Qt::AlignRight); 0048 setTextAlignment(1, Qt::AlignRight); 0049 setTextAlignment(2, Qt::AlignRight); 0050 0051 if (src == QLatin1String("...")) 0052 setText(0, src); 0053 else 0054 setText(0, QString::number(lineno)); 0055 0056 QString s = src; 0057 setText(4, s.replace( QStringLiteral("\t"), QStringLiteral(" ") )); 0058 0059 QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); 0060 setFont(4, font); // the source code line 0061 0062 // to allow text selection after double click 0063 setFlags(flags() | Qt::ItemIsEditable); 0064 0065 updateGroup(); 0066 updateCost(); 0067 } 0068 0069 // for call lines 0070 SourceItem::SourceItem(SourceView* sv, QTreeWidgetItem* parent, 0071 int fileno, unsigned int lineno, 0072 TraceLine* line, TraceLineCall* lineCall) 0073 : QTreeWidgetItem(parent) 0074 { 0075 _view = sv; 0076 _lineno = lineno; 0077 _fileno = fileno; 0078 _inside = true; 0079 _line = line; 0080 _lineCall = lineCall; 0081 _lineJump = nullptr; 0082 0083 setTextAlignment(0, Qt::AlignRight); 0084 setTextAlignment(1, Qt::AlignRight); 0085 setTextAlignment(2, Qt::AlignRight); 0086 0087 //qDebug("SourceItem: (file %d, line %d) Linecall to %s", 0088 // fileno, lineno, _lineCall->call()->called()->prettyName().toAscii()); 0089 0090 SubCost cc = _lineCall->callCount(); 0091 QString callStr = QStringLiteral(" "); 0092 if (cc==0) { 0093 if (((TraceItemView*)_view)->data()->maxCallCount() > 0) 0094 callStr += QObject::tr("Active call to '%1'"); 0095 else 0096 callStr += QObject::tr("Call to '%1'"); 0097 callStr = callStr.arg(_lineCall->call()->calledName()); 0098 } 0099 else 0100 callStr += QObject::tr("%n call(s) to '%2'", "", (uint64)cc) 0101 .arg(_lineCall->call()->calledName()); 0102 0103 TraceFunction* calledF = _lineCall->call()->called(); 0104 calledF->addPrettyLocation(callStr); 0105 0106 setText(4, callStr); 0107 0108 updateGroup(); 0109 updateCost(); 0110 } 0111 0112 // for jump lines 0113 SourceItem::SourceItem(SourceView* sv, QTreeWidgetItem* parent, 0114 int fileno, unsigned int lineno, 0115 TraceLine* line, TraceLineJump* lineJump) 0116 : QTreeWidgetItem(parent) 0117 { 0118 _view = sv; 0119 _lineno = lineno; 0120 _fileno = fileno; 0121 _inside = true; 0122 _line = line; 0123 _lineCall = nullptr; 0124 _lineJump = lineJump; 0125 0126 setTextAlignment(0, Qt::AlignRight); 0127 setTextAlignment(1, Qt::AlignRight); 0128 setTextAlignment(2, Qt::AlignRight); 0129 0130 //qDebug("SourceItem: (file %d, line %d) Linecall to %s", 0131 // fileno, lineno, _lineCall->call()->called()->prettyName().toAscii()); 0132 0133 QString to; 0134 if (_lineJump->lineTo()->functionSource() == _line->functionSource()) 0135 to = _lineJump->lineTo()->name(); 0136 else 0137 to = _lineJump->lineTo()->prettyName(); 0138 0139 QString jStr; 0140 if (_lineJump->isCondJump()) 0141 jStr = QObject::tr("Jump %1 of %2 times to %3") 0142 .arg(_lineJump->followedCount().pretty()) 0143 .arg(_lineJump->executedCount().pretty()) 0144 .arg(to); 0145 else 0146 jStr = QObject::tr("Jump %1 times to %2") 0147 .arg(_lineJump->executedCount().pretty()) 0148 .arg(to); 0149 0150 setText(4, jStr); 0151 } 0152 0153 0154 void SourceItem::updateGroup() 0155 { 0156 if (!_lineCall) return; 0157 0158 TraceFunction* f = _lineCall->call()->called(); 0159 QColor c = GlobalGUIConfig::functionColor(_view->groupType(), f); 0160 setIcon(4, colorPixmap(10, 10, c)); 0161 } 0162 0163 void SourceItem::updateCost() 0164 { 0165 _pure = SubCost(0); 0166 _pure2 = SubCost(0); 0167 0168 if (!_line) return; 0169 if (_lineJump) return; 0170 0171 ProfileCostArray* lineCost = _lineCall ? (ProfileCostArray*)_lineCall : (ProfileCostArray*)_line; 0172 0173 // do not show any cost inside of cycles 0174 if (_lineCall && 0175 ((_lineCall->call()->inCycle()>0) || 0176 (_lineCall->call()->isRecursion()))) { 0177 QString str; 0178 QPixmap p; 0179 0180 QFontMetrics fm(font(4)); 0181 p = QIcon::fromTheme(QStringLiteral("edit-undo")).pixmap(fm.height()); 0182 0183 if (p.isNull()) 0184 str = QObject::tr("(cycle)"); 0185 0186 setText(1, str); 0187 setIcon(1, p); 0188 setText(2, str); 0189 setIcon(2, p); 0190 return; 0191 } 0192 0193 ProfileCostArray* totalCost; 0194 if (GlobalConfig::showExpanded()) 0195 totalCost = _line->functionSource()->function()->inclusive(); 0196 else 0197 totalCost = _line->functionSource()->function()->data(); 0198 0199 EventType* ct = _view->eventType(); 0200 _pure = ct ? lineCost->subCost(ct) : SubCost(0); 0201 if (_pure == 0) { 0202 setText(1, QString()); 0203 setIcon(1, QPixmap()); 0204 } 0205 else { 0206 double total = totalCost->subCost(ct); 0207 double pure = 100.0 * _pure / total; 0208 0209 if (GlobalConfig::showPercentage()) 0210 setText(1, QStringLiteral("%1") 0211 .arg(pure, 0, 'f', GlobalConfig::percentPrecision())); 0212 else 0213 setText(1, _pure.pretty()); 0214 0215 setIcon(1, costPixmap(ct, lineCost, total, false)); 0216 } 0217 0218 EventType* ct2 = _view->eventType2(); 0219 _pure2 = ct2 ? lineCost->subCost(ct2) : SubCost(0); 0220 if (_pure2 == 0) { 0221 setText(2, QString()); 0222 setIcon(2, QPixmap()); 0223 } 0224 else { 0225 double total = totalCost->subCost(ct2); 0226 double pure2 = 100.0 * _pure2 / total; 0227 0228 if (GlobalConfig::showPercentage()) 0229 setText(2, QStringLiteral("%1") 0230 .arg(pure2, 0, 'f', GlobalConfig::percentPrecision())); 0231 else 0232 setText(2, _pure2.pretty()); 0233 0234 setIcon(2, costPixmap(ct2, lineCost, total, false)); 0235 } 0236 } 0237 0238 0239 bool SourceItem::operator < ( const QTreeWidgetItem & other ) const 0240 { 0241 const SourceItem* si1 = this; 0242 const SourceItem* si2 = (SourceItem*) &other; 0243 int col = treeWidget()->sortColumn(); 0244 0245 if (col==1) { 0246 return (si1->_pure < si2->_pure); 0247 } 0248 if (col==2) { 0249 return (si1->_pure2 < si2->_pure2); 0250 } 0251 if (col==0) { 0252 // Sort file numbers 0253 if (si1->_fileno < si2->_fileno) return true; 0254 if (si1->_fileno > si2->_fileno) return false; 0255 0256 // Sort line numbers 0257 if (si1->_lineno < si2->_lineno) return true; 0258 if (si1->_lineno > si2->_lineno) return false; 0259 0260 // Same line: code gets above calls/jumps 0261 if (!si1->_lineCall && !si1->_lineJump) return true; 0262 if (!si2->_lineCall && !si2->_lineJump) return false; 0263 0264 // calls above jumps 0265 if (si1->_lineCall && !si2->_lineCall) return true; 0266 if (si2->_lineCall && !si1->_lineCall) return false; 0267 0268 if (si1->_lineCall && si2->_lineCall) { 0269 // Two calls: desending sort according costs 0270 if (si1->_pure < si2->_pure) return true; 0271 if (si1->_pure > si2->_pure) return false; 0272 0273 // Two calls: sort according function names 0274 TraceFunction* f1 = si1->_lineCall->call()->called(); 0275 TraceFunction* f2 = si2->_lineCall->call()->called(); 0276 if (f1->prettyName() > f2->prettyName()) return false; 0277 return true; 0278 } 0279 0280 // Two jumps: descending sort according target line 0281 return (si1->_lineJump->lineTo()->lineno() < 0282 si2->_lineJump->lineTo()->lineno()); 0283 } 0284 return QTreeWidgetItem::operator <(other); 0285 } 0286 0287 void SourceItem::setJumpArray(const QVector<TraceLineJump*>& a) 0288 { 0289 _jump = a; 0290 } 0291 0292 0293 0294 // 0295 // SourceItemDelegate 0296 // 0297 0298 SourceItemDelegate::SourceItemDelegate(SourceView *parent) 0299 : QItemDelegate(parent) 0300 { 0301 _parent = parent; 0302 } 0303 0304 QWidget* SourceItemDelegate::createEditor(QWidget *parent, 0305 const QStyleOptionViewItem &option, 0306 const QModelIndex &index) const 0307 { 0308 QWidget* w = QItemDelegate::createEditor(parent, option, index); 0309 QLineEdit* e = qobject_cast<QLineEdit*>(w); 0310 if (e) e->setReadOnly(true); 0311 return w; 0312 } 0313 0314 QSize SourceItemDelegate::sizeHint(const QStyleOptionViewItem &option, 0315 const QModelIndex &index) const 0316 { 0317 QSize sz = QItemDelegate::sizeHint(option, index); 0318 0319 int c = index.column(); 0320 if (c != 3) return sz; 0321 0322 SourceView* sv = (SourceView*) _parent; 0323 int levels = sv->arrowLevels(); 0324 0325 if (levels == 0) 0326 return QSize(0, sz.height()); 0327 0328 // 10 pixels for the arrow, 1 pixel margin left and right 0329 return QSize(10 + 6*levels + 2, sz.height()); 0330 } 0331 0332 void SourceItemDelegate::paint(QPainter *painter, 0333 const QStyleOptionViewItem &option, 0334 const QModelIndex &index) const 0335 { 0336 int column = index.column(); 0337 SourceItem* item = static_cast<SourceItem*>(index.internalPointer()); 0338 0339 QColor color; 0340 if ( !item->inside() || ((column==1) || (column==2))) 0341 color = option.palette.color( QPalette::Button ); 0342 else if ((item->lineCall() || item->lineJump()) && column>2) 0343 color = option.palette.color( QPalette::Midlight ); 0344 if (color.isValid()) 0345 _parent->model()->setData(index, color, Qt::BackgroundRole); 0346 0347 if(column==3) 0348 paintArrows(painter, option, index); 0349 else 0350 QItemDelegate::paint(painter, option, index); 0351 } 0352 0353 void SourceItemDelegate::paintArrows(QPainter *p, 0354 const QStyleOptionViewItem &option, 0355 const QModelIndex &index) const 0356 { 0357 QTreeWidget *lv = _parent; 0358 if ( !lv ) return; 0359 SourceView* sv = (SourceView*) lv; 0360 SourceItem* item = static_cast<SourceItem*>(index.internalPointer()); 0361 const QRect& rect = option.rect; 0362 int height = rect.height(); 0363 0364 p->save(); 0365 drawBackground(p, option, index); 0366 p->translate(rect.topLeft()); 0367 0368 int marg = 1; 0369 int yy = height/2, y1, y2; 0370 QColor c; 0371 0372 int start = -1, end = -1; 0373 0374 TraceLineJump* lineJump = item->lineJump(); 0375 uint lineno = item->lineno(); 0376 TraceLineCall* lineCall = item->lineCall(); 0377 0378 // draw line borders, detect start/stop of a line 0379 for(int i=0; i< item->jumpCount(); i++) { 0380 TraceLineJump* jump = item->jump(i); 0381 if (jump == nullptr) continue; 0382 0383 y1 = 0; 0384 y2 = height; 0385 if (lineJump && 0386 (lineJump->lineTo() == jump->lineTo()) && 0387 (jump->lineFrom()->lineno() == lineno)) { 0388 0389 if (start<0) start = i; 0390 if (lineJump == jump) { 0391 if (jump->lineTo()->lineno() <= lineno) 0392 y2 = yy; 0393 else 0394 y1 = yy; 0395 } 0396 } 0397 else if (!lineJump && !lineCall && 0398 (jump->lineTo()->lineno() == lineno)) { 0399 if (end<0) end = i; 0400 if (jump->lineFrom()->lineno() < lineno) 0401 y2 = yy; 0402 else 0403 y1 = yy; 0404 } 0405 0406 c = jump->isCondJump() ? Qt::red : Qt::blue; 0407 p->fillRect( marg + 6*i, y1, 4, y2, c); 0408 p->setPen(c.lighter()); 0409 p->drawLine( marg + 6*i, y1, marg + 6*i, y2); 0410 p->setPen(c.darker()); 0411 p->drawLine( marg + 6*i +3, y1, marg + 6*i +3, y2); 0412 } 0413 0414 // draw start/stop horizontal line 0415 int x, y = yy-2, w, h = 4; 0416 if (start >= 0) { 0417 c = item->jump(start)->isCondJump() ? Qt::red : Qt::blue; 0418 x = marg + 6*start; 0419 w = 6*(sv->arrowLevels() - start) + 10; 0420 p->fillRect( x, y, w, h, c); 0421 p->setPen(c.lighter()); 0422 p->drawLine(x, y, x+w-1, y); 0423 p->drawLine(x, y, x, y+h-1); 0424 p->setPen(c.darker()); 0425 p->drawLine(x+w-1, y, x+w-1, y+h-1); 0426 p->drawLine(x+1, y+h-1, x+w-1, y+h-1); 0427 } 0428 if (end >= 0) { 0429 c = item->jump(end)->isCondJump() ? Qt::red : Qt::blue; 0430 x = marg + 6*end; 0431 w = 6*(sv->arrowLevels() - end) + 10; 0432 0433 QPolygon a; 0434 a.putPoints(0, 8, x,y+h, 0435 x,y, x+w-8,y, x+w-8,y-2, 0436 x+w,yy, 0437 x+w-8,y+h+2, x+w-8,y+h, 0438 x,y+h); 0439 p->setBrush(c); 0440 p->drawConvexPolygon(a); 0441 0442 p->setPen(c.lighter()); 0443 p->drawPolyline(a.constData(), 5); 0444 p->setPen(c.darker()); 0445 p->drawPolyline(a.constData() + 4, 2); 0446 p->setPen(c.lighter()); 0447 p->drawPolyline(a.constData() + 5, 2); 0448 p->setPen(c.darker()); 0449 p->drawPolyline(a.constData() + 6, 2); 0450 } 0451 0452 // draw inner vertical line for start/stop 0453 // this overwrites borders of horizontal line 0454 for(int i=0;i< item->jumpCount();i++) { 0455 TraceLineJump* jump = item->jump(i); 0456 if (jump == nullptr) continue; 0457 0458 c = jump->isCondJump() ? Qt::red : Qt::blue; 0459 0460 if (jump->lineFrom()->lineno() == lineno) { 0461 bool drawUp = true; 0462 if (jump->lineTo()->lineno() == lineno) 0463 if (start<0) drawUp = false; 0464 if (jump->lineTo()->lineno() > lineno) drawUp = false; 0465 if (drawUp) 0466 p->fillRect( marg + 6*i +1, 0, 2, yy, c); 0467 else 0468 p->fillRect( marg + 6*i +1, yy, 2, height-yy, c); 0469 } 0470 else if (jump->lineTo()->lineno() == lineno) { 0471 if (end<0) end = i; 0472 if (jump->lineFrom()->lineno() < lineno) 0473 p->fillRect( marg + 6*i +1, 0, 2, yy, c); 0474 else 0475 p->fillRect( marg + 6*i +1, yy, 2, height-yy, c); 0476 } 0477 } 0478 p->restore(); 0479 } 0480 0481 #include "moc_sourceitem.cpp"