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 * Instruction View 0011 */ 0012 0013 #include "instrview.h" 0014 0015 #include <assert.h> 0016 0017 #include <QDebug> 0018 #include <QDir> 0019 #include <QFile> 0020 #include <QFileInfo> 0021 #include <QProcess> 0022 #include <QAction> 0023 #include <QMenu> 0024 #include <QScrollBar> 0025 #include <QHeaderView> 0026 #include <QKeyEvent> 0027 #include <QProcessEnvironment> 0028 0029 #include "config.h" 0030 #include "globalconfig.h" 0031 #include "instritem.h" 0032 0033 0034 // InstrView defaults 0035 0036 #define DEFAULT_SHOWHEXCODE true 0037 0038 0039 // Helpers 0040 0041 // check environment variables 0042 0043 QProcessEnvironment env; 0044 0045 static 0046 QString getSysRoot() 0047 { 0048 if (env.isEmpty()) 0049 env = QProcessEnvironment::systemEnvironment(); 0050 0051 return env.value(QStringLiteral("SYSROOT")); 0052 } 0053 0054 static 0055 QString getObjDump() 0056 { 0057 if (env.isEmpty()) 0058 env = QProcessEnvironment::systemEnvironment(); 0059 0060 return env.value(QStringLiteral("OBJDUMP"), QStringLiteral("objdump")); 0061 } 0062 0063 static 0064 QString getObjDumpFormat() 0065 { 0066 if (env.isEmpty()) 0067 env = QProcessEnvironment::systemEnvironment(); 0068 0069 return env.value(QStringLiteral("OBJDUMP_FORMAT")); 0070 } 0071 0072 0073 // parsing output of 'objdump' 0074 0075 static Addr parseAddr(char* buf) 0076 { 0077 Addr addr; 0078 uint pos = 0; 0079 0080 // check for instruction line: <space>* <hex address> ":" <space>* 0081 while(buf[pos]==' ' || buf[pos]=='\t') pos++; 0082 0083 int digits = addr.set(buf + pos); 0084 if ((digits==0) || (buf[pos+digits] != ':')) return Addr(0); 0085 0086 return addr; 0087 } 0088 0089 static bool isHexDigit(char c) 0090 { 0091 return (c >='0' && c <='9') || (c >='a' && c <='f'); 0092 } 0093 0094 /** 0095 * Parses a line from objdump assembly output, returning false for 0096 * a line without an assembly instruction. Otherwise, it sets the 0097 * output parameters addr, code, mnemonic, operands. 0098 */ 0099 static bool parseLine(const char* buf, Addr& addr, 0100 QString& code, QString& mnemonic, QString& operands) 0101 { 0102 uint pos, start; 0103 0104 // check for instruction line: <space>* <hex address> ":" <space>* 0105 0106 pos = 0; 0107 while(buf[pos]==' ' || buf[pos]=='\t') pos++; 0108 0109 int digits = addr.set(buf + pos); 0110 pos += digits; 0111 if ((digits==0) || (buf[pos] != ':')) return false; 0112 0113 // further parsing of objdump output... 0114 pos++; 0115 while(buf[pos]==' ' || buf[pos]=='\t') pos++; 0116 0117 // check for hex code, patterns "xx "* / "xxxx "* / "xxxxxxxx" 0118 // (with "x" being a lower case hex digit) 0119 start = pos; 0120 while(1) { 0121 if (! isHexDigit(buf[pos])) break; 0122 if (! isHexDigit(buf[pos+1])) break; 0123 if (buf[pos+2] == ' ') { 0124 pos += 3; 0125 continue; 0126 } 0127 if (! isHexDigit(buf[pos+2])) break; 0128 if (! isHexDigit(buf[pos+3])) break; 0129 if (buf[pos+4] == ' ') { 0130 pos += 5; 0131 continue; 0132 } 0133 if (! isHexDigit(buf[pos+4])) break; 0134 if (! isHexDigit(buf[pos+5])) break; 0135 if (! isHexDigit(buf[pos+6])) break; 0136 if (! isHexDigit(buf[pos+7])) break; 0137 if (buf[pos+8] != ' ') break; 0138 pos += 9; 0139 } 0140 if (pos <= start) return false; 0141 code = QString::fromLatin1(buf + start, pos - start - 1); 0142 0143 // skip whitespace 0144 while(buf[pos]==' ' || buf[pos]=='\t') pos++; 0145 0146 // check for mnemonic 0147 start = pos; 0148 while(buf[pos] && buf[pos]!=' ' && buf[pos]!='\t') pos++; 0149 mnemonic = QString::fromLatin1(buf + start, pos - start); 0150 0151 // skip whitespace 0152 while(buf[pos]==' '|| buf[pos]=='\t') pos++; 0153 0154 // last part are the operands 0155 int operandsLen = strlen(buf + pos); 0156 0157 // ignore a newline at end 0158 if ((operandsLen>0) && (buf[pos + operandsLen - 1] == '\n')) 0159 operandsLen--; 0160 0161 // maximal 50 chars 0162 if (operandsLen > 50) 0163 operands = QString::fromLatin1(buf + pos, 47) + QStringLiteral("..."); 0164 else 0165 operands = QString::fromLatin1(buf+pos, operandsLen); 0166 0167 if (0) qDebug("For 0x%s: Code '%s', Mnemonic '%s', Operands '%s'", 0168 qPrintable(addr.toString()), qPrintable(code), 0169 qPrintable(mnemonic), qPrintable(operands)); 0170 0171 return true; 0172 } 0173 0174 0175 0176 0177 // 0178 // InstrView 0179 // 0180 0181 0182 InstrView::InstrView(TraceItemView* parentView, 0183 QWidget* parent) 0184 : QTreeWidget(parent), TraceItemView(parentView) 0185 { 0186 _showHexCode = DEFAULT_SHOWHEXCODE; 0187 _lastHexCodeWidth = 50; 0188 0189 _inSelectionUpdate = false; 0190 _arrowLevels = 0; 0191 0192 QStringList headerLabels; 0193 headerLabels << tr( "#" ) 0194 << tr( "Cost" ) 0195 << tr( "Cost 2" ) 0196 << QString() 0197 << tr( "Hex" ) 0198 << QString() // Mnenomic 0199 << tr( "Assembly Instructions" ) 0200 << tr( "Source Position" ); 0201 setHeaderLabels(headerLabels); 0202 setRootIsDecorated(false); 0203 setAllColumnsShowFocus(true); 0204 setUniformRowHeights(true); 0205 // collapsing call/jump lines by double-click is confusing 0206 setExpandsOnDoubleClick(false); 0207 0208 // sorting will be enabled after refresh() 0209 sortByColumn(0, Qt::AscendingOrder); 0210 header()->setSortIndicatorShown(false); 0211 setItemDelegate(new InstrItemDelegate(this)); 0212 setWhatsThis( whatsThis() ); 0213 0214 connect( this, 0215 &QTreeWidget::currentItemChanged, 0216 this, &InstrView::selectedSlot ); 0217 0218 setContextMenuPolicy(Qt::CustomContextMenu); 0219 connect( this, 0220 &QWidget::customContextMenuRequested, 0221 this, &InstrView::context); 0222 0223 connect(this, 0224 &QTreeWidget::itemDoubleClicked, 0225 this, &InstrView::activatedSlot); 0226 0227 connect(header(), &QHeaderView::sectionClicked, 0228 this, &InstrView::headerClicked); 0229 0230 this->setWhatsThis( whatsThis()); 0231 } 0232 0233 0234 QString InstrView::whatsThis() const 0235 { 0236 return tr( "<b>Annotated Machine Code</b>" 0237 "<p>The annotated machine code list shows the " 0238 "assembly instructions of the current selected " 0239 "function together with (self) cost spent while " 0240 "executing an instruction. If this is a call " 0241 "instruction, lines with details on the " 0242 "call happening are inserted into the source: " 0243 "the cost spent inside of the call, the " 0244 "number of calls happening, and the call destination.</p>" 0245 "<p>The machine code shown is generated with " 0246 "the 'objdump' utility from the 'binutils' package.</p>" 0247 "<p>Select a line with call information to " 0248 "make the destination function of this call current.</p>"); 0249 } 0250 0251 void InstrView::context(const QPoint & p) 0252 { 0253 QMenu popup; 0254 int c = columnAt(p.x()); 0255 QTreeWidgetItem* i = itemAt(p); 0256 0257 TraceInstrCall* ic = i ? ((InstrItem*) i)->instrCall() : nullptr; 0258 TraceInstrJump* ij = i ? ((InstrItem*) i)->instrJump() : nullptr; 0259 TraceFunction* f = ic ? ic->call()->called() : nullptr; 0260 TraceInstr* instr = ij ? ij->instrTo() : nullptr; 0261 0262 QAction* activateFunctionAction = nullptr; 0263 QAction* activateInstrAction = nullptr; 0264 if (f) { 0265 QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName())); 0266 activateFunctionAction = popup.addAction(menuText); 0267 popup.addSeparator(); 0268 } 0269 else if (instr) { 0270 QString menuText = tr("Go to Address %1").arg(instr->name()); 0271 activateInstrAction = popup.addAction(menuText); 0272 popup.addSeparator(); 0273 } 0274 0275 if ((c == 1) || (c == 2)) { 0276 addEventTypeMenu(&popup); 0277 popup.addSeparator(); 0278 } 0279 addGoMenu(&popup); 0280 popup.addSeparator(); 0281 0282 QAction* toggleHexAction = new QAction(tr("Hex Code"), &popup); 0283 toggleHexAction->setCheckable(true); 0284 toggleHexAction->setChecked(_showHexCode); 0285 popup.addAction(toggleHexAction); 0286 0287 QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height()))); 0288 if (a == activateFunctionAction) 0289 TraceItemView::activated(f); 0290 else if (a == activateInstrAction) 0291 TraceItemView::activated(instr); 0292 else if (a == toggleHexAction) { 0293 _showHexCode = !_showHexCode; 0294 // remember width when hiding 0295 if (!_showHexCode) 0296 _lastHexCodeWidth = columnWidth(4); 0297 // fixme: when showing, width may be wrong if not initially shown 0298 setColumnWidths(); 0299 } 0300 } 0301 0302 0303 void InstrView::selectedSlot(QTreeWidgetItem *i, QTreeWidgetItem *) 0304 { 0305 if (!i) return; 0306 // programatically selected items are not signalled 0307 if (_inSelectionUpdate) return; 0308 0309 TraceInstrCall* ic = ((InstrItem*) i)->instrCall(); 0310 TraceInstrJump* ij = ((InstrItem*) i)->instrJump(); 0311 0312 if (!ic && !ij) { 0313 TraceInstr* instr = ((InstrItem*) i)->instr(); 0314 if (instr) { 0315 _selectedItem = instr; 0316 selected(instr); 0317 } 0318 return; 0319 } 0320 0321 if (ic) { 0322 _selectedItem = ic; 0323 selected(ic); 0324 } 0325 else if (ij) { 0326 _selectedItem = ij; 0327 selected(ij); 0328 } 0329 } 0330 0331 void InstrView::activatedSlot(QTreeWidgetItem* i, int) 0332 { 0333 if (!i) return; 0334 TraceInstrCall* ic = ((InstrItem*) i)->instrCall(); 0335 TraceInstrJump* ij = ((InstrItem*) i)->instrJump(); 0336 0337 if (!ic && !ij) { 0338 TraceInstr* instr = ((InstrItem*) i)->instr(); 0339 if (instr) TraceItemView::activated(instr); 0340 return; 0341 } 0342 0343 if (ic) { 0344 TraceFunction* f = ic->call()->called(); 0345 if (f) TraceItemView::activated(f); 0346 } 0347 else if (ij) { 0348 TraceInstr* instr = ij->instrTo(); 0349 if (instr) TraceItemView::activated(instr); 0350 } 0351 } 0352 0353 void InstrView::keyPressEvent(QKeyEvent* event) 0354 { 0355 QTreeWidgetItem *item = currentItem(); 0356 if (item && ((event->key() == Qt::Key_Return) || 0357 (event->key() == Qt::Key_Space))) 0358 { 0359 activatedSlot(item, 0); 0360 } 0361 QTreeView::keyPressEvent(event); 0362 } 0363 0364 CostItem* InstrView::canShow(CostItem* i) 0365 { 0366 ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType; 0367 0368 switch(t) { 0369 case ProfileContext::Function: 0370 case ProfileContext::Instr: 0371 case ProfileContext::InstrJump: 0372 case ProfileContext::Line: 0373 return i; 0374 0375 default: 0376 break; 0377 } 0378 0379 return nullptr; 0380 } 0381 0382 0383 void InstrView::doUpdate(int changeType, bool) 0384 { 0385 // Special case ? 0386 if (changeType == selectedItemChanged) { 0387 0388 if (!_selectedItem) { 0389 clearSelection(); 0390 return; 0391 } 0392 0393 QList<QTreeWidgetItem*> items = selectedItems(); 0394 InstrItem* ii = (items.count() > 0) ? (InstrItem*)items[0] : nullptr; 0395 if (ii) { 0396 if ((ii->instr() == _selectedItem) || 0397 (ii->instr() && (ii->instr()->line() == _selectedItem))) return; 0398 if (ii->instrCall() && 0399 (ii->instrCall()->call()->called() == _selectedItem)) return; 0400 } 0401 0402 TraceInstrJump* ij = nullptr; 0403 if (_selectedItem->type() == ProfileContext::InstrJump) 0404 ij = (TraceInstrJump*) _selectedItem; 0405 0406 QTreeWidgetItem *item, *item2; 0407 for (int i=0; i<topLevelItemCount(); i++) { 0408 item = topLevelItem(i); 0409 ii = (InstrItem*)item; 0410 if ((ii->instr() == _selectedItem) || 0411 (ii->instr() && (ii->instr()->line() == _selectedItem)) || 0412 (ij && (ij->instrTo() == ii->instr())) ) { 0413 scrollToItem(item); 0414 _inSelectionUpdate = true; 0415 setCurrentItem(item); 0416 _inSelectionUpdate = false; 0417 break; 0418 } 0419 item2 = nullptr; 0420 for (int j=0; i<item->childCount(); j++) { 0421 item2 = item->child(j); 0422 ii = (InstrItem*)item2; 0423 if (!ii->instrCall()) continue; 0424 if (ii->instrCall()->call()->called() == _selectedItem) { 0425 scrollToItem(item2); 0426 _inSelectionUpdate = true; 0427 setCurrentItem(item2); 0428 _inSelectionUpdate = false; 0429 break; 0430 } 0431 } 0432 if (item2) break; 0433 } 0434 return; 0435 } 0436 0437 if (changeType == groupTypeChanged) { 0438 // update group colors for call lines 0439 QTreeWidgetItem *item, *item2; 0440 for (int i=0; i<topLevelItemCount(); i++) { 0441 item = topLevelItem(i); 0442 for (int j=0; i<item->childCount(); i++) { 0443 item2 = item->child(j); 0444 ((InstrItem*)item2)->updateGroup(); 0445 } 0446 } 0447 return; 0448 } 0449 0450 // On eventTypeChanged, we can not just change the costs shown in 0451 // already existing items, as costs of 0 should make the line to not 0452 // be shown at all. So we do a full refresh. 0453 0454 refresh(); 0455 } 0456 0457 void InstrView::setColumnWidths() 0458 { 0459 #if QT_VERSION >= 0x050000 0460 header()->setSectionResizeMode(4, QHeaderView::Interactive); 0461 #else 0462 header()->setResizeMode(4, QHeaderView::Interactive); 0463 #endif 0464 setColumnHidden(4, !_showHexCode); 0465 if (_showHexCode) 0466 setColumnWidth(4, _lastHexCodeWidth); 0467 } 0468 0469 // compare functions for jump arrow drawing 0470 0471 // helper for compare 0472 void getInstrJumpAddresses(const TraceInstrJump* ij, Addr& low, Addr& high) 0473 { 0474 low = ij->instrFrom()->addr(); 0475 high = ij->instrTo()->addr(); 0476 0477 if (low > high) { 0478 Addr t = low; 0479 low = high; 0480 high = t; 0481 } 0482 } 0483 0484 // sort jumps according to lower instruction address 0485 bool instrJumpLowLessThan(const TraceInstrJump* ij1, 0486 const TraceInstrJump* ij2) 0487 { 0488 Addr addr1Low, addr1High, addr2Low, addr2High; 0489 0490 getInstrJumpAddresses(ij1, addr1Low, addr1High); 0491 getInstrJumpAddresses(ij2, addr2Low, addr2High); 0492 0493 if (addr1Low != addr2Low) return (addr1Low < addr2Low); 0494 // jump ends come before jump starts 0495 bool low1IsEnd = (addr1Low == ij1->instrTo()->addr()); 0496 bool low2IsEnd = (addr2Low == ij2->instrTo()->addr()); 0497 if (low1IsEnd && !low2IsEnd) return true; 0498 if (low2IsEnd && !low1IsEnd) return false; 0499 // both the low address of jump 1 and 2 are end or start 0500 return (addr1High < addr2High); 0501 } 0502 0503 // sort jumps according to higher instruction address 0504 bool instrJumpHighLessThan(const TraceInstrJump* ij1, 0505 const TraceInstrJump* ij2) 0506 { 0507 Addr addr1Low, addr1High, addr2Low, addr2High; 0508 0509 getInstrJumpAddresses(ij1, addr1Low, addr1High); 0510 getInstrJumpAddresses(ij2, addr2Low, addr2High); 0511 0512 if (addr1High != addr2High) return (addr1High < addr2High); 0513 // jump ends come before jump starts 0514 bool high1IsEnd = (addr1High == ij1->instrTo()->addr()); 0515 bool high2IsEnd = (addr2High == ij2->instrTo()->addr()); 0516 if (high1IsEnd && !high2IsEnd) return true; 0517 if (high2IsEnd && !high1IsEnd) return false; 0518 // both the high address of jump 1 and 2 are end or start 0519 return (addr1Low < addr2Low); 0520 } 0521 0522 void InstrView::refresh() 0523 { 0524 int originalPosition = verticalScrollBar()->value(); 0525 0526 clear(); 0527 setColumnWidth(0, 20); 0528 setColumnWidth(1, 50); 0529 setColumnHidden(2, (_eventType2 == nullptr)); 0530 setColumnWidth(2, 50); 0531 setColumnHidden(3, true); // arrows, defaults to invisible 0532 setColumnHidden(4, true); // hex code column, defaults to invisible 0533 setColumnWidth(5, 50); // command column 0534 setColumnWidth(6, 250); // arg column 0535 0536 if (_eventType) 0537 headerItem()->setText(1, _eventType->name()); 0538 if (_eventType2) 0539 headerItem()->setText(2, _eventType2->name()); 0540 0541 _arrowLevels = 0; 0542 if (!_data || !_activeItem) return; 0543 0544 ProfileContext::Type t = _activeItem->type(); 0545 TraceFunction* f = nullptr; 0546 if (t == ProfileContext::Function) f = (TraceFunction*) _activeItem; 0547 if (t == ProfileContext::Instr) { 0548 f = ((TraceInstr*)_activeItem)->function(); 0549 if (!_selectedItem) _selectedItem = _activeItem; 0550 } 0551 if (t == ProfileContext::Line) { 0552 f = ((TraceLine*)_activeItem)->functionSource()->function(); 0553 if (!_selectedItem) _selectedItem = _activeItem; 0554 } 0555 0556 if (!f) return; 0557 0558 // check for instruction map 0559 TraceInstrMap::Iterator itStart, it, tmpIt, itEnd; 0560 TraceInstrMap* instrMap = f->instrMap(); 0561 if (instrMap) { 0562 it = instrMap->begin(); 0563 itEnd = instrMap->end(); 0564 // get first instruction with cost of selected type 0565 while(it != itEnd) { 0566 if ((*it).hasCost(_eventType)) break; 0567 if (_eventType2 && (*it).hasCost(_eventType2)) break; 0568 ++it; 0569 } 0570 } 0571 if (!instrMap || (it == itEnd)) { 0572 new InstrItem(this, this, 1, 0573 tr("There is no instruction info in the profile data file.")); 0574 new InstrItem(this, this, 2, 0575 tr("Tip: For Callgrind, rerun with option")); 0576 new InstrItem(this, this, 3, tr(" --dump-instr=yes")); 0577 new InstrItem(this, this, 4, tr("To see (conditional) jumps, additionally specify")); 0578 new InstrItem(this, this, 5, tr(" --collect-jumps=yes")); 0579 setColumnWidths(); 0580 return; 0581 } 0582 0583 if (_showHexCode) { 0584 // make column visible and set to automatic sizing to get column width 0585 setColumnHidden(4, false); 0586 #if QT_VERSION >= 0x050000 0587 header()->setSectionResizeMode(4, QHeaderView::ResizeToContents); 0588 #else 0589 header()->setResizeMode(4, QHeaderView::ResizeToContents); 0590 #endif 0591 } 0592 0593 // initialisation for arrow drawing 0594 // create sorted list of jumps (for jump arrows) 0595 _lowList.clear(); 0596 _highList.clear(); 0597 itStart = it; 0598 while(1) { 0599 TraceInstrJumpList jlist = (*it).instrJumps(); 0600 foreach(TraceInstrJump* ij, jlist) { 0601 if (ij->executedCount()==0) continue; 0602 _lowList.append(ij); 0603 _highList.append(ij); 0604 } 0605 ++it; 0606 while(it != itEnd) { 0607 if ((*it).hasCost(_eventType)) break; 0608 if (_eventType2 && (*it).hasCost(_eventType2)) break; 0609 ++it; 0610 } 0611 if (it == itEnd) break; 0612 } 0613 std::sort(_lowList.begin(), _lowList.end(), instrJumpLowLessThan); 0614 std::sort(_highList.begin(), _highList.end(), instrJumpHighLessThan); 0615 _lowListIter = _lowList.begin(); // iterators to list start 0616 _highListIter = _highList.begin(); 0617 _arrowLevels = 0; 0618 _jump.resize(0); 0619 0620 0621 // do multiple calls to 'objdump' if there are large gaps in addresses 0622 it = itStart; 0623 while(1) { 0624 itStart = it; 0625 while(1) { 0626 tmpIt = it; 0627 ++it; 0628 while(it != itEnd) { 0629 if ((*it).hasCost(_eventType)) break; 0630 if (_eventType2 && (*it).hasCost(_eventType2)) break; 0631 ++it; 0632 } 0633 if (it == itEnd) break; 0634 if (!(*it).addr().isInRange( (*tmpIt).addr(),10000) ) break; 0635 } 0636 0637 // tmpIt is always last instruction with cost 0638 if (!fillInstrRange(f, itStart, ++tmpIt)) break; 0639 if (it == itEnd) break; 0640 } 0641 0642 _lastHexCodeWidth = columnWidth(4); 0643 setColumnWidths(); 0644 0645 if (!_eventType2) { 0646 #if QT_VERSION >= 0x050000 0647 header()->setSectionResizeMode(2, QHeaderView::Interactive); 0648 #else 0649 header()->setResizeMode(2, QHeaderView::Interactive); 0650 #endif 0651 } 0652 0653 // reset to the original position - this is useful when the view 0654 // is refreshed just because we change between relative/absolute 0655 verticalScrollBar()->setValue(originalPosition); 0656 } 0657 0658 /* This is called after adding instrItems, for each of them in 0659 * address order. _jump is the global array of valid jumps 0660 * for a line while we iterate downwards. 0661 * The existing jumps, sorted in lowList according lower address, 0662 * is iterated in the same way. 0663 */ 0664 void InstrView::updateJumpArray(Addr addr, InstrItem* ii, 0665 bool ignoreFrom, bool ignoreTo) 0666 { 0667 Addr lowAddr, highAddr; 0668 int iEnd = -1, iStart = -1; 0669 0670 if (0) qDebug("updateJumpArray(addr 0x%s, jump to %s)", 0671 qPrintable(addr.toString()), 0672 ii->instrJump() 0673 ? qPrintable(ii->instrJump()->instrTo()->name()) : "?" ); 0674 0675 // check for new arrows starting from here downwards 0676 while(_lowListIter != _lowList.end()) { 0677 TraceInstrJump* ij= *_lowListIter; 0678 lowAddr = ij->instrFrom()->addr(); 0679 if (ij->instrTo()->addr() < lowAddr) 0680 lowAddr = ij->instrTo()->addr(); 0681 0682 if (lowAddr > addr) break; 0683 0684 // if target is downwards but we draw no source, break 0685 if (ignoreFrom && (lowAddr < ij->instrTo()->addr())) break; 0686 // if source is downward but we draw no target, break 0687 if (ignoreTo && (lowAddr < ij->instrFrom()->addr())) break; 0688 // if this is another jump start, break 0689 if (ii->instrJump() && (ij != ii->instrJump())) break; 0690 0691 #if 0 0692 for(iStart=0;iStart<_arrowLevels;iStart++) 0693 if (_jump[iStart] && 0694 (_jump[iStart]->instrTo() == ij->instrTo())) break; 0695 #else 0696 iStart = _arrowLevels; 0697 #endif 0698 0699 if (iStart==_arrowLevels) { 0700 for(iStart=0; iStart<_arrowLevels; ++iStart) 0701 if (_jump[iStart] == nullptr) break; 0702 if (iStart==_arrowLevels) { 0703 _arrowLevels++; 0704 _jump.resize(_arrowLevels); 0705 } 0706 if (0) qDebug(" new start at %d for %s", 0707 iStart, qPrintable(ij->name())); 0708 _jump[iStart] = ij; 0709 } 0710 _lowListIter++; 0711 } 0712 0713 ii->setJumpArray(_jump); 0714 0715 // check for active arrows ending here 0716 while(_highListIter != _highList.end()) { 0717 TraceInstrJump* ij= *_highListIter; 0718 highAddr = ij->instrFrom()->addr(); 0719 if (ij->instrTo()->addr() > highAddr) { 0720 highAddr = ij->instrTo()->addr(); 0721 if (ignoreTo) break; 0722 } 0723 else if (ignoreFrom) break; 0724 0725 if (highAddr > addr) break; 0726 0727 for(iEnd=0; iEnd<_arrowLevels; ++iEnd) 0728 if (_jump[iEnd] == ij) break; 0729 if (iEnd==_arrowLevels) { 0730 qDebug() << "InstrView: no jump start for end at 0x" 0731 << highAddr.toString() << " ?"; 0732 iEnd = -1; 0733 } 0734 0735 if (0 && (iEnd>=0)) 0736 qDebug(" end %d (%s to %s)", 0737 iEnd, 0738 qPrintable(_jump[iEnd]->instrFrom()->name()), 0739 qPrintable(_jump[iEnd]->instrTo()->name())); 0740 0741 if (0 && ij) qDebug("next end: %s to %s", 0742 qPrintable(ij->instrFrom()->name()), 0743 qPrintable(ij->instrTo()->name())); 0744 0745 _highListIter++; 0746 0747 if (highAddr > addr) 0748 break; 0749 else { 0750 if (iEnd>=0) _jump[iEnd] = nullptr; 0751 iEnd = -1; 0752 } 0753 } 0754 if (iEnd>=0) _jump[iEnd] = nullptr; 0755 } 0756 0757 0758 bool InstrView::searchFile(QString& dir, TraceObject* o) 0759 { 0760 QString filename = o->shortName(); 0761 0762 if (QDir::isAbsolutePath(dir)) { 0763 if (QFile::exists(dir + '/' + filename)) 0764 return true; 0765 0766 QString sysRoot = getSysRoot(); 0767 if (!sysRoot.isEmpty()) { 0768 if (!dir.startsWith('/') && !sysRoot.endsWith('/')) 0769 sysRoot += '/'; 0770 dir = sysRoot + dir; 0771 return QFile::exists(dir + '/' + filename); 0772 } 0773 return false; 0774 } 0775 0776 QFileInfo fi(dir, filename); 0777 if (fi.exists()) { 0778 dir = fi.absolutePath(); 0779 return true; 0780 } 0781 0782 TracePart* firstPart = _data->parts().first(); 0783 if (firstPart) { 0784 QFileInfo partFile(firstPart->name()); 0785 if (QFileInfo(partFile.absolutePath(), filename).exists()) { 0786 dir = partFile.absolutePath(); 0787 return true; 0788 } 0789 } 0790 0791 return false; 0792 } 0793 0794 /** 0795 * Fill up with instructions from cost range [it;itEnd[ 0796 */ 0797 bool InstrView::fillInstrRange(TraceFunction* function, 0798 TraceInstrMap::Iterator it, 0799 TraceInstrMap::Iterator itEnd) 0800 { 0801 Addr costAddr, nextCostAddr, objAddr, addr; 0802 Addr dumpStartAddr, dumpEndAddr; 0803 TraceInstrMap::Iterator costIt; 0804 bool isArm = (function->data()->architecture() == TraceData::ArchARM); 0805 0806 // should not happen 0807 if (it == itEnd) return false; 0808 0809 // calculate address range for call to objdump 0810 TraceInstrMap::Iterator tmpIt = itEnd; 0811 --tmpIt; 0812 nextCostAddr = (*it).addr(); 0813 0814 if (isArm) { 0815 // for Arm: address always even (even for Thumb encoding) 0816 nextCostAddr = nextCostAddr.alignedDown(2); 0817 } 0818 0819 dumpStartAddr = (nextCostAddr<20) ? Addr(0) : nextCostAddr -20; 0820 dumpEndAddr = (*tmpIt).addr() +20; 0821 0822 0823 QString dir = function->object()->directory(); 0824 if (!searchFile(dir, function->object())) { 0825 new InstrItem(this, this, 1, 0826 tr("For annotated machine code, " 0827 "the following object file is needed:")); 0828 new InstrItem(this, this, 2, 0829 QStringLiteral(" '%1'").arg(function->object()->name())); 0830 new InstrItem(this, this, 3, 0831 tr("This file can not be found.")); 0832 if (isArm) 0833 new InstrItem(this, this, 4, 0834 tr("If cross-compiled, set SYSROOT variable.")); 0835 return false; 0836 } 0837 function->object()->setDirectory(dir); 0838 0839 // call objdump synchronously 0840 QString objfile = dir + '/' + function->object()->shortName(); 0841 QString objdump_format = getObjDumpFormat(); 0842 if (objdump_format.isEmpty()) 0843 objdump_format = getObjDump() + " -C -d --start-address=0x%1 --stop-address=0x%2 %3"; 0844 QString objdumpCmd = objdump_format 0845 .arg(dumpStartAddr.toString()) 0846 .arg(dumpEndAddr.toString()) 0847 .arg(objfile); 0848 0849 qDebug("Running '%s'...", qPrintable(objdumpCmd)); 0850 0851 // and run... 0852 QProcess objdump; 0853 objdump.start(objdumpCmd); 0854 if (!objdump.waitForStarted() || 0855 !objdump.waitForFinished()) { 0856 0857 new InstrItem(this, this, 1, 0858 tr("There is an error trying to execute the command")); 0859 new InstrItem(this, this, 2, 0860 QStringLiteral(" '%1'").arg(objdumpCmd)); 0861 new InstrItem(this, this, 3, 0862 tr("Check that you have installed 'objdump'.")); 0863 new InstrItem(this, this, 4, 0864 tr("This utility can be found in the 'binutils' package.")); 0865 return false; 0866 } 0867 0868 0869 #define BUF_SIZE 256 0870 0871 char buf[BUF_SIZE]; 0872 bool inside = false, skipLineWritten = true; 0873 int readBytes = -1; 0874 int objdumpLineno = 0, dumpedLines = 0, noAssLines = 0; 0875 SubCost most = 0; 0876 TraceInstr* currInstr; 0877 InstrItem *ii, *ii2, *item = nullptr, *first = nullptr, *selected = nullptr; 0878 QString code, cmd, args; 0879 bool needObjAddr = true, needCostAddr = true; 0880 0881 costAddr = 0; 0882 objAddr = 0; 0883 0884 QList<QTreeWidgetItem*> items; 0885 while (1) { 0886 0887 if (needObjAddr) { 0888 needObjAddr = false; 0889 0890 // read next objdump line 0891 while (1) { 0892 readBytes=objdump.readLine(buf, BUF_SIZE); 0893 if (readBytes<=0) { 0894 objAddr = 0; 0895 break; 0896 } 0897 0898 objdumpLineno++; 0899 if (readBytes == BUF_SIZE) { 0900 qDebug("ERROR: Line %d of '%s' too long\n", 0901 objdumpLineno, qPrintable(objdumpCmd)); 0902 } 0903 else if ((readBytes>0) && (buf[readBytes-1] == '\n')) 0904 buf[readBytes-1] = 0; 0905 0906 objAddr = parseAddr(buf); 0907 if ((objAddr<dumpStartAddr) || (objAddr>dumpEndAddr)) 0908 objAddr = 0; 0909 if (objAddr != 0) break; 0910 } 0911 0912 if (0) qDebug() << "Got ObjAddr: 0x" << objAddr.toString(); 0913 } 0914 0915 // try to keep objAddr in [costAddr;nextCostAddr] 0916 if (needCostAddr && 0917 (nextCostAddr > 0) && 0918 ((objAddr == Addr(0)) || (objAddr >= nextCostAddr)) ) { 0919 needCostAddr = false; 0920 0921 costIt = it; 0922 ++it; 0923 while(it != itEnd) { 0924 if ((*it).hasCost(_eventType)) break; 0925 if (_eventType2 && (*it).hasCost(_eventType2)) break; 0926 ++it; 0927 } 0928 costAddr = nextCostAddr; 0929 nextCostAddr = (it == itEnd) ? Addr(0) : (*it).addr(); 0930 if (isArm) 0931 nextCostAddr = nextCostAddr.alignedDown(2); 0932 0933 if (0) qDebug() << "Got nextCostAddr: 0x" << nextCostAddr.toString() 0934 << ", costAddr 0x" << costAddr.toString(); 0935 } 0936 0937 // if we have no more address from objdump, stop 0938 if (objAddr == 0) break; 0939 0940 if ((nextCostAddr==0) || (costAddr == 0) || 0941 (objAddr < nextCostAddr)) { 0942 // next line is objAddr 0943 0944 // this sets addr, code, cmd, args 0945 bool isAssemblyInstr = parseLine(buf, addr, code, cmd, args); 0946 Q_UNUSED(isAssemblyInstr); 0947 assert(isAssemblyInstr && (objAddr == addr)); 0948 0949 if (costAddr == objAddr) { 0950 currInstr = &(*costIt); 0951 needCostAddr = true; 0952 } 0953 else 0954 currInstr = nullptr; 0955 0956 needObjAddr = true; 0957 0958 if (0) qDebug() << "Dump Obj Addr: 0x" << addr.toString() 0959 << " [" << cmd << " " << args << "], cost (0x" 0960 << costAddr.toString() << ", next 0x" 0961 << nextCostAddr.toString() << ")"; 0962 } 0963 else { 0964 addr = costAddr; 0965 code = cmd = QString(); 0966 args = tr("(No Instruction)"); 0967 0968 currInstr = &(*costIt); 0969 needCostAddr = true; 0970 0971 noAssLines++; 0972 if (0) qDebug() << "Dump Cost Addr: 0x" << addr.toString() 0973 << " (no ass), objAddr 0x" << objAddr.toString(); 0974 } 0975 0976 // update inside 0977 if (!inside) { 0978 if (currInstr) inside = true; 0979 } 0980 else { 0981 if (0) qDebug() << "Check if 0x" << addr.toString() << " is in ]0x" 0982 << costAddr.toString() << ",0x" 0983 << (nextCostAddr - 3*GlobalConfig::noCostInside()).toString() 0984 << "["; 0985 0986 // Suppose a average instruction len of 3 bytes 0987 if ( (addr > costAddr) && 0988 ((nextCostAddr==0) || 0989 (addr < nextCostAddr - 3*GlobalConfig::noCostInside()) )) 0990 inside = false; 0991 } 0992 0993 int context = GlobalConfig::context(); 0994 0995 if ( ((costAddr==0) || (addr > costAddr + 3*context)) && 0996 ((nextCostAddr==0) || (addr < nextCostAddr - 3*context)) ) { 0997 0998 // the very last skipLine can be omitted 0999 if ((it == itEnd) && 1000 (itEnd == function->instrMap()->end())) skipLineWritten=true; 1001 1002 if (!skipLineWritten) { 1003 skipLineWritten = true; 1004 // a "skipping" line: print "..." instead of a line number 1005 code = cmd = QString(); 1006 args = QStringLiteral("..."); 1007 } 1008 else 1009 continue; 1010 } 1011 else 1012 skipLineWritten = false; 1013 1014 1015 ii = new InstrItem(this, nullptr, addr, inside, 1016 code, cmd, args, currInstr); 1017 items.append(ii); 1018 1019 dumpedLines++; 1020 if (0) qDebug() << "Dumped 0x" << addr.toString() << " " 1021 << (inside ? "Inside " : "Outside") 1022 << (currInstr ? "Cost" : ""); 1023 1024 // no calls/jumps if we have no cost for this line 1025 if (!currInstr) continue; 1026 1027 if (!selected && 1028 ((currInstr == _selectedItem) || 1029 (currInstr->line() == _selectedItem))) selected = ii; 1030 1031 if (!first) first = ii; 1032 1033 if (currInstr->subCost(_eventType) > most) { 1034 item = ii; 1035 most = currInstr->subCost(_eventType); 1036 } 1037 1038 ii->setExpanded(true); 1039 foreach(TraceInstrCall* ic, currInstr->instrCalls()) { 1040 if ((ic->subCost(_eventType)==0) && 1041 (ic->subCost(_eventType2)==0)) continue; 1042 1043 if (ic->subCost(_eventType) > most) { 1044 item = ii; 1045 most = ic->subCost(_eventType); 1046 } 1047 1048 ii2 = new InstrItem(this, ii, addr, currInstr, ic); 1049 1050 if (!selected && (ic->call()->called() == _selectedItem)) 1051 selected = ii2; 1052 } 1053 1054 foreach(TraceInstrJump* ij, currInstr->instrJumps()) { 1055 if (ij->executedCount()==0) continue; 1056 1057 new InstrItem(this, ii, addr, currInstr, ij); 1058 } 1059 } 1060 1061 // Resize columns with address/counts/opcode to contents 1062 #if QT_VERSION >= 0x050000 1063 header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); 1064 header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); 1065 header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); 1066 header()->setSectionResizeMode(5, QHeaderView::ResizeToContents); 1067 #else 1068 header()->setResizeMode(0, QHeaderView::ResizeToContents); 1069 header()->setResizeMode(1, QHeaderView::ResizeToContents); 1070 header()->setResizeMode(2, QHeaderView::ResizeToContents); 1071 header()->setResizeMode(5, QHeaderView::ResizeToContents); 1072 #endif 1073 1074 setSortingEnabled(false); 1075 addTopLevelItems(items); 1076 expandAll(); 1077 setSortingEnabled(true); 1078 // always reset to line number sort 1079 sortByColumn(0, Qt::AscendingOrder); 1080 header()->setSortIndicatorShown(false); 1081 1082 // Reallow interactive column size change after resizing to content 1083 #if QT_VERSION >= 0x050000 1084 header()->setSectionResizeMode(0, QHeaderView::Interactive); 1085 header()->setSectionResizeMode(1, QHeaderView::Interactive); 1086 header()->setSectionResizeMode(2, QHeaderView::Interactive); 1087 header()->setSectionResizeMode(5, QHeaderView::Interactive); 1088 #else 1089 header()->setResizeMode(0, QHeaderView::Interactive); 1090 header()->setResizeMode(1, QHeaderView::Interactive); 1091 header()->setResizeMode(2, QHeaderView::Interactive); 1092 header()->setResizeMode(5, QHeaderView::Interactive); 1093 #endif 1094 1095 if (selected) item = selected; 1096 if (item) first = item; 1097 if (first) { 1098 scrollToItem(first); 1099 _inSelectionUpdate = true; 1100 setCurrentItem(first); 1101 _inSelectionUpdate = false; 1102 } 1103 1104 // for arrows: go down the list according to list sorting 1105 QTreeWidgetItem *item1, *item2; 1106 for (int i=0; i<topLevelItemCount(); i++) { 1107 item1 = topLevelItem(i); 1108 ii = (InstrItem*)item1; 1109 updateJumpArray(ii->addr(), ii, true, false); 1110 1111 for (int j=0; j<item1->childCount(); j++) { 1112 item2 = item1->child(j); 1113 ii2 = (InstrItem*)item2; 1114 if (ii2->instrJump()) 1115 updateJumpArray(ii->addr(), ii2, false, true); 1116 else 1117 ii2->setJumpArray(_jump); 1118 } 1119 } 1120 1121 if (arrowLevels()) { 1122 setColumnHidden(3, false); 1123 setColumnWidth(3, 10 + 6*arrowLevels() + 2); 1124 } 1125 else 1126 setColumnHidden(3, true); 1127 1128 if (noAssLines > 1) { 1129 // trace cost not matching code 1130 1131 //~ singular There is %n cost line without machine code. 1132 //~ plural There are %n cost lines without machine code. 1133 new InstrItem(this, this, 1, 1134 tr("There are %n cost line(s) without machine code.", "", noAssLines)); 1135 new InstrItem(this, this, 2, 1136 tr("This happens because the code of")); 1137 new InstrItem(this, this, 3, QStringLiteral(" %1").arg(objfile)); 1138 new InstrItem(this, this, 4, 1139 tr("does not seem to match the profile data file.")); 1140 new InstrItem(this, this, 5, QString()); 1141 new InstrItem(this, this, 6, 1142 tr("Are you using an old profile data file or is the above mentioned")); 1143 new InstrItem(this, this, 7, 1144 tr("ELF object from an updated installation/another machine?")); 1145 new InstrItem(this, this, 8, QString()); 1146 return false; 1147 } 1148 1149 if (dumpedLines == 0) { 1150 // no matching line read from popen 1151 new InstrItem(this, this, 1, 1152 tr("There seems to be an error trying to execute the command")); 1153 new InstrItem(this, this, 2, 1154 QStringLiteral(" '%1'").arg(objdumpCmd)); 1155 new InstrItem(this, this, 3, 1156 tr("Check that the ELF object used in the command exists.")); 1157 new InstrItem(this, this, 4, 1158 tr("Check that you have installed 'objdump'.")); 1159 new InstrItem(this, this, 5, 1160 tr("This utility can be found in the 'binutils' package.")); 1161 return false; 1162 } 1163 1164 return true; 1165 } 1166 1167 void InstrView::headerClicked(int col) 1168 { 1169 if (col == 0) { 1170 sortByColumn(col, Qt::AscendingOrder); 1171 } 1172 //All others but Source Text column Descending 1173 else if (col <4) { 1174 sortByColumn(col, Qt::DescendingOrder); 1175 } 1176 } 1177 1178 void InstrView::restoreOptions(const QString& prefix, const QString& postfix) 1179 { 1180 ConfigGroup* g = ConfigStorage::group(prefix, postfix); 1181 1182 _showHexCode = g->value(QStringLiteral("ShowHexCode"), DEFAULT_SHOWHEXCODE).toBool(); 1183 delete g; 1184 } 1185 1186 void InstrView::saveOptions(const QString& prefix, const QString& postfix) 1187 { 1188 ConfigGroup* g = ConfigStorage::group(prefix + postfix); 1189 1190 g->setValue(QStringLiteral("ShowHexCode"), _showHexCode, DEFAULT_SHOWHEXCODE); 1191 delete g; 1192 } 1193 1194 #include "moc_instrview.cpp"