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"