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

kcachegrind

  • sources
  • kde-4.14
  • kdesdk
  • kcachegrind
  • libcore
cachegrindloader.cpp
Go to the documentation of this file.
1 /* This file is part of KCachegrind.
2  Copyright (C) 2002 - 2010 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 #include "loader.h"
20 
21 #include <QIODevice>
22 #include <QVector>
23 #include <QDebug>
24 
25 #include "addr.h"
26 #include "tracedata.h"
27 #include "utils.h"
28 #include "fixcost.h"
29 
30 
31 #define TRACE_LOADER 0
32 
33 /*
34  * Loader for Callgrind Profile data (format based on Cachegrind format).
35  * See Callgrind documentation for the file format.
36  */
37 
38 class CachegrindLoader: public Loader
39 {
40 public:
41  CachegrindLoader();
42 
43  bool canLoad(QIODevice* file);
44  int load(TraceData*, QIODevice* file, const QString& filename);
45 
46 private:
47  void error(QString);
48  void warning(QString);
49 
50  int loadInternal(TraceData*, QIODevice* file, const QString& filename);
51 
52  enum lineType { SelfCost, CallCost, BoringJump, CondJump };
53 
54  bool parsePosition(FixString& s, PositionSpec& newPos);
55 
56  // position setters
57  void clearPosition();
58  void ensureObject();
59  void ensureFile();
60  void ensureFunction();
61  void setObject(const QString&);
62  void setCalledObject(const QString&);
63  void setFile(const QString&);
64  void setCalledFile(const QString&);
65  void setFunction(const QString&);
66  void setCalledFunction(const QString&);
67 
68  void prepareNewPart();
69 
70  QString _emptyString;
71 
72  // current line in file to read in
73  QString _filename;
74  int _lineNo;
75 
76  EventTypeMapping* mapping;
77  TraceData* _data;
78  TracePart* _part;
79  int partsAdded;
80 
81  // current position
82  lineType nextLineType;
83  bool hasLineInfo, hasAddrInfo;
84  PositionSpec currentPos;
85 
86  // current function/line
87  TraceObject* currentObject;
88  TracePartObject* currentPartObject;
89  TraceFile* currentFile;
90  TraceFile* currentFunctionFile;
91  TracePartFile* currentPartFile;
92  TraceFunction* currentFunction;
93  TracePartFunction* currentPartFunction;
94  TraceFunctionSource* currentFunctionSource;
95  TraceInstr* currentInstr;
96  TracePartInstr* currentPartInstr;
97  TraceLine* currentLine;
98  TracePartLine* currentPartLine;
99 
100  // current call
101  TraceObject* currentCalledObject;
102  TracePartObject* currentCalledPartObject;
103  TraceFile* currentCalledFile;
104  TracePartFile* currentCalledPartFile;
105  TraceFunction* currentCalledFunction;
106  TracePartFunction* currentCalledPartFunction;
107  SubCost currentCallCount;
108 
109  // current jump
110  TraceFile* currentJumpToFile;
111  TraceFunction* currentJumpToFunction;
112  PositionSpec targetPos;
113  SubCost jumpsFollowed, jumpsExecuted;
114 
126  void clearCompression();
127  const QString& checkUnknown(const QString& n);
128  TraceObject* compressedObject(const QString& name);
129  TraceFile* compressedFile(const QString& name);
130  TraceFunction* compressedFunction(const QString& name,
131  TraceFile*, TraceObject*);
132 
133  QVector<TraceCostItem*> _objectVector, _fileVector, _functionVector;
134 };
135 
136 
137 
138 /**********************************************************
139  * Loader
140  */
141 
142 
143 CachegrindLoader::CachegrindLoader()
144  : Loader("Callgrind",
145  QObject::tr( "Import filter for Cachegrind/Callgrind generated profile data files") )
146 {
147  _emptyString = QString("");
148 }
149 
150 bool CachegrindLoader::canLoad(QIODevice* file)
151 {
152  if (!file) return false;
153 
154  Q_ASSERT(file->isOpen());
155 
156  /*
157  * We recognize this as cachegrind/callgrind format if in the first
158  * 2047 bytes we see the string "\nevents:"
159  */
160  char buf[2048];
161  int read = file->read(buf,2047);
162  if (read < 0)
163  return false;
164  buf[read] = 0;
165 
166  QByteArray s = QByteArray::fromRawData(buf, read+1);
167  int pos = s.indexOf("events:");
168  if (pos>0 && buf[pos-1] != '\n') pos = -1;
169  return (pos>=0);
170 }
171 
172 int CachegrindLoader::load(TraceData* d,
173  QIODevice* file, const QString& filename)
174 {
175  /* do the loading in a new object so parallel load
176  * operations do not interfere each other.
177  */
178  CachegrindLoader l;
179 
180  l.setLogger(_logger);
181 
182  return l.loadInternal(d, file, filename);
183 }
184 
185 Loader* createCachegrindLoader()
186 {
187  return new CachegrindLoader();
188 }
189 
190 void CachegrindLoader::error(QString msg)
191 {
192  loadError(_lineNo, msg);
193 }
194 
195 void CachegrindLoader::warning(QString msg)
196 {
197  loadWarning(_lineNo, msg);
198 }
199 
203 bool CachegrindLoader::parsePosition(FixString& line,
204  PositionSpec& newPos)
205 {
206  char c;
207  uint diff;
208 
209  if (hasAddrInfo) {
210 
211  if (!line.first(c)) return false;
212 
213  if (c == '*') {
214  // nothing changed
215  line.stripFirst(c);
216  newPos.fromAddr = currentPos.fromAddr;
217  newPos.toAddr = currentPos.toAddr;
218  }
219  else if (c == '+') {
220  line.stripFirst(c);
221  line.stripUInt(diff, false);
222  newPos.fromAddr = currentPos.fromAddr + diff;
223  newPos.toAddr = newPos.fromAddr;
224  }
225  else if (c == '-') {
226  line.stripFirst(c);
227  line.stripUInt(diff, false);
228  newPos.fromAddr = currentPos.fromAddr - diff;
229  newPos.toAddr = newPos.fromAddr;
230  }
231  else if (c >= '0') {
232  uint64 v;
233  line.stripUInt64(v, false);
234  newPos.fromAddr = Addr(v);
235  newPos.toAddr = newPos.fromAddr;
236  }
237  else return false;
238 
239  // Range specification
240  if (line.first(c)) {
241  if (c == '+') {
242  line.stripFirst(c);
243  line.stripUInt(diff);
244  newPos.toAddr = newPos.fromAddr + diff;
245  }
246  else if ((c == '-') || (c == ':')) {
247  line.stripFirst(c);
248  uint64 v;
249  line.stripUInt64(v);
250  newPos.toAddr = Addr(v);
251  }
252  }
253  line.stripSpaces();
254 
255 #if TRACE_LOADER
256  if (newPos.fromAddr == newPos.toAddr)
257  qDebug() << " Got Addr " << newPos.fromAddr.toString();
258  else
259  qDebug() << " Got AddrRange " << newPos.fromAddr.toString()
260  << ":" << newPos.toAddr.toString();
261 #endif
262 
263  }
264 
265  if (hasLineInfo) {
266 
267  if (!line.first(c)) return false;
268 
269  if (c > '9') return false;
270  else if (c == '*') {
271  // nothing changed
272  line.stripFirst(c);
273  newPos.fromLine = currentPos.fromLine;
274  newPos.toLine = currentPos.toLine;
275  }
276  else if (c == '+') {
277  line.stripFirst(c);
278  line.stripUInt(diff, false);
279  newPos.fromLine = currentPos.fromLine + diff;
280  newPos.toLine = newPos.fromLine;
281  }
282  else if (c == '-') {
283  line.stripFirst(c);
284  line.stripUInt(diff, false);
285  if (currentPos.fromLine < diff) {
286  error(QString("Negative line number %1")
287  .arg((int)currentPos.fromLine - (int)diff));
288  diff = currentPos.fromLine;
289  }
290  newPos.fromLine = currentPos.fromLine - diff;
291  newPos.toLine = newPos.fromLine;
292  }
293  else if (c >= '0') {
294  line.stripUInt(newPos.fromLine, false);
295  newPos.toLine = newPos.fromLine;
296  }
297  else return false;
298 
299  // Range specification
300  if (line.first(c)) {
301  if (c == '+') {
302  line.stripFirst(c);
303  line.stripUInt(diff);
304  newPos.toLine = newPos.fromLine + diff;
305  }
306  else if ((c == '-') || (c == ':')) {
307  line.stripFirst(c);
308  line.stripUInt(newPos.toLine);
309  }
310  }
311  line.stripSpaces();
312 
313 #if TRACE_LOADER
314  if (newPos.fromLine == newPos.toLine)
315  qDebug() << " Got Line " << newPos.fromLine;
316  else
317  qDebug() << " Got LineRange " << newPos.fromLine
318  << ":" << newPos.toLine;
319 #endif
320 
321  }
322 
323  return true;
324 }
325 
326 // Support for compressed strings
327 void CachegrindLoader::clearCompression()
328 {
329  // this does not delete previous contained objects
330  _objectVector.clear();
331  _fileVector.clear();
332  _functionVector.clear();
333 
334  // reset to reasonable init size. We double lengths if needed.
335  _objectVector.resize(100);
336  _fileVector.resize(1000);
337  _functionVector.resize(10000);
338 }
339 
340 const QString& CachegrindLoader::checkUnknown(const QString& n)
341 {
342  if (n == "???") return _emptyString;
343  return n;
344 }
345 
346 TraceObject* CachegrindLoader::compressedObject(const QString& name)
347 {
348  if ((name[0] != '(') || !name[1].isDigit()) return _data->object(checkUnknown(name));
349 
350  // compressed format using _objectVector
351  int p = name.indexOf(')');
352  if (p<2) {
353  error(QString("Invalid compressed ELF object ('%1')").arg(name));
354  return 0;
355  }
356  int index = name.mid(1, p-1).toInt();
357  TraceObject* o = 0;
358  p++;
359  while((name.length()>p) && name.at(p).isSpace()) p++;
360  if (name.length()>p) {
361  if (_objectVector.size() <= index) {
362  int newSize = index * 2;
363 #if TRACE_LOADER
364  qDebug() << " CachegrindLoader: objectVector enlarged to "
365  << newSize;
366 #endif
367  _objectVector.resize(newSize);
368  }
369 
370  QString realName = checkUnknown(name.mid(p));
371  o = (TraceObject*) _objectVector.at(index);
372  if (o && (o->name() != realName)) {
373  error(QString("Redefinition of compressed ELF object index %1 (was '%2') to %3")
374  .arg(index).arg(o->name()).arg(realName));
375  }
376 
377  o = _data->object(realName);
378  _objectVector.replace(index, o);
379  }
380  else {
381  if ((_objectVector.size() <= index) ||
382  ( (o=(TraceObject*)_objectVector.at(index)) == 0)) {
383  error(QString("Undefined compressed ELF object index %1").arg(index));
384  return 0;
385  }
386  }
387 
388  return o;
389 }
390 
391 
392 // Note: Callgrind sometimes gives different IDs for same file
393 // (when references to same source file come from different ELF objects)
394 TraceFile* CachegrindLoader::compressedFile(const QString& name)
395 {
396  if ((name[0] != '(') || !name[1].isDigit()) return _data->file(checkUnknown(name));
397 
398  // compressed format using _fileVector
399  int p = name.indexOf(')');
400  if (p<2) {
401  error(QString("Invalid compressed file ('%1')").arg(name));
402  return 0;
403  }
404  int index = name.mid(1, p-1).toUInt();
405  TraceFile* f = 0;
406  p++;
407  while((name.length()>p) && name.at(p).isSpace()) p++;
408  if (name.length()>p) {
409  if (_fileVector.size() <= index) {
410  int newSize = index * 2;
411 #if TRACE_LOADER
412  qDebug() << " CachegrindLoader::fileVector enlarged to "
413  << newSize;
414 #endif
415  _fileVector.resize(newSize);
416  }
417 
418  QString realName = checkUnknown(name.mid(p));
419  f = (TraceFile*) _fileVector.at(index);
420  if (f && (f->name() != realName)) {
421  error(QString("Redefinition of compressed file index %1 (was '%2') to %3")
422  .arg(index).arg(f->name()).arg(realName));
423  }
424 
425  f = _data->file(realName);
426  _fileVector.replace(index, f);
427  }
428  else {
429  if ((_fileVector.size() <= index) ||
430  ( (f=(TraceFile*)_fileVector.at(index)) == 0)) {
431  error(QString("Undefined compressed file index %1").arg(index));
432  return 0;
433  }
434  }
435 
436  return f;
437 }
438 
439 // Note: Callgrind gives different IDs even for same function
440 // when parts of the function are from different source files.
441 // Thus, it is no error when multiple indexes map to same function.
442 TraceFunction* CachegrindLoader::compressedFunction(const QString& name,
443  TraceFile* file,
444  TraceObject* object)
445 {
446  if ((name[0] != '(') || !name[1].isDigit())
447  return _data->function(checkUnknown(name), file, object);
448 
449  // compressed format using _functionVector
450  int p = name.indexOf(')');
451  if (p<2) {
452  error(QString("Invalid compressed function ('%1')").arg(name));
453  return 0;
454  }
455 
456 
457  int index = name.mid(1, p-1).toUInt();
458  TraceFunction* f = 0;
459  p++;
460  while((name.length()>p) && name.at(p).isSpace()) p++;
461  if (name.length()>p) {
462  if (_functionVector.size() <= index) {
463  int newSize = index * 2;
464 #if TRACE_LOADER
465  qDebug() << " CachegrindLoader::functionVector enlarged to "
466  << newSize;
467 #endif
468  _functionVector.resize(newSize);
469  }
470 
471  QString realName = checkUnknown(name.mid(p));
472  f = (TraceFunction*) _functionVector.at(index);
473  if (f && (f->name() != realName)) {
474  error(QString("Redefinition of compressed function index %1 (was '%2') to %3")
475  .arg(index).arg(f->name()).arg(realName));
476  }
477 
478  f = _data->function(realName, file, object);
479  _functionVector.replace(index, f);
480 
481 #if TRACE_LOADER
482  qDebug() << "compressedFunction: Inserted at Index " << index
483  << "\n " << f->fullName()
484  << "\n in " << f->cls()->fullName()
485  << "\n in " << f->file()->fullName()
486  << "\n in " << f->object()->fullName();
487 #endif
488  }
489  else {
490  if ((_functionVector.size() <= index) ||
491  ( (f=(TraceFunction*)_functionVector.at(index)) == 0)) {
492  error(QString("Undefined compressed function index %1").arg(index));
493  return 0;
494  }
495 
496  // there was a check if the used function (returned from KCachegrinds
497  // model) has the same object and file as here given to us, but that was wrong:
498  // that holds only if we make this assumption on the model...
499  }
500 
501 
502  return f;
503 }
504 
505 
506 // make sure that a valid object is set, at least dummy with empty name
507 void CachegrindLoader::ensureObject()
508 {
509  if (currentObject) return;
510 
511  currentObject = _data->object(_emptyString);
512  currentPartObject = currentObject->partObject(_part);
513 }
514 
515 void CachegrindLoader::setObject(const QString& name)
516 {
517  currentObject = compressedObject(name);
518  if (!currentObject) {
519  error(QString("Invalid ELF object specification, setting to unknown"));
520 
521  currentObject = _data->object(_emptyString);
522  }
523 
524  currentPartObject = currentObject->partObject(_part);
525  currentFunction = 0;
526  currentPartFunction = 0;
527 }
528 
529 void CachegrindLoader::setCalledObject(const QString& name)
530 {
531  currentCalledObject = compressedObject(name);
532 
533  if (!currentCalledObject) {
534  error(QString("Invalid specification of called ELF object, setting to unknown"));
535 
536  currentCalledObject = _data->object(_emptyString);
537  }
538 
539  currentCalledPartObject = currentCalledObject->partObject(_part);
540 }
541 
542 
543 // make sure that a valid file is set, at least dummy with empty name
544 void CachegrindLoader::ensureFile()
545 {
546  if (currentFile) return;
547 
548  currentFile = _data->file(_emptyString);
549  currentPartFile = currentFile->partFile(_part);
550 }
551 
552 void CachegrindLoader::setFile(const QString& name)
553 {
554  currentFile = compressedFile(name);
555 
556  if (!currentFile) {
557  error(QString("Invalid file specification, setting to unknown"));
558 
559  currentFile = _data->file(_emptyString);
560  }
561 
562  currentPartFile = currentFile->partFile(_part);
563  currentLine = 0;
564  currentPartLine = 0;
565 }
566 
567 void CachegrindLoader::setCalledFile(const QString& name)
568 {
569  currentCalledFile = compressedFile(name);
570 
571  if (!currentCalledFile) {
572  error(QString("Invalid specification of called file, setting to unknown"));
573 
574  currentCalledFile = _data->file(_emptyString);
575  }
576 
577  currentCalledPartFile = currentCalledFile->partFile(_part);
578 }
579 
580 // make sure that a valid function is set, at least dummy with empty name
581 void CachegrindLoader::ensureFunction()
582 {
583  if (currentFunction) return;
584 
585  error(QString("Function not specified, setting to unknown"));
586 
587  ensureFile();
588  ensureObject();
589 
590  currentFunction = _data->function(_emptyString,
591  currentFile,
592  currentObject);
593  currentPartFunction = currentFunction->partFunction(_part,
594  currentPartFile,
595  currentPartObject);
596 }
597 
598 void CachegrindLoader::setFunction(const QString& name)
599 {
600  ensureFile();
601  ensureObject();
602 
603  currentFunction = compressedFunction( name,
604  currentFile,
605  currentObject);
606 
607  if (!currentFunction) {
608  error(QString("Invalid function specification, setting to unknown"));
609 
610  currentFunction = _data->function(_emptyString,
611  currentFile,
612  currentObject);
613  }
614 
615  currentPartFunction = currentFunction->partFunction(_part,
616  currentPartFile,
617  currentPartObject);
618 
619  currentFunctionSource = 0;
620  currentLine = 0;
621  currentPartLine = 0;
622 }
623 
624 void CachegrindLoader::setCalledFunction(const QString& name)
625 {
626  // if called object/file not set, use current object/file
627  if (!currentCalledObject) {
628  currentCalledObject = currentObject;
629  currentCalledPartObject = currentPartObject;
630  }
631 
632  if (!currentCalledFile) {
633  // !=0 as functions needs file
634  currentCalledFile = currentFile;
635  currentCalledPartFile = currentPartFile;
636  }
637 
638  currentCalledFunction = compressedFunction(name,
639  currentCalledFile,
640  currentCalledObject);
641  if (!currentCalledFunction) {
642  error("Invalid called function, setting to unknown");
643 
644  currentCalledFunction = _data->function(_emptyString,
645  currentCalledFile,
646  currentCalledObject);
647  }
648 
649  currentCalledPartFunction =
650  currentCalledFunction->partFunction(_part,
651  currentCalledPartFile,
652  currentCalledPartObject);
653 }
654 
655 
656 void CachegrindLoader::clearPosition()
657 {
658  currentPos = PositionSpec();
659 
660  // current function/line
661  currentFunction = 0;
662  currentPartFunction = 0;
663  currentFunctionSource = 0;
664  currentFile = 0;
665  currentFunctionFile = 0;
666  currentPartFile = 0;
667  currentObject = 0;
668  currentPartObject = 0;
669  currentLine = 0;
670  currentPartLine = 0;
671  currentInstr = 0;
672  currentPartInstr = 0;
673 
674  // current call
675  currentCalledObject = 0;
676  currentCalledPartObject = 0;
677  currentCalledFile = 0;
678  currentCalledPartFile = 0;
679  currentCalledFunction = 0;
680  currentCalledPartFunction = 0;
681  currentCallCount = 0;
682 
683  // current jump
684  currentJumpToFile = 0;
685  currentJumpToFunction = 0;
686  targetPos = PositionSpec();
687  jumpsFollowed = 0;
688  jumpsExecuted = 0;
689 
690  mapping = 0;
691 }
692 
693 void CachegrindLoader::prepareNewPart()
694 {
695  if (_part) {
696  // really new part needed?
697  if (mapping == 0) return;
698 
699  // yes
700  _part->invalidate();
701  _part->totals()->clear();
702  _part->totals()->addCost(_part);
703  _data->addPart(_part);
704  partsAdded++;
705  }
706 
707  clearCompression();
708  clearPosition();
709 
710  _part = new TracePart(_data);
711  _part->setName(_filename);
712 }
713 
717 int CachegrindLoader::loadInternal(TraceData* data,
718  QIODevice* device, const QString& filename)
719 {
720  if (!data || !device) return 0;
721 
722  _data = data;
723  _filename = filename;
724  _lineNo = 0;
725 
726  loadStart(_filename);
727 
728  FixFile file(device, _filename);
729  if (!file.exists()) {
730  loadFinished("File does not exist");
731  return 0;
732  }
733 
734  int statusProgress = 0;
735 
736 #if USE_FIXCOST
737  // FixCost Memory Pool
738  FixPool* pool = _data->fixPool();
739 #endif
740 
741  _part = 0;
742  partsAdded = 0;
743  prepareNewPart();
744 
745  FixString line;
746  char c;
747 
748  // current position
749  nextLineType = SelfCost;
750  // default if there is no "positions:" line
751  hasLineInfo = true;
752  hasAddrInfo = false;
753 
754  while (file.nextLine(line)) {
755 
756  _lineNo++;
757 
758 #if TRACE_LOADER
759  qDebug() << "[CachegrindLoader] " << _filename << ":" << _lineNo
760  << " - '" << QString(line) << "'";
761 #endif
762 
763  // if we cannot strip a character, this was an empty line
764  if (!line.first(c)) continue;
765 
766  if (c <= '9') {
767 
768  if (c == '#') continue;
769 
770  // parse position(s)
771  if (!parsePosition(line, currentPos)) {
772  error(QString("Invalid position specification '%1'").arg(line));
773  continue;
774  }
775 
776  // go through after big switch
777  }
778  else { // if (c > '9')
779 
780  line.stripFirst(c);
781 
782  /* in order of probability */
783  switch(c) {
784 
785  case 'f':
786 
787  // fl=
788  if (line.stripPrefix("l=")) {
789 
790  setFile(line);
791  // this is the default for new functions
792  currentFunctionFile = currentFile;
793  continue;
794  }
795 
796  // fi=, fe=
797  if (line.stripPrefix("i=") ||
798  line.stripPrefix("e=")) {
799 
800  setFile(line);
801  continue;
802  }
803 
804  // fn=
805  if (line.stripPrefix("n=")) {
806 
807  if (currentFile != currentFunctionFile)
808  currentFile = currentFunctionFile;
809  setFunction(line);
810 
811  // on a new function, update status
812  int progress = (int)(100.0 * file.current() / file.len() +.5);
813  if (progress != statusProgress) {
814  statusProgress = progress;
815 
816  /* When this signal is connected, it most probably
817  * should lead to GUI update. Thus, when multiple
818  * "long operations" (like file loading) are in progress,
819  * this can temporarly switch to another operation.
820  */
821  loadProgress(statusProgress);
822  }
823 
824  continue;
825  }
826 
827  break;
828 
829  case 'c':
830  // cob=
831  if (line.stripPrefix("ob=")) {
832  setCalledObject(line);
833  continue;
834  }
835 
836  // cfi= / cfl=
837  if (line.stripPrefix("fl=") ||
838  line.stripPrefix("fi=")) {
839  setCalledFile(line);
840  continue;
841  }
842 
843  // cfn=
844  if (line.stripPrefix("fn=")) {
845 
846  setCalledFunction(line);
847  continue;
848  }
849 
850  // calls=
851  if (line.stripPrefix("alls=")) {
852  // ignore long lines...
853  line.stripUInt64(currentCallCount);
854  nextLineType = CallCost;
855  continue;
856  }
857 
858  // cmd:
859  if (line.stripPrefix("md:")) {
860  QString command = QString(line).trimmed();
861  if (!_data->command().isEmpty() &&
862  _data->command() != command) {
863 
864  error(QString("Redefined command, was '%1'").arg(_data->command()));
865  }
866  _data->setCommand(command);
867  continue;
868  }
869 
870  // creator:
871  if (line.stripPrefix("reator:")) {
872  // ignore ...
873  continue;
874  }
875 
876  break;
877 
878  case 'j':
879 
880  // jcnd=
881  if (line.stripPrefix("cnd=")) {
882  bool valid;
883 
884  valid = line.stripUInt64(jumpsFollowed) &&
885  line.stripPrefix("/") &&
886  line.stripUInt64(jumpsExecuted) &&
887  parsePosition(line, targetPos);
888 
889  if (!valid) {
890  error(QString("Invalid line after 'jcnd'"));
891  }
892  else
893  nextLineType = CondJump;
894  continue;
895  }
896 
897  if (line.stripPrefix("ump=")) {
898  bool valid;
899 
900  valid = line.stripUInt64(jumpsExecuted) &&
901  parsePosition(line, targetPos);
902 
903  if (!valid) {
904  error(QString("Invalid line after 'jump'"));
905  }
906  else
907  nextLineType = BoringJump;
908  continue;
909  }
910 
911  // jfi=
912  if (line.stripPrefix("fi=")) {
913  currentJumpToFile = compressedFile(line);
914  continue;
915  }
916 
917  // jfn=
918  if (line.stripPrefix("fn=")) {
919 
920  if (!currentJumpToFile) {
921  // !=0 as functions needs file
922  currentJumpToFile = currentFile;
923  }
924 
925  currentJumpToFunction =
926  compressedFunction(line,
927  currentJumpToFile,
928  currentObject);
929  continue;
930  }
931 
932  break;
933 
934  case 'o':
935 
936  // ob=
937  if (line.stripPrefix("b=")) {
938  setObject(line);
939  continue;
940  }
941 
942  break;
943 
944  case '#':
945  continue;
946 
947  case 't':
948 
949  // totals:
950  if (line.stripPrefix("otals:")) continue;
951 
952  // thread:
953  if (line.stripPrefix("hread:")) {
954  prepareNewPart();
955  _part->setThreadID(QString(line).toInt());
956  continue;
957  }
958 
959  // timeframe (BB):
960  if (line.stripPrefix("imeframe (BB):")) {
961  _part->setTimeframe(line);
962  continue;
963  }
964 
965  break;
966 
967  case 'd':
968 
969  // desc:
970  if (line.stripPrefix("esc:")) {
971 
972  line.stripSurroundingSpaces();
973 
974  // desc: Trigger:
975  if (line.stripPrefix("Trigger:")) {
976  _part->setTrigger(line);
977  }
978 
979  continue;
980  }
981  break;
982 
983  case 'e':
984 
985  // events:
986  if (line.stripPrefix("vents:")) {
987  prepareNewPart();
988  mapping = _data->eventTypes()->createMapping(line);
989  _part->setEventMapping(mapping);
990  continue;
991  }
992 
993  // event:<name>[=<formula>][:<long name>]
994  if (line.stripPrefix("vent:")) {
995  line.stripSurroundingSpaces();
996 
997  FixString e, f, l;
998  if (!line.stripName(e)) {
999  error(QString("Invalid event"));
1000  continue;
1001  }
1002  line.stripSpaces();
1003  if (!line.stripFirst(c)) continue;
1004 
1005  if (c=='=') f = line.stripUntil(':');
1006  line.stripSpaces();
1007 
1008  // add to known cost types
1009  if (line.isEmpty()) line = e;
1010  EventType::add(new EventType(e,line,f));
1011  continue;
1012  }
1013  break;
1014 
1015  case 'p':
1016 
1017  // part:
1018  if (line.stripPrefix("art:")) {
1019  prepareNewPart();
1020  _part->setPartNumber(QString(line).toInt());
1021  continue;
1022  }
1023 
1024  // pid:
1025  if (line.stripPrefix("id:")) {
1026  prepareNewPart();
1027  _part->setProcessID(QString(line).toInt());
1028  continue;
1029  }
1030 
1031  // positions:
1032  if (line.stripPrefix("ositions:")) {
1033  prepareNewPart();
1034  QString positions(line);
1035  hasLineInfo = positions.contains("line");
1036  hasAddrInfo = positions.contains("instr");
1037  continue;
1038  }
1039  break;
1040 
1041  case 'v':
1042 
1043  // version:
1044  if (line.stripPrefix("ersion:")) {
1045  // ignore for now
1046  continue;
1047  }
1048  break;
1049 
1050  case 's':
1051 
1052  // summary:
1053  if (line.stripPrefix("ummary:")) {
1054  if (!mapping) {
1055  error(QString("No event line found. Skipping file"));
1056  delete _part;
1057  return false;
1058  }
1059 
1060  _part->totals()->set(mapping, line);
1061  continue;
1062  }
1063 
1064  case 'r':
1065 
1066  // rcalls= (deprecated)
1067  if (line.stripPrefix("calls=")) {
1068  // handle like normal calls: we need the sum of call count
1069  // recursive cost is discarded in cycle detection
1070  line.stripUInt64(currentCallCount);
1071  nextLineType = CallCost;
1072 
1073  warning(QString("Old file format using deprecated 'rcalls'"));
1074  continue;
1075  }
1076  break;
1077 
1078  default:
1079  break;
1080  }
1081 
1082  error(QString("Invalid line '%1%2'").arg(c).arg(line));
1083  continue;
1084  }
1085 
1086  if (!mapping) {
1087  error(QString("No event line found. Skipping file"));
1088  delete _part;
1089  return false;
1090  }
1091 
1092  // for a cost line, we always need a current function
1093  ensureFunction();
1094 
1095 
1096 
1097  if (!currentFunctionSource ||
1098  (currentFunctionSource->file() != currentFile)) {
1099  currentFunctionSource = currentFunction->sourceFile(currentFile,
1100  true);
1101  }
1102 
1103 #if !USE_FIXCOST
1104  if (hasAddrInfo) {
1105  if (!currentInstr ||
1106  (currentInstr->addr() != currentPos.fromAddr)) {
1107  currentInstr = currentFunction->instr(currentPos.fromAddr,
1108  true);
1109 
1110  if (!currentInstr) {
1111  error(QString("Invalid address '%1'").arg(currentPos.fromAddr.toString()));
1112 
1113  continue;
1114  }
1115 
1116  currentPartInstr = currentInstr->partInstr(_part,
1117  currentPartFunction);
1118  }
1119  }
1120 
1121  if (hasLineInfo) {
1122  if (!currentLine ||
1123  (currentLine->lineno() != currentPos.fromLine)) {
1124 
1125  currentLine = currentFunctionSource->line(currentPos.fromLine,
1126  true);
1127  currentPartLine = currentLine->partLine(_part,
1128  currentPartFunction);
1129  }
1130  if (hasAddrInfo && currentInstr)
1131  currentInstr->setLine(currentLine);
1132  }
1133 #endif
1134 
1135 #if TRACE_LOADER
1136  qDebug() << _filename << ":" << _lineNo;
1137  qDebug() << " currentInstr "
1138  << (currentInstr ? qPrintable(currentInstr->toString()) : ".");
1139  qDebug() << " currentLine "
1140  << (currentLine ? qPrintable(currentLine->toString()) : ".")
1141  << "( file " << currentFile->name() << ")";
1142  qDebug() << " currentFunction "
1143  << qPrintable(currentFunction->prettyName());
1144  qDebug() << " currentCalled "
1145  << (currentCalledFunction ? qPrintable(currentCalledFunction->prettyName()) : ".");
1146 #endif
1147 
1148  // create cost item
1149 
1150  if (nextLineType == SelfCost) {
1151 
1152 #if USE_FIXCOST
1153  new (pool) FixCost(_part, pool,
1154  currentFunctionSource,
1155  currentPos,
1156  currentPartFunction,
1157  line);
1158 #else
1159  if (hasAddrInfo) {
1160  TracePartInstr* partInstr;
1161  partInstr = currentInstr->partInstr(_part, currentPartFunction);
1162 
1163  if (hasLineInfo) {
1164  // we need to set <line> back after reading for the line
1165  int l = line.len();
1166  const char* s = line.ascii();
1167 
1168  partInstr->addCost(mapping, line);
1169  line.set(s,l);
1170  }
1171  else
1172  partInstr->addCost(mapping, line);
1173  }
1174 
1175  if (hasLineInfo) {
1176  TracePartLine* partLine;
1177  partLine = currentLine->partLine(_part, currentPartFunction);
1178  partLine->addCost(mapping, line);
1179  }
1180 #endif
1181 
1182  if (!line.isEmpty()) {
1183  error(QString("Garbage at end of cost line ('%1')").arg(line));
1184  }
1185  }
1186  else if (nextLineType == CallCost) {
1187  nextLineType = SelfCost;
1188 
1189  TraceCall* calling = currentFunction->calling(currentCalledFunction);
1190  TracePartCall* partCalling =
1191  calling->partCall(_part, currentPartFunction,
1192  currentCalledPartFunction);
1193 
1194 #if USE_FIXCOST
1195  FixCallCost* fcc;
1196  fcc = new (pool) FixCallCost(_part, pool,
1197  currentFunctionSource,
1198  hasLineInfo ? currentPos.fromLine : 0,
1199  hasAddrInfo ? currentPos.fromAddr : Addr(0),
1200  partCalling,
1201  currentCallCount, line);
1202  fcc->setMax(_data->callMax());
1203 #else
1204  if (hasAddrInfo) {
1205  TraceInstrCall* instrCall;
1206  TracePartInstrCall* partInstrCall;
1207 
1208  instrCall = calling->instrCall(currentInstr);
1209  partInstrCall = instrCall->partInstrCall(_part, partCalling);
1210  partInstrCall->addCallCount(currentCallCount);
1211 
1212  if (hasLineInfo) {
1213  // we need to set <line> back after reading for the line
1214  int l = line.len();
1215  const char* s = line.ascii();
1216 
1217  partInstrCall->addCost(mapping, line);
1218  line.set(s,l);
1219  }
1220  else
1221  partInstrCall->addCost(mapping, line);
1222 
1223  // update maximum of call cost
1224  _data->callMax()->maxCost(partInstrCall);
1225  }
1226 
1227  if (hasLineInfo) {
1228  TraceLineCall* lineCall;
1229  TracePartLineCall* partLineCall;
1230 
1231  lineCall = calling->lineCall(currentLine);
1232  partLineCall = lineCall->partLineCall(_part, partCalling);
1233 
1234  partLineCall->addCallCount(currentCallCount);
1235  partLineCall->addCost(mapping, line);
1236 
1237  // update maximum of call cost
1238  _data->callMax()->maxCost(partLineCall);
1239  }
1240 #endif
1241  currentCalledFile = 0;
1242  currentCalledPartFile = 0;
1243  currentCalledObject = 0;
1244  currentCalledPartObject = 0;
1245  currentCallCount = 0;
1246 
1247  if (!line.isEmpty()) {
1248  error(QString("Garbage at end of call cost line ('%1')").arg(line));
1249  }
1250  }
1251  else { // (nextLineType == BoringJump || nextLineType == CondJump)
1252 
1253  TraceFunctionSource* targetSource;
1254 
1255  if (!currentJumpToFunction)
1256  currentJumpToFunction = currentFunction;
1257 
1258  targetSource = (currentJumpToFile) ?
1259  currentJumpToFunction->sourceFile(currentJumpToFile, true) :
1260  currentFunctionSource;
1261 
1262 #if USE_FIXCOST
1263  new (pool) FixJump(_part, pool,
1264  /* source */
1265  hasLineInfo ? currentPos.fromLine : 0,
1266  hasAddrInfo ? currentPos.fromAddr : 0,
1267  currentPartFunction,
1268  currentFunctionSource,
1269  /* target */
1270  hasLineInfo ? targetPos.fromLine : 0,
1271  hasAddrInfo ? targetPos.fromAddr : Addr(0),
1272  currentJumpToFunction,
1273  targetSource,
1274  (nextLineType == CondJump),
1275  jumpsExecuted, jumpsFollowed);
1276 #else
1277  if (hasAddrInfo) {
1278  TraceInstr* jumpToInstr;
1279  TraceInstrJump* instrJump;
1280  TracePartInstrJump* partInstrJump;
1281 
1282  jumpToInstr = currentJumpToFunction->instr(targetPos.fromAddr,
1283  true);
1284  instrJump = currentInstr->instrJump(jumpToInstr,
1285  (nextLineType == CondJump));
1286  partInstrJump = instrJump->partInstrJump(_part);
1287  partInstrJump->addExecutedCount(jumpsExecuted);
1288  if (nextLineType == CondJump)
1289  partInstrJump->addFollowedCount(jumpsFollowed);
1290  }
1291 
1292  if (hasLineInfo) {
1293  TraceLine* jumpToLine;
1294  TraceLineJump* lineJump;
1295  TracePartLineJump* partLineJump;
1296 
1297  jumpToLine = targetSource->line(targetPos.fromLine, true);
1298  lineJump = currentLine->lineJump(jumpToLine,
1299  (nextLineType == CondJump));
1300  partLineJump = lineJump->partLineJump(_part);
1301 
1302  partLineJump->addExecutedCount(jumpsExecuted);
1303  if (nextLineType == CondJump)
1304  partLineJump->addFollowedCount(jumpsFollowed);
1305  }
1306 #endif
1307 
1308  if (0) {
1309  qDebug() << _filename << ":" << _lineNo
1310  << " - jump from 0x" << currentPos.fromAddr.toString()
1311  << " (line " << currentPos.fromLine
1312  << ") to 0x" << targetPos.fromAddr.toString()
1313  << " (line " << targetPos.fromLine << ")";
1314 
1315  if (nextLineType == BoringJump)
1316  qDebug() << " Boring Jump, count " << jumpsExecuted.pretty();
1317  else
1318  qDebug() << " Cond. Jump, followed " << jumpsFollowed.pretty()
1319  << ", executed " << jumpsExecuted.pretty();
1320  }
1321 
1322  nextLineType = SelfCost;
1323  currentJumpToFunction = 0;
1324  currentJumpToFile = 0;
1325 
1326  if (!line.isEmpty()) {
1327  error(QString("Garbage at end of jump cost line ('%1')").arg(line));
1328  }
1329 
1330  }
1331  }
1332 
1333  loadFinished();
1334 
1335  if (mapping) {
1336  _part->invalidate();
1337  _part->totals()->clear();
1338  _part->totals()->addCost(_part);
1339  data->addPart(_part);
1340  partsAdded++;
1341  }
1342  else {
1343  delete _part;
1344  }
1345 
1346  device->close();
1347 
1348  return partsAdded;
1349 }
1350 
QIODevice
createCachegrindLoader
Loader * createCachegrindLoader()
Definition: cachegrindloader.cpp:185
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
TraceFile
A source file containing function definitions.
Definition: tracedata.h:1295
FixString::stripSpaces
void stripSpaces()
Strip leading spaces.
Definition: utils.cpp:155
FixString::stripUInt
bool stripUInt(uint &, bool stripSpaces=true)
Definition: utils.cpp:75
TracePartLineCall
Cost of a call at a line from a trace file.
Definition: tracedata.h:467
QByteArray
PositionSpec
Definition: fixcost.h:32
TraceFunction
A traced function.
Definition: tracedata.h:1122
TraceCallCost::addCallCount
void addCallCount(SubCost c)
Definition: tracedata.cpp:132
PositionSpec::toLine
uint toLine
Definition: fixcost.h:43
uint64
unsigned long long uint64
Definition: subcost.h:27
QByteArray::fromRawData
QByteArray fromRawData(const char *data, int size)
TraceInstrCall
A call from an instruction of one function to another function.
Definition: tracedata.h:782
Loader
To implement a new loader, inherit from the Loader class and and reimplement canLoad() and load()...
Definition: loader.h:51
TraceLineCall
A call from a line of one function to another function.
Definition: tracedata.h:808
Loader::canLoad
virtual bool canLoad(QIODevice *file)
Definition: loader.cpp:42
TraceInstrJump::partInstrJump
TracePartInstrJump * partInstrJump(TracePart *)
Definition: tracedata.cpp:949
PositionSpec::toAddr
Addr toAddr
Definition: fixcost.h:44
QIODevice::close
virtual void close()
EventTypeMapping
A index list into a EventTypeSet.
Definition: eventtype.h:170
Addr
Addresses are 64bit values like costs to be able to always load profile data produced on 64bit archit...
Definition: addr.h:31
EventType
A cost type, e.g.
Definition: eventtype.h:43
TraceObject
A object containing a text segment (shared lib/executable) with defined functions.
Definition: tracedata.h:1331
TraceCall::instrCall
TraceInstrCall * instrCall(TraceInstr *)
Definition: tracedata.cpp:1147
FixFile
A class for fast line by line reading of a read-only ASCII file.
Definition: utils.h:100
TracePartInstrCall
Cost of a call at a instruction code address from a trace file.
Definition: tracedata.h:419
utils.h
Addr::toString
QString toString() const
Definition: addr.cpp:50
FixString::stripSurroundingSpaces
void stripSurroundingSpaces()
Strip leading and trailing spaces.
Definition: utils.cpp:139
FixCost
A class holding an unchangable cost item of an input file.
Definition: fixcost.h:53
QByteArray::indexOf
int indexOf(char ch, int from) const
tracedata.h
FixPool
FixPool.
Definition: pool.h:34
TraceInstrCall::partInstrCall
TracePartInstrCall * partInstrCall(TracePart *, TracePartCall *)
Definition: tracedata.cpp:1054
QChar::isSpace
bool isSpace() const
TraceInstr
A code instruction address of the program.
Definition: tracedata.h:887
loader.h
QObject
QString::toInt
int toInt(bool *ok, int base) const
CostItem::fullName
QString fullName() const
Returns type name + dynamic name.
Definition: costitem.cpp:76
FixString::stripFirst
bool stripFirst(char &)
Definition: utils.cpp:40
QString::trimmed
QString trimmed() const
TraceCall::partCall
TracePartCall * partCall(TracePart *, TracePartFunction *, TracePartFunction *)
Definition: tracedata.cpp:1132
TraceFunctionSource
A container helper class for TraceFunction for source lines where a function is implemented in...
Definition: tracedata.h:1029
TracePartLine
Cost of a line from a trace file.
Definition: tracedata.h:484
FixCallCost::setMax
void setMax(ProfileCostArray *)
Definition: fixcost.cpp:131
FixString::first
bool first(char &c)
Definition: utils.h:57
QIODevice::read
qint64 read(char *data, qint64 maxSize)
QIODevice::isOpen
bool isOpen() const
QString
FixString
A simple, constant string class.
Definition: utils.h:38
fixcost.h
TraceLine
A source line of the program.
Definition: tracedata.h:935
TracePartFunction
Cost of a function, from a single trace file.
Definition: tracedata.h:543
FixString::len
int len()
Definition: utils.h:51
TracePartCall
Cost of a call at a function to another function, from a single trace file.
Definition: tracedata.h:516
FixString::stripName
bool stripName(FixString &)
Strip name: [A-Za-z_][0-9A_Za-z_]*.
Definition: utils.cpp:163
TraceJumpCost::addFollowedCount
void addFollowedCount(SubCost c)
Definition: tracedata.h:216
TraceCall::lineCall
TraceLineCall * lineCall(TraceLine *)
Definition: tracedata.cpp:1165
TracePartInstr
Cost of a code instruction address from a trace file.
Definition: tracedata.h:435
TracePart
A Trace Part: All data read from a trace file, containing all costs that happened in a specified time...
Definition: tracedata.h:655
FixString::stripPrefix
bool stripPrefix(const char *)
Definition: utils.cpp:53
FixString::isEmpty
bool isEmpty()
Definition: utils.h:53
FixString::set
void set(const char *s, int l)
Definition: utils.h:60
TraceInstrJump
A jump from an instruction to another inside of a function.
Definition: tracedata.h:725
FixString::stripUntil
FixString stripUntil(char)
Strip string until char appears or end.
Definition: utils.cpp:189
QString::mid
QString mid(int position, int n) const
QVector< TraceCostItem * >
EventType::add
static void add(EventType *, bool overwriteExisting=true)
Definition: eventtype.cpp:263
FixJump
A class holding a jump (mostly) inside of a function.
Definition: fixcost.h:135
TracePartInstrJump
Cost of jump at a instruction code address from a trace file.
Definition: tracedata.h:398
SubCost
Cost event counter, simple wrapper around a 64bit entity.
Definition: subcost.h:32
TracePartFile
Cost of a source file, from a single trace file.
Definition: tracedata.h:624
QString::at
const QChar at(int position) const
TracePartLineJump
Cost of jump at a source line from a trace file.
Definition: tracedata.h:451
QString::length
int length() const
ProfileCostArray::addCost
void addCost(EventTypeMapping *, const char *)
Definition: costitem.cpp:252
FixString::stripUInt64
bool stripUInt64(uint64 &, bool stripSpaces=true)
Definition: utils.cpp:210
Loader::load
virtual int load(TraceData *, QIODevice *file, const QString &filename)
Definition: loader.cpp:47
PositionSpec::fromAddr
Addr fromAddr
Definition: fixcost.h:44
TraceJumpCost::addExecutedCount
void addExecutedCount(SubCost c)
Definition: tracedata.h:217
TraceLineCall::partLineCall
TracePartLineCall * partLineCall(TracePart *, TracePartCall *)
Definition: tracedata.cpp:1094
TraceData
This class holds profiling data of multiple tracefiles generated with cachegrind on one command...
Definition: tracedata.h:1363
FixString::ascii
const char * ascii()
Definition: utils.h:52
TraceLineJump::partLineJump
TracePartLineJump * partLineJump(TracePart *)
Definition: tracedata.cpp:1017
TraceCall
A call from one to another function.
Definition: tracedata.h:835
PositionSpec::fromLine
uint fromLine
Definition: fixcost.h:43
TraceData::addPart
void addPart(TracePart *)
Definition: tracedata.cpp:3260
TraceLineJump
A jump from one line to another inside of a function.
Definition: tracedata.h:754
addr.h
TracePartObject
Cost of a object, from a single trace file.
Definition: tracedata.h:639
QString::toUInt
uint toUInt(bool *ok, int base) const
FixCallCost
A FixCallCost will be inserted into a.
Definition: fixcost.h:96
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:39:49 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
  • 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