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"