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