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"