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("&amp;");
1948             break;
1949         case '<':
1950             d++;
1951             rich.append("&lt;");
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("&gt; "); // 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