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"