• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdesdk API Reference
  • KDE Home
  • Contact Us
 

kcachegrind

  • sources
  • kde-4.12
  • kdesdk
  • kcachegrind
  • libviews
instrview.cpp
Go to the documentation of this file.
1 /* This file is part of KCachegrind.
2  Copyright (C) 2003-2011 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
3 
4  KCachegrind is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public
6  License as published by the Free Software Foundation, version 2.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; see the file COPYING. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 */
18 
19 /*
20  * Instruction View
21  */
22 
23 #include "instrview.h"
24 
25 #include <assert.h>
26 
27 #include <QDebug>
28 #include <QDir>
29 #include <QFile>
30 #include <QFileInfo>
31 #include <QProcess>
32 #include <QAction>
33 #include <QMenu>
34 #include <QScrollBar>
35 #include <QHeaderView>
36 #include <QKeyEvent>
37 
38 #include "config.h"
39 #include "globalconfig.h"
40 #include "instritem.h"
41 
42 
43 // InstrView defaults
44 
45 #define DEFAULT_SHOWHEXCODE true
46 
47 
48 // Helpers for parsing output of 'objdump'
49 
50 static Addr parseAddr(char* buf)
51 {
52  Addr addr;
53  uint pos = 0;
54 
55  // check for instruction line: <space>* <hex address> ":" <space>*
56  while(buf[pos]==' ' || buf[pos]=='\t') pos++;
57 
58  int digits = addr.set(buf + pos);
59  if ((digits==0) || (buf[pos+digits] != ':')) return Addr(0);
60 
61  return addr;
62 }
63 
64 static bool isHexDigit(char c)
65 {
66  return (c >='0' && c <='9') || (c >='a' && c <='f');
67 }
68 
74 static bool parseLine(const char* buf, Addr& addr,
75  QString& code, QString& mnemonic, QString& operands)
76 {
77  uint pos, start;
78 
79  // check for instruction line: <space>* <hex address> ":" <space>*
80 
81  pos = 0;
82  while(buf[pos]==' ' || buf[pos]=='\t') pos++;
83 
84  int digits = addr.set(buf + pos);
85  pos += digits;
86  if ((digits==0) || (buf[pos] != ':')) return false;
87 
88  // further parsing of objdump output...
89  pos++;
90  while(buf[pos]==' ' || buf[pos]=='\t') pos++;
91 
92  // check for hex code, patterns "xx "* / "xxxx "* / "xxxxxxxx"
93  // (with "x" being a lower case hex digit)
94  start = pos;
95  while(1) {
96  if (! isHexDigit(buf[pos])) break;
97  if (! isHexDigit(buf[pos+1])) break;
98  if (buf[pos+2] == ' ') {
99  pos += 3;
100  continue;
101  }
102  if (! isHexDigit(buf[pos+2])) break;
103  if (! isHexDigit(buf[pos+3])) break;
104  if (buf[pos+4] == ' ') {
105  pos += 5;
106  continue;
107  }
108  if (! isHexDigit(buf[pos+4])) break;
109  if (! isHexDigit(buf[pos+5])) break;
110  if (! isHexDigit(buf[pos+6])) break;
111  if (! isHexDigit(buf[pos+7])) break;
112  if (buf[pos+8] != ' ') break;
113  pos += 9;
114  }
115  if (pos <= start) return false;
116  code = QString::fromLatin1(buf + start, pos - start - 1);
117 
118  // skip whitespace
119  while(buf[pos]==' ' || buf[pos]=='\t') pos++;
120 
121  // check for mnemonic
122  start = pos;
123  while(buf[pos] && buf[pos]!=' ' && buf[pos]!='\t') pos++;
124  mnemonic = QString::fromLatin1(buf + start, pos - start);
125 
126  // skip whitespace
127  while(buf[pos]==' '|| buf[pos]=='\t') pos++;
128 
129  // last part are the operands
130  int operandsLen = strlen(buf + pos);
131 
132  // ignore a newline at end
133  if ((operandsLen>0) && (buf[pos + operandsLen - 1] == '\n'))
134  operandsLen--;
135 
136  // maximal 50 chars
137  if (operandsLen > 50)
138  operands = QString::fromLatin1(buf + pos, 47) + QString("...");
139  else
140  operands = QString::fromLatin1(buf+pos, operandsLen);
141 
142  if (0) qDebug("For 0x%s: Code '%s', Mnemonic '%s', Operands '%s'",
143  qPrintable(addr.toString()), qPrintable(code),
144  qPrintable(mnemonic), qPrintable(operands));
145 
146  return true;
147 }
148 
149 
150 
151 
152 //
153 // InstrView
154 //
155 
156 
157 InstrView::InstrView(TraceItemView* parentView,
158  QWidget* parent)
159  : QTreeWidget(parent), TraceItemView(parentView)
160 {
161  _showHexCode = DEFAULT_SHOWHEXCODE;
162  _lastHexCodeWidth = 50;
163 
164  _inSelectionUpdate = false;
165  _arrowLevels = 0;
166 
167  QStringList headerLabels;
168  headerLabels << tr( "#" )
169  << tr( "Cost" )
170  << tr( "Cost 2" )
171  << ""
172  << tr( "Hex" )
173  << "" // Mnenomic
174  << tr( "Assembly Instructions" )
175  << tr( "Source Position" );
176  setHeaderLabels(headerLabels);
177  setRootIsDecorated(false);
178  setAllColumnsShowFocus(true);
179  setUniformRowHeights(true);
180  // collapsing call/jump lines by double-click is confusing
181  setExpandsOnDoubleClick(false);
182 
183  // sorting will be enabled after refresh()
184  sortByColumn(0, Qt::AscendingOrder);
185  header()->setSortIndicatorShown(false);
186  setItemDelegate(new InstrItemDelegate(this));
187  setWhatsThis( whatsThis() );
188 
189  connect( this,
190  SIGNAL( currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
191  SLOT( selectedSlot(QTreeWidgetItem*,QTreeWidgetItem*) ) );
192 
193  setContextMenuPolicy(Qt::CustomContextMenu);
194  connect( this,
195  SIGNAL(customContextMenuRequested(const QPoint &) ),
196  SLOT(context(const QPoint &)));
197 
198  connect(this,
199  SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
200  SLOT(activatedSlot(QTreeWidgetItem*,int)));
201 
202  connect(header(), SIGNAL(sectionClicked(int)),
203  this, SLOT(headerClicked(int)));
204 
205  this->setWhatsThis( whatsThis());
206 }
207 
208 
209 QString InstrView::whatsThis() const
210 {
211  return tr( "<b>Annotated Machine Code</b>"
212  "<p>The annotated machine code list shows the "
213  "assembly instructions of the current selected "
214  "function together with (self) cost spent while "
215  "executing an instruction. If this is a call "
216  "instruction, lines with details on the "
217  "call happening are inserted into the source: "
218  "the cost spent inside of the call, the "
219  "number of calls happening, and the call destination.</p>"
220  "<p>The machine code shown is generated with "
221  "the 'objdump' utility from the 'binutils' package.</p>"
222  "<p>Select a line with call information to "
223  "make the destination function of this call current.</p>");
224 }
225 
226 void InstrView::context(const QPoint & p)
227 {
228  QMenu popup;
229  int c = columnAt(p.x());
230  QTreeWidgetItem* i = itemAt(p);
231 
232  TraceInstrCall* ic = i ? ((InstrItem*) i)->instrCall() : 0;
233  TraceInstrJump* ij = i ? ((InstrItem*) i)->instrJump() : 0;
234  TraceFunction* f = ic ? ic->call()->called() : 0;
235  TraceInstr* instr = ij ? ij->instrTo() : 0;
236 
237  QAction* activateFunctionAction = 0;
238  QAction* activateInstrAction = 0;
239  if (f) {
240  QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName()));
241  activateFunctionAction = popup.addAction(menuText);
242  popup.addSeparator();
243  }
244  else if (instr) {
245  QString menuText = tr("Go to Address %1").arg(instr->name());
246  activateInstrAction = popup.addAction(menuText);
247  popup.addSeparator();
248  }
249 
250  if ((c == 1) || (c == 2)) {
251  addEventTypeMenu(&popup);
252  popup.addSeparator();
253  }
254  addGoMenu(&popup);
255  popup.addSeparator();
256 
257  QAction* toggleHexAction = new QAction(tr("Hex Code"), &popup);
258  toggleHexAction->setCheckable(true);
259  toggleHexAction->setChecked(_showHexCode);
260  popup.addAction(toggleHexAction);
261 
262  QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height())));
263  if (a == activateFunctionAction)
264  TraceItemView::activated(f);
265  else if (a == activateInstrAction)
266  TraceItemView::activated(instr);
267  else if (a == toggleHexAction) {
268  _showHexCode = !_showHexCode;
269  // remember width when hiding
270  if (!_showHexCode)
271  _lastHexCodeWidth = columnWidth(4);
272  setColumnWidths();
273  }
274 }
275 
276 
277 void InstrView::selectedSlot(QTreeWidgetItem *i, QTreeWidgetItem *)
278 {
279  if (!i) return;
280  // programatically selected items are not signalled
281  if (_inSelectionUpdate) return;
282 
283  TraceInstrCall* ic = ((InstrItem*) i)->instrCall();
284  TraceInstrJump* ij = ((InstrItem*) i)->instrJump();
285 
286  if (!ic && !ij) {
287  TraceInstr* instr = ((InstrItem*) i)->instr();
288  if (instr) {
289  _selectedItem = instr;
290  selected(instr);
291  }
292  return;
293  }
294 
295  if (ic) {
296  _selectedItem = ic;
297  selected(ic);
298  }
299  else if (ij) {
300  _selectedItem = ij;
301  selected(ij);
302  }
303 }
304 
305 void InstrView::activatedSlot(QTreeWidgetItem* i, int)
306 {
307  if (!i) return;
308  TraceInstrCall* ic = ((InstrItem*) i)->instrCall();
309  TraceInstrJump* ij = ((InstrItem*) i)->instrJump();
310 
311  if (!ic && !ij) {
312  TraceInstr* instr = ((InstrItem*) i)->instr();
313  if (instr) TraceItemView::activated(instr);
314  return;
315  }
316 
317  if (ic) {
318  TraceFunction* f = ic->call()->called();
319  if (f) TraceItemView::activated(f);
320  }
321  else if (ij) {
322  TraceInstr* instr = ij->instrTo();
323  if (instr) TraceItemView::activated(instr);
324  }
325 }
326 
327 void InstrView::keyPressEvent(QKeyEvent* event)
328 {
329  QTreeWidgetItem *item = currentItem();
330  if (item && ((event->key() == Qt::Key_Return) ||
331  (event->key() == Qt::Key_Space)))
332  {
333  activatedSlot(item, 0);
334  }
335  QTreeView::keyPressEvent(event);
336 }
337 
338 CostItem* InstrView::canShow(CostItem* i)
339 {
340  ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
341  TraceFunction* f = 0;
342 
343  switch(t) {
344  case ProfileContext::Function:
345  f = (TraceFunction*) i;
346  break;
347 
348  case ProfileContext::Instr:
349  f = ((TraceInstr*)i)->function();
350  select(i);
351  break;
352 
353  case ProfileContext::Line:
354  f = ((TraceLine*)i)->functionSource()->function();
355  select(i);
356  break;
357 
358  default:
359  break;
360  }
361 
362  return f;
363 }
364 
365 
366 void InstrView::doUpdate(int changeType, bool)
367 {
368  // Special case ?
369  if (changeType == selectedItemChanged) {
370 
371  if (!_selectedItem) {
372  clearSelection();
373  return;
374  }
375 
376  QList<QTreeWidgetItem*> items = selectedItems();
377  InstrItem* ii = (items.count() > 0) ? (InstrItem*)items[0] : 0;
378  if (ii) {
379  if ((ii->instr() == _selectedItem) ||
380  (ii->instr() && (ii->instr()->line() == _selectedItem))) return;
381  if (ii->instrCall() &&
382  (ii->instrCall()->call()->called() == _selectedItem)) return;
383  }
384 
385  QTreeWidgetItem *item, *item2;
386  for (int i=0; i<topLevelItemCount(); i++) {
387  item = topLevelItem(i);
388  ii = (InstrItem*)item;
389  if ((ii->instr() == _selectedItem) ||
390  (ii->instr() && (ii->instr()->line() == _selectedItem))) {
391  scrollToItem(item);
392  _inSelectionUpdate = true;
393  setCurrentItem(item);
394  _inSelectionUpdate = false;
395  break;
396  }
397  item2 = 0;
398  for (int j=0; i<item->childCount(); j++) {
399  item2 = item->child(j);
400  ii = (InstrItem*)item2;
401  if (!ii->instrCall()) continue;
402  if (ii->instrCall()->call()->called() == _selectedItem) {
403  scrollToItem(item2);
404  _inSelectionUpdate = true;
405  setCurrentItem(item2);
406  _inSelectionUpdate = false;
407  break;
408  }
409  }
410  if (item2) break;
411  }
412  return;
413  }
414 
415  if (changeType == groupTypeChanged) {
416  // update group colors for call lines
417  QTreeWidgetItem *item, *item2;
418  for (int i=0; i<topLevelItemCount(); i++) {
419  item = topLevelItem(i);
420  for (int j=0; i<item->childCount(); i++) {
421  item2 = item->child(j);
422  ((InstrItem*)item2)->updateGroup();
423  }
424  }
425  return;
426  }
427 
428  // On eventTypeChanged, we can not just change the costs shown in
429  // already existing items, as costs of 0 should make the line to not
430  // be shown at all. So we do a full refresh.
431 
432  refresh();
433 }
434 
435 void InstrView::setColumnWidths()
436 {
437 #if QT_VERSION >= 0x050000
438  header()->setSectionResizeMode(4, QHeaderView::Interactive);
439 #else
440  header()->setResizeMode(4, QHeaderView::Interactive);
441 #endif
442  if (_showHexCode) {
443  setColumnWidth(4, _lastHexCodeWidth);
444  }
445  else {
446  setColumnWidth(4, 0);
447  }
448 }
449 
450 // compare functions for jump arrow drawing
451 
452 // helper for compare
453 void getInstrJumpAddresses(const TraceInstrJump* ij, Addr& low, Addr& high)
454 {
455  low = ij->instrFrom()->addr();
456  high = ij->instrTo()->addr();
457 
458  if (low > high) {
459  Addr t = low;
460  low = high;
461  high = t;
462  }
463 }
464 
465 // sort jumps according to lower instruction address
466 bool instrJumpLowLessThan(const TraceInstrJump* ij1,
467  const TraceInstrJump* ij2)
468 {
469  Addr addr1Low, addr1High, addr2Low, addr2High;
470 
471  getInstrJumpAddresses(ij1, addr1Low, addr1High);
472  getInstrJumpAddresses(ij2, addr2Low, addr2High);
473 
474  if (addr1Low != addr2Low) return (addr1Low < addr2Low);
475  // jump ends come before jump starts
476  if (addr1Low == ij1->instrTo()->addr()) return true;
477  if (addr2Low == ij2->instrTo()->addr()) return false;
478  return (addr1High < addr2High);
479 }
480 
481 // sort jumps according to higher instruction address
482 bool instrJumpHighLessThan(const TraceInstrJump* ij1,
483  const TraceInstrJump* ij2)
484 {
485  Addr addr1Low, addr1High, addr2Low, addr2High;
486 
487  getInstrJumpAddresses(ij1, addr1Low, addr1High);
488  getInstrJumpAddresses(ij2, addr2Low, addr2High);
489 
490  if (addr1High != addr2High) return (addr1High < addr2High);
491  // jump ends come before jump starts
492  if (addr1High == ij1->instrTo()->addr()) return true;
493  if (addr2High == ij2->instrTo()->addr()) return false;
494  return (addr1Low < addr2Low);
495 }
496 
497 void InstrView::refresh()
498 {
499  int originalPosition = verticalScrollBar()->value();
500 
501  clear();
502  setColumnWidth(0, 20);
503  setColumnWidth(1, 50);
504  setColumnWidth(2, _eventType2 ? 50:0);
505  setColumnWidth(3, 0); // arrows, defaults to invisible
506  setColumnWidth(4, 0); // hex code column
507  setColumnWidth(5, 50); // command column
508  setColumnWidth(6, 250); // arg column
509 
510  // reset to automatic sizing to get column width
511 #if QT_VERSION >= 0x050000
512  header()->setSectionResizeMode(4, QHeaderView::ResizeToContents);
513 #else
514  header()->setResizeMode(4, QHeaderView::ResizeToContents);
515 #endif
516 
517 
518  if (_eventType)
519  headerItem()->setText(1, _eventType->name());
520  if (_eventType2)
521  headerItem()->setText(2, _eventType2->name());
522 
523  _arrowLevels = 0;
524  if (!_data || !_activeItem) return;
525 
526  ProfileContext::Type t = _activeItem->type();
527  TraceFunction* f = 0;
528  if (t == ProfileContext::Function) f = (TraceFunction*) _activeItem;
529  if (t == ProfileContext::Instr) {
530  f = ((TraceInstr*)_activeItem)->function();
531  if (!_selectedItem) _selectedItem = _activeItem;
532  }
533  if (t == ProfileContext::Line) {
534  f = ((TraceLine*)_activeItem)->functionSource()->function();
535  if (!_selectedItem) _selectedItem = _activeItem;
536  }
537 
538  if (!f) return;
539 
540  // check for instruction map
541  TraceInstrMap::Iterator itStart, it, tmpIt, itEnd;
542  TraceInstrMap* instrMap = f->instrMap();
543  if (instrMap) {
544  it = instrMap->begin();
545  itEnd = instrMap->end();
546  // get first instruction with cost of selected type
547  while(it != itEnd) {
548  if ((*it).hasCost(_eventType)) break;
549  if (_eventType2 && (*it).hasCost(_eventType2)) break;
550  ++it;
551  }
552  }
553  if (!instrMap || (it == itEnd)) {
554  new InstrItem(this, this, 1,
555  tr("There is no instruction info in the profile data file."));
556  new InstrItem(this, this, 2,
557  tr("Tip: For Callgrind, rerun with option"));
558  new InstrItem(this, this, 3, tr(" --dump-instr=yes"));
559  new InstrItem(this, this, 4, tr("To see (conditional) jumps, additionally specify"));
560  new InstrItem(this, this, 5, tr(" --collect-jumps=yes"));
561  return;
562  }
563 
564  // initialisation for arrow drawing
565  // create sorted list of jumps (for jump arrows)
566  _lowList.clear();
567  _highList.clear();
568  itStart = it;
569  while(1) {
570  TraceInstrJumpList jlist = (*it).instrJumps();
571  foreach(TraceInstrJump* ij, jlist) {
572  if (ij->executedCount()==0) continue;
573  _lowList.append(ij);
574  _highList.append(ij);
575  }
576  ++it;
577  while(it != itEnd) {
578  if ((*it).hasCost(_eventType)) break;
579  if (_eventType2 && (*it).hasCost(_eventType2)) break;
580  ++it;
581  }
582  if (it == itEnd) break;
583  }
584  qSort(_lowList.begin(), _lowList.end(), instrJumpLowLessThan);
585  qSort(_highList.begin(), _highList.end(), instrJumpHighLessThan);
586  _lowListIter = _lowList.begin(); // iterators to list start
587  _highListIter = _highList.begin();
588  _arrowLevels = 0;
589  _jump.resize(0);
590 
591 
592  // do multiple calls to 'objdump' if there are large gaps in addresses
593  it = itStart;
594  while(1) {
595  itStart = it;
596  while(1) {
597  tmpIt = it;
598  ++it;
599  while(it != itEnd) {
600  if ((*it).hasCost(_eventType)) break;
601  if (_eventType2 && (*it).hasCost(_eventType2)) break;
602  ++it;
603  }
604  if (it == itEnd) break;
605  if (!(*it).addr().isInRange( (*tmpIt).addr(),10000) ) break;
606  }
607 
608  // tmpIt is always last instruction with cost
609  if (!fillInstrRange(f, itStart, ++tmpIt)) break;
610  if (it == itEnd) break;
611  }
612 
613  _lastHexCodeWidth = columnWidth(4);
614  setColumnWidths();
615 
616  if (!_eventType2) {
617 #if QT_VERSION >= 0x050000
618  header()->setSectionResizeMode(2, QHeaderView::Interactive);
619 #else
620  header()->setResizeMode(2, QHeaderView::Interactive);
621 #endif
622  setColumnWidth(2, 0);
623  }
624 
625  // reset to the original position - this is useful when the view
626  // is refreshed just because we change between relative/absolute
627  verticalScrollBar()->setValue(originalPosition);
628 }
629 
630 /* This is called after adding instrItems, for each of them in
631  * address order. _jump is the global array of valid jumps
632  * for a line while we iterate downwards.
633  * The existing jumps, sorted in lowList according lower address,
634  * is iterated in the same way.
635  */
636 void InstrView::updateJumpArray(Addr addr, InstrItem* ii,
637  bool ignoreFrom, bool ignoreTo)
638 {
639  Addr lowAddr, highAddr;
640  int iEnd = -1, iStart = -1;
641 
642  if (0) qDebug("updateJumpArray(addr 0x%s, jump to %s)",
643  qPrintable(addr.toString()),
644  ii->instrJump()
645  ? qPrintable(ii->instrJump()->instrTo()->name()) : "?" );
646 
647  // check for new arrows starting from here downwards
648  while(_lowListIter != _lowList.end()) {
649  TraceInstrJump* ij= *_lowListIter;
650  lowAddr = ij->instrFrom()->addr();
651  if (ij->instrTo()->addr() < lowAddr)
652  lowAddr = ij->instrTo()->addr();
653 
654  if (lowAddr > addr) break;
655 
656  // if target is downwards but we draw no source, break
657  if (ignoreFrom && (lowAddr < ij->instrTo()->addr())) break;
658  // if source is downward but we draw no target, break
659  if (ignoreTo && (lowAddr < ij->instrFrom()->addr())) break;
660  // if this is another jump start, break
661  if (ii->instrJump() && (ij != ii->instrJump())) break;
662 
663 #if 0
664  for(iStart=0;iStart<_arrowLevels;iStart++)
665  if (_jump[iStart] &&
666  (_jump[iStart]->instrTo() == ij->instrTo())) break;
667 #else
668  iStart = _arrowLevels;
669 #endif
670 
671  if (iStart==_arrowLevels) {
672  for(iStart=0;iStart<_arrowLevels;iStart++)
673  if (_jump[iStart] == 0) break;
674  if (iStart==_arrowLevels) {
675  _arrowLevels++;
676  _jump.resize(_arrowLevels);
677  }
678  if (0) qDebug(" new start at %d for %s",
679  iStart, qPrintable(ij->name()));
680  _jump[iStart] = ij;
681  }
682  _lowListIter++;
683  }
684 
685  ii->setJumpArray(_jump);
686 
687  // check for active arrows ending here
688  while(_highListIter != _highList.end()) {
689  TraceInstrJump* ij= *_highListIter;
690  highAddr = ij->instrFrom()->addr();
691  if (ij->instrTo()->addr() > highAddr) {
692  highAddr = ij->instrTo()->addr();
693  if (ignoreTo) break;
694  }
695  else if (ignoreFrom) break;
696 
697  if (highAddr > addr) break;
698 
699  for(iEnd=0;iEnd<_arrowLevels;iEnd++)
700  if (_jump[iEnd] == ij) break;
701  if (iEnd==_arrowLevels) {
702  qDebug() << "InstrView: no jump start for end at 0x"
703  << highAddr.toString() << " ?";
704  iEnd = -1;
705  }
706 
707  if (0 && (iEnd>=0))
708  qDebug(" end %d (%s to %s)",
709  iEnd,
710  qPrintable(_jump[iEnd]->instrFrom()->name()),
711  qPrintable(_jump[iEnd]->instrTo()->name()));
712 
713  if (0 && ij) qDebug("next end: %s to %s",
714  qPrintable(ij->instrFrom()->name()),
715  qPrintable(ij->instrTo()->name()));
716 
717  _highListIter++;
718 
719  if (highAddr > addr)
720  break;
721  else {
722  if (iEnd>=0) _jump[iEnd] = 0;
723  iEnd = -1;
724  }
725  }
726  if (iEnd>=0) _jump[iEnd] = 0;
727 }
728 
729 
730 bool InstrView::searchFile(QString& dir, TraceObject* o)
731 {
732  QString filename = o->shortName();
733 
734  if (QDir::isAbsolutePath(dir)) {
735  return QFile::exists(dir + '/' + filename);
736  }
737 
738  QFileInfo fi(dir, filename);
739  if (fi.exists()) {
740  dir = fi.absolutePath();
741  return true;
742  }
743 
744  TracePart* firstPart = _data->parts().first();
745  if (firstPart) {
746  QFileInfo partFile(firstPart->name());
747  if (QFileInfo(partFile.absolutePath(), filename).exists()) {
748  dir = partFile.absolutePath();
749  return true;
750  }
751  }
752 
753  return false;
754 }
755 
759 bool InstrView::fillInstrRange(TraceFunction* function,
760  TraceInstrMap::Iterator it,
761  TraceInstrMap::Iterator itEnd)
762 {
763  Addr costAddr, nextCostAddr, objAddr, addr;
764  Addr dumpStartAddr, dumpEndAddr;
765  TraceInstrMap::Iterator costIt;
766 
767  // should not happen
768  if (it == itEnd) return false;
769 
770  // calculate address range for call to objdump
771  TraceInstrMap::Iterator tmpIt = itEnd;
772  --tmpIt;
773  nextCostAddr = (*it).addr();
774  dumpStartAddr = (nextCostAddr<20) ? Addr(0) : nextCostAddr -20;
775  dumpEndAddr = (*tmpIt).addr() +20;
776 
777  QString dir = function->object()->directory();
778  if (!searchFile(dir, function->object())) {
779  new InstrItem(this, this, 1,
780  tr("For annotated machine code, "
781  "the following object file is needed:"));
782  new InstrItem(this, this, 2,
783  QString(" '%1'").arg(function->object()->name()));
784  new InstrItem(this, this, 3,
785  tr("This file can not be found."));
786  return false;
787  }
788  function->object()->setDirectory(dir);
789 
790  // call objdump synchronously
791  QString objfile = dir + '/' + function->object()->shortName();
792  QStringList objdumpArgs = QStringList()
793  << "-C" << "-d"
794  << QString("--start-address=0x%1").arg(dumpStartAddr.toString())
795  << QString("--stop-address=0x%1").arg(dumpEndAddr.toString())
796  << objfile;
797  QString objdumpCmd = "objdump " + objdumpArgs.join(" ");
798 
799  if (1) qDebug("Running '%s'...", qPrintable(objdumpCmd));
800 
801  // and run...
802  QProcess objdump;
803  objdump.start("objdump", objdumpArgs);
804  if (!objdump.waitForStarted() ||
805  !objdump.waitForFinished()) {
806 
807  new InstrItem(this, this, 1,
808  tr("There is an error trying to execute the command"));
809  new InstrItem(this, this, 2,
810  QString(" '%1'").arg(objdumpCmd));
811  new InstrItem(this, this, 3,
812  tr("Check that you have installed 'objdump'."));
813  new InstrItem(this, this, 4,
814  tr("This utility can be found in the 'binutils' package."));
815  return false;
816  }
817 
818 
819 #define BUF_SIZE 256
820 
821  char buf[BUF_SIZE];
822  bool inside = false, skipLineWritten = true;
823  int readBytes = -1;
824  int objdumpLineno = 0, dumpedLines = 0, noAssLines = 0;
825  SubCost most = 0;
826  TraceInstr* currInstr;
827  InstrItem *ii, *ii2, *item = 0, *first = 0, *selected = 0;
828  QString code, cmd, args;
829  bool needObjAddr = true, needCostAddr = true;
830 
831  costAddr = 0;
832  objAddr = 0;
833 
834  QList<QTreeWidgetItem*> items;
835  while (1) {
836 
837  if (needObjAddr) {
838  needObjAddr = false;
839 
840  // read next objdump line
841  while (1) {
842  readBytes=objdump.readLine(buf, BUF_SIZE);
843  if (readBytes<=0) {
844  objAddr = 0;
845  break;
846  }
847 
848  objdumpLineno++;
849  if (readBytes == BUF_SIZE) {
850  qDebug("ERROR: Line %d of '%s' too long\n",
851  objdumpLineno, qPrintable(objdumpCmd));
852  }
853  else if ((readBytes>0) && (buf[readBytes-1] == '\n'))
854  buf[readBytes-1] = 0;
855 
856  objAddr = parseAddr(buf);
857  if ((objAddr<dumpStartAddr) || (objAddr>dumpEndAddr))
858  objAddr = 0;
859  if (objAddr != 0) break;
860  }
861 
862  if (0) qDebug() << "Got ObjAddr: 0x" << objAddr.toString();
863  }
864 
865  // try to keep objAddr in [costAddr;nextCostAddr]
866  if (needCostAddr &&
867  (nextCostAddr > 0) &&
868  ((objAddr == Addr(0)) || (objAddr >= nextCostAddr)) ) {
869  needCostAddr = false;
870 
871  costIt = it;
872  ++it;
873  while(it != itEnd) {
874  if ((*it).hasCost(_eventType)) break;
875  if (_eventType2 && (*it).hasCost(_eventType2)) break;
876  ++it;
877  }
878  costAddr = nextCostAddr;
879  nextCostAddr = (it == itEnd) ? Addr(0) : (*it).addr();
880 
881  if (0) qDebug() << "Got nextCostAddr: 0x" << nextCostAddr.toString()
882  << ", costAddr 0x" << costAddr.toString();
883  }
884 
885  // if we have no more address from objdump, stop
886  if (objAddr == 0) break;
887 
888  if ((nextCostAddr==0) || (costAddr == 0) ||
889  (objAddr < nextCostAddr)) {
890  // next line is objAddr
891 
892  // this sets addr, code, cmd, args
893  bool isAssemblyInstr = parseLine(buf, addr, code, cmd, args);
894  Q_UNUSED(isAssemblyInstr);
895  assert(isAssemblyInstr && (objAddr == addr));
896 
897  if (costAddr == objAddr) {
898  currInstr = &(*costIt);
899  needCostAddr = true;
900  }
901  else
902  currInstr = 0;
903 
904  needObjAddr = true;
905 
906  if (0) qDebug() << "Dump Obj Addr: 0x" << addr.toString()
907  << " [" << cmd << " " << args << "], cost (0x"
908  << costAddr.toString() << ", next 0x"
909  << nextCostAddr.toString() << ")";
910  }
911  else {
912  addr = costAddr;
913  code = cmd = QString();
914  args = tr("(No Instruction)");
915 
916  currInstr = &(*costIt);
917  needCostAddr = true;
918 
919  noAssLines++;
920  if (0) qDebug() << "Dump Cost Addr: 0x" << addr.toString()
921  << " (no ass), objAddr 0x" << objAddr.toString();
922  }
923 
924  // update inside
925  if (!inside) {
926  if (currInstr) inside = true;
927  }
928  else {
929  if (0) qDebug() << "Check if 0x" << addr.toString() << " is in ]0x"
930  << costAddr.toString() << ",0x"
931  << (nextCostAddr - 3*GlobalConfig::noCostInside()).toString()
932  << "[" << endl;
933 
934  // Suppose a average instruction len of 3 bytes
935  if ( (addr > costAddr) &&
936  ((nextCostAddr==0) ||
937  (addr < nextCostAddr - 3*GlobalConfig::noCostInside()) ))
938  inside = false;
939  }
940 
941  int context = GlobalConfig::context();
942 
943  if ( ((costAddr==0) || (addr > costAddr + 3*context)) &&
944  ((nextCostAddr==0) || (addr < nextCostAddr - 3*context)) ) {
945 
946  // the very last skipLine can be ommitted
947  if ((it == itEnd) &&
948  (itEnd == function->instrMap()->end())) skipLineWritten=true;
949 
950  if (!skipLineWritten) {
951  skipLineWritten = true;
952  // a "skipping" line: print "..." instead of a line number
953  code = cmd = QString();
954  args = QString("...");
955  }
956  else
957  continue;
958  }
959  else
960  skipLineWritten = false;
961 
962 
963  ii = new InstrItem(this, 0, addr, inside,
964  code, cmd, args, currInstr);
965  items.append(ii);
966 
967  dumpedLines++;
968  if (0) qDebug() << "Dumped 0x" << addr.toString() << " "
969  << (inside ? "Inside " : "Outside")
970  << (currInstr ? "Cost" : "");
971 
972  // no calls/jumps if we have no cost for this line
973  if (!currInstr) continue;
974 
975  if (!selected &&
976  ((currInstr == _selectedItem) ||
977  (currInstr->line() == _selectedItem))) selected = ii;
978 
979  if (!first) first = ii;
980 
981  if (currInstr->subCost(_eventType) > most) {
982  item = ii;
983  most = currInstr->subCost(_eventType);
984  }
985 
986  ii->setExpanded(true);
987  foreach(TraceInstrCall* ic, currInstr->instrCalls()) {
988  if ((ic->subCost(_eventType)==0) &&
989  (ic->subCost(_eventType2)==0)) continue;
990 
991  if (ic->subCost(_eventType) > most) {
992  item = ii;
993  most = ic->subCost(_eventType);
994  }
995 
996  ii2 = new InstrItem(this, ii, addr, currInstr, ic);
997 
998  if (!selected && (ic->call()->called() == _selectedItem))
999  selected = ii2;
1000  }
1001 
1002  foreach(TraceInstrJump* ij, currInstr->instrJumps()) {
1003  if (ij->executedCount()==0) continue;
1004 
1005  new InstrItem(this, ii, addr, currInstr, ij);
1006  }
1007  }
1008 
1009  // Resize columns with address/counts/opcode to contents
1010 #if QT_VERSION >= 0x050000
1011  header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
1012  header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
1013  header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
1014  header()->setSectionResizeMode(5, QHeaderView::ResizeToContents);
1015 #else
1016  header()->setResizeMode(0, QHeaderView::ResizeToContents);
1017  header()->setResizeMode(1, QHeaderView::ResizeToContents);
1018  header()->setResizeMode(2, QHeaderView::ResizeToContents);
1019  header()->setResizeMode(5, QHeaderView::ResizeToContents);
1020 #endif
1021 
1022  setSortingEnabled(false);
1023  addTopLevelItems(items);
1024  expandAll();
1025  setSortingEnabled(true);
1026  // always reset to line number sort
1027  sortByColumn(0, Qt::AscendingOrder);
1028  header()->setSortIndicatorShown(false);
1029 
1030  // Reallow interactive column size change after resizing to content
1031 #if QT_VERSION >= 0x050000
1032  header()->setSectionResizeMode(0, QHeaderView::Interactive);
1033  header()->setSectionResizeMode(1, QHeaderView::Interactive);
1034  header()->setSectionResizeMode(2, QHeaderView::Interactive);
1035  header()->setSectionResizeMode(5, QHeaderView::Interactive);
1036 #else
1037  header()->setResizeMode(0, QHeaderView::Interactive);
1038  header()->setResizeMode(1, QHeaderView::Interactive);
1039  header()->setResizeMode(2, QHeaderView::Interactive);
1040  header()->setResizeMode(5, QHeaderView::Interactive);
1041 #endif
1042 
1043  if (selected) item = selected;
1044  if (item) first = item;
1045  if (first) {
1046  scrollToItem(first);
1047  _inSelectionUpdate = true;
1048  setCurrentItem(first);
1049  _inSelectionUpdate = false;
1050  }
1051 
1052  // for arrows: go down the list according to list sorting
1053  QTreeWidgetItem *item1, *item2;
1054  for (int i=0; i<topLevelItemCount(); i++) {
1055  item1 = topLevelItem(i);
1056  ii = (InstrItem*)item1;
1057  updateJumpArray(ii->addr(), ii, true, false);
1058 
1059  for (int j=0; j<item1->childCount(); j++) {
1060  item2 = item1->child(j);
1061  ii2 = (InstrItem*)item2;
1062  if (ii2->instrJump())
1063  updateJumpArray(ii->addr(), ii2, false, true);
1064  else
1065  ii2->setJumpArray(_jump);
1066  }
1067  }
1068 
1069  if (arrowLevels())
1070  setColumnWidth(3, 10 + 6*arrowLevels() + 2);
1071  else
1072  setColumnWidth(3, 0);
1073 
1074  if (noAssLines > 1) {
1075  // trace cost not matching code
1076 
1077  new InstrItem(this, this, 1,
1078  tr("There are %n cost line(s) without machine code.", "", noAssLines));
1079  new InstrItem(this, this, 2,
1080  tr("This happens because the code of"));
1081  new InstrItem(this, this, 3, QString(" %1").arg(objfile));
1082  new InstrItem(this, this, 4,
1083  tr("does not seem to match the profile data file."));
1084  new InstrItem(this, this, 5, "");
1085  new InstrItem(this, this, 6,
1086  tr("Are you using an old profile data file or is the above mentioned"));
1087  new InstrItem(this, this, 7,
1088  tr("ELF object from an updated installation/another machine?"));
1089  new InstrItem(this, this, 8, "");
1090  return false;
1091  }
1092 
1093  if (dumpedLines == 0) {
1094  // no matching line read from popen
1095  new InstrItem(this, this, 1,
1096  tr("There seems to be an error trying to execute the command"));
1097  new InstrItem(this, this, 2,
1098  QString(" '%1'").arg(objdumpCmd));
1099  new InstrItem(this, this, 3,
1100  tr("Check that the ELF object used in the command exists."));
1101  new InstrItem(this, this, 4,
1102  tr("Check that you have installed 'objdump'."));
1103  new InstrItem(this, this, 5,
1104  tr("This utility can be found in the 'binutils' package."));
1105  return false;
1106  }
1107 
1108  return true;
1109 }
1110 
1111 void InstrView::headerClicked(int col)
1112 {
1113  if (col == 0) {
1114  sortByColumn(col, Qt::AscendingOrder);
1115  }
1116  //All others but Source Text column Descending
1117  else if (col <4) {
1118  sortByColumn(col, Qt::DescendingOrder);
1119  }
1120 }
1121 
1122 void InstrView::restoreOptions(const QString& prefix, const QString& postfix)
1123 {
1124  ConfigGroup* g = ConfigStorage::group(prefix, postfix);
1125 
1126  _showHexCode = g->value("ShowHexCode", DEFAULT_SHOWHEXCODE).toBool();
1127  delete g;
1128 }
1129 
1130 void InstrView::saveOptions(const QString& prefix, const QString& postfix)
1131 {
1132  ConfigGroup* g = ConfigStorage::group(prefix + postfix);
1133 
1134  g->setValue("ShowHexCode", _showHexCode, DEFAULT_SHOWHEXCODE);
1135  delete g;
1136 }
1137 
1138 #include "instrview.moc"
TraceItemView::selectedItemChanged
Definition: traceitemview.h:86
TraceInstrJumpList
QList< TraceInstrJump * > TraceInstrJumpList
Definition: tracedata.h:180
TraceItemView::_selectedItem
CostItem * _selectedItem
Definition: traceitemview.h:211
parseLine
static bool parseLine(const char *buf, Addr &addr, QString &code, QString &mnemonic, QString &operands)
Parses a line from objdump assembly output, returning false for a line without an assembly instructio...
Definition: instrview.cpp:74
InstrView::activatedSlot
void activatedSlot(QTreeWidgetItem *, int)
Definition: instrview.cpp:305
InstrView::whatsThis
QString whatsThis() const
Definition: instrview.cpp:209
instrJumpHighLessThan
bool instrJumpHighLessThan(const TraceInstrJump *ij1, const TraceInstrJump *ij2)
Definition: instrview.cpp:482
TracePart::name
QString name() const
Definition: tracedata.h:667
ProfileCostArray::subCost
SubCost subCost(EventType *)
Returns a sub cost.
Definition: costitem.cpp:591
InstrItem::instr
TraceInstr * instr() const
Definition: instritem.h:58
InstrView::InstrView
InstrView(TraceItemView *parentView, QWidget *parent=0)
Definition: instrview.cpp:157
globalconfig.h
ProfileContext::Line
Definition: context.h:39
InstrView::headerClicked
void headerClicked(int)
Definition: instrview.cpp:1111
isHexDigit
static bool isHexDigit(char c)
Definition: instrview.cpp:64
QTreeWidget
TraceItemView::addEventTypeMenu
void addEventTypeMenu(QMenu *, bool withCost2=true)
Definition: traceitemview.cpp:456
TraceInstrJump::instrTo
TraceInstr * instrTo() const
Definition: tracedata.h:737
TraceInstr::instrJumps
const TraceInstrJumpList & instrJumps() const
Definition: tracedata.h:908
CostItem::type
ProfileContext::Type type() const
Definition: costitem.h:45
InstrItem::setJumpArray
void setJumpArray(const QVector< TraceInstrJump * > &a)
Definition: instritem.cpp:298
getInstrJumpAddresses
void getInstrJumpAddresses(const TraceInstrJump *ij, Addr &low, Addr &high)
Definition: instrview.cpp:453
QWidget
TraceFunction
A traced function.
Definition: tracedata.h:1122
TraceItemView::_data
TraceData * _data
Definition: traceitemview.h:209
TraceJumpCost::executedCount
SubCost executedCount()
Definition: tracedata.cpp:55
TraceInstrMap
QMap< Addr, TraceInstr > TraceInstrMap
Definition: tracedata.h:195
TraceItemView::addGoMenu
void addGoMenu(QMenu *)
Definition: traceitemview.cpp:461
ConfigGroup::setValue
virtual void setValue(const QString &key, const QVariant &value, const QVariant &defaultValue=QVariant())
Definition: config.cpp:57
InstrView::restoreOptions
void restoreOptions(const QString &prefix, const QString &postfix)
Definition: instrview.cpp:1122
CostItem
Base class for cost items.
Definition: costitem.h:37
InstrView::InstrItem
friend class InstrItem
Definition: instrview.h:34
config.h
TraceInstrCall
A call from an instruction of one function to another function.
Definition: tracedata.h:782
ProfileContext::Instr
Definition: context.h:38
Addr
Addresses are 64bit values like costs to be able to always load profile data produced on 64bit archit...
Definition: addr.h:31
TraceItemView::_eventType2
EventType * _eventType2
Definition: traceitemview.h:212
ConfigStorage::group
static ConfigGroup * group(const QString &group, const QString &optSuffix=QString())
Definition: config.cpp:80
TraceItemView
Abstract Base Class for KCachegrind Views.
Definition: traceitemview.h:70
TraceObject
A object containing a text segment (shared lib/executable) with defined functions.
Definition: tracedata.h:1331
InstrItemDelegate
Definition: instritem.h:85
TraceData::parts
TracePartList parts() const
Definition: tracedata.h:1397
Addr::toString
QString toString() const
Definition: addr.cpp:50
TraceItemView::_eventType
EventType * _eventType
Definition: traceitemview.h:212
TraceInstrJump::name
virtual QString name() const
Returns dynamic name info (without type)
Definition: tracedata.cpp:990
InstrView::context
void context(const QPoint &)
Definition: instrview.cpp:226
InstrView::keyPressEvent
void keyPressEvent(QKeyEvent *event)
Definition: instrview.cpp:327
TraceInstr
A code instruction address of the program.
Definition: tracedata.h:887
GlobalConfig::context
static int context()
Definition: globalconfig.cpp:412
TraceFunction::prettyName
QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
Definition: tracedata.cpp:1889
InstrView::saveOptions
void saveOptions(const QString &prefix, const QString &postfix)
Definition: instrview.cpp:1130
TraceItemView::_activeItem
CostItem * _activeItem
Definition: traceitemview.h:211
TraceInstr::instrCalls
const TraceInstrCallList & instrCalls() const
Definition: tracedata.h:909
TraceLine
A source line of the program.
Definition: tracedata.h:935
GlobalConfig::shortenSymbol
static QString shortenSymbol(const QString &)
Definition: globalconfig.cpp:395
ConfigGroup
A group of configuration settings.
Definition: config.h:35
Addr::set
int set(const char *s)
Definition: addr.cpp:29
InstrView::arrowLevels
int arrowLevels()
Definition: instrview.h:44
TraceItemView::selected
virtual void selected(TraceItemView *sender, CostItem *)
Notification from child views.
Definition: traceitemview.cpp:313
QTreeWidgetItem
TraceItemView::activated
virtual void activated(TraceItemView *sender, CostItem *)
Definition: traceitemview.cpp:342
instrview.h
TraceInstr::line
TraceLine * line() const
Definition: tracedata.h:907
TraceFunction::instrMap
TraceInstrMap * instrMap()
Definition: tracedata.cpp:2521
TracePart
A Trace Part: All data read from a trace file, containing all costs that happened in a specified time...
Definition: tracedata.h:655
TraceInstrJump
A jump from an instruction to another inside of a function.
Definition: tracedata.h:725
TraceInstr::name
virtual QString name() const
Returns dynamic name info (without type)
Definition: tracedata.cpp:1366
ProfileContext::Type
Type
Definition: context.h:36
InstrItem::addr
Addr addr() const
Definition: instritem.h:56
SubCost
Cost event counter, simple wrapper around a 64bit entity.
Definition: subcost.h:32
TraceCall::called
TraceFunction * called(bool skipCycle=false) const
Definition: tracedata.cpp:1235
BUF_SIZE
#define BUF_SIZE
TraceItemView::groupTypeChanged
Definition: traceitemview.h:83
instritem.h
DEFAULT_SHOWHEXCODE
#define DEFAULT_SHOWHEXCODE
Definition: instrview.cpp:45
instrJumpLowLessThan
bool instrJumpLowLessThan(const TraceInstrJump *ij1, const TraceInstrJump *ij2)
Definition: instrview.cpp:466
InstrItem
Definition: instritem.h:34
TraceInstrJump::instrFrom
TraceInstr * instrFrom() const
Definition: tracedata.h:736
InstrView::selectedSlot
void selectedSlot(QTreeWidgetItem *, QTreeWidgetItem *)
Definition: instrview.cpp:277
TraceItemView::select
void select(CostItem *i)
Definition: traceitemview.cpp:79
parseAddr
static Addr parseAddr(char *buf)
Definition: instrview.cpp:50
InstrItem::instrCall
TraceInstrCall * instrCall() const
Definition: instritem.h:59
GlobalConfig::noCostInside
static int noCostInside()
Definition: globalconfig.cpp:417
InstrItem::instrJump
TraceInstrJump * instrJump() const
Definition: instritem.h:60
TraceInstrCall::call
TraceCall * call() const
Definition: tracedata.h:791
EventType::name
const QString & name()
Definition: eventtype.h:65
TraceInstr::addr
Addr addr() const
Definition: tracedata.h:905
TraceObject::shortName
QString shortName() const
Definition: tracedata.cpp:2950
ProfileContext::Function
Definition: context.h:46
QList
ProfileContext::InvalidType
Definition: context.h:37
ConfigGroup::value
virtual QVariant value(const QString &key, const QVariant &defaultValue) const
Definition: config.cpp:60
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:03:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kcachegrind

Skip menu "kcachegrind"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdesdk API Reference

Skip menu "kdesdk API Reference"
  • kapptemplate
  • kcachegrind
  • kompare
  • lokalize
  • okteta
  • umbrello
  •   umbrello

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal