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  * Source View
0011  */
0012 
0013 #include "sourceview.h"
0014 
0015 #include <QDebug>
0016 #include <QDir>
0017 #include <QFile>
0018 #include <QFileInfo>
0019 #include <QAction>
0020 #include <QMenu>
0021 #include <QScrollBar>
0022 #include <QHeaderView>
0023 #include <QKeyEvent>
0024 
0025 #include "globalconfig.h"
0026 #include "sourceitem.h"
0027 
0028 
0029 
0030 //
0031 // SourceView
0032 //
0033 
0034 
0035 SourceView::SourceView(TraceItemView* parentView,
0036                        QWidget* parent)
0037     : QTreeWidget(parent), TraceItemView(parentView)
0038 {
0039     _inSelectionUpdate = false;
0040 
0041     _arrowLevels = 0;
0042 
0043     setColumnCount(5);
0044     setRootIsDecorated(false);
0045     setAllColumnsShowFocus(true);
0046     setUniformRowHeights(true);
0047     // collapsing call/jump lines by double-click is confusing
0048     setExpandsOnDoubleClick(false);
0049 
0050     QStringList headerLabels;
0051     headerLabels << tr( "#" )
0052                  << tr( "Cost" )
0053                  << tr( "Cost 2" )
0054                  << QString()
0055                  <<  tr( "Source");
0056     setHeaderLabels(headerLabels);
0057 
0058     // sorting will be enabled after refresh()
0059     sortByColumn(0, Qt::AscendingOrder);
0060     header()->setSortIndicatorShown(false);
0061     this->setItemDelegate(new SourceItemDelegate(this));
0062     this->setWhatsThis( whatsThis());
0063 
0064     connect( this,
0065              &QTreeWidget::currentItemChanged,
0066              this, &SourceView::selectedSlot);
0067 
0068     setContextMenuPolicy(Qt::CustomContextMenu);
0069     connect( this,
0070              &QWidget::customContextMenuRequested,
0071              this, &SourceView::context);
0072 
0073     connect(this,
0074             &QTreeWidget::itemDoubleClicked,
0075             this, &SourceView::activatedSlot);
0076 
0077     connect(header(), &QHeaderView::sectionClicked,
0078             this, &SourceView::headerClicked);
0079 }
0080 
0081 QString SourceView::whatsThis() const
0082 {
0083     return tr( "<b>Annotated Source</b>"
0084                "<p>The annotated source list shows the "
0085                "source lines of the current selected function "
0086                "together with (self) cost spent while executing the "
0087                "code of this source line. If there was a call "
0088                "in a source line, lines with details on the "
0089                "call happening are inserted into the source: "
0090                "the cost spent inside of the call, the "
0091                "number of calls happening, and the call destination.</p>"
0092                "<p>Select a inserted call information line to "
0093                "make the destination function current.</p>");
0094 }
0095 
0096 void SourceView::context(const QPoint & p)
0097 {
0098     int c = columnAt(p.x());
0099     QTreeWidgetItem* i = itemAt(p);
0100     QMenu popup;
0101 
0102     TraceLineCall* lc = i ? ((SourceItem*) i)->lineCall() : nullptr;
0103     TraceLineJump* lj = i ? ((SourceItem*) i)->lineJump() : nullptr;
0104     TraceFunction* f = lc ? lc->call()->called() : nullptr;
0105     TraceLine* line = lj ? lj->lineTo() : nullptr;
0106 
0107     QAction* activateFunctionAction = nullptr;
0108     QAction* activateLineAction = nullptr;
0109     if (f) {
0110         QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName()));
0111         activateFunctionAction = popup.addAction(menuText);
0112         popup.addSeparator();
0113     }
0114     else if (line) {
0115         QString menuText = tr("Go to Line %1").arg(line->name());
0116         activateLineAction = popup.addAction(menuText);
0117         popup.addSeparator();
0118     }
0119 
0120     if ((c == 1) || (c == 2)) {
0121         addEventTypeMenu(&popup);
0122         popup.addSeparator();
0123     }
0124     addGoMenu(&popup);
0125 
0126     QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height())));
0127     if (a == activateFunctionAction)
0128         TraceItemView::activated(f);
0129     else if (a == activateLineAction)
0130         TraceItemView::activated(line);
0131 }
0132 
0133 
0134 void SourceView::selectedSlot(QTreeWidgetItem *i, QTreeWidgetItem *)
0135 {
0136     if (!i) return;
0137     // programatically selected items are not signalled
0138     if (_inSelectionUpdate) return;
0139 
0140     TraceLineCall* lc = ((SourceItem*) i)->lineCall();
0141     TraceLineJump* lj = ((SourceItem*) i)->lineJump();
0142 
0143     if (!lc && !lj) {
0144         TraceLine* l = ((SourceItem*) i)->line();
0145         if (l) {
0146             _selectedItem = l;
0147             selected(l);
0148         }
0149         return;
0150     }
0151 
0152     TraceFunction* f = lc ? lc->call()->called() : nullptr;
0153     if (f) {
0154         _selectedItem = f;
0155         selected(f);
0156     }
0157     else {
0158         TraceLine* line = lj ? lj->lineTo() : nullptr;
0159         if (line) {
0160             _selectedItem = line;
0161             selected(line);
0162         }
0163     }
0164 }
0165 
0166 void SourceView::activatedSlot(QTreeWidgetItem* i, int)
0167 {
0168     if (!i) return;
0169 
0170     TraceLineCall* lc = ((SourceItem*) i)->lineCall();
0171     TraceLineJump* lj = ((SourceItem*) i)->lineJump();
0172 
0173     if (!lc && !lj) {
0174         TraceLine* l = ((SourceItem*) i)->line();
0175         if (l) TraceItemView::activated(l);
0176         return;
0177     }
0178 
0179     TraceFunction* f = lc ? lc->call()->called() : nullptr;
0180     if (f) TraceItemView::activated(f);
0181     else {
0182         TraceLine* line = lj ? lj->lineTo() : nullptr;
0183         if (line) TraceItemView::activated(line);
0184     }
0185 }
0186 
0187 void SourceView::keyPressEvent(QKeyEvent* event)
0188 {
0189     QTreeWidgetItem *item = currentItem();
0190     if (item && ((event->key() == Qt::Key_Return) ||
0191                  (event->key() == Qt::Key_Space)))
0192     {
0193         activatedSlot(item, 0);
0194     }
0195     QTreeView::keyPressEvent(event);
0196 }
0197 
0198 CostItem* SourceView::canShow(CostItem* i)
0199 {
0200     ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
0201 
0202     switch(t) {
0203     case ProfileContext::Function:
0204     case ProfileContext::Instr:
0205     case ProfileContext::Line:
0206         return i;
0207 
0208     default:
0209         break;
0210     }
0211 
0212     return nullptr;
0213 }
0214 
0215 void SourceView::doUpdate(int changeType, bool)
0216 {
0217     // Special case ?
0218     if (changeType == selectedItemChanged) {
0219 
0220         if (!_selectedItem) {
0221             clearSelection();
0222             return;
0223         }
0224 
0225         TraceLine* sLine = nullptr;
0226         if (_selectedItem->type() == ProfileContext::Line)
0227             sLine = (TraceLine*) _selectedItem;
0228         if (_selectedItem->type() == ProfileContext::Instr)
0229             sLine = ((TraceInstr*)_selectedItem)->line();
0230         if ((_selectedItem->type() != ProfileContext::Function) && (sLine == nullptr))
0231             return;
0232 
0233         QList<QTreeWidgetItem*> items = selectedItems();
0234         SourceItem* si = (items.count() > 0) ? (SourceItem*)items[0] : nullptr;
0235         if (si) {
0236             if (sLine && (si->line() == sLine)) return;
0237             if (si->lineCall() &&
0238                 (si->lineCall()->call()->called() == _selectedItem)) return;
0239         }
0240 
0241         QTreeWidgetItem *item, *item2;
0242         for (int i=0; i<topLevelItemCount(); i++) {
0243             item = topLevelItem(i);
0244             si = (SourceItem*)item;
0245             if (sLine && (si->line() == sLine)) {
0246                 scrollToItem(item);
0247                 _inSelectionUpdate = true;
0248                 setCurrentItem(item);
0249                 _inSelectionUpdate = false;
0250                 break;
0251             }
0252             bool foundCall = false;
0253             for (int j=0; j<item->childCount(); j++) {
0254                 item2 = item->child(j);
0255                 si = (SourceItem*)item2;
0256                 if (!si->lineCall()) continue;
0257                 if (si->lineCall()->call()->called() == _selectedItem) {
0258                     scrollToItem(item2);
0259                     _inSelectionUpdate = true;
0260                     setCurrentItem(item2);
0261                     _inSelectionUpdate = false;
0262                     foundCall = true;
0263                     break;
0264                 }
0265             }
0266             if (foundCall) break;
0267         }
0268         return;
0269     }
0270 
0271     if (changeType == groupTypeChanged) {
0272         // update group colors for call lines
0273         QTreeWidgetItem *item, *item2;
0274         for (int i=0; i<topLevelItemCount(); i++) {
0275             item = topLevelItem(i);
0276             for (int j=0; i<item->childCount(); i++) {
0277                 item2 = item->child(j);
0278                 ((SourceItem*)item2)->updateGroup();
0279             }
0280         }
0281         return;
0282     }
0283 
0284     // On eventTypeChanged, we can not just change the costs shown in
0285     // already existing items, as costs of 0 should make the line to not
0286     // be shown at all. So we do a full refresh.
0287 
0288     refresh();
0289 }
0290 
0291 void SourceView::refresh()
0292 {
0293     int originalPosition = verticalScrollBar()->value();
0294     clear();
0295     setColumnWidth(0, 20);
0296     setColumnWidth(1, 50);
0297     setColumnHidden(2, (_eventType2 == nullptr));
0298     setColumnWidth(2, 50);
0299     // arrows, defaults to invisible
0300     setColumnHidden(3, true);
0301     if (_eventType)
0302         headerItem()->setText(1, _eventType->name());
0303     if (_eventType2)
0304         headerItem()->setText(2, _eventType2->name());
0305 
0306     _arrowLevels = 0;
0307     if (!_data || !_activeItem) {
0308         return;
0309     }
0310 
0311     ProfileContext::Type t = _activeItem->type();
0312     TraceFunction* f = nullptr;
0313     if (t == ProfileContext::Function) f = (TraceFunction*) _activeItem;
0314     if (t == ProfileContext::Instr) {
0315         f = ((TraceInstr*)_activeItem)->function();
0316         if (!_selectedItem)
0317             _selectedItem = ((TraceInstr*)_activeItem)->line();
0318     }
0319     if (t == ProfileContext::Line) {
0320         f = ((TraceLine*)_activeItem)->functionSource()->function();
0321         if (!_selectedItem) _selectedItem = _activeItem;
0322     }
0323 
0324     if (!f) return;
0325 
0326     TraceFunctionSource* mainSF = f->sourceFile();
0327 
0328     // skip first source if there is no debug info and there are more sources
0329     // (this is for a bug in GCC 2.95.x giving unknown source for prologs)
0330     if (mainSF &&
0331         (mainSF->firstLineno() == 0) &&
0332         (mainSF->lastLineno() == 0) &&
0333         (f->sourceFiles().count()>1) ) {
0334         // skip
0335     }
0336     else
0337         fillSourceFile(mainSF, 0);
0338 
0339     int fileno = 0;
0340     foreach(TraceFunctionSource* sf, f->sourceFiles()) {
0341         fileno++;
0342         if (sf != mainSF)
0343             fillSourceFile(sf, fileno);
0344     }
0345 
0346     if (!_eventType2) {
0347 #if QT_VERSION >= 0x050000
0348         header()->setSectionResizeMode(2, QHeaderView::Interactive);
0349 #else
0350         header()->setResizeMode(2, QHeaderView::Interactive);
0351 #endif
0352     }
0353     // reset to the original position - this is useful when the view
0354     // is refreshed just because we change between relative/absolute
0355     // FIXME: this overrides scrolling to selected item
0356     verticalScrollBar()->setValue(originalPosition);
0357 }
0358 
0359 
0360 /* Helper for fillSourceList:
0361  * search recursive for a file, starting from a base dir
0362  * If found, returns true and <dir> is set to the file path.
0363  */
0364 static bool searchFileRecursive(QString& dir, const QString& name)
0365 {
0366     // we leave this in...
0367     qDebug("Checking %s/%s", qPrintable(dir), qPrintable(name));
0368 
0369     if (QFile::exists(dir + '/' + name)) return true;
0370 
0371     // check in subdirectories
0372     QDir d(dir);
0373     d.setFilter( QDir::Dirs | QDir::NoSymLinks );
0374     d.setSorting( QDir::Unsorted );
0375     QStringList subdirs = d.entryList();
0376     QStringList::const_iterator it =subdirs.constBegin();
0377     for(; it != subdirs.constEnd(); ++it ) {
0378         if (*it == QLatin1Char('.') || *it == QLatin1String("..") || *it == QLatin1String("CVS")) continue;
0379 
0380         dir = d.filePath(*it);
0381         if (searchFileRecursive(dir, name)) return true;
0382     }
0383     return false;
0384 }
0385 
0386 /* Search for a source file in different places.
0387  * If found, returns true and <dir> is set to the file path.
0388  */
0389 bool SourceView::searchFile(QString& dir,
0390                             TraceFunctionSource* sf)
0391 {
0392     QString name = sf->file()->shortName();
0393 
0394     if (QDir::isAbsolutePath(dir)) {
0395         if (QFile::exists(dir + '/' + name)) return true;
0396     }
0397     else {
0398         /* Directory is relative. Check
0399      * - relative to cwd
0400      * - relative to path of data file
0401      */
0402         QString base = QDir::currentPath() + '/' + dir;
0403         if (QFile::exists(base + '/' + name)) {
0404             dir = base;
0405             return true;
0406         }
0407 
0408         TracePart* firstPart = _data->parts().first();
0409         if (firstPart) {
0410             QFileInfo partFile(firstPart->name());
0411             if (QFileInfo(partFile.absolutePath(), name).exists()) {
0412                 dir = partFile.absolutePath();
0413                 return true;
0414             }
0415         }
0416     }
0417 
0418     QStringList list = GlobalConfig::sourceDirs(_data,
0419                                                 sf->function()->object());
0420     QStringList::const_iterator it;
0421     for ( it = list.constBegin(); it != list.constEnd(); ++it ) {
0422         dir = *it;
0423         if (searchFileRecursive(dir, name)) return true;
0424     }
0425 
0426     return false;
0427 }
0428 
0429 
0430 void SourceView::updateJumpArray(uint lineno, SourceItem* si,
0431                                  bool ignoreFrom, bool ignoreTo)
0432 {
0433     uint lowLineno, highLineno;
0434     int iEnd = -1, iStart = -1;
0435 
0436     if (0) qDebug("updateJumpArray(line %d, jump to %s)",
0437                   lineno,
0438                   si->lineJump()
0439                   ? qPrintable(si->lineJump()->lineTo()->name()) : "?" );
0440 
0441     while(_lowListIter != _lowList.end()) {
0442         TraceLineJump* lj= *_lowListIter;
0443         lowLineno = lj->lineFrom()->lineno();
0444         if (lj->lineTo()->lineno() < lowLineno)
0445             lowLineno = lj->lineTo()->lineno();
0446 
0447         if (lowLineno > lineno) break;
0448 
0449         if (ignoreFrom && (lowLineno < lj->lineTo()->lineno())) break;
0450         if (ignoreTo && (lowLineno < lj->lineFrom()->lineno())) break;
0451 
0452         if (si->lineJump() && (lj != si->lineJump())) break;
0453 
0454         int asize = (int)_jump.size();
0455 #if 0
0456         for(iStart=0;iStart<asize;iStart++)
0457             if (_jump[iStart] &&
0458                 (_jump[iStart]->lineTo() == lj->lineTo())) break;
0459 #else
0460         iStart = asize;
0461 #endif
0462 
0463         if (iStart == asize) {
0464             for(iStart=0; iStart<asize; ++iStart)
0465                 if (_jump[iStart] == nullptr) break;
0466 
0467             if (iStart== asize) {
0468                 asize++;
0469                 _jump.resize(asize);
0470                 if (asize > _arrowLevels) _arrowLevels = asize;
0471             }
0472 
0473             if (0) qDebug(" start %d (%s to %s)",
0474                           iStart,
0475                           qPrintable(lj->lineFrom()->name()),
0476                           qPrintable(lj->lineTo()->name()));
0477 
0478             _jump[iStart] = lj;
0479         }
0480         _lowListIter++;
0481     }
0482 
0483     si->setJumpArray(_jump);
0484 
0485     while(_highListIter != _highList.end()) {
0486         TraceLineJump* lj= *_highListIter;
0487         highLineno = lj->lineFrom()->lineno();
0488         if (lj->lineTo()->lineno() > highLineno) {
0489             highLineno = lj->lineTo()->lineno();
0490             if (ignoreTo) break;
0491         }
0492         else if (ignoreFrom) break;
0493 
0494         if (highLineno > lineno) break;
0495 
0496         for(iEnd=0; iEnd< (int)_jump.size(); ++iEnd)
0497             if (_jump[iEnd] == lj) break;
0498         if (iEnd == (int)_jump.size()) {
0499             qDebug("LineView: no jump start for end at %x ?", highLineno);
0500             iEnd = -1;
0501         }
0502 
0503         if (0 && (iEnd>=0))
0504             qDebug(" end %d (%s to %s)",
0505                    iEnd,
0506                    qPrintable(_jump[iEnd]->lineFrom()->name()),
0507                    qPrintable(_jump[iEnd]->lineTo()->name()));
0508 
0509         if (0 && lj) qDebug("next end: %s to %s",
0510                             qPrintable(lj->lineFrom()->name()),
0511                             qPrintable(lj->lineTo()->name()));
0512 
0513         _highListIter++;
0514 
0515         if (highLineno > lineno)
0516             break;
0517         else {
0518             if (iEnd>=0) _jump[iEnd] = nullptr;
0519             iEnd = -1;
0520         }
0521     }
0522     if (iEnd>=0) _jump[iEnd] = nullptr;
0523 }
0524 
0525 
0526 // compare functions for jump arrow drawing
0527 
0528 void getJumpLines(const TraceLineJump* jump, uint& low, uint& high)
0529 {
0530     low  = jump->lineFrom()->lineno();
0531     high = jump->lineTo()->lineno();
0532 
0533     if (low > high) {
0534         uint t = low;
0535         low = high;
0536         high = t;
0537     }
0538 }
0539 
0540 // sort jumps according to lower line number
0541 bool lineJumpLowLessThan(const TraceLineJump* jump1,
0542                          const TraceLineJump* jump2)
0543 {
0544     uint line1Low, line1High, line2Low, line2High;
0545 
0546     getJumpLines(jump1, line1Low, line1High);
0547     getJumpLines(jump2, line2Low, line2High);
0548 
0549     if (line1Low != line2Low) return (line1Low < line2Low);
0550     // jump ends come before jump starts
0551     bool low1IsEnd = (line1Low == jump1->lineTo()->lineno());
0552     bool low2IsEnd = (line2Low == jump2->lineTo()->lineno());
0553     if (low1IsEnd && !low2IsEnd) return true;
0554     if (low2IsEnd && !low1IsEnd) return false;
0555     // both the low line of jump 1 and 2 are end or start
0556     return (line1High < line2High);
0557 }
0558 
0559 // sort jumps according to higher line number
0560 bool lineJumpHighLessThan(const TraceLineJump* jump1,
0561                           const TraceLineJump* jump2)
0562 {
0563     uint line1Low, line1High, line2Low, line2High;
0564 
0565     getJumpLines(jump1, line1Low, line1High);
0566     getJumpLines(jump2, line2Low, line2High);
0567 
0568     if (line1High != line2High) return (line1High < line2High);
0569     // jump ends come before jump starts
0570     bool high1IsEnd = (line1High == jump1->lineTo()->lineno());
0571     bool high2IsEnd = (line2High == jump2->lineTo()->lineno());
0572     if (high1IsEnd && !high2IsEnd) return true;
0573     if (high2IsEnd && !high1IsEnd) return false;
0574     // both the high line of jump 1 and 2 are end or start
0575     return (line1Low < line2Low);
0576 }
0577 
0578 /* If sourceList is empty we set the source file name into the header,
0579  * else this code is of a inlined function, and we add "inlined from..."
0580  */
0581 void SourceView::fillSourceFile(TraceFunctionSource* sf, int fileno)
0582 {
0583     if (!sf) return;
0584 
0585     if (0) qDebug("Selected Item %s",
0586                   _selectedItem ? qPrintable(_selectedItem->name()) : "(none)");
0587 
0588     TraceLineMap::Iterator lineIt, lineItEnd;
0589     int nextCostLineno = 0, lastCostLineno = 0;
0590 
0591     bool validSourceFile = (!sf->file()->name().isEmpty());
0592 
0593     TraceLine* sLine = nullptr;
0594     if (_selectedItem) {
0595         if (_selectedItem->type() == ProfileContext::Line)
0596             sLine = (TraceLine*) _selectedItem;
0597         if (_selectedItem->type() == ProfileContext::Instr)
0598             sLine = ((TraceInstr*)_selectedItem)->line();
0599     }
0600 
0601     if (validSourceFile) {
0602         TraceLineMap* lineMap = sf->lineMap();
0603         if (lineMap) {
0604             lineIt    = lineMap->begin();
0605             lineItEnd = lineMap->end();
0606             // get first line with cost of selected type
0607             while(lineIt != lineItEnd) {
0608                 if (&(*lineIt) == sLine) break;
0609                 if ((*lineIt).hasCost(_eventType)) break;
0610                 if (_eventType2 && (*lineIt).hasCost(_eventType2)) break;
0611                 ++lineIt;
0612             }
0613 
0614             nextCostLineno     = (lineIt == lineItEnd) ? 0 : (*lineIt).lineno();
0615             if (nextCostLineno<0) {
0616                 qDebug() << "SourceView::fillSourceFile: Negative line number "
0617                          << nextCostLineno;
0618                 qDebug() << "  Function '" << sf->function()->name() << "'";
0619                 qDebug() << "  File '" << sf->file()->name() << "'";
0620                 nextCostLineno = 0;
0621             }
0622 
0623         }
0624 
0625         if (nextCostLineno == 0) {
0626             new SourceItem(this, this, fileno, 1, false,
0627                            tr("There is no cost of current selected type associated"));
0628             new SourceItem(this, this, fileno, 2, false,
0629                            tr("with any source line of this function in file"));
0630             new SourceItem(this, this, fileno, 3, false,
0631                            QStringLiteral("    '%1'").arg(sf->file()->prettyName()));
0632             new SourceItem(this, this, fileno, 4, false,
0633                            tr("Thus, no annotated source can be shown."));
0634             return;
0635         }
0636     }
0637 
0638     QString filename = sf->file()->shortName();
0639     QString dir = sf->file()->directory();
0640     if (!dir.isEmpty())
0641         filename = dir + '/' + filename;
0642 
0643     if (nextCostLineno>0) {
0644         // we have debug info... search for source file
0645         if (searchFile(dir, sf)) {
0646             filename = dir + '/' + sf->file()->shortName();
0647             // no need to search again
0648             sf->file()->setDirectory(dir);
0649         }
0650         else
0651             nextCostLineno = 0;
0652     }
0653 
0654     // do it here, because the source directory could have been set before
0655     if (topLevelItemCount()==0) {
0656         if (validSourceFile && (nextCostLineno != 0))
0657             new SourceItem(this, this, fileno, 0, true,
0658                            tr("--- From '%1' ---").arg(filename));
0659     }
0660     else {
0661         new SourceItem(this, this, fileno, 0, true,
0662                        validSourceFile ?
0663                            tr("--- Inlined from '%1' ---").arg(filename) :
0664                            tr("--- Inlined from unknown source ---"));
0665     }
0666 
0667     if (nextCostLineno == 0) {
0668         new SourceItem(this, this, fileno, 1, false,
0669                        tr("There is no source available for the following function:"));
0670         new SourceItem(this, this, fileno, 2, false,
0671                        QStringLiteral("    '%1'").arg(sf->function()->prettyName()));
0672         if (sf->file()->name().isEmpty()) {
0673             new SourceItem(this, this, fileno, 3, false,
0674                            tr("This is because no debug information is present."));
0675             new SourceItem(this, this, fileno, 4, false,
0676                            tr("Recompile source and redo the profile run."));
0677             if (sf->function()->object()) {
0678                 new SourceItem(this, this, fileno, 5, false,
0679                                tr("The function is located in this ELF object:"));
0680                 new SourceItem(this, this, fileno, 6, false,
0681                                QStringLiteral("    '%1'")
0682                                .arg(sf->function()->object()->prettyName()));
0683             }
0684         }
0685         else {
0686             new SourceItem(this, this, fileno, 3, false,
0687                            tr("This is because its source file cannot be found:"));
0688             new SourceItem(this, this, fileno, 4, false,
0689                            QStringLiteral("    '%1'").arg(sf->file()->name()));
0690             new SourceItem(this, this, fileno, 5, false,
0691                            tr("Add the folder of this file to the source folder list."));
0692             new SourceItem(this, this, fileno, 6, false,
0693                            tr("The list can be found in the configuration dialog."));
0694         }
0695         return;
0696     }
0697 
0698     // initialisation for arrow drawing
0699     // create sorted list of jumps (for jump arrows)
0700     TraceLineMap::Iterator it = lineIt, nextIt;
0701     _lowList.clear();
0702     _highList.clear();
0703     while(1) {
0704 
0705         nextIt = it;
0706         ++nextIt;
0707         while(nextIt != lineItEnd) {
0708             if (&(*nextIt) == sLine) break;
0709             if ((*nextIt).hasCost(_eventType)) break;
0710             if (_eventType2 && (*nextIt).hasCost(_eventType2)) break;
0711             ++nextIt;
0712         }
0713 
0714         TraceLineJumpList jlist = (*it).lineJumps();
0715         foreach(TraceLineJump* lj, jlist) {
0716             if (lj->executedCount()==0) continue;
0717             // skip jumps to next source line with cost
0718             //if (lj->lineTo() == &(*nextIt)) continue;
0719 
0720             _lowList.append(lj);
0721             _highList.append(lj);
0722         }
0723         it = nextIt;
0724         if (it == lineItEnd) break;
0725     }
0726     std::sort(_lowList.begin(), _lowList.end(), lineJumpLowLessThan);
0727     std::sort(_highList.begin(), _highList.end(), lineJumpHighLessThan);
0728     _lowListIter = _lowList.begin(); // iterators to list start
0729     _highListIter = _highList.begin();
0730     _jump.resize(0);
0731 
0732     char buf[160];
0733     bool inside = false, skipLineWritten = true;
0734     int readBytes;
0735     int fileLineno = 0;
0736     SubCost most = 0;
0737 
0738     QList<QTreeWidgetItem*> items;
0739     TraceLine* currLine;
0740     SourceItem *si, *si2, *item = nullptr, *first = nullptr, *selected = nullptr;
0741     QFile file(filename);
0742     bool fileEndReached = false;
0743     if (!file.open(QIODevice::ReadOnly)) return;
0744     while (1) {
0745         readBytes=file.readLine(buf, sizeof( buf ));
0746         if (readBytes<=0) {
0747             // for nice empty 4 lines after function with EOF
0748             buf[0] = 0;
0749             if (readBytes<0) fileEndReached = true;
0750         }
0751 
0752         if ((readBytes >0) && (buf[readBytes-1] != '\n')) {
0753             /* Something was read but not ending in newline. I.e.
0754          * - buffer was not big enough: discard rest of line, add "..."
0755          * - this is last line of file, not ending in newline
0756          * NB: checking for '\n' is enough for all systems.
0757          */
0758             int r;
0759             char buf2[32];
0760             bool somethingRead = false;
0761             while(1) {
0762                 r = file.readLine(buf2, sizeof(buf2));
0763                 if ((r<=0) || (buf2[r-1] == '\n')) break;
0764                 somethingRead = true;
0765             }
0766             if (somethingRead) {
0767                 // add dots as sign that we truncated the line
0768                 Q_ASSERT(readBytes>3);
0769                 buf[readBytes-1] = buf[readBytes-2] = buf[readBytes-3] = '.';
0770             }
0771         }
0772         else if ((readBytes>0) && (buf[readBytes-1] == '\n'))
0773             buf[readBytes-1] = 0;
0774 
0775 
0776         // keep fileLineno inside [lastCostLineno;nextCostLineno]
0777         fileLineno++;
0778         if (fileLineno == nextCostLineno) {
0779             currLine = &(*lineIt);
0780 
0781             // get next line with cost of selected type
0782             ++lineIt;
0783             while(lineIt != lineItEnd) {
0784                 if (&(*lineIt) == sLine) break;
0785                 if ((*lineIt).hasCost(_eventType)) break;
0786                 if (_eventType2 && (*lineIt).hasCost(_eventType2)) break;
0787                 ++lineIt;
0788             }
0789 
0790             lastCostLineno = nextCostLineno;
0791             nextCostLineno = (lineIt == lineItEnd) ? 0 : (*lineIt).lineno();
0792         }
0793         else
0794             currLine = nullptr;
0795 
0796         // update inside
0797         if (!inside) {
0798             if (currLine) inside = true;
0799         }
0800         else {
0801             if ( (fileLineno > lastCostLineno) &&
0802                  ((nextCostLineno == 0) ||
0803                   (fileLineno < nextCostLineno - GlobalConfig::noCostInside()) ))
0804                 inside = false;
0805         }
0806 
0807         int context = GlobalConfig::context();
0808 
0809         if ( ((lastCostLineno==0) || (fileLineno > lastCostLineno + context)) &&
0810              ((nextCostLineno==0) || (fileLineno < nextCostLineno - context))) {
0811             if ((lineIt == lineItEnd) || fileEndReached) break;
0812 
0813             if (!skipLineWritten) {
0814                 skipLineWritten = true;
0815                 // a "skipping" line: print "..." instead of a line number
0816                 strcpy(buf,"...");
0817             }
0818             else
0819                 continue;
0820         }
0821         else
0822             skipLineWritten = false;
0823 
0824         QString s = QString(buf);
0825         if(s.size() > 0 && s.at(s.length()-1) == '\r')
0826             s = s.left(s.length()-1);
0827         si = new SourceItem(this, nullptr,
0828                             fileno, fileLineno, inside, s,
0829                             currLine);
0830         items.append(si);
0831 
0832         if (!currLine) continue;
0833 
0834         if (!selected && (currLine == sLine)) selected = si;
0835         if (!first) first = si;
0836 
0837         if (currLine->subCost(_eventType) > most) {
0838             item = si;
0839             most = currLine->subCost(_eventType);
0840         }
0841 
0842         si->setExpanded(true);
0843         foreach(TraceLineCall* lc,  currLine->lineCalls()) {
0844             if ((lc->subCost(_eventType)==0) &&
0845                 (lc->subCost(_eventType2)==0)) continue;
0846 
0847             if (lc->subCost(_eventType) > most) {
0848                 item = si;
0849                 most = lc->subCost(_eventType);
0850             }
0851 
0852             si2 = new SourceItem(this, si, fileno, fileLineno, currLine, lc);
0853 
0854             if (!selected && (lc->call()->called() == _selectedItem))
0855                 selected = si2;
0856         }
0857 
0858         foreach(TraceLineJump* lj, currLine->lineJumps()) {
0859             if (lj->executedCount()==0) continue;
0860 
0861             new SourceItem(this, si, fileno, fileLineno, currLine, lj);
0862         }
0863     }
0864 
0865     file.close();
0866 
0867     // Resize column 0 (line number) and 1/2 (cost) to contents
0868 #if QT_VERSION >= 0x050000
0869     header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
0870     header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
0871     header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
0872 #else
0873     header()->setResizeMode(0, QHeaderView::ResizeToContents);
0874     header()->setResizeMode(1, QHeaderView::ResizeToContents);
0875     header()->setResizeMode(2, QHeaderView::ResizeToContents);
0876 #endif
0877 
0878     setSortingEnabled(false);
0879     addTopLevelItems(items);
0880     this->expandAll();
0881     setSortingEnabled(true);
0882     // always reset to line number sort
0883     sortByColumn(0, Qt::AscendingOrder);
0884     header()->setSortIndicatorShown(false);
0885 
0886     // Reallow interactive column size change after resizing to content
0887 #if QT_VERSION >= 0x050000
0888     header()->setSectionResizeMode(0, QHeaderView::Interactive);
0889     header()->setSectionResizeMode(1, QHeaderView::Interactive);
0890     header()->setSectionResizeMode(2, QHeaderView::Interactive);
0891 #else
0892     header()->setResizeMode(0, QHeaderView::Interactive);
0893     header()->setResizeMode(1, QHeaderView::Interactive);
0894     header()->setResizeMode(2, QHeaderView::Interactive);
0895 #endif
0896 
0897     if (selected) item = selected;
0898     if (item) first = item;
0899     if (first) {
0900         scrollToItem(first);
0901         _inSelectionUpdate = true;
0902         setCurrentItem(first);
0903         _inSelectionUpdate = false;
0904     }
0905 
0906     // for arrows: go down the list according to list sorting
0907     QTreeWidgetItem *item1, *item2;
0908     for (int i=0; i<topLevelItemCount(); i++) {
0909         item1 = topLevelItem(i);
0910         si = (SourceItem*)item1;
0911         updateJumpArray(si->lineno(), si, true, false);
0912 
0913         for (int j=0; j<item1->childCount(); j++) {
0914             item2 = item1->child(j);
0915             si2 = (SourceItem*)item2;
0916             if (si2->lineJump())
0917                 updateJumpArray(si->lineno(), si2, false, true);
0918             else
0919                 si2->setJumpArray(_jump);
0920         }
0921     }
0922 
0923     if (arrowLevels()) {
0924         //fix this: setColumnWidth(3, 10 + 6*arrowLevels() + itemMargin() * 2);
0925         setColumnWidth(3, 10 + 6*arrowLevels() + 2);
0926         setColumnHidden(3, false);
0927     }
0928     else
0929         setColumnHidden(3, true);
0930 }
0931 
0932 
0933 void SourceView::headerClicked(int col)
0934 {
0935     if (col == 0) {
0936         sortByColumn(col, Qt::AscendingOrder);
0937     }
0938     //All others but Source Text column Descending
0939     else if (col !=4) {
0940         sortByColumn(col, Qt::DescendingOrder);
0941     }
0942 }
0943 
0944 #include "moc_sourceview.cpp"