1 /*
2 This file is part of KCachegrind.
3
4 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
5
6 SPDX-License-Identifier: GPL-2.0-only
7 */
8
9
10 #include "tracedata.h"
11
12 #include <errno.h>
13 #include <stdlib.h>
14
15 #include <QFile>
16 #include <QDir>
17 #include <QFileInfo>
18 #include <QDebug>
19
20 #include "logger.h"
21 #include "loader.h"
22 #include "globalconfig.h"
23 #include "utils.h"
24 #include "fixcost.h"
25
26
27 #define TRACE_DEBUG 0
28 #define TRACE_ASSERTIONS 0
29
30
31
32
33 //---------------------------------------------------
34 // TraceJumpCost
35
TraceJumpCost(ProfileContext * c)36 TraceJumpCost::TraceJumpCost(ProfileContext* c)
37 :CostItem(c)
38 {
39 TraceJumpCost::clear();
40 }
41
~TraceJumpCost()42 TraceJumpCost::~TraceJumpCost()
43 {}
44
executedCount()45 SubCost TraceJumpCost::executedCount()
46 {
47 if (_dirty) update();
48
49 return _executedCount;
50 }
51
followedCount()52 SubCost TraceJumpCost::followedCount()
53 {
54 if (_dirty) update();
55
56 return _followedCount;
57 }
58
costString(EventTypeSet *)59 QString TraceJumpCost::costString(EventTypeSet*)
60 {
61 if (_dirty) update();
62
63 return QStringLiteral("%1/%2")
64 .arg(_followedCount.pretty())
65 .arg(_executedCount.pretty());
66 }
67
clear()68 void TraceJumpCost::clear()
69 {
70 _followedCount = 0;
71 _executedCount = 0;
72 }
73
addCost(TraceJumpCost * item)74 void TraceJumpCost::addCost(TraceJumpCost* item)
75 {
76 if (item->_dirty) item->update();
77
78 _followedCount += item->followedCount();
79 _executedCount += item->executedCount();
80 }
81
82
83
84 //---------------------------------------------------
85 // TraceCallCost
86
TraceCallCost(ProfileContext * context)87 TraceCallCost::TraceCallCost(ProfileContext* context)
88 : ProfileCostArray(context)
89 {
90 _callCount = 0;
91 }
92
~TraceCallCost()93 TraceCallCost::~TraceCallCost()
94 {}
95
96
costString(EventTypeSet * m)97 QString TraceCallCost::costString(EventTypeSet* m)
98 {
99 return QStringLiteral("%1, Calls %2")
100 .arg(ProfileCostArray::costString(m))
101 .arg(_callCount.pretty());
102 }
103
prettyCallCount()104 QString TraceCallCost::prettyCallCount()
105 {
106 return _callCount.pretty();
107 }
108
clear()109 void TraceCallCost::clear()
110 {
111 _callCount = 0;
112 ProfileCostArray::clear();
113 }
114
callCount()115 SubCost TraceCallCost::callCount()
116 {
117 if (_dirty) update();
118
119 return _callCount;
120 }
121
addCallCount(SubCost c)122 void TraceCallCost::addCallCount(SubCost c)
123 {
124 _callCount += c;
125
126 invalidate();
127 }
128
129
130 //---------------------------------------------------
131 // TraceInclusiveCost
132
TraceInclusiveCost(ProfileContext * context)133 TraceInclusiveCost::TraceInclusiveCost(ProfileContext* context)
134 : ProfileCostArray(context), _inclusive(context)
135 {}
136
~TraceInclusiveCost()137 TraceInclusiveCost::~TraceInclusiveCost()
138 {}
139
costString(EventTypeSet * m)140 QString TraceInclusiveCost::costString(EventTypeSet* m)
141 {
142 return QStringLiteral("%1, Inclusive %2")
143 .arg(ProfileCostArray::costString(m))
144 .arg(_inclusive.costString(m));
145 }
146
clear()147 void TraceInclusiveCost::clear()
148 {
149 _inclusive.clear();
150 ProfileCostArray::clear();
151 }
152
inclusive()153 ProfileCostArray* TraceInclusiveCost::inclusive()
154 {
155 if (_dirty) update();
156
157 return &_inclusive;
158 }
159
addInclusive(ProfileCostArray * c)160 void TraceInclusiveCost::addInclusive(ProfileCostArray* c)
161 {
162 _inclusive.addCost(c);
163
164 invalidate();
165 }
166
167
168 //---------------------------------------------------
169 // TraceListCost
170
TraceListCost(ProfileContext * context)171 TraceListCost::TraceListCost(ProfileContext* context)
172 : ProfileCostArray(context)
173 {
174 _lastDep = nullptr;
175 }
176
~TraceListCost()177 TraceListCost::~TraceListCost()
178 {}
179
addDep(ProfileCostArray * dep)180 void TraceListCost::addDep(ProfileCostArray* dep)
181 {
182 #if TRACE_ASSERTIONS
183 if (_deps.contains(dep)) {
184 qDebug("addDep: %s already in list!",
185 qPrintable(dep->fullName()));
186 return;
187 }
188 #endif
189
190 _deps.append(dep);
191 _lastDep = dep;
192 invalidate();
193
194 #if TRACE_DEBUG
195 qDebug("%s added\n %s (now %d)",
196 qPrintable( fullName() ), qPrintable(dep->fullName()),
197 _deps.count());
198 #endif
199 }
200
findDepFromPart(TracePart * part)201 ProfileCostArray* TraceListCost::findDepFromPart(TracePart* part)
202 {
203 if (_lastDep && _lastDep->part() == part)
204 return _lastDep;
205
206 foreach(ProfileCostArray* dep, _deps) {
207 if (dep->part() == part) {
208 _lastDep = dep;
209 return dep;
210 }
211 }
212 return nullptr;
213 }
214
215
update()216 void TraceListCost::update()
217 {
218 if (!_dirty) return;
219
220 #if TRACE_DEBUG
221 qDebug("update %s (count %d)",
222 qPrintable( fullName() ), _deps.count());
223 #endif
224
225 clear();
226 foreach(ProfileCostArray* item, _deps) {
227 if (onlyActiveParts())
228 if (!item->part() || !item->part()->isActive()) continue;
229
230 addCost(item);
231 }
232
233 _dirty = false;
234
235 #if TRACE_DEBUG
236 qDebug(" > %s", qPrintable(costString(0)));
237 #endif
238 }
239
240
241
242 //---------------------------------------------------
243 // TraceJumpListCost
244
TraceJumpListCost(ProfileContext * context)245 TraceJumpListCost::TraceJumpListCost(ProfileContext* context)
246 : TraceJumpCost(context)
247 {
248 _lastDep = nullptr;
249 }
250
~TraceJumpListCost()251 TraceJumpListCost::~TraceJumpListCost()
252 {}
253
addDep(TraceJumpCost * dep)254 void TraceJumpListCost::addDep(TraceJumpCost* dep)
255 {
256 #if TRACE_ASSERTIONS
257 if (_deps.contains(dep)) {
258 qDebug("addDep: %s already in list!",
259 qPrintable(dep->fullName()));
260 return;
261 }
262 #endif
263
264 _deps.append(dep);
265 _lastDep = dep;
266 invalidate();
267
268 #if TRACE_DEBUG
269 qDebug("%s added\n %s (now %d)",
270 qPrintable( fullName() ), qPrintable(dep->fullName()),
271 _deps.count());
272 #endif
273 }
274
findDepFromPart(TracePart * part)275 TraceJumpCost* TraceJumpListCost::findDepFromPart(TracePart* part)
276 {
277 if (_lastDep && _lastDep->part() == part)
278 return _lastDep;
279
280 foreach(TraceJumpCost* dep, _deps) {
281 if (dep->part() == part) {
282 _lastDep = dep;
283 return dep;
284 }
285 }
286 return nullptr;
287 }
288
289
update()290 void TraceJumpListCost::update()
291 {
292 if (!_dirty) return;
293
294 #if TRACE_DEBUG
295 qDebug("update %s (count %d)",
296 qPrintable( fullName() ), _deps.count());
297 #endif
298
299 clear();
300 foreach(TraceJumpCost* item, _deps) {
301 if (onlyActiveParts())
302 if (!item->part() || !item->part()->isActive()) continue;
303
304 addCost(item);
305 }
306
307 _dirty = false;
308
309 #if TRACE_DEBUG
310 qDebug(" > %s", qPrintable(costString(0)));
311 #endif
312 }
313
314
315
316 //---------------------------------------------------
317 // TraceCallListCost
318
TraceCallListCost(ProfileContext * context)319 TraceCallListCost::TraceCallListCost(ProfileContext* context)
320 : TraceCallCost(context)
321 {
322 _lastDep = nullptr;
323 }
324
~TraceCallListCost()325 TraceCallListCost::~TraceCallListCost()
326 {}
327
addDep(TraceCallCost * dep)328 void TraceCallListCost::addDep(TraceCallCost* dep)
329 {
330 #if TRACE_ASSERTIONS
331 if (_deps.contains(dep)) {
332 qDebug("addDep: %s already in list!",
333 qPrintable(dep->fullName()));
334 return;
335 }
336 #endif
337
338 _deps.append(dep);
339 _lastDep = dep;
340 invalidate();
341
342 #if TRACE_DEBUG
343 qDebug("%s added\n %s (now %d)",
344 qPrintable( fullName() ), qPrintable(dep->fullName()),
345 _deps.count());
346 #endif
347 }
348
findDepFromPart(TracePart * part)349 TraceCallCost* TraceCallListCost::findDepFromPart(TracePart* part)
350 {
351 if (_lastDep && _lastDep->part() == part)
352 return _lastDep;
353
354 foreach(TraceCallCost* dep, _deps) {
355 if (dep->part() == part) {
356 _lastDep = dep;
357 return dep;
358 }
359 }
360 return nullptr;
361 }
362
363
update()364 void TraceCallListCost::update()
365 {
366 if (!_dirty) return;
367
368 #if TRACE_DEBUG
369 qDebug("update %s (count %d)",
370 qPrintable( fullName() ), _deps.count());
371 #endif
372
373 /* Without dependent cost items, assume fixed costs,
374 * i.e. do not change cost */
375 if (_deps.count()>0) {
376 clear();
377 foreach(TraceCallCost* item, _deps) {
378 if (onlyActiveParts())
379 if (!item->part() || !item->part()->isActive()) continue;
380
381 addCost(item);
382 addCallCount(item->callCount());
383 }
384 }
385
386 _dirty = false;
387
388 #if TRACE_DEBUG
389 qDebug(" > %s", qPrintable(costString(0)));
390 #endif
391 }
392
393
394 //---------------------------------------------------
395 // TraceInclusiveListCost
396
TraceInclusiveListCost(ProfileContext * context)397 TraceInclusiveListCost::TraceInclusiveListCost(ProfileContext* context)
398 : TraceInclusiveCost(context)
399 {
400 _lastDep = nullptr;
401 }
402
~TraceInclusiveListCost()403 TraceInclusiveListCost::~TraceInclusiveListCost()
404 {}
405
406
addDep(TraceInclusiveCost * dep)407 void TraceInclusiveListCost::addDep(TraceInclusiveCost* dep)
408 {
409 #if TRACE_ASSERTIONS
410 if (_deps.contains(dep)) {
411 qDebug("addDep: %s already in list!",
412 qPrintable(dep->fullName()));
413 return;
414 }
415 #endif
416
417 _deps.append(dep);
418 _lastDep = dep;
419 invalidate();
420
421 #if TRACE_DEBUG
422 qDebug("%s added\n %s (now %d)",
423 qPrintable( fullName() ), qPrintable(dep->fullName()),
424 _deps.count());
425 #endif
426 }
427
findDepFromPart(TracePart * part)428 TraceInclusiveCost* TraceInclusiveListCost::findDepFromPart(TracePart* part)
429 {
430 if (_lastDep && _lastDep->part() == part)
431 return _lastDep;
432
433 foreach(TraceInclusiveCost* dep, _deps) {
434 if (dep->part() == part) {
435 _lastDep = dep;
436 return dep;
437 }
438 }
439 return nullptr;
440 }
441
update()442 void TraceInclusiveListCost::update()
443 {
444 if (!_dirty) return;
445
446 #if TRACE_DEBUG
447 qDebug("update %s (count %d)",
448 qPrintable( fullName() ), _deps.count());
449 #endif
450
451 clear();
452 foreach(TraceInclusiveCost* item, _deps) {
453 if (onlyActiveParts())
454 if (!item->part() || !item->part()->isActive()) continue;
455
456 addCost(item);
457 addInclusive(item->inclusive());
458 }
459
460 _dirty = false;
461
462 #if TRACE_DEBUG
463 qDebug(" > %s", qPrintable(costString(0)));
464 #endif
465 }
466
467
468
469 //---------------------------------------------------
470 // TracePartInstrJump
471
TracePartInstrJump(TraceInstrJump * instrJump,TracePartInstrJump * next)472 TracePartInstrJump::TracePartInstrJump(TraceInstrJump* instrJump,
473 TracePartInstrJump* next)
474 : TraceJumpCost(ProfileContext::context(ProfileContext::PartInstrJump))
475 {
476 _dep = instrJump;
477 _next = next;
478 }
479
~TracePartInstrJump()480 TracePartInstrJump::~TracePartInstrJump()
481 {}
482
483
484 //---------------------------------------------------
485 // TracePartInstrCall
486
TracePartInstrCall(TraceInstrCall * instrCall)487 TracePartInstrCall::TracePartInstrCall(TraceInstrCall* instrCall)
488 : TraceCallCost(ProfileContext::context(ProfileContext::PartInstrCall))
489 {
490 _dep = instrCall;
491 }
492
~TracePartInstrCall()493 TracePartInstrCall::~TracePartInstrCall()
494 {}
495
496
497
498 //---------------------------------------------------
499 // TracePartInstr
500
TracePartInstr(TraceInstr * instr)501 TracePartInstr::TracePartInstr(TraceInstr* instr)
502 : ProfileCostArray(ProfileContext::context(ProfileContext::PartInstr))
503 {
504 _dep = instr;
505 }
506
~TracePartInstr()507 TracePartInstr::~TracePartInstr()
508 {}
509
510
511
512 //---------------------------------------------------
513 // TracePartLineJump
514
TracePartLineJump(TraceLineJump * lineJump)515 TracePartLineJump::TracePartLineJump(TraceLineJump* lineJump)
516 : TraceJumpCost(ProfileContext::context(ProfileContext::PartLineJump))
517 {
518 _dep = lineJump;
519 }
520
~TracePartLineJump()521 TracePartLineJump::~TracePartLineJump()
522 {}
523
524
525 //---------------------------------------------------
526 // TracePartLineCall
527
TracePartLineCall(TraceLineCall * lineCall)528 TracePartLineCall::TracePartLineCall(TraceLineCall* lineCall)
529 : TraceCallCost(ProfileContext::context(ProfileContext::PartLineCall))
530 {
531 _dep = lineCall;
532 }
533
~TracePartLineCall()534 TracePartLineCall::~TracePartLineCall()
535 {}
536
537
538 //---------------------------------------------------
539 // TracePartLine
540
TracePartLine(TraceLine * line)541 TracePartLine::TracePartLine(TraceLine* line)
542 : ProfileCostArray(ProfileContext::context(ProfileContext::PartLine))
543 {
544 _dep = line;
545 }
546
~TracePartLine()547 TracePartLine::~TracePartLine()
548 {}
549
550
551
552
553 //---------------------------------------------------
554 // TracePartCall
555
TracePartCall(TraceCall * call)556 TracePartCall::TracePartCall(TraceCall* call)
557 : TraceCallListCost(ProfileContext::context(ProfileContext::PartCall))
558 {
559 _dep = call;
560
561 _firstFixCallCost = nullptr;
562 }
563
~TracePartCall()564 TracePartCall::~TracePartCall()
565 {}
566
isRecursion()567 bool TracePartCall::isRecursion()
568 {
569 return call()->isRecursion();
570 }
571
update()572 void TracePartCall::update()
573 {
574 #if !USE_FIXCOST
575 TraceCallListCost::update();
576 #else
577
578 if (!_dirty) return;
579
580 #if TRACE_DEBUG
581 qDebug("update %s", qPrintable( fullName() ));
582 #endif
583
584 /* Without dependent cost items, assume fixed costs,
585 * i.e. do not change cost */
586 if (_firstFixCallCost) {
587 clear();
588 FixCallCost* item;
589 for (item = _firstFixCallCost; item; item = item->nextCostOfPartCall())
590 item->addTo(this);
591 }
592
593 _dirty = false;
594
595 #if TRACE_DEBUG
596 qDebug(" > %s", qPrintable(costString(0)));
597 #endif
598
599 #endif // USE_FIXCOST
600 }
601
602
603 //---------------------------------------------------
604 // TracePartFunction
605
TracePartFunction(TraceFunction * function,TracePartObject * partObject,TracePartFile * partFile)606 TracePartFunction::TracePartFunction(TraceFunction* function,
607 TracePartObject* partObject,
608 TracePartFile *partFile)
609 : TraceInclusiveCost(ProfileContext::context(ProfileContext::PartFunction))
610 {
611 _dep = function;
612 _partObject = partObject;
613 _partFile = partFile;
614 _partClass = nullptr;
615
616 _calledCount = 0;
617 _callingCount = 0;
618 _calledContexts = 0;
619 _callingContexts = 0;
620
621 _firstFixCost = nullptr;
622 _firstFixJump = nullptr;
623 }
624
~TracePartFunction()625 TracePartFunction::~TracePartFunction()
626 {}
627
prettyCalledCount()628 QString TracePartFunction::prettyCalledCount()
629 {
630 return _calledCount.pretty();
631 }
632
prettyCallingCount()633 QString TracePartFunction::prettyCallingCount()
634 {
635 return _callingCount.pretty();
636 }
637
costString(EventTypeSet * m)638 QString TracePartFunction::costString(EventTypeSet* m)
639 {
640 update();
641
642 QString res = TraceInclusiveCost::costString(m);
643 res += QStringLiteral(", called from %1: %2")
644 .arg(_calledContexts).arg(prettyCalledCount());
645 res += QStringLiteral(", calling from %1: %2")
646 .arg(_callingContexts).arg(prettyCallingCount());
647
648 return res;
649 }
650
651
addPartInstr(TracePartInstr * ref)652 void TracePartFunction::addPartInstr(TracePartInstr* ref)
653 {
654 #if TRACE_ASSERTIONS
655 if (_partInstr.contains(ref)) {
656 qDebug("TracePartFunction::addPartInstr: %s already in list!",
657 qPrintable(ref->name()));
658 return;
659 }
660 #endif
661
662 _partInstr.append(ref);
663 invalidate();
664
665 #if TRACE_DEBUG
666 qDebug("%s added\n %s (now %d)",
667 qPrintable( fullName() ), qPrintable(ref->fullName()),
668 _partInstr.count());
669 #endif
670 }
671
672
addPartLine(TracePartLine * ref)673 void TracePartFunction::addPartLine(TracePartLine* ref)
674 {
675 #if TRACE_ASSERTIONS
676 if (_partLines.contains(ref)) {
677 qDebug("TracePartFunction::addPartLine: %s already in list!",
678 qPrintable(ref->name()));
679 return;
680 }
681 #endif
682
683 _partLines.append(ref);
684 invalidate();
685
686 #if TRACE_DEBUG
687 qDebug("%s added\n %s (now %d)",
688 qPrintable( fullName() ), qPrintable(ref->fullName()),
689 _partLines.count());
690 #endif
691 }
692
693
addPartCaller(TracePartCall * ref)694 void TracePartFunction::addPartCaller(TracePartCall* ref)
695 {
696 #if TRACE_ASSERTIONS
697 if (_partCallers.contains(ref)) {
698 qDebug("TracePartFunction::addPartCaller: %s already in list!",
699 qPrintable(ref->name()));
700 return;
701 }
702 #endif
703
704 _partCallers.append(ref);
705 invalidate();
706
707 #if TRACE_DEBUG
708 qDebug("%s added Caller\n %s (now %d)",
709 qPrintable( fullName() ), qPrintable(ref->fullName()),
710 _partCallers.count());
711 #endif
712 }
713
714
addPartCalling(TracePartCall * ref)715 void TracePartFunction::addPartCalling(TracePartCall* ref)
716 {
717 #if TRACE_ASSERTIONS
718 if (_partCallings.contains(ref)) {
719 qDebug("TracePartFunction::addPartCalling: %s already in list!",
720 qPrintable(ref->name()));
721 return;
722 }
723 #endif
724
725 _partCallings.append(ref);
726 invalidate();
727
728 #if TRACE_DEBUG
729 qDebug("%s added Calling\n %s (now %d)",
730 qPrintable( fullName() ), qPrintable(ref->fullName()),
731 _partCallings.count());
732 #endif
733 }
734
calledCount()735 SubCost TracePartFunction::calledCount()
736 {
737 if (_dirty) update();
738
739 return _calledCount;
740 }
741
calledContexts()742 int TracePartFunction::calledContexts()
743 {
744 if (_dirty) update();
745
746 return _calledContexts;
747 }
748
callingCount()749 SubCost TracePartFunction::callingCount()
750 {
751 if (_dirty) update();
752
753 return _callingCount;
754 }
755
756
callingContexts()757 int TracePartFunction::callingContexts()
758 {
759 if (_dirty) update();
760
761 return _callingContexts;
762 }
763
764
update()765 void TracePartFunction::update()
766 {
767 if (!_dirty) return;
768
769 #if TRACE_DEBUG
770 qDebug("TracePartFunction::update %s (Callers %d, Callings %d, lines %d)",
771 qPrintable(name()), _partCallers.count(), _partCallings.count(),
772 _partLines.count());
773 #endif
774
775 _calledCount = 0;
776 _callingCount = 0;
777 _calledContexts = 0;
778 _callingContexts = 0;
779
780 // To calculate context counts, we just use first real event type (FIXME?)
781 EventType* e = data() ? data()->eventTypes()->realType(0) : nullptr;
782
783 // calculate additional cost metrics
784 foreach(TracePartCall* caller, _partCallers) {
785 if (e && (caller->subCost(e) >0))
786 _calledContexts++;
787
788 SubCost c = caller->callCount();
789 if (c>0) {
790 _calledCount += c;
791 }
792 }
793 foreach(TracePartCall* calling, _partCallings) {
794 if (e && (calling->subCost(e)>0))
795 _callingContexts++;
796
797 SubCost c = calling->callCount();
798 if (c>0) {
799 _callingCount += c;
800 }
801 }
802
803 // self cost
804 #if !USE_FIXCOST
805 if (_partLines.count()>0) {
806 ProfileCostArray::clear();
807
808 foreach(TracePartLine* line, _partLines)
809 addCost(line);
810 }
811 #else
812 if (_firstFixCost) {
813 ProfileCostArray::clear();
814
815 FixCost* item;
816 for (item = _firstFixCost; item; item = item->nextCostOfPartFunction())
817 item->addTo(this);
818 }
819 #endif
820
821
822 /* There are two possibilities to calculate inclusive cost:
823 * 1) sum of call costs to this function
824 * 2) sum of call costs from this function + self cost
825 *
826 * 1) is wrong if a function was called spontaneous, but also by a call.
827 * This eventually can happen with thread/process startup functions,
828 * and signal handlers.
829 *
830 * 2) is wrong with "skipped PLT" and the calltree skin, because
831 * cost of PLT is attributed to called function (?)
832 *
833 * For now, do 1) if there are callers, otherwise 2).
834 * Should this be fixed to take the maximum of 1) and 2) ?
835 */
836 _inclusive.clear();
837 if (_calledCount>0) {
838 // inclusive cost: if possible, use caller sums
839 foreach(TracePartCall* caller, _partCallers) {
840 // detect simple recursion (no cycle)
841 if (caller->isRecursion()) continue;
842
843 addInclusive(caller);
844 }
845 }
846 else {
847 // without caller info, use calling sum + line costs
848 foreach(TracePartCall* calling, _partCallings) {
849 // detect simple recursion (no cycle)
850 if (calling->isRecursion()) continue;
851
852 addInclusive(calling);
853 }
854 _dirty = false; // do not recurse!
855 addInclusive(this);
856 }
857
858 _dirty = false;
859
860 #if TRACE_DEBUG
861 qDebug(" > %s", qPrintable(costString(0)));
862 #endif
863 }
864
865
866
867 //---------------------------------------------------
868 // TracePartClass
869
TracePartClass(TraceClass * cls)870 TracePartClass::TracePartClass(TraceClass* cls)
871 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartClass))
872 {
873 _dep = cls;
874 }
875
~TracePartClass()876 TracePartClass::~TracePartClass()
877 {}
878
prettyName() const879 QString TracePartClass::prettyName() const
880 {
881 return QStringLiteral("%1 from %2")
882 .arg( _dep->name().isEmpty() ? QStringLiteral("(global)") : _dep->name())
883 .arg(part()->name());
884 }
885
886 //---------------------------------------------------
887 // TracePartFile
888
TracePartFile(TraceFile * file)889 TracePartFile::TracePartFile(TraceFile* file)
890 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartFile))
891 {
892 _dep = file;
893 }
894
~TracePartFile()895 TracePartFile::~TracePartFile()
896 {}
897
898
899 //---------------------------------------------------
900 // TracePartObject
901
TracePartObject(TraceObject * object)902 TracePartObject::TracePartObject(TraceObject* object)
903 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartObject))
904 {
905 _dep = object;
906 }
907
~TracePartObject()908 TracePartObject::~TracePartObject()
909 {}
910
911
912
913
914 //---------------------------------------------------
915 // TraceInstrJump
916
TraceInstrJump(TraceInstr * instrFrom,TraceInstr * instrTo,bool isCondJump)917 TraceInstrJump::TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo,
918 bool isCondJump)
919 : TraceJumpCost(ProfileContext::context(ProfileContext::InstrJump))
920 {
921 _first = nullptr;
922
923 _instrFrom = instrFrom;
924 _instrTo = instrTo;
925 _isCondJump = isCondJump;
926 }
927
~TraceInstrJump()928 TraceInstrJump::~TraceInstrJump()
929 {
930 // we are the owner of the TracePartInstrJump's generated in our factory
931 TracePartInstrJump* item = _first, *next;
932 while(item) {
933 next = item->next();
934 delete item;
935 item = next;
936 }
937 }
938
partInstrJump(TracePart * part)939 TracePartInstrJump* TraceInstrJump::partInstrJump(TracePart* part)
940 {
941 static TracePartInstrJump* item = nullptr;
942
943 // shortcut if recently used
944 if (item &&
945 (item->instrJump()==this) &&
946 (item->part() == part)) return item;
947
948 for(item = _first; item; item = item->next())
949 if (item->part() == part)
950 return item;
951
952 item = new TracePartInstrJump(this, _first);
953 item->setPosition(part);
954 _first = item;
955 return item;
956 }
957
update()958 void TraceInstrJump::update()
959 {
960 if (!_dirty) return;
961
962 clear();
963 TracePartInstrJump* item;
964 for (item = _first; item; item = item->next()) {
965 if (!item->part() || !item->part()->isActive()) continue;
966
967 addCost(item);
968 }
969 _dirty = false;
970
971 #if TRACE_DEBUG
972 qDebug("updated %s", qPrintable( fullName() ));
973 #endif
974
975 #if TRACE_DEBUG
976 qDebug(" > %s", qPrintable(costString(0)));
977 #endif
978 }
979
name() const980 QString TraceInstrJump::name() const
981 {
982 return QStringLiteral("jump at 0x%1 to 0x%2")
983 .arg(_instrFrom->addr().toString())
984 .arg(_instrTo->addr().toString());
985 }
986
987
988 //---------------------------------------------------
989 // TraceLineJump
990
TraceLineJump(TraceLine * lineFrom,TraceLine * lineTo,bool isCondJump)991 TraceLineJump::TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo,
992 bool isCondJump)
993 : TraceJumpListCost(ProfileContext::context(ProfileContext::LineJump))
994 {
995 _lineFrom = lineFrom;
996 _lineTo = lineTo;
997 _isCondJump = isCondJump;
998 }
999
~TraceLineJump()1000 TraceLineJump::~TraceLineJump()
1001 {
1002 // we are the owner of TracePartLineJump's generated in our factory
1003 qDeleteAll(_deps);
1004 }
1005
1006
partLineJump(TracePart * part)1007 TracePartLineJump* TraceLineJump::partLineJump(TracePart* part)
1008 {
1009 TracePartLineJump* item = (TracePartLineJump*) findDepFromPart(part);
1010 if (!item) {
1011 item = new TracePartLineJump(this);
1012 item->setPosition(part);
1013 addDep(item);
1014 }
1015 return item;
1016 }
1017
1018
name() const1019 QString TraceLineJump::name() const
1020 {
1021 return QStringLiteral("jump at %1 to %2")
1022 .arg(_lineFrom->prettyName())
1023 .arg(_lineTo->prettyName());
1024 }
1025
1026
1027 //---------------------------------------------------
1028 // TraceInstrCall
1029
TraceInstrCall(TraceCall * call,TraceInstr * instr)1030 TraceInstrCall::TraceInstrCall(TraceCall* call, TraceInstr* instr)
1031 : TraceCallListCost(ProfileContext::context(ProfileContext::InstrCall))
1032 {
1033 _call = call;
1034 _instr = instr;
1035 }
1036
~TraceInstrCall()1037 TraceInstrCall::~TraceInstrCall()
1038 {
1039 // we are the owner of TracePartInstrCall's generated in our factory
1040 qDeleteAll(_deps);
1041 }
1042
1043
partInstrCall(TracePart * part,TracePartCall *)1044 TracePartInstrCall* TraceInstrCall::partInstrCall(TracePart* part,
1045 TracePartCall*)
1046 {
1047 TracePartInstrCall* item = (TracePartInstrCall*) findDepFromPart(part);
1048 if (!item) {
1049 item = new TracePartInstrCall(this);
1050 item->setPosition(part);
1051 addDep(item);
1052 // instruction calls are not registered in function calls
1053 // as together with line calls calls are duplicated
1054 //partCall->addDep(item);
1055 }
1056 return item;
1057 }
1058
1059
name() const1060 QString TraceInstrCall::name() const
1061 {
1062 return QStringLiteral("%1 at %2").arg(_call->name()).arg(_instr->name());
1063 }
1064
1065
1066 //---------------------------------------------------
1067 // TraceLineCall
1068
TraceLineCall(TraceCall * call,TraceLine * line)1069 TraceLineCall::TraceLineCall(TraceCall* call, TraceLine* line)
1070 : TraceCallListCost(ProfileContext::context(ProfileContext::LineCall))
1071 {
1072 _call = call;
1073
1074 _line = line;
1075 }
1076
~TraceLineCall()1077 TraceLineCall::~TraceLineCall()
1078 {
1079 // we are the owner of TracePartLineCall's generated in our factory
1080 qDeleteAll(_deps);
1081 }
1082
1083
partLineCall(TracePart * part,TracePartCall * partCall)1084 TracePartLineCall* TraceLineCall::partLineCall(TracePart* part,
1085 TracePartCall* partCall)
1086 {
1087 TracePartLineCall* item = (TracePartLineCall*) findDepFromPart(part);
1088 if (!item) {
1089 item = new TracePartLineCall(this);
1090 item->setPosition(part);
1091 addDep(item);
1092 partCall->addDep(item);
1093 }
1094 return item;
1095 }
1096
1097
name() const1098 QString TraceLineCall::name() const
1099 {
1100 return QStringLiteral("%1 at %2").arg(_call->name()).arg(_line->name());
1101 }
1102
1103
1104 //---------------------------------------------------
1105 // TraceCall
1106
TraceCall(TraceFunction * caller,TraceFunction * called)1107 TraceCall::TraceCall(TraceFunction* caller, TraceFunction* called)
1108 : TraceCallListCost(ProfileContext::context(ProfileContext::Call))
1109 {
1110 _caller = caller;
1111 _called = called;
1112 }
1113
1114
~TraceCall()1115 TraceCall::~TraceCall()
1116 {
1117 // we are the owner of all items generated in our factories
1118 qDeleteAll(_deps);
1119 qDeleteAll(_lineCalls);
1120 }
1121
partCall(TracePart * part,TracePartFunction * partCaller,TracePartFunction * partCalling)1122 TracePartCall* TraceCall::partCall(TracePart* part,
1123 TracePartFunction* partCaller,
1124 TracePartFunction* partCalling)
1125 {
1126 TracePartCall* item = (TracePartCall*) findDepFromPart(part);
1127 if (!item) {
1128 item = new TracePartCall(this);
1129 item->setPosition(part);
1130 addDep(item);
1131 partCaller->addPartCalling(item);
1132 partCalling->addPartCaller(item);
1133 }
1134 return item;
1135 }
1136
instrCall(TraceInstr * i)1137 TraceInstrCall* TraceCall::instrCall(TraceInstr* i)
1138 {
1139 foreach(TraceInstrCall* icall, _instrCalls)
1140 if (icall->instr() == i)
1141 return icall;
1142
1143 TraceInstrCall* icall = new TraceInstrCall(this, i);
1144 _instrCalls.append(icall);
1145 invalidate();
1146
1147 #if TRACE_DEBUG
1148 qDebug("Created %s [TraceCall::instrCall]", qPrintable(icall->fullName()));
1149 #endif
1150 i->addInstrCall(icall);
1151 return icall;
1152 }
1153
1154
lineCall(TraceLine * l)1155 TraceLineCall* TraceCall::lineCall(TraceLine* l)
1156 {
1157 foreach(TraceLineCall* lcall, _lineCalls)
1158 if (lcall->line() == l)
1159 return lcall;
1160
1161 TraceLineCall* lcall = new TraceLineCall(this, l);
1162 _lineCalls.append(lcall);
1163 invalidate();
1164
1165 #if TRACE_DEBUG
1166 qDebug("Created %s [TraceCall::lineCall]", qPrintable(lcall->fullName()));
1167 #endif
1168 l->addLineCall(lcall);
1169 return lcall;
1170 }
1171
1172
invalidateDynamicCost()1173 void TraceCall::invalidateDynamicCost()
1174 {
1175 foreach(TraceLineCall* lc, _lineCalls)
1176 lc->invalidate();
1177
1178 foreach(TraceInstrCall* ic, _instrCalls)
1179 ic->invalidate();
1180
1181 invalidate();
1182 }
1183
1184
name() const1185 QString TraceCall::name() const
1186 {
1187 return QStringLiteral("%1 => %2")
1188 .arg(_caller->name())
1189 .arg(_called->name());
1190 }
1191
inCycle()1192 int TraceCall::inCycle()
1193 {
1194 if (!_caller || !_called) return 0;
1195 if (!_caller->cycle()) return 0;
1196 if (_caller == _caller->cycle()) return 0;
1197 if (_caller->cycle() != _called->cycle()) return 0;
1198
1199 return _caller->cycle()->cycleNo();
1200 }
1201
update()1202 void TraceCall::update()
1203 {
1204 if (!_dirty) return;
1205
1206 // special handling for cycles
1207 if (_caller && _caller->cycle() && _caller==_caller->cycle()) {
1208
1209 // we have no part calls: use inclusive cost of called function
1210 clear();
1211 if (_called)
1212 addCost(_called->inclusive());
1213 _dirty = false;
1214 return;
1215 }
1216
1217 TraceCallListCost::update();
1218 }
1219
caller(bool) const1220 TraceFunction* TraceCall::caller(bool /*skipCycle*/) const
1221 {
1222 return _caller;
1223 }
1224
called(bool skipCycle) const1225 TraceFunction* TraceCall::called(bool skipCycle) const
1226 {
1227 if (!skipCycle && _called) {
1228 // if this is a call to a cycle member from outside of the cycle,
1229 // fake it to be a call to the whole cycle
1230 if (_called->cycle() && _caller &&
1231 (_caller->cycle() != _called->cycle()))
1232 return _called->cycle();
1233 }
1234
1235 return _called;
1236 }
1237
callerName(bool skipCycle) const1238 QString TraceCall::callerName(bool skipCycle) const
1239 {
1240 if (!_caller) return QObject::tr("(no caller)");
1241
1242 if (!skipCycle) {
1243 // if this call goes into a cycle, add the entry function
1244 TraceFunctionCycle* c = _called->cycle();
1245 if (c && _caller && (_caller->cycle() != c)) {
1246 QString via = _called->prettyName();
1247 return QObject::tr("%1 via %2").arg(_caller->prettyName()).arg(via);
1248 }
1249 }
1250
1251 return _caller->prettyName();
1252 }
1253
calledName(bool skipCycle) const1254 QString TraceCall::calledName(bool skipCycle) const
1255 {
1256 if (!_called) return QObject::tr("(no callee)");
1257
1258 if (!skipCycle) {
1259 // if this call goes into a cycle, add the entry function
1260 TraceFunctionCycle* c = _called->cycle();
1261 if (c && _caller && (_caller->cycle() != c)) {
1262 // HACK to get rid of cycle postfix...
1263 _called->setCycle(nullptr);
1264 QString via = _called->prettyName();
1265 _called->setCycle(c);
1266 return QObject::tr("%1 via %2").arg(c->name()).arg(via);
1267 }
1268 }
1269 return _called->prettyName();
1270 }
1271
1272
1273 //---------------------------------------------------
1274 // TraceInstr
1275
TraceInstr()1276 TraceInstr::TraceInstr()
1277 : TraceListCost(ProfileContext::context(ProfileContext::Instr))
1278 {
1279 _addr = 0;
1280 _line = nullptr;
1281 _function = nullptr;
1282 }
1283
~TraceInstr()1284 TraceInstr::~TraceInstr()
1285 {
1286 // we are the owner of items generated in our factories
1287 qDeleteAll(_deps);
1288 qDeleteAll(_instrJumps);
1289 }
1290
hasCost(EventType * ct)1291 bool TraceInstr::hasCost(EventType* ct)
1292 {
1293 if (subCost(ct) >0)
1294 return true;
1295
1296 foreach(TraceInstrCall* ic, _instrCalls)
1297 if (ic->subCost(ct) >0)
1298 return true;
1299
1300 foreach(TraceInstrJump* ij, _instrJumps)
1301 if (ij->executedCount() >0)
1302 return true;
1303
1304 return false;
1305 }
1306
partInstr(TracePart * part,TracePartFunction * partFunction)1307 TracePartInstr* TraceInstr::partInstr(TracePart* part,
1308 TracePartFunction* partFunction)
1309 {
1310 TracePartInstr* item = (TracePartInstr*) findDepFromPart(part);
1311 if (!item) {
1312 item = new TracePartInstr(this);
1313 item->setPosition(part);
1314 addDep(item);
1315 //part->addDep(item);
1316 partFunction->addPartInstr(item);
1317 }
1318 return item;
1319 }
1320
instrJump(TraceInstr * to,bool isJmpCond)1321 TraceInstrJump* TraceInstr::instrJump(TraceInstr* to, bool isJmpCond)
1322 {
1323 foreach(TraceInstrJump* jump, _instrJumps)
1324 if (jump->instrTo() == to)
1325 return jump;
1326
1327 TraceInstrJump* jump = new TraceInstrJump(this, to, isJmpCond);
1328 _instrJumps.append(jump);
1329 return jump;
1330 }
1331
1332
1333
addInstrCall(TraceInstrCall * instrCall)1334 void TraceInstr::addInstrCall(TraceInstrCall* instrCall)
1335 {
1336 #if TRACE_ASSERTIONS
1337 if (_instrCalls.contains(instrCall)) return;
1338
1339 if (instrCall->instr() != this) {
1340 qDebug("Can not add instruction call to another instruction!");
1341 return;
1342 }
1343 #endif
1344
1345 _instrCalls.append(instrCall);
1346 invalidate();
1347
1348 #if TRACE_DEBUG
1349 qDebug("%s added\n %s (now %d)",
1350 qPrintable( fullName() ),
1351 qPrintable(instrCall->fullName()), _instrCalls.count());
1352 #endif
1353 }
1354
1355
name() const1356 QString TraceInstr::name() const
1357 {
1358 return QStringLiteral("0x%1").arg(_addr.toString());
1359 }
1360
prettyName() const1361 QString TraceInstr::prettyName() const
1362 {
1363 return QStringLiteral("0x%1").arg(_addr.toString());
1364 }
1365
1366
1367 //---------------------------------------------------
1368 // TraceLine
1369
TraceLine()1370 TraceLine::TraceLine()
1371 : TraceListCost(ProfileContext::context(ProfileContext::Line))
1372 {
1373 _lineno = 0;
1374 _sourceFile = nullptr;
1375 }
1376
~TraceLine()1377 TraceLine::~TraceLine()
1378 {
1379 // we are the owner of items generated in our factories
1380 qDeleteAll(_deps);
1381 qDeleteAll(_lineJumps);
1382 }
1383
hasCost(EventType * ct)1384 bool TraceLine::hasCost(EventType* ct)
1385 {
1386 if (subCost(ct) >0)
1387 return true;
1388
1389 foreach(TraceLineCall* lc, _lineCalls)
1390 if (lc->subCost(ct) >0)
1391 return true;
1392
1393 foreach(TraceLineJump* lj, _lineJumps)
1394 if (lj->executedCount() >0)
1395 return true;
1396
1397 return false;
1398 }
1399
partLine(TracePart * part,TracePartFunction * partFunction)1400 TracePartLine* TraceLine::partLine(TracePart* part,
1401 TracePartFunction* partFunction)
1402 {
1403 TracePartLine* item = (TracePartLine*) findDepFromPart(part);
1404 if (!item) {
1405 item = new TracePartLine(this);
1406 item->setPosition(part);
1407 addDep(item);
1408 #if !USE_FIXCOST
1409 part->addDep(item);
1410 #endif
1411 partFunction->addPartLine(item);
1412 }
1413 return item;
1414 }
1415
lineJump(TraceLine * to,bool isJmpCond)1416 TraceLineJump* TraceLine::lineJump(TraceLine* to, bool isJmpCond)
1417 {
1418 foreach(TraceLineJump* jump, _lineJumps)
1419 if (jump->lineTo() == to)
1420 return jump;
1421
1422 TraceLineJump* jump = new TraceLineJump(this, to, isJmpCond);
1423 _lineJumps.append(jump);
1424 return jump;
1425 }
1426
1427
addLineCall(TraceLineCall * lineCall)1428 void TraceLine::addLineCall(TraceLineCall* lineCall)
1429 {
1430 #if TRACE_ASSERTIONS
1431 if (_lineCalls.contains(lineCall)) return;
1432
1433 if (lineCall->line() != this) {
1434 qDebug("Can not add line call to another line!");
1435 return;
1436 }
1437 #endif
1438
1439 TraceFunction* caller = lineCall->call()->caller();
1440 TraceFunction* function = _sourceFile->function();
1441 if (caller != function) {
1442 // We regard 2 functions as the same if they have
1443 // same class, name, object
1444 if ((caller->cls() != function->cls()) ||
1445 (caller->name() != function->name()) ||
1446 (caller->object() != function->object())) {
1447
1448 qDebug("ERROR: Adding line call, line %d\n of %s to\n %s ?!",
1449 lineCall->line()->lineno(),
1450 qPrintable(caller->info()), qPrintable(function->info()));
1451 }
1452 }
1453
1454 _lineCalls.append(lineCall);
1455 invalidate();
1456
1457 #if TRACE_DEBUG
1458 qDebug("%s added\n %s (now %d)",
1459 qPrintable( fullName() ),
1460 qPrintable(lineCall->fullName()), _lineCalls.count());
1461 #endif
1462 }
1463
1464
name() const1465 QString TraceLine::name() const
1466 {
1467 QString fileShortName = _sourceFile->file()->shortName();
1468 if (fileShortName.isEmpty())
1469 return TraceFile::prettyEmptyName();
1470
1471 return QStringLiteral("%1:%2")
1472 .arg(fileShortName).arg(_lineno);
1473 }
1474
prettyName() const1475 QString TraceLine::prettyName() const
1476 {
1477 return QStringLiteral("%1 [%2]")
1478 .arg(name()).arg(_sourceFile->function()->prettyName());
1479 }
1480
1481 //---------------------------------------------------
1482 // TraceCostItem
1483
TraceCostItem(ProfileContext * context)1484 TraceCostItem::TraceCostItem(ProfileContext* context)
1485 : TraceInclusiveListCost(context)
1486 {
1487 }
1488
~TraceCostItem()1489 TraceCostItem::~TraceCostItem()
1490 {}
1491
1492
1493 //---------------------------------------------------
1494 // TraceFunctionSource
1495
TraceFunctionSource(TraceFunction * function,TraceFile * file)1496 TraceFunctionSource::TraceFunctionSource(TraceFunction* function,
1497 TraceFile* file)
1498 : ProfileCostArray(ProfileContext::context(ProfileContext::FunctionSource))
1499 {
1500 _file = file;
1501 _function = function;
1502
1503 // the function is dependent from our cost sum
1504 _dep = _function;
1505
1506 _lineMap = nullptr;
1507 _lineMapFilled = false;
1508 _line0 = nullptr;
1509 }
1510
~TraceFunctionSource()1511 TraceFunctionSource::~TraceFunctionSource()
1512 {
1513 delete _lineMap;
1514 delete _line0;
1515 }
1516
name() const1517 QString TraceFunctionSource::name() const
1518 {
1519 return QStringLiteral("%1 for %2").arg(_file->name()).arg(_function->name());
1520 }
1521
firstLineno()1522 uint TraceFunctionSource::firstLineno()
1523 {
1524 // lazy generate the map if not done up to now
1525 TraceLineMap* map = lineMap();
1526 // ignore line 0 here
1527 if (!map || map->count() == 0) return 0;
1528 TraceLineMap::Iterator it = map->begin();
1529 return (*it).lineno();
1530 }
1531
lastLineno()1532 uint TraceFunctionSource::lastLineno()
1533 {
1534 // lazy generate the map if not done up to now
1535 TraceLineMap* map = lineMap();
1536 // ignore line 0 here
1537 if (!map || map->count() == 0) return 0;
1538 TraceLineMap::Iterator it = map->end();
1539 --it;
1540 return (*it).lineno();
1541 }
1542
1543 /* factory */
line(uint lineno,bool createNew)1544 TraceLine* TraceFunctionSource::line(uint lineno, bool createNew)
1545 {
1546 if (lineno == 0) {
1547 if (!_line0) {
1548 if (!createNew) return nullptr;
1549 _line0 = new TraceLine;
1550 _line0->setSourceFile(this);
1551 _line0->setLineno(0);
1552 }
1553 return _line0;
1554 }
1555
1556 if (!createNew) {
1557 if (!_lineMap) return nullptr;
1558 TraceLineMap::Iterator it = _lineMap->find(lineno);
1559 if (it == _lineMap->end()) return nullptr;
1560 return &(it.value());
1561 }
1562
1563 if (!_lineMap) _lineMap = new TraceLineMap;
1564
1565 TraceLine& l = (*_lineMap)[lineno];
1566 if (!l.isValid()) {
1567 l.setSourceFile(this);
1568 l.setLineno(lineno);
1569
1570 #if TRACE_DEBUG
1571 qDebug("Created %s [TraceFunctionSource::line]",
1572 qPrintable(l.fullName()));
1573 #endif
1574 }
1575 return &l;
1576 }
1577
update()1578 void TraceFunctionSource::update()
1579 {
1580 if (!_dirty) return;
1581
1582 clear();
1583
1584 // no need to create lineMap if not already created
1585 if (_lineMap) {
1586 TraceLineMap::Iterator lit;
1587 for ( lit = _lineMap->begin();
1588 lit != _lineMap->end(); ++lit )
1589 addCost( &(*lit) );
1590 }
1591
1592 _dirty = false;
1593 }
1594
invalidateDynamicCost()1595 void TraceFunctionSource::invalidateDynamicCost()
1596 {
1597 // no need to create lineMap if not already created
1598 if (_lineMap) {
1599 TraceLineMap::Iterator lit;
1600 for ( lit = _lineMap->begin();
1601 lit != _lineMap->end(); ++lit )
1602 (*lit).invalidate();
1603 }
1604
1605 invalidate();
1606 }
1607
lineMap()1608 TraceLineMap* TraceFunctionSource::lineMap()
1609 {
1610 #if USE_FIXCOST
1611
1612 if (_lineMapFilled) return _lineMap;
1613 _lineMapFilled = true;
1614 if (!_lineMap)
1615 _lineMap = new TraceLineMap;
1616
1617 TraceLine* l = nullptr;
1618 TracePartLine* pl = nullptr;
1619 TraceLineCall* lc = nullptr;
1620 TracePartLineCall* plc = nullptr;
1621
1622 /* go over all part objects for this function, and
1623 * - build TraceLines (the line map) using FixCost objects
1624 * - build TraceJumpLines using FixJump objects
1625 */
1626 foreach(TraceInclusiveCost* ic, _function->deps()) {
1627 TracePartFunction* pf = (TracePartFunction*) ic;
1628
1629 if (0) qDebug("PartFunction %s:%d",
1630 qPrintable(pf->function()->name()),
1631 pf->part()->partNumber());
1632
1633 FixCost* fc = pf->firstFixCost();
1634 for(; fc; fc = fc->nextCostOfPartFunction()) {
1635 if (fc->line() == 0) continue;
1636 if (fc->functionSource() != this) continue;
1637
1638 if (!l || l->lineno() != fc->line()) {
1639 l = &(*_lineMap)[fc->line()];
1640 if (!l->isValid()) {
1641 l->setSourceFile(this);
1642 l->setLineno(fc->line());
1643 }
1644 pl = nullptr;
1645 }
1646 if (!pl || pl->part() != fc->part())
1647 pl = l->partLine(fc->part(), pf);
1648 fc->addTo(pl);
1649 }
1650
1651 TraceLine* to = nullptr;
1652 TraceLineJump* lj;
1653 TracePartLineJump* plj;
1654 FixJump* fj = pf->firstFixJump();
1655 for(; fj; fj = fj->nextJumpOfPartFunction()) {
1656 if (fj->line() == 0) continue;
1657 if (fj->source() != this) continue;
1658 if (!fj->targetSource()) {
1659 // be robust against buggy loaders
1660 continue;
1661 }
1662
1663 // do not display jumps to same or following line
1664 if ((fj->line() == fj->targetLine()) ||
1665 (fj->line()+1 == fj->targetLine())) continue;
1666
1667 if (!l || l->lineno() != fj->line()) {
1668 l = &(*_lineMap)[fj->line()];
1669 if (!l->isValid()) {
1670 l->setSourceFile(this);
1671 l->setLineno(fj->line());
1672 }
1673 }
1674
1675 to = fj->targetSource()->line(fj->targetLine(), true);
1676
1677 lj = l->lineJump(to, fj->isCondJump());
1678 plj = lj->partLineJump(fj->part());
1679
1680 fj->addTo(plj);
1681 }
1682
1683 foreach(TracePartCall* pc, pf->partCallings()) {
1684
1685 if (0) qDebug("PartCall %s:%d",
1686 qPrintable(pc->call()->name()),
1687 pf->part()->partNumber());
1688
1689 FixCallCost* fcc = pc->firstFixCallCost();
1690 for(; fcc; fcc = fcc->nextCostOfPartCall()) {
1691 if (fcc->line() == 0) continue;
1692 if (fcc->functionSource() != this) continue;
1693
1694 if (!l || l->lineno() != fcc->line()) {
1695 l = &(*_lineMap)[fcc->line()];
1696 if (!l->isValid()) {
1697 l->setSourceFile(this);
1698 l->setLineno(fcc->line());
1699 }
1700 }
1701 if (!lc || lc->call() != pc->call() || lc->line() != l) {
1702 lc = pc->call()->lineCall(l);
1703 plc = nullptr;
1704 }
1705 if (!plc || plc->part() != fcc->part())
1706 plc = lc->partLineCall(fcc->part(), pc);
1707
1708 fcc->addTo(plc);
1709 if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
1710 qPrintable(fcc->functionSource()->file()->shortName()),
1711 fcc->line(), qPrintable(fcc->addr().toString()),
1712 qPrintable(fcc->callCount().pretty()));
1713 }
1714 }
1715 }
1716
1717 #endif
1718
1719 return _lineMap;
1720 }
1721
1722
1723
1724 //---------------------------------------------------
1725 // TraceAssociation
1726
TraceAssociation()1727 TraceAssociation::TraceAssociation()
1728 {
1729 _function = nullptr;
1730 _valid = false;
1731 }
1732
~TraceAssociation()1733 TraceAssociation::~TraceAssociation()
1734 {
1735 // do not delete from TraceFunction
1736 if (_function) _function->removeAssociation(this);
1737 }
1738
isAssociated()1739 bool TraceAssociation::isAssociated()
1740 {
1741 if (!_function) return false;
1742
1743 return _function->association(rtti())==this;
1744 }
1745
setFunction(TraceFunction * f)1746 bool TraceAssociation::setFunction(TraceFunction* f)
1747 {
1748 if (_function == f)
1749 return isAssociated();
1750
1751 if (_function) {
1752 // do not delete ourself
1753 _function->removeAssociation(this);
1754 }
1755
1756 _function = f;
1757 if (f && f->association(rtti()) == nullptr) {
1758 f->addAssociation(this);
1759 return true;
1760 }
1761 return false;
1762 }
1763
clear(TraceData * d,int rtti)1764 void TraceAssociation::clear(TraceData* d, int rtti)
1765 {
1766 TraceFunctionMap::Iterator it;
1767 for ( it = d->functionMap().begin();
1768 it != d->functionMap().end(); ++it )
1769 (*it).removeAssociation(rtti);
1770 }
1771
invalidate(TraceData * d,int rtti)1772 void TraceAssociation::invalidate(TraceData* d, int rtti)
1773 {
1774 TraceFunctionMap::Iterator it;
1775 for ( it = d->functionMap().begin();
1776 it != d->functionMap().end(); ++it )
1777 (*it).invalidateAssociation(rtti);
1778 }
1779
1780
1781 //---------------------------------------------------
1782 // TraceFunction
1783
TraceFunction()1784 TraceFunction::TraceFunction()
1785 : TraceCostItem(ProfileContext::context(ProfileContext::Function))
1786 {
1787 _object = nullptr;
1788 _file = nullptr;
1789 _cls = nullptr;
1790 _cycle = nullptr;
1791
1792 _calledCount = 0;
1793 _callingCount = 0;
1794 _calledContexts = 0;
1795 _callingContexts = 0;
1796
1797 _instrMap = nullptr;
1798 _instrMapFilled = false;
1799 }
1800
1801
~TraceFunction()1802 TraceFunction::~TraceFunction()
1803 {
1804 qDeleteAll(_associations);
1805
1806 // we are the owner of items generated in our factories
1807 qDeleteAll(_deps);
1808 qDeleteAll(_callings);
1809 qDeleteAll(_sourceFiles);
1810
1811 delete _instrMap;
1812 }
1813
1814 // no unique check is done!
addAssociation(TraceAssociation * a)1815 void TraceFunction::addAssociation(TraceAssociation* a)
1816 {
1817 if (!a) return;
1818 _associations.append(a);
1819 }
1820
removeAssociation(TraceAssociation * a)1821 void TraceFunction::removeAssociation(TraceAssociation* a)
1822 {
1823 _associations.removeAll(a);
1824 }
1825
removeAssociation(int rtti,bool reallyDelete)1826 void TraceFunction::removeAssociation(int rtti, bool reallyDelete)
1827 {
1828 if (rtti==0) {
1829 if (reallyDelete)
1830 qDeleteAll(_associations);
1831 _associations.clear();
1832 return;
1833 }
1834
1835 foreach(TraceAssociation* a, _associations) {
1836 if (a->rtti() == rtti) {
1837 if (reallyDelete) delete a;
1838 _associations.removeAll(a);
1839 return;
1840 }
1841 }
1842 }
1843
invalidateAssociation(int rtti)1844 void TraceFunction::invalidateAssociation(int rtti)
1845 {
1846 foreach(TraceAssociation* a, _associations) {
1847 if ((rtti==0) || (a->rtti() == rtti))
1848 a->invalidate();
1849 }
1850 }
1851
association(int rtti)1852 TraceAssociation* TraceFunction::association(int rtti)
1853 {
1854 foreach(TraceAssociation* a, _associations) {
1855 if (a->rtti() == rtti)
1856 return a;
1857 }
1858 return nullptr;
1859 }
1860
1861 #if 0
1862 // helper for prettyName
1863 bool TraceFunction::isUniquePrefix(const QString& prefix) const
1864 {
1865 TraceFunctionMap::ConstIterator it, it2;
1866 it = it2 = _myMapIterator;
1867 if (it != data()->functionBeginIterator()) {
1868 it2--;
1869 if ((*it2).name().startsWith(prefix)) return false;
1870 }
1871 if (it != data()->functionEndIterator()) {
1872 it++;
1873 if ((*it).name().startsWith(prefix)) return false;
1874 }
1875 return true;
1876 }
1877 #endif
1878
prettyName() const1879 QString TraceFunction::prettyName() const
1880 {
1881 QString res = _name;
1882
1883 if (_name.isEmpty())
1884 return prettyEmptyName();
1885
1886 if (GlobalConfig::hideTemplates()) {
1887
1888 res = QString();
1889 int d = 0;
1890 for(int i=0;i<_name.length();i++) {
1891 switch(_name[i].toLatin1()) {
1892 case '<':
1893 if (d<=0) res.append(_name[i]);
1894 d++;
1895 break;
1896 case '>':
1897 d--;
1898 // fall through
1899 default:
1900 if (d<=0) res.append(_name[i]);
1901 break;
1902 }
1903 }
1904 }
1905 #if 0
1906 // TODO: make it a configuration, but disabled by default.
1907 //
1908 // Stripping parameter signature of C++ symbols is fine
1909 // if the function name is unique in the whole program.
1910 // However, we only can detect if it is unique in the profile,
1911 // which makes this "beautification" potentially confusing
1912 int p = _name.indexOf('(');
1913 if (p>0) {
1914 // handle C++ "operator()" correct
1915 if ( (p+2 < _name.size()) && (_name[p+1] == ')') && (_name[p+2] == '(')) p+=2;
1916
1917 // we have a C++ symbol with argument types:
1918 // check for unique function name (inclusive '(' !)
1919 if (isUniquePrefix(_name.left(p+1)))
1920 res = _name.left(p);
1921 }
1922 #endif
1923
1924 // cycle members
1925 if (_cycle) {
1926 if (_cycle != this)
1927 res = QStringLiteral("%1 <cycle %2>").arg(res).arg(_cycle->cycleNo());
1928 else
1929 res = QStringLiteral("<cycle %2>").arg(_cycle->cycleNo());
1930 }
1931
1932
1933 return res;
1934 }
1935
formattedName() const1936 QString TraceFunction::formattedName() const
1937 {
1938 // produce a "rich" name only if templates are hidden
1939 if (!GlobalConfig::hideTemplates() || _name.isEmpty()) return QString();
1940
1941 // bold, but inside template parameters normal, function arguments italic
1942 QString rich(QStringLiteral("<b>"));
1943 int d = 0;
1944 for(int i=0;i<_name.length();i++) {
1945 switch(_name[i].toLatin1()) {
1946 case '&':
1947 rich.append("&");
1948 break;
1949 case '<':
1950 d++;
1951 rich.append("<");
1952 if (d==1)
1953 rich.append("</b>");
1954 break;
1955 case '>':
1956 d--;
1957 if (d==0)
1958 rich.append("<b>");
1959 rich.append("> "); // add space to allow for line break
1960 break;
1961 case '(':
1962 rich.append("</b>(<i><b>");
1963 break;
1964 case ')':
1965 rich.append("</b></i>)<b>");
1966 break;
1967 default:
1968 rich.append(_name[i]);
1969 break;
1970 }
1971 }
1972 rich.append("</b>");
1973 return rich;
1974 }
1975
prettyEmptyName()1976 QString TraceFunction::prettyEmptyName()
1977 {
1978 return QObject::tr("(unknown)");
1979 }
1980
1981 /*
1982 * Returns location string: ELF object and source file(s).
1983 */
location(int maxFiles) const1984 QString TraceFunction::location(int maxFiles) const
1985 {
1986 QString loc;
1987
1988 // add object file with address range
1989 if (_object) {
1990 loc = _object->shortName();
1991
1992 #if 0
1993 uint from = firstAddress();
1994 uint to = lastAddress();
1995 if (from != 0 && to != 0) {
1996 if (from == to)
1997 loc += QString(" (0x%1)").arg(to, 0, 16);
1998 else
1999 loc += QString(" (0x%1-0x%2)").arg(from, 0, 16).arg(to, 0, 16);
2000 }
2001 #endif
2002 }
2003
2004 // add all source files
2005 int filesAdded = 0;
2006 foreach(TraceFunctionSource* sourceFile, _sourceFiles) {
2007 if (!sourceFile->file() ||
2008 (sourceFile->file()->name().isEmpty()) )
2009 continue;
2010
2011 if (!loc.isEmpty())
2012 loc += (filesAdded>0) ? ", " : ": ";
2013 filesAdded++;
2014
2015 if ((maxFiles>0) && (filesAdded>maxFiles)) {
2016 loc += QLatin1String("...");
2017 break;
2018 }
2019 loc += sourceFile->file()->shortName();
2020
2021 #if 0
2022 from = sourceFile->firstLineno();
2023 to = sourceFile->lastLineno();
2024 if (from != 0 && to != 0) {
2025 if (from == to)
2026 loc += QString(" (%1)").arg(to);
2027 else
2028 loc += QString(" (%1-%2)").arg(from).arg(to);
2029 }
2030 #endif
2031 }
2032
2033 return loc;
2034 }
2035
2036 // pretty version is allowed to mangle the string...
prettyLocation(int maxFiles) const2037 QString TraceFunction::prettyLocation(int maxFiles) const
2038 {
2039 QString l = location(maxFiles);
2040 if (l.isEmpty()) return QObject::tr("(unknown)");
2041
2042 return l;
2043 }
2044
addPrettyLocation(QString & s,int maxFiles) const2045 void TraceFunction::addPrettyLocation(QString& s, int maxFiles) const
2046 {
2047 QString l = location(maxFiles);
2048 if (l.isEmpty()) return;
2049
2050 s += QStringLiteral(" (%1)").arg(l);
2051 }
2052
prettyNameWithLocation(int maxFiles) const2053 QString TraceFunction::prettyNameWithLocation(int maxFiles) const
2054 {
2055 QString l = location(maxFiles);
2056 if (l.isEmpty()) return prettyName();
2057
2058 return QStringLiteral("%1 (%2)").arg(prettyName()).arg(l);
2059 }
2060
info() const2061 QString TraceFunction::info() const
2062 {
2063 QString l = location();
2064 if (l.isEmpty())
2065 return QStringLiteral("Function %1").arg(name());
2066
2067 return QStringLiteral("Function %1 (location %2)")
2068 .arg(name()).arg(l);
2069 }
2070
2071
firstAddress() const2072 Addr TraceFunction::firstAddress() const
2073 {
2074 // ignore address 0 here
2075 if (!_instrMap || _instrMap->count() == 0) return 0;
2076 TraceInstrMap::ConstIterator it = _instrMap->constBegin();
2077 return (*it).addr();
2078 }
2079
lastAddress() const2080 Addr TraceFunction::lastAddress() const
2081 {
2082 // ignore address 0 here
2083 if (!_instrMap || _instrMap->count() == 0) return 0;
2084 TraceInstrMap::ConstIterator it = _instrMap->constEnd();
2085 --it;
2086 return (*it).addr();
2087 }
2088
2089 /* factory */
instr(Addr addr,bool createNew)2090 TraceInstr* TraceFunction::instr(Addr addr, bool createNew)
2091 {
2092 // address 0 not allowed
2093 if (addr == Addr(0)) return nullptr;
2094
2095 if (!createNew) {
2096 if (!_instrMap) return nullptr;
2097 TraceInstrMap::Iterator it = _instrMap->find(addr);
2098 if (it == _instrMap->end())
2099 return nullptr;
2100 return &(it.value());
2101 }
2102
2103 if (!_instrMap) _instrMap = new TraceInstrMap;
2104
2105 TraceInstr& i = (*_instrMap)[addr];
2106 if (!i.isValid()) {
2107 i.setAddr(addr);
2108 i.setFunction(this);
2109
2110 #if TRACE_DEBUG
2111 qDebug("Created %s [TraceFunction::instr]",
2112 qPrintable(i.fullName()));
2113 #endif
2114 }
2115 return &i;
2116 }
2117
addCaller(TraceCall * caller)2118 void TraceFunction::addCaller(TraceCall* caller)
2119 {
2120 #if TRACE_ASSERTIONS
2121 if (caller->called() != this) {
2122 qDebug("Can not add call to another line!\n");
2123 return;
2124 }
2125
2126 if (_callers.contains(caller)) return;
2127 #endif
2128
2129 _callers.append(caller);
2130 invalidate();
2131
2132 #if TRACE_DEBUG
2133 qDebug("%s added Caller\n %s (now %d)",
2134 qPrintable( fullName() ), qPrintable(caller->fullName()), _callers.count());
2135 #endif
2136 }
2137
2138
2139
calling(TraceFunction * called)2140 TraceCall* TraceFunction::calling(TraceFunction* called)
2141 {
2142 foreach(TraceCall* calling, _callings)
2143 if (calling->called() == called)
2144 return calling;
2145
2146 TraceCall* calling = new TraceCall(this, called);
2147 _callings.append(calling);
2148
2149 // we have to invalidate ourself so invalidations from item propagate up
2150 invalidate();
2151
2152 #if TRACE_DEBUG
2153 qDebug("Created %s [TraceFunction::calling]", qPrintable(calling->fullName()));
2154 #endif
2155 called->addCaller(calling);
2156 return calling;
2157 }
2158
sourceFile(TraceFile * file,bool createNew)2159 TraceFunctionSource* TraceFunction::sourceFile(TraceFile* file,
2160 bool createNew)
2161 {
2162 if (!file) file = _file;
2163
2164 foreach(TraceFunctionSource* sourceFile, _sourceFiles)
2165 if (sourceFile->file() == file)
2166 return sourceFile;
2167
2168 if (!createNew) return nullptr;
2169
2170 TraceFunctionSource* sourceFile = new TraceFunctionSource(this, file);
2171 _sourceFiles.append(sourceFile);
2172
2173 // we have to invalidate ourself so invalidations from item propagate up
2174 invalidate();
2175
2176 #if TRACE_DEBUG
2177 qDebug("Created SourceFile %s [TraceFunction::line]",
2178 qPrintable(file->name()));
2179 #endif
2180 file->addSourceFile(sourceFile);
2181 return sourceFile;
2182 }
2183
line(TraceFile * file,uint lineno,bool createNew)2184 TraceLine* TraceFunction::line(TraceFile* file, uint lineno,
2185 bool createNew)
2186 {
2187 Q_ASSERT(file!=nullptr);
2188
2189 TraceFunctionSource* sf = sourceFile(file, createNew);
2190 if (!sf)
2191 return nullptr;
2192 else
2193 return sf->line(lineno, createNew);
2194 }
2195
2196
partFunction(TracePart * part,TracePartFile * partFile,TracePartObject * partObject)2197 TracePartFunction* TraceFunction::partFunction(TracePart* part,
2198 TracePartFile* partFile,
2199 TracePartObject* partObject)
2200 {
2201 TracePartFunction* item = (TracePartFunction*) findDepFromPart(part);
2202 if (!item) {
2203 item = new TracePartFunction(this, partObject, partFile);
2204 item->setPosition(part);
2205 addDep(item);
2206 #if USE_FIXCOST
2207 part->addDep(item);
2208 #endif
2209
2210 if (_cls) {
2211 TracePartClass* partClass = _cls->partClass(part);
2212 partClass->addPartFunction(item);
2213 item->setPartClass(partClass);
2214 }
2215
2216 partFile->addPartFunction(item);
2217 if (partObject)
2218 partObject->addPartFunction(item);
2219 }
2220 else if (item->partObject()==nullptr && partObject) {
2221 item->setPartObject(partObject);
2222 partObject->addPartFunction(item);
2223 }
2224
2225 return item;
2226 }
2227
2228
calledCount()2229 SubCost TraceFunction::calledCount()
2230 {
2231 if (_dirty) update();
2232
2233 return _calledCount;
2234 }
2235
calledContexts()2236 int TraceFunction::calledContexts()
2237 {
2238 if (_dirty) update();
2239
2240 return _calledContexts;
2241 }
2242
callingCount()2243 SubCost TraceFunction::callingCount()
2244 {
2245 if (_dirty) update();
2246
2247 return _callingCount;
2248 }
2249
callingContexts()2250 int TraceFunction::callingContexts()
2251 {
2252 if (_dirty) update();
2253
2254 return _callingContexts;
2255 }
2256
prettyCalledCount()2257 QString TraceFunction::prettyCalledCount()
2258 {
2259 return _calledCount.pretty();
2260 }
2261
prettyCallingCount()2262 QString TraceFunction::prettyCallingCount()
2263 {
2264 return _callingCount.pretty();
2265 }
2266
2267
callers(bool skipCycle) const2268 TraceCallList TraceFunction::callers(bool skipCycle) const
2269 {
2270 if (skipCycle) return _callers;
2271
2272 // fake the callers for cycle members
2273 if (_cycle && (_cycle != this)) {
2274 TraceCallList l;
2275
2276 // inner-cycle-callers
2277 foreach(TraceCall* c, _callers)
2278 if (c->caller()->cycle() == _cycle)
2279 l.append(c);
2280
2281 // call from cycle itself
2282 foreach(TraceCall* c, _cycle->_callings)
2283 if (c->called() == this) {
2284 l.append(c);
2285 return l;
2286 }
2287 }
2288
2289 return _callers;
2290 }
2291
callings(bool) const2292 const TraceCallList& TraceFunction::callings(bool /* skipCycle */) const
2293 {
2294 return _callings;
2295 }
2296
invalidateDynamicCost()2297 void TraceFunction::invalidateDynamicCost()
2298 {
2299 foreach(TraceCall* c, _callings)
2300 c->invalidateDynamicCost();
2301
2302 foreach(TraceFunctionSource* sf, _sourceFiles)
2303 sf->invalidateDynamicCost();
2304
2305 if (_instrMap) {
2306 TraceInstrMap::Iterator iit;
2307 for ( iit = _instrMap->begin();
2308 iit != _instrMap->end(); ++iit )
2309 (*iit).invalidate();
2310 }
2311
2312 invalidate();
2313 }
2314
update()2315 void TraceFunction::update()
2316 {
2317 if (!_dirty) return;
2318
2319 #if TRACE_DEBUG
2320 qDebug("Update %s (Callers %d, sourceFiles %d, instrs %d)",
2321 qPrintable(_name), _callers.count(),
2322 _sourceFiles.count(), _instrMap ? _instrMap->count():0);
2323 #endif
2324
2325 _calledCount = 0;
2326 _callingCount = 0;
2327 _calledContexts = 0;
2328 _callingContexts = 0;
2329 clear();
2330
2331 // To calculate context counts, we just use first real event type (FIXME?)
2332 EventType* e = data() ? data()->eventTypes()->realType(0) : nullptr;
2333
2334 // context count is NOT the sum of part contexts
2335 foreach(TraceCall *caller, _callers) {
2336 if (e && (caller->subCost(e) >0))
2337 _calledContexts++;
2338 _calledCount += caller->callCount();
2339 }
2340
2341 foreach(TraceCall* callee, _callings) {
2342 if (e && (callee->subCost(e) >0))
2343 _callingContexts++;
2344 _callingCount += callee->callCount();
2345 }
2346
2347 if (data()->inFunctionCycleUpdate() || !_cycle) {
2348 // usual case (no cycle member)
2349 foreach(TraceInclusiveCost* item, _deps) {
2350 if (!item->part() || !item->part()->isActive()) continue;
2351
2352 addCost(item);
2353 addInclusive(item->inclusive());
2354 }
2355 }
2356 else {
2357 // this is a cycle or cycle member
2358 foreach(TraceCall* callee, _callings) {
2359
2360 // ignore inner-cycle member calls for inclusive cost
2361 if ((_cycle != this) &&
2362 (callee->inCycle()>0)) continue;
2363
2364 addInclusive(callee);
2365 }
2366
2367 // self cost
2368 if (type() == ProfileContext::FunctionCycle) {
2369 // cycle: self cost is sum of cycle member self costs, but
2370 // does not add to inclusive cost
2371 foreach(TraceFunction* m, ((TraceFunctionCycle*)this)->members())
2372 addCost(m);
2373 }
2374 else {
2375 // cycle member
2376 foreach(TraceInclusiveCost* item, _deps) {
2377 if (!item->part() || !item->part()->isActive()) continue;
2378
2379 addCost(item);
2380 }
2381 _dirty = false; // do not recurse
2382 addInclusive(this);
2383 }
2384 }
2385 _dirty = false;
2386
2387 #if TRACE_DEBUG
2388 qDebug("> %s", qPrintable(costString(0)));
2389 #endif
2390 }
2391
isCycle()2392 bool TraceFunction::isCycle()
2393 {
2394 return _cycle == this;
2395 }
2396
isCycleMember()2397 bool TraceFunction::isCycleMember()
2398 {
2399 return _cycle && (_cycle != this);
2400 }
2401
cycleReset()2402 void TraceFunction::cycleReset()
2403 {
2404 _cycle = nullptr;
2405 _cycleStackDown = nullptr;
2406 _cycleLow = 0;
2407 }
2408
2409 // this does not mark functions calling themself !
cycleDFS(int d,int & pNo,TraceFunction ** pTop)2410 void TraceFunction::cycleDFS(int d, int& pNo, TraceFunction** pTop)
2411 {
2412 if (_cycleLow != 0) return;
2413
2414 // initialize with prefix order
2415 pNo++;
2416 int prefixNo = pNo;
2417 _cycleLow = prefixNo;
2418
2419 // put myself on stack
2420 _cycleStackDown = *pTop;
2421 *pTop = this;
2422
2423 /* cycle cut heuristic:
2424 * skip calls for cycle detection if they make less than _cycleCut
2425 * percent of the cost of the function.
2426 * FIXME: Which cost type to use for this heuristic ?!
2427 */
2428 Q_ASSERT((data() != nullptr) && (data()->eventTypes()->realCount()>0));
2429 EventType* e = data()->eventTypes()->realType(0);
2430
2431 SubCost base = 0;
2432 if (_callers.count()>0) {
2433 foreach(TraceCall* caller, _callers)
2434 if (caller->subCost(e) > base)
2435 base = caller->subCost(e);
2436 }
2437 else base = inclusive()->subCost(e);
2438
2439 SubCost cutLimit = SubCost(base * GlobalConfig::cycleCut());
2440
2441 if (0) {
2442 qDebug("%s (%d) Visiting %s",
2443 qPrintable(QString().fill(' ', d)),
2444 pNo, qPrintable(prettyName()));
2445 qDebug("%s Cum. %s, Max Caller %s, cut limit %s",
2446 qPrintable(QString().fill(' ', d)),
2447 qPrintable(inclusive()->subCost(e).pretty()),
2448 qPrintable(base.pretty()),
2449 qPrintable(cutLimit.pretty()));
2450 }
2451
2452 foreach(TraceCall *callee, _callings) {
2453 TraceFunction* called = callee->called();
2454
2455 // cycle cut heuristic
2456 if (callee->subCost(e) < cutLimit) {
2457 if (0) qDebug("%s Cut call to %s (cum. %s)",
2458 qPrintable(QString().fill(' ', d)),
2459 qPrintable(called->prettyName()),
2460 qPrintable(callee->subCost(e).pretty()));
2461
2462 continue;
2463 }
2464
2465 if (called->_cycleLow==0) {
2466 // not visited yet
2467 called->cycleDFS(d+1, pNo, pTop);
2468 if (called->_cycleLow < _cycleLow)
2469 _cycleLow = called->_cycleLow;
2470 }
2471 else if (called->_cycleStackDown) {
2472 // backlink to same SCC (still in stack)
2473 if (called->_cycleLow < _cycleLow)
2474 _cycleLow = called->_cycleLow;
2475
2476 if (0) qDebug("%s (low %d) Back to %s",
2477 qPrintable(QString().fill(' ', d)),
2478 _cycleLow, qPrintable(called->prettyName()));
2479 }
2480 }
2481
2482 if (prefixNo == _cycleLow) {
2483 // this is the base of a SCC.
2484
2485 if (*pTop == this) {
2486 *pTop = _cycleStackDown;
2487 _cycleStackDown = nullptr;
2488 }
2489 else {
2490 // a SCC with >1 members
2491
2492 TraceFunctionCycle* cycle = data()->functionCycle(this);
2493 if (0) qDebug("Found Cycle %d with base %s:",
2494 cycle->cycleNo(), qPrintable(prettyName()));
2495 while(*pTop) {
2496 TraceFunction* top = *pTop;
2497 cycle->add(top);
2498
2499 // remove from stack
2500 *pTop = top->_cycleStackDown;
2501 top->_cycleStackDown = nullptr;
2502
2503 if (0) qDebug(" %s", qPrintable(top->prettyName()));
2504 if (top == this) break;
2505 }
2506 }
2507 }
2508 }
2509
2510
instrMap()2511 TraceInstrMap* TraceFunction::instrMap()
2512 {
2513 #if USE_FIXCOST
2514
2515 if (_instrMapFilled) return _instrMap;
2516 _instrMapFilled = true;
2517 if (!_instrMap)
2518 _instrMap = new TraceInstrMap;
2519
2520 TraceLine* l = nullptr;
2521 TraceInstr* i = nullptr;
2522 TracePartInstr* pi = nullptr;
2523 TraceInstrCall* ic = nullptr;
2524 TracePartInstrCall* pic = nullptr;
2525
2526 foreach(TraceInclusiveCost* icost, deps()) {
2527 TracePartFunction* pf = (TracePartFunction*) icost;
2528
2529 if (0) qDebug("PartFunction %s:%d",
2530 qPrintable(pf->function()->name()),
2531 pf->part()->partNumber());
2532
2533 FixCost* fc = pf->firstFixCost();
2534 for(; fc; fc = fc->nextCostOfPartFunction()) {
2535 if (fc->addr() == 0) continue;
2536
2537 if (!l || (l->lineno() != fc->line()) ||
2538 (l->functionSource() != fc->functionSource()))
2539 l = fc->functionSource()->line(fc->line(),true);
2540
2541 if (!i || i->addr() != fc->addr()) {
2542 i = &(*_instrMap)[fc->addr()];
2543 if (!i->isValid()) {
2544 i->setFunction(this);
2545 i->setAddr(fc->addr());
2546 i->setLine(l);
2547 }
2548 pi = nullptr;
2549 }
2550 if (!pi || pi->part() != fc->part())
2551 pi = i->partInstr(fc->part(), pf);
2552 fc->addTo(pi);
2553 }
2554
2555 TraceInstr* to = nullptr;
2556 TraceInstrJump* ij;
2557 TracePartInstrJump* pij;
2558 FixJump* fj = pf->firstFixJump();
2559 for(; fj; fj = fj->nextJumpOfPartFunction()) {
2560 if (fj->addr() == 0) continue;
2561
2562 if (!l || (l->lineno() != fj->line()) ||
2563 (l->functionSource() != fj->source()))
2564 l = fj->source()->line(fj->line(),true);
2565
2566 if (!i || i->addr() != fj->addr()) {
2567 i = &(*_instrMap)[fj->addr()];
2568 if (!i->isValid()) {
2569 i->setFunction(this);
2570 i->setAddr(fj->addr());
2571 i->setLine(l);
2572 }
2573 }
2574
2575 to = fj->targetFunction()->instr(fj->targetAddr(), true);
2576
2577 ij = i->instrJump(to, fj->isCondJump());
2578 pij = ij->partInstrJump(fj->part());
2579
2580 fj->addTo(pij);
2581 }
2582
2583 foreach(TracePartCall* pc, pf->partCallings()) {
2584
2585 if (0) qDebug("PartCall %s:%d",
2586 qPrintable(pc->call()->name()),
2587 pf->part()->partNumber());
2588
2589 FixCallCost* fcc = pc->firstFixCallCost();
2590 for(; fcc; fcc = fcc->nextCostOfPartCall()) {
2591 if (fcc->addr() == 0) continue;
2592
2593 if (!l || (l->lineno() != fcc->line()) ||
2594 (l->functionSource() != fcc->functionSource()))
2595 l = fcc->functionSource()->line(fcc->line(),true);
2596
2597 if (!i || i->addr() != fcc->addr()) {
2598 i = &(*_instrMap)[fcc->addr()];
2599 if (!i->isValid()) {
2600 i->setFunction(this);
2601 i->setAddr(fcc->addr());
2602 i->setLine(l);
2603 }
2604 }
2605 if (!ic || ic->call() != pc->call() || ic->instr() != i) {
2606 ic = pc->call()->instrCall(i);
2607 pic = nullptr;
2608 }
2609 if (!pic || pic->part() != fcc->part())
2610 pic = ic->partInstrCall(fcc->part(), pc);
2611
2612 fcc->addTo(pic);
2613 if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
2614 qPrintable(fcc->functionSource()->file()->shortName()),
2615 fcc->line(), qPrintable(fcc->addr().toString()),
2616 qPrintable(fcc->callCount().pretty()));
2617 }
2618 }
2619 }
2620
2621 #endif
2622
2623 return _instrMap;
2624 }
2625
2626
2627
2628 //---------------------------------------------------
2629 // TraceFunctionCycle
2630
TraceFunctionCycle(TraceFunction * f,int n)2631 TraceFunctionCycle::TraceFunctionCycle(TraceFunction* f, int n)
2632 {
2633 _base = f;
2634 _cycleNo = n;
2635 _cycle = this;
2636
2637 setContext(ProfileContext::context(ProfileContext::FunctionCycle));
2638
2639 setPosition(f->data());
2640 setName(QStringLiteral("<cycle %1>").arg(n));
2641
2642 // reset to attributes of base function
2643 setFile(_base->file());
2644 setClass(_base->cls());
2645 setObject(_base->object());
2646 }
2647
init()2648 void TraceFunctionCycle::init()
2649 {
2650 _members.clear();
2651 _callers.clear();
2652 // this deletes all TraceCall's to members
2653 _callings.clear();
2654
2655 invalidate();
2656 }
2657
add(TraceFunction * f)2658 void TraceFunctionCycle::add(TraceFunction* f)
2659 {
2660 _members.append(f);
2661 }
2662
setup()2663 void TraceFunctionCycle::setup()
2664 {
2665 if (_members.isEmpty()) return;
2666
2667 foreach(TraceFunction* f, _members) {
2668
2669 // the cycle takes all outside callers from its members
2670 foreach(TraceCall* call, f->callers()) {
2671 if (_members.contains(call->caller())) continue;
2672 _callers.append(call);
2673 }
2674
2675 // the cycle has a call to each member
2676 TraceCall* call = new TraceCall(this, f);
2677 call->invalidate();
2678 _callings.append(call);
2679
2680 // now do some faking...
2681 f->setCycle(this);
2682 }
2683 invalidate();
2684 }
2685
2686
2687 //---------------------------------------------------
2688 // TraceClass
2689
TraceClass()2690 TraceClass::TraceClass()
2691 : TraceCostItem(ProfileContext::context(ProfileContext::Class))
2692 {}
2693
~TraceClass()2694 TraceClass::~TraceClass()
2695 {
2696 // we are the owner of items generated in our factory
2697 qDeleteAll(_deps);
2698 }
2699
prettyName() const2700 QString TraceClass::prettyName() const
2701 {
2702 if (_name.isEmpty())
2703 return prettyEmptyName();
2704 return _name;
2705 }
2706
prettyEmptyName()2707 QString TraceClass::prettyEmptyName()
2708 {
2709 return QObject::tr("(global)");
2710 }
2711
partClass(TracePart * part)2712 TracePartClass* TraceClass::partClass(TracePart* part)
2713 {
2714 TracePartClass* item = (TracePartClass*) findDepFromPart(part);
2715 if (!item) {
2716 item = new TracePartClass(this);
2717 item->setPosition(part);
2718 addDep(item);
2719 }
2720 return item;
2721 }
2722
addFunction(TraceFunction * function)2723 void TraceClass::addFunction(TraceFunction* function)
2724 {
2725 #if TRACE_ASSERTIONS
2726 if (function->cls() != this) {
2727 qDebug("Can not add function to a class not enclosing this function\n");
2728 return;
2729 }
2730
2731 if (_functions.contains(function)) return;
2732 #endif
2733
2734 _functions.append(function);
2735
2736 invalidate();
2737
2738 #if TRACE_DEBUG
2739 qDebug("%s added\n %s (now %d)",
2740 qPrintable( fullName() ),
2741 qPrintable(function->fullName()), _functions.count());
2742 #endif
2743 }
2744
2745
2746
2747 //---------------------------------------------------
2748 // TraceFile
2749
TraceFile()2750 TraceFile::TraceFile()
2751 : TraceCostItem(ProfileContext::context(ProfileContext::File))
2752 {}
2753
~TraceFile()2754 TraceFile::~TraceFile()
2755 {
2756 // we are the owner of items generated in our factory
2757 qDeleteAll(_deps);
2758 }
2759
partFile(TracePart * part)2760 TracePartFile* TraceFile::partFile(TracePart* part)
2761 {
2762 TracePartFile* item = (TracePartFile*) findDepFromPart(part);
2763 if (!item) {
2764 item = new TracePartFile(this);
2765 item->setPosition(part);
2766 addDep(item);
2767 }
2768 return item;
2769 }
2770
addFunction(TraceFunction * function)2771 void TraceFile::addFunction(TraceFunction* function)
2772 {
2773 #if TRACE_ASSERTIONS
2774 if (function->file() != this) {
2775 qDebug("Can not add function to a file not enclosing this function\n");
2776 return;
2777 }
2778
2779 if (_functions.contains(function)) return;
2780 #endif
2781
2782 _functions.append(function);
2783
2784 invalidate();
2785
2786 #if TRACE_DEBUG
2787 qDebug("%s added\n %s (now %d)",
2788 qPrintable( fullName() ),
2789 qPrintable(function->fullName()), _functions.count());
2790 #endif
2791 }
2792
2793
addSourceFile(TraceFunctionSource * sourceFile)2794 void TraceFile::addSourceFile(TraceFunctionSource* sourceFile)
2795 {
2796 #if TRACE_ASSERTIONS
2797 if (sourceFile->file() != this) {
2798 qDebug("Can not add sourceFile to a file not having lines for it\n");
2799 return;
2800 }
2801 #endif
2802
2803 _sourceFiles.append(sourceFile);
2804 // not truly needed, as we do not use the sourceFiles for cost update
2805 invalidate();
2806
2807 #if TRACE_DEBUG
2808 qDebug("%s \n added SourceFile %s (now %d)",
2809 qPrintable( fullName() ), qPrintable(sourceFile->fullName()),
2810 _sourceFiles.count());
2811 #endif
2812 }
2813
2814
2815
setDirectory(const QString & dir)2816 void TraceFile::setDirectory(const QString& dir)
2817 {
2818 if (dir.endsWith('/'))
2819 _dir = dir.left(dir.length()-1);
2820 else
2821 _dir = dir;
2822 }
2823
directory()2824 QString TraceFile::directory()
2825 {
2826 if (!_dir.isEmpty()) return _dir;
2827
2828 int lastIndex = 0, index;
2829 while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
2830 lastIndex = index+1;
2831
2832 if (lastIndex==0) return QString();
2833
2834 // without ending "/"
2835 return _name.left(lastIndex-1);
2836 }
2837
2838
shortName() const2839 QString TraceFile::shortName() const
2840 {
2841 int lastIndex = 0, index;
2842 while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
2843 lastIndex = index+1;
2844
2845 return _name.mid(lastIndex);
2846 }
2847
prettyName() const2848 QString TraceFile::prettyName() const
2849 {
2850 QString sn = shortName();
2851
2852 if (sn.isEmpty())
2853 return prettyEmptyName();
2854
2855 return sn;
2856 }
2857
prettyEmptyName()2858 QString TraceFile::prettyEmptyName()
2859 {
2860 return QObject::tr("(unknown)");
2861 }
2862
prettyLongName() const2863 QString TraceFile::prettyLongName() const
2864 {
2865 if (_name.isEmpty())
2866 return prettyEmptyName();
2867 return _name;
2868 }
2869
2870
2871 //---------------------------------------------------
2872 // TraceObject
2873
TraceObject()2874 TraceObject::TraceObject()
2875 : TraceCostItem(ProfileContext::context(ProfileContext::Object))
2876 {}
2877
~TraceObject()2878 TraceObject::~TraceObject()
2879 {
2880 // we are the owner of items generated in our factory
2881 qDeleteAll(_deps);
2882 }
2883
partObject(TracePart * part)2884 TracePartObject* TraceObject::partObject(TracePart* part)
2885 {
2886 TracePartObject* item = (TracePartObject*) findDepFromPart(part);
2887 if (!item) {
2888 item = new TracePartObject(this);
2889 item->setPosition(part);
2890 addDep(item);
2891 }
2892 return item;
2893 }
2894
addFunction(TraceFunction * function)2895 void TraceObject::addFunction(TraceFunction* function)
2896 {
2897 #if TRACE_ASSERTIONS
2898 if (function->object() != this) {
2899 qDebug("Can not add function to an object not enclosing this function\n");
2900 return;
2901 }
2902
2903 if (_functions.contains(function)) return;
2904 #endif
2905
2906 _functions.append(function);
2907
2908 invalidate();
2909
2910 #if TRACE_DEBUG
2911 qDebug("%s added\n %s (now %d)",
2912 qPrintable( fullName() ),
2913 qPrintable(function->fullName()), _functions.count());
2914 #endif
2915 }
2916
setDirectory(const QString & dir)2917 void TraceObject::setDirectory(const QString& dir)
2918 {
2919 if (dir.endsWith('/'))
2920 _dir = dir.left(dir.length()-1);
2921 else
2922 _dir = dir;
2923 }
2924
directory()2925 QString TraceObject::directory()
2926 {
2927 if (!_dir.isEmpty()) return _dir;
2928
2929 int lastIndex = 0, index;
2930 while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
2931 lastIndex = index+1;
2932
2933 if (lastIndex==0) return QString();
2934
2935 // without ending "/"
2936 return _name.left(lastIndex-1);
2937 }
2938
2939
shortName() const2940 QString TraceObject::shortName() const
2941 {
2942 int lastIndex = 0, index;
2943 while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
2944 lastIndex = index+1;
2945
2946 return _name.mid(lastIndex);
2947 }
2948
prettyName() const2949 QString TraceObject::prettyName() const
2950 {
2951 QString sn = shortName();
2952
2953 if (sn.isEmpty())
2954 return prettyEmptyName();
2955
2956 return sn;
2957 }
2958
prettyEmptyName()2959 QString TraceObject::prettyEmptyName()
2960 {
2961 return QObject::tr("(unknown)");
2962 }
2963
2964 //---------------------------------------------------
2965 // TracePart
2966
TracePart(TraceData * data)2967 TracePart::TracePart(TraceData* data)
2968 : TraceListCost(ProfileContext::context(ProfileContext::Part))
2969 {
2970 setPosition(data);
2971
2972 _dep = data;
2973 _active = true;
2974 _number = 0;
2975 _tid = 0;
2976 _pid = 0;
2977
2978 _eventTypeMapping = nullptr;
2979 }
2980
~TracePart()2981 TracePart::~TracePart()
2982 {
2983 delete _eventTypeMapping;
2984 }
2985
setPartNumber(int n)2986 void TracePart::setPartNumber(int n)
2987 {
2988 if (data()->maxPartNumber() <n) data()->setMaxPartNumber(n);
2989 _number = n;
2990 }
2991
setThreadID(int tid)2992 void TracePart::setThreadID(int tid)
2993 {
2994 if (data()->maxThreadID() <tid) data()->setMaxThreadID(tid);
2995 _tid = tid;
2996 }
2997
setProcessID(int pid)2998 void TracePart::setProcessID(int pid)
2999 {
3000 _pid = pid;
3001 }
3002
3003
3004
3005 // strip path
shortName() const3006 QString TracePart::shortName() const
3007 {
3008 int lastIndex = 0, index;
3009 while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
3010 lastIndex = index+1;
3011
3012 return _name.mid(lastIndex);
3013 }
3014
prettyName() const3015 QString TracePart::prettyName() const
3016 {
3017 if (_pid==0) return shortName();
3018 QString name = QStringLiteral("PID %1").arg(_pid);
3019 if (_number>0)
3020 name += QStringLiteral(", section %2").arg(_number);
3021 if ((data()->maxThreadID()>1) && (_tid>0))
3022 name += QStringLiteral(", thread %3").arg(_tid);
3023 return name;
3024 }
3025
activate(bool active)3026 bool TracePart::activate(bool active)
3027 {
3028 if (_active == active) return false;
3029 _active = active;
3030
3031 // to be done by the client of this function
3032 // data()->invalidateDynamicCost();
3033 // So better use the TraceData functions...
3034
3035 return true;
3036 }
3037
operator <(const TracePart & p2) const3038 bool TracePart::operator<(const TracePart& p2) const
3039 {
3040 if (processID() < p2.processID()) return true;
3041
3042 if (processID() == p2.processID()) {
3043 if (partNumber() < p2.partNumber()) return true;
3044
3045 if (partNumber() == p2.partNumber())
3046 return (threadID() < p2.threadID());
3047 }
3048 return false;
3049 }
3050
3051
3052 //---------------------------------------------------
3053 // TraceData
3054
3055
3056 // create vectors with reasonable default sizes, but not wasting memory
TraceData(Logger * l)3057 TraceData::TraceData(Logger* l)
3058 : ProfileCostArray(ProfileContext::context(ProfileContext::Data))
3059 {
3060 _logger = l;
3061 init();
3062 }
3063
init()3064 void TraceData::init()
3065 {
3066 _functionCycleCount = 0;
3067 _inFunctionCycleUpdate = false;
3068
3069 _maxThreadID = 0;
3070 _maxPartNumber = 0;
3071 _fixPool = nullptr;
3072 _dynPool = nullptr;
3073
3074 _arch = ArchUnknown;
3075 }
3076
~TraceData()3077 TraceData::~TraceData()
3078 {
3079 qDeleteAll(_parts);
3080
3081 delete _fixPool;
3082 delete _dynPool;
3083 }
3084
shortTraceName() const3085 QString TraceData::shortTraceName() const
3086 {
3087 int lastIndex = 0, index;
3088 while ( (index=_traceName.indexOf(QLatin1Char('/'), lastIndex)) >=0)
3089 lastIndex = index+1;
3090
3091 return _traceName.mid(lastIndex);
3092 }
3093
fixPool()3094 FixPool* TraceData::fixPool()
3095 {
3096 if (!_fixPool)
3097 _fixPool = new FixPool();
3098
3099 return _fixPool;
3100 }
3101
dynPool()3102 DynPool* TraceData::dynPool()
3103 {
3104 if (!_dynPool)
3105 _dynPool = new DynPool();
3106
3107 return _dynPool;
3108 }
3109
partLessThan(const TracePart * p1,const TracePart * p2)3110 bool partLessThan(const TracePart* p1, const TracePart* p2)
3111 {
3112 return *p1 < *p2;
3113 }
3114
3115 /**
3116 * Load a list of files.
3117 * If only one file is given, it is assumed to be a prefix, and all
3118 * existing files with that prefix are loaded.
3119 *
3120 * Returns 0 if nothing found to load
3121 */
load(QStringList files)3122 int TraceData::load(QStringList files)
3123 {
3124 if (files.isEmpty()) return 0;
3125
3126 _traceName = files[0];
3127 if (files.count() == 1) {
3128 QFileInfo finfo(_traceName);
3129 QString prefix = finfo.fileName();
3130 QDir dir = finfo.dir();
3131 if (finfo.isDir()) {
3132 prefix = QStringLiteral("callgrind.out");
3133 _traceName += QLatin1String("/callgrind.out");
3134 }
3135
3136 files = dir.entryList(QStringList() << prefix + '*', QDir::Files);
3137 QStringList::Iterator it = files.begin();
3138 for (; it != files.end(); ++it ) {
3139 *it = dir.path() + '/' + *it;
3140 }
3141 }
3142
3143 if (files.isEmpty()) {
3144 _traceName += ' ' + QObject::tr("(not found)");
3145 return 0;
3146 }
3147
3148 QStringList::const_iterator it;
3149 int partsLoaded = 0;
3150 for (it = files.constBegin(); it != files.constEnd(); ++it ) {
3151 QFile file(*it);
3152 partsLoaded += internalLoad(&file, *it);
3153 }
3154 if (partsLoaded == 0) return 0;
3155
3156 std::sort(_parts.begin(), _parts.end(), partLessThan);
3157 invalidateDynamicCost();
3158 updateFunctionCycles();
3159
3160 return partsLoaded;
3161 }
3162
load(QString file)3163 int TraceData::load(QString file)
3164 {
3165 return load(QStringList(file));
3166 }
3167
load(QIODevice * file,const QString & filename)3168 int TraceData::load(QIODevice* file, const QString& filename)
3169 {
3170 _traceName = filename;
3171 int partsLoaded = internalLoad(file, filename);
3172 if (partsLoaded>0) {
3173 invalidateDynamicCost();
3174 updateFunctionCycles();
3175 }
3176 return partsLoaded;
3177 }
3178
internalLoad(QIODevice * device,const QString & filename)3179 int TraceData::internalLoad(QIODevice* device, const QString& filename)
3180 {
3181 if (!device->open( QIODevice::ReadOnly ) ) {
3182 _logger->loadStart(filename);
3183 _logger->loadFinished(QString::fromLocal8Bit(strerror( errno )));
3184 return 0;
3185 }
3186
3187 Loader* l = Loader::matchingLoader(device);
3188 if (!l) {
3189 // special case empty file: ignore...
3190 if (device->size() == 0) return 0;
3191
3192 _logger->loadStart(filename);
3193 _logger->loadFinished(QStringLiteral("Unknown file format"));
3194 return 0;
3195 }
3196 l->setLogger(_logger);
3197
3198 int partsLoaded = l->load(this, device, filename);
3199
3200 l->setLogger(nullptr);
3201
3202 return partsLoaded;
3203 }
3204
activateParts(const TracePartList & l)3205 bool TraceData::activateParts(const TracePartList& l)
3206 {
3207 bool changed = false;
3208
3209 foreach(TracePart* part, _parts)
3210 if (part->activate(l.contains(part)))
3211 changed = true;
3212
3213 if (changed) {
3214 // because active parts have changed, throw away calculated
3215 // costs...
3216 invalidateDynamicCost();
3217 updateFunctionCycles();
3218 }
3219
3220 return changed;
3221 }
3222
3223
activateParts(TracePartList l,bool active)3224 bool TraceData::activateParts(TracePartList l, bool active)
3225 {
3226 bool changed = false;
3227
3228 foreach(TracePart* part, l) {
3229 if (_parts.contains(part))
3230 if (part->activate(active))
3231 changed = true;
3232 }
3233
3234 if (changed) {
3235 invalidateDynamicCost();
3236 updateFunctionCycles();
3237 }
3238
3239 return changed;
3240 }
3241
activatePart(TracePart * p,bool active)3242 bool TraceData::activatePart(TracePart* p, bool active)
3243 {
3244 return p->activate(active);
3245 }
3246
activateAll(bool active)3247 bool TraceData::activateAll(bool active)
3248 {
3249 return activateParts(_parts, active);
3250 }
3251
addPart(TracePart * part)3252 void TraceData::addPart(TracePart* part)
3253 {
3254 if (_parts.contains(part)>0) return;
3255
3256 if ((part->partNumber()==0) &&
3257 (part->processID()==0)) {
3258 _maxPartNumber++;
3259 part->setPartNumber(_maxPartNumber);
3260 }
3261 _parts.append(part);
3262 }
3263
partWithName(const QString & name)3264 TracePart* TraceData::partWithName(const QString& name)
3265 {
3266 foreach(TracePart* part, _parts)
3267 if (part->name() == name)
3268 return part;
3269 return nullptr;
3270 }
3271
activePartRange()3272 QString TraceData::activePartRange()
3273 {
3274 QString res;
3275 int r1=-1, r2=-1, count=0;
3276 foreach(TracePart* part, _parts) {
3277 count++;
3278 if (part->isActive()) {
3279 if (r1<0) { r1 = r2 = count; }
3280 else if (r2 == count-1) { r2 = count; }
3281 else {
3282 if (!res.isEmpty()) res += ';';
3283 if (r1==r2) res += QString::number(r1);
3284 else res += QStringLiteral("%1-%2").arg(r1).arg(r2);
3285 r1 = r2 = count;
3286 }
3287 }
3288 }
3289 if (r1>=0) {
3290 if (!res.isEmpty()) res += ';';
3291 if (r1==r2) res += QString::number(r1);
3292 else res += QStringLiteral("%1-%2").arg(r1).arg(r2);
3293 }
3294
3295 return res;
3296 }
3297
invalidateDynamicCost()3298 void TraceData::invalidateDynamicCost()
3299 {
3300 // invalidate all dynamic costs
3301
3302 TraceObjectMap::Iterator oit;
3303 for ( oit = _objectMap.begin();
3304 oit != _objectMap.end(); ++oit )
3305 (*oit).invalidate();
3306
3307 TraceClassMap::Iterator cit;
3308 for ( cit = _classMap.begin();
3309 cit != _classMap.end(); ++cit )
3310 (*cit).invalidate();
3311
3312 TraceFileMap::Iterator fit;
3313 for ( fit = _fileMap.begin();
3314 fit != _fileMap.end(); ++fit )
3315 (*fit).invalidate();
3316
3317 TraceFunctionMap::Iterator it;
3318 for ( it = _functionMap.begin();
3319 it != _functionMap.end(); ++it ) {
3320 (*it).invalidateDynamicCost();
3321 }
3322
3323 invalidate();
3324
3325 }
3326
3327
object(const QString & name)3328 TraceObject* TraceData::object(const QString& name)
3329 {
3330 TraceObject& o = _objectMap[name];
3331 if (!o.data()) {
3332 // was created
3333 o.setPosition(this);
3334 o.setName(name);
3335
3336 #if TRACE_DEBUG
3337 qDebug("Created %s [TraceData::object]",
3338 qPrintable(o.fullName()));
3339 #endif
3340 }
3341 return &o;
3342 }
3343
3344
file(const QString & name)3345 TraceFile* TraceData::file(const QString& name)
3346 {
3347 TraceFile& f = _fileMap[name];
3348 if (!f.data()) {
3349 // was created
3350 f.setPosition(this);
3351 f.setName(name);
3352
3353 #if TRACE_DEBUG
3354 qDebug("Created %s [TraceData::file]",
3355 qPrintable(f.fullName()));
3356 #endif
3357 }
3358 return &f;
3359 }
3360
3361
3362 // usually only called by function()
cls(const QString & fnName,QString & shortName)3363 TraceClass* TraceData::cls(const QString& fnName, QString& shortName)
3364 {
3365 int lastIndex = 0, index, pIndex;
3366
3367 // we ignore any "::" after a '(' or a space
3368 pIndex=fnName.indexOf('(', 0);
3369
3370 #if 0
3371 int sIndex=fnName.find(" ", 0);
3372 if (sIndex>=0)
3373 if ((pIndex == -1) || (sIndex < pIndex))
3374 pIndex = sIndex;
3375 #endif
3376
3377 while ((index=fnName.indexOf(QLatin1String("::"), lastIndex)) >=0) {
3378 if (pIndex>=0 && pIndex<index) break;
3379 lastIndex = index+2;
3380 }
3381
3382 QString clsName = (lastIndex < 3) ? QString() :
3383 fnName.left(lastIndex-2);
3384 shortName = fnName.mid(lastIndex);
3385
3386 TraceClass& c = _classMap[clsName];
3387 if (!c.data()) {
3388 // was created
3389 c.setPosition(this);
3390 c.setName(clsName);
3391
3392 #if TRACE_DEBUG
3393 qDebug("Created %s [TraceData::cls]",
3394 qPrintable(c.fullName()));
3395 #endif
3396 }
3397 return &c;
3398 }
3399
3400
3401 // name is inclusive class/namespace prefix
function(const QString & name,TraceFile * file,TraceObject * object)3402 TraceFunction* TraceData::function(const QString& name,
3403 TraceFile* file, TraceObject* object)
3404 {
3405 // strip class name
3406 QString shortName;
3407 TraceClass* c = cls(name, shortName);
3408
3409 if (!file || !object || !c) {
3410 qDebug("ERROR - no file/object/class for %s ?!", qPrintable(name));
3411 return nullptr;
3412 }
3413
3414 // Use object name and file name as part of key, to get distinct
3415 // function objects for functions with same name but defined in
3416 // different ELF objects or different files (this is possible e.g.
3417 // in C by using "static").
3418 //
3419 // Note about usage of this factory method by the Cachegrind loader:
3420 // that dump format does not explicitly specify the attribution
3421 // of functions to ELF objects and files. Rather, cost is attributed
3422 // to ELF object, source file and function. We use the first cost
3423 // seen for a function to bind an ELF object and source file to that
3424 // function. Callgrind always prints the cost of the instruction at
3425 // function entry first, so there, this strategy works.
3426 // But such an order is not enforced by the format. If the cost of
3427 // an inlined function from another source file would be printed first,
3428 // the attribution would go wrong. The format also allows cost of
3429 // the same function to be spread over the dump. With wrong
3430 // attributions, it can happen that cost of the same function is
3431 // interpreted as being from distinct functions.
3432 // For a correct solution, the format needs to be more expressive,
3433 // or the ordering of costs specified.
3434 // Previously, the file name was left out from the key.
3435 // The change was motivated by bug ID 3014067 (on SourceForge).
3436 QString key = name + file->shortName() + object->shortName();
3437
3438 TraceFunctionMap::Iterator it;
3439 it = _functionMap.find(key);
3440 if (it == _functionMap.end()) {
3441 it = _functionMap.insert(key, TraceFunction());
3442 TraceFunction& f = it.value();
3443
3444 f.setPosition(this);
3445 f.setName(name);
3446 f.setClass(c);
3447 f.setObject(object);
3448 f.setFile(file);
3449 //f.setMapIterator(it);
3450
3451 #if TRACE_DEBUG
3452 qDebug("Created %s [TraceData::function]\n for %s, %s, %s",
3453 qPrintable(f.fullName()),
3454 qPrintable(c->fullName()), qPrintable(file->fullName()),
3455 object ? qPrintable(object->fullName()) : "(unknown object)");
3456 #endif
3457
3458 c->addFunction(&f);
3459 object->addFunction(&f);
3460 file->addFunction(&f);
3461 }
3462
3463 return &(it.value());
3464 }
3465
functionIterator(TraceFunction * f)3466 TraceFunctionMap::Iterator TraceData::functionIterator(TraceFunction* f)
3467 {
3468
3469 // IMPORTANT: build as SAME key as used in function() above !!
3470 QString key;
3471
3472 if (f->cls()) key = f->cls()->name() + "::";
3473 key += f->name();
3474 key += f->object()->shortName();
3475
3476 return _functionMap.find(key);
3477 }
3478
functionBeginIterator() const3479 TraceFunctionMap::ConstIterator TraceData::functionBeginIterator() const
3480 {
3481 return _functionMap.begin();
3482 }
3483
functionEndIterator() const3484 TraceFunctionMap::ConstIterator TraceData::functionEndIterator() const
3485 {
3486 return _functionMap.end();
3487 }
3488
3489 // _maxCallCount is maintained globally, and only updated at loading
updateMaxCallCount(SubCost c)3490 void TraceData::updateMaxCallCount(SubCost c)
3491 {
3492 if (_maxCallCount < c)
3493 _maxCallCount = c;
3494 }
3495
resetSourceDirs()3496 void TraceData::resetSourceDirs()
3497 {
3498 TraceFileMap::Iterator fit;
3499 for ( fit = _fileMap.begin();
3500 fit != _fileMap.end(); ++fit )
3501 (*fit).resetDirectory();
3502 }
3503
update()3504 void TraceData::update()
3505 {
3506 if (!_dirty) return;
3507
3508 clear();
3509 _totals.clear();
3510
3511 foreach(TracePart* part, _parts) {
3512 _totals.addCost(part->totals());
3513 if (part->isActive())
3514 addCost(part->totals());
3515 }
3516
3517 _dirty = false;
3518 }
3519
search(ProfileContext::Type t,QString name,EventType * ct,ProfileCostArray * parent)3520 ProfileCostArray* TraceData::search(ProfileContext::Type t, QString name,
3521 EventType* ct, ProfileCostArray* parent)
3522 {
3523 ProfileCostArray* result = nullptr;
3524 ProfileContext::Type pt;
3525 SubCost sc, scTop = 0;
3526
3527 pt = parent ? parent->type() : ProfileContext::InvalidType;
3528 switch(t) {
3529 case ProfileContext::Function:
3530 {
3531 TraceFunction *f;
3532 TraceFunctionMap::Iterator it;
3533 for ( it = _functionMap.begin();
3534 it != _functionMap.end(); ++it ) {
3535 f = &(*it);
3536
3537 if (f->name() != name) continue;
3538
3539 if ((pt == ProfileContext::Class) && (parent != f->cls())) continue;
3540 if ((pt == ProfileContext::File) && (parent != f->file())) continue;
3541 if ((pt == ProfileContext::Object) && (parent != f->object())) continue;
3542
3543 if (ct) {
3544 sc = f->inclusive()->subCost(ct);
3545 if (sc <= scTop) continue;
3546 scTop = sc;
3547 }
3548
3549 result = f;
3550 }
3551 }
3552 break;
3553
3554 case ProfileContext::File:
3555 {
3556 TraceFile *f;
3557 TraceFileMap::Iterator it;
3558 for ( it = _fileMap.begin();
3559 it != _fileMap.end(); ++it ) {
3560 f = &(*it);
3561 if (f->name() != name) continue;
3562 if (ct) {
3563 sc = f->subCost(ct);
3564 if (sc <= scTop) continue;
3565 scTop = sc;
3566 }
3567 result = f;
3568 }
3569 }
3570 break;
3571
3572 case ProfileContext::Class:
3573 {
3574 TraceClass *c;
3575 TraceClassMap::Iterator it;
3576 for ( it = _classMap.begin();
3577 it != _classMap.end(); ++it ) {
3578 c = &(*it);
3579 if (c->name() != name) continue;
3580 if (ct) {
3581 sc = c->subCost(ct);
3582 if (sc <= scTop) continue;
3583 scTop = sc;
3584 }
3585 result = c;
3586 }
3587 }
3588 break;
3589
3590 case ProfileContext::Object:
3591 {
3592 TraceObject *o;
3593 TraceObjectMap::Iterator it;
3594 for ( it = _objectMap.begin();
3595 it != _objectMap.end(); ++it ) {
3596 o = &(*it);
3597 if (o->name() != name) continue;
3598 if (ct) {
3599 sc = o->subCost(ct);
3600 if (sc <= scTop) continue;
3601 scTop = sc;
3602 }
3603 result = o;
3604 }
3605 }
3606 break;
3607
3608 case ProfileContext::Instr:
3609 if (pt == ProfileContext::Function) {
3610 TraceInstrMap* instrMap = ((TraceFunction*)parent)->instrMap();
3611 if (!instrMap) break;
3612
3613 TraceInstr *instr;
3614 TraceInstrMap::Iterator it;
3615 for ( it = instrMap->begin();
3616 it != instrMap->end(); ++it ) {
3617 instr = &(*it);
3618 if (instr->name() != name) continue;
3619 result = instr;
3620 }
3621 }
3622 break;
3623
3624 case ProfileContext::Line:
3625 {
3626 TraceFunctionSourceList sList;
3627 if (pt == ProfileContext::Function)
3628 sList = ((TraceFunction*)parent)->sourceFiles();
3629 else if (pt == ProfileContext::FunctionSource)
3630 sList.append((TraceFunctionSource*) parent);
3631 else break;
3632
3633 TraceLineMap* lineMap;
3634 TraceLine* line;
3635 TraceLineMap::Iterator it;
3636 foreach(TraceFunctionSource* fs, sList) {
3637 lineMap = fs->lineMap();
3638 if (!lineMap) continue;
3639
3640 for ( it = lineMap->begin();
3641 it != lineMap->end(); ++it ) {
3642 line = &(*it);
3643 if (line->name() != name) continue;
3644 result = line;
3645 }
3646 }
3647 }
3648 break;
3649
3650 default:
3651 break;
3652 }
3653
3654 return result;
3655 }
3656
3657
functionCycle(TraceFunction * f)3658 TraceFunctionCycle* TraceData::functionCycle(TraceFunction* f)
3659 {
3660 TraceFunctionCycle* cycle;
3661 foreach(cycle, _functionCycles)
3662 if (cycle->base() == f)
3663 return cycle;
3664
3665 _functionCycleCount++;
3666 cycle = new TraceFunctionCycle(f, _functionCycleCount);
3667
3668 _functionCycles.append(cycle);
3669 return cycle;
3670 }
3671
3672
updateFunctionCycles()3673 void TraceData::updateFunctionCycles()
3674 {
3675 //qDebug("Updating cycles...");
3676
3677 // init cycle info
3678 foreach(TraceFunctionCycle* cycle, _functionCycles)
3679 cycle->init();
3680
3681 TraceFunctionMap::Iterator it;
3682 for ( it = _functionMap.begin(); it != _functionMap.end(); ++it )
3683 (*it).cycleReset();
3684
3685 if (!GlobalConfig::showCycles()) return;
3686
3687 _inFunctionCycleUpdate = true;
3688
3689
3690 #if 0
3691 int fCount = _functionMap.size(), fNo = 0, progress=0, p;
3692 QString msg = tr("Recalculating Function Cycles...");
3693 if (_topLevel) _topLevel->showStatus(msg,0);
3694 #endif
3695
3696 // DFS and collapse strong connected components (Tarjan)
3697 int pNo = 0;
3698 TraceFunction* stackTop;
3699 for ( it = _functionMap.begin(); it != _functionMap.end(); ++it ) {
3700
3701 #if 0
3702 if (_topLevel) {
3703 fNo++;
3704 p = 100*fNo/fCount;
3705 if (p> progress) {
3706 progress = p;
3707 _topLevel->showStatus(msg, p);
3708 }
3709 }
3710 #endif
3711
3712 stackTop = nullptr;
3713 (*it).cycleDFS(1, pNo, &stackTop);
3714 }
3715
3716 // postprocess cycles
3717 foreach(TraceFunctionCycle* cycle, _functionCycles)
3718 cycle->setup();
3719
3720 _inFunctionCycleUpdate = false;
3721 // we have to invalidate costs because cycles are now taken into account
3722 invalidateDynamicCost();
3723
3724 #if 0
3725 if (0) if (_topLevel) _topLevel->showStatus(QString(), 0);
3726 #endif
3727 }
3728
updateObjectCycles()3729 void TraceData::updateObjectCycles()
3730 {
3731 }
3732
3733
updateClassCycles()3734 void TraceData::updateClassCycles()
3735 {
3736 }
3737
3738
updateFileCycles()3739 void TraceData::updateFileCycles()
3740 {
3741 }
3742
3743