File indexing completed on 2024-04-28 05:41:28
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 0010 #include "tracedata.h" 0011 0012 #include <errno.h> 0013 #include <stdlib.h> 0014 0015 #include <QFile> 0016 #include <QDir> 0017 #include <QFileInfo> 0018 #include <QDebug> 0019 0020 #include "logger.h" 0021 #include "loader.h" 0022 #include "globalconfig.h" 0023 #include "utils.h" 0024 #include "fixcost.h" 0025 0026 0027 #define TRACE_DEBUG 0 0028 #define TRACE_ASSERTIONS 0 0029 0030 0031 0032 0033 //--------------------------------------------------- 0034 // TraceJumpCost 0035 0036 TraceJumpCost::TraceJumpCost(ProfileContext* c) 0037 :CostItem(c) 0038 { 0039 TraceJumpCost::clear(); 0040 } 0041 0042 TraceJumpCost::~TraceJumpCost() 0043 {} 0044 0045 SubCost TraceJumpCost::executedCount() 0046 { 0047 if (_dirty) update(); 0048 0049 return _executedCount; 0050 } 0051 0052 SubCost TraceJumpCost::followedCount() 0053 { 0054 if (_dirty) update(); 0055 0056 return _followedCount; 0057 } 0058 0059 QString TraceJumpCost::costString(EventTypeSet*) 0060 { 0061 if (_dirty) update(); 0062 0063 return QStringLiteral("%1/%2") 0064 .arg(_followedCount.pretty()) 0065 .arg(_executedCount.pretty()); 0066 } 0067 0068 void TraceJumpCost::clear() 0069 { 0070 _followedCount = 0; 0071 _executedCount = 0; 0072 } 0073 0074 void TraceJumpCost::addCost(TraceJumpCost* item) 0075 { 0076 if (item->_dirty) item->update(); 0077 0078 _followedCount += item->followedCount(); 0079 _executedCount += item->executedCount(); 0080 } 0081 0082 0083 0084 //--------------------------------------------------- 0085 // TraceCallCost 0086 0087 TraceCallCost::TraceCallCost(ProfileContext* context) 0088 : ProfileCostArray(context) 0089 { 0090 _callCount = 0; 0091 } 0092 0093 TraceCallCost::~TraceCallCost() 0094 {} 0095 0096 0097 QString TraceCallCost::costString(EventTypeSet* m) 0098 { 0099 return QStringLiteral("%1, Calls %2") 0100 .arg(ProfileCostArray::costString(m)) 0101 .arg(_callCount.pretty()); 0102 } 0103 0104 QString TraceCallCost::prettyCallCount() 0105 { 0106 return _callCount.pretty(); 0107 } 0108 0109 void TraceCallCost::clear() 0110 { 0111 _callCount = 0; 0112 ProfileCostArray::clear(); 0113 } 0114 0115 SubCost TraceCallCost::callCount() 0116 { 0117 if (_dirty) update(); 0118 0119 return _callCount; 0120 } 0121 0122 void TraceCallCost::addCallCount(SubCost c) 0123 { 0124 _callCount += c; 0125 0126 invalidate(); 0127 } 0128 0129 0130 //--------------------------------------------------- 0131 // TraceInclusiveCost 0132 0133 TraceInclusiveCost::TraceInclusiveCost(ProfileContext* context) 0134 : ProfileCostArray(context), _inclusive(context) 0135 {} 0136 0137 TraceInclusiveCost::~TraceInclusiveCost() 0138 {} 0139 0140 QString TraceInclusiveCost::costString(EventTypeSet* m) 0141 { 0142 return QStringLiteral("%1, Inclusive %2") 0143 .arg(ProfileCostArray::costString(m)) 0144 .arg(_inclusive.costString(m)); 0145 } 0146 0147 void TraceInclusiveCost::clear() 0148 { 0149 _inclusive.clear(); 0150 ProfileCostArray::clear(); 0151 } 0152 0153 ProfileCostArray* TraceInclusiveCost::inclusive() 0154 { 0155 if (_dirty) update(); 0156 0157 return &_inclusive; 0158 } 0159 0160 void TraceInclusiveCost::addInclusive(ProfileCostArray* c) 0161 { 0162 _inclusive.addCost(c); 0163 0164 invalidate(); 0165 } 0166 0167 0168 //--------------------------------------------------- 0169 // TraceListCost 0170 0171 TraceListCost::TraceListCost(ProfileContext* context) 0172 : ProfileCostArray(context) 0173 { 0174 _lastDep = nullptr; 0175 } 0176 0177 TraceListCost::~TraceListCost() 0178 {} 0179 0180 void TraceListCost::addDep(ProfileCostArray* dep) 0181 { 0182 #if TRACE_ASSERTIONS 0183 if (_deps.contains(dep)) { 0184 qDebug("addDep: %s already in list!", 0185 qPrintable(dep->fullName())); 0186 return; 0187 } 0188 #endif 0189 0190 _deps.append(dep); 0191 _lastDep = dep; 0192 invalidate(); 0193 0194 #if TRACE_DEBUG 0195 qDebug("%s added\n %s (now %d)", 0196 qPrintable( fullName() ), qPrintable(dep->fullName()), 0197 _deps.count()); 0198 #endif 0199 } 0200 0201 ProfileCostArray* TraceListCost::findDepFromPart(TracePart* part) 0202 { 0203 if (_lastDep && _lastDep->part() == part) 0204 return _lastDep; 0205 0206 foreach(ProfileCostArray* dep, _deps) { 0207 if (dep->part() == part) { 0208 _lastDep = dep; 0209 return dep; 0210 } 0211 } 0212 return nullptr; 0213 } 0214 0215 0216 void TraceListCost::update() 0217 { 0218 if (!_dirty) return; 0219 0220 #if TRACE_DEBUG 0221 qDebug("update %s (count %d)", 0222 qPrintable( fullName() ), _deps.count()); 0223 #endif 0224 0225 clear(); 0226 foreach(ProfileCostArray* item, _deps) { 0227 if (onlyActiveParts()) 0228 if (!item->part() || !item->part()->isActive()) continue; 0229 0230 addCost(item); 0231 } 0232 0233 _dirty = false; 0234 0235 #if TRACE_DEBUG 0236 qDebug(" > %s", qPrintable(costString(0))); 0237 #endif 0238 } 0239 0240 0241 0242 //--------------------------------------------------- 0243 // TraceJumpListCost 0244 0245 TraceJumpListCost::TraceJumpListCost(ProfileContext* context) 0246 : TraceJumpCost(context) 0247 { 0248 _lastDep = nullptr; 0249 } 0250 0251 TraceJumpListCost::~TraceJumpListCost() 0252 {} 0253 0254 void TraceJumpListCost::addDep(TraceJumpCost* dep) 0255 { 0256 #if TRACE_ASSERTIONS 0257 if (_deps.contains(dep)) { 0258 qDebug("addDep: %s already in list!", 0259 qPrintable(dep->fullName())); 0260 return; 0261 } 0262 #endif 0263 0264 _deps.append(dep); 0265 _lastDep = dep; 0266 invalidate(); 0267 0268 #if TRACE_DEBUG 0269 qDebug("%s added\n %s (now %d)", 0270 qPrintable( fullName() ), qPrintable(dep->fullName()), 0271 _deps.count()); 0272 #endif 0273 } 0274 0275 TraceJumpCost* TraceJumpListCost::findDepFromPart(TracePart* part) 0276 { 0277 if (_lastDep && _lastDep->part() == part) 0278 return _lastDep; 0279 0280 foreach(TraceJumpCost* dep, _deps) { 0281 if (dep->part() == part) { 0282 _lastDep = dep; 0283 return dep; 0284 } 0285 } 0286 return nullptr; 0287 } 0288 0289 0290 void TraceJumpListCost::update() 0291 { 0292 if (!_dirty) return; 0293 0294 #if TRACE_DEBUG 0295 qDebug("update %s (count %d)", 0296 qPrintable( fullName() ), _deps.count()); 0297 #endif 0298 0299 clear(); 0300 foreach(TraceJumpCost* item, _deps) { 0301 if (onlyActiveParts()) 0302 if (!item->part() || !item->part()->isActive()) continue; 0303 0304 addCost(item); 0305 } 0306 0307 _dirty = false; 0308 0309 #if TRACE_DEBUG 0310 qDebug(" > %s", qPrintable(costString(0))); 0311 #endif 0312 } 0313 0314 0315 0316 //--------------------------------------------------- 0317 // TraceCallListCost 0318 0319 TraceCallListCost::TraceCallListCost(ProfileContext* context) 0320 : TraceCallCost(context) 0321 { 0322 _lastDep = nullptr; 0323 } 0324 0325 TraceCallListCost::~TraceCallListCost() 0326 {} 0327 0328 void TraceCallListCost::addDep(TraceCallCost* dep) 0329 { 0330 #if TRACE_ASSERTIONS 0331 if (_deps.contains(dep)) { 0332 qDebug("addDep: %s already in list!", 0333 qPrintable(dep->fullName())); 0334 return; 0335 } 0336 #endif 0337 0338 _deps.append(dep); 0339 _lastDep = dep; 0340 invalidate(); 0341 0342 #if TRACE_DEBUG 0343 qDebug("%s added\n %s (now %d)", 0344 qPrintable( fullName() ), qPrintable(dep->fullName()), 0345 _deps.count()); 0346 #endif 0347 } 0348 0349 TraceCallCost* TraceCallListCost::findDepFromPart(TracePart* part) 0350 { 0351 if (_lastDep && _lastDep->part() == part) 0352 return _lastDep; 0353 0354 foreach(TraceCallCost* dep, _deps) { 0355 if (dep->part() == part) { 0356 _lastDep = dep; 0357 return dep; 0358 } 0359 } 0360 return nullptr; 0361 } 0362 0363 0364 void TraceCallListCost::update() 0365 { 0366 if (!_dirty) return; 0367 0368 #if TRACE_DEBUG 0369 qDebug("update %s (count %d)", 0370 qPrintable( fullName() ), _deps.count()); 0371 #endif 0372 0373 /* Without dependent cost items, assume fixed costs, 0374 * i.e. do not change cost */ 0375 if (_deps.count()>0) { 0376 clear(); 0377 foreach(TraceCallCost* item, _deps) { 0378 if (onlyActiveParts()) 0379 if (!item->part() || !item->part()->isActive()) continue; 0380 0381 addCost(item); 0382 addCallCount(item->callCount()); 0383 } 0384 } 0385 0386 _dirty = false; 0387 0388 #if TRACE_DEBUG 0389 qDebug(" > %s", qPrintable(costString(0))); 0390 #endif 0391 } 0392 0393 0394 //--------------------------------------------------- 0395 // TraceInclusiveListCost 0396 0397 TraceInclusiveListCost::TraceInclusiveListCost(ProfileContext* context) 0398 : TraceInclusiveCost(context) 0399 { 0400 _lastDep = nullptr; 0401 } 0402 0403 TraceInclusiveListCost::~TraceInclusiveListCost() 0404 {} 0405 0406 0407 void TraceInclusiveListCost::addDep(TraceInclusiveCost* dep) 0408 { 0409 #if TRACE_ASSERTIONS 0410 if (_deps.contains(dep)) { 0411 qDebug("addDep: %s already in list!", 0412 qPrintable(dep->fullName())); 0413 return; 0414 } 0415 #endif 0416 0417 _deps.append(dep); 0418 _lastDep = dep; 0419 invalidate(); 0420 0421 #if TRACE_DEBUG 0422 qDebug("%s added\n %s (now %d)", 0423 qPrintable( fullName() ), qPrintable(dep->fullName()), 0424 _deps.count()); 0425 #endif 0426 } 0427 0428 TraceInclusiveCost* TraceInclusiveListCost::findDepFromPart(TracePart* part) 0429 { 0430 if (_lastDep && _lastDep->part() == part) 0431 return _lastDep; 0432 0433 foreach(TraceInclusiveCost* dep, _deps) { 0434 if (dep->part() == part) { 0435 _lastDep = dep; 0436 return dep; 0437 } 0438 } 0439 return nullptr; 0440 } 0441 0442 void TraceInclusiveListCost::update() 0443 { 0444 if (!_dirty) return; 0445 0446 #if TRACE_DEBUG 0447 qDebug("update %s (count %d)", 0448 qPrintable( fullName() ), _deps.count()); 0449 #endif 0450 0451 clear(); 0452 foreach(TraceInclusiveCost* item, _deps) { 0453 if (onlyActiveParts()) 0454 if (!item->part() || !item->part()->isActive()) continue; 0455 0456 addCost(item); 0457 addInclusive(item->inclusive()); 0458 } 0459 0460 _dirty = false; 0461 0462 #if TRACE_DEBUG 0463 qDebug(" > %s", qPrintable(costString(0))); 0464 #endif 0465 } 0466 0467 0468 0469 //--------------------------------------------------- 0470 // TracePartInstrJump 0471 0472 TracePartInstrJump::TracePartInstrJump(TraceInstrJump* instrJump, 0473 TracePartInstrJump* next) 0474 : TraceJumpCost(ProfileContext::context(ProfileContext::PartInstrJump)) 0475 { 0476 _dep = instrJump; 0477 _next = next; 0478 } 0479 0480 TracePartInstrJump::~TracePartInstrJump() 0481 {} 0482 0483 0484 //--------------------------------------------------- 0485 // TracePartInstrCall 0486 0487 TracePartInstrCall::TracePartInstrCall(TraceInstrCall* instrCall) 0488 : TraceCallCost(ProfileContext::context(ProfileContext::PartInstrCall)) 0489 { 0490 _dep = instrCall; 0491 } 0492 0493 TracePartInstrCall::~TracePartInstrCall() 0494 {} 0495 0496 0497 0498 //--------------------------------------------------- 0499 // TracePartInstr 0500 0501 TracePartInstr::TracePartInstr(TraceInstr* instr) 0502 : ProfileCostArray(ProfileContext::context(ProfileContext::PartInstr)) 0503 { 0504 _dep = instr; 0505 } 0506 0507 TracePartInstr::~TracePartInstr() 0508 {} 0509 0510 0511 0512 //--------------------------------------------------- 0513 // TracePartLineJump 0514 0515 TracePartLineJump::TracePartLineJump(TraceLineJump* lineJump) 0516 : TraceJumpCost(ProfileContext::context(ProfileContext::PartLineJump)) 0517 { 0518 _dep = lineJump; 0519 } 0520 0521 TracePartLineJump::~TracePartLineJump() 0522 {} 0523 0524 0525 //--------------------------------------------------- 0526 // TracePartLineCall 0527 0528 TracePartLineCall::TracePartLineCall(TraceLineCall* lineCall) 0529 : TraceCallCost(ProfileContext::context(ProfileContext::PartLineCall)) 0530 { 0531 _dep = lineCall; 0532 } 0533 0534 TracePartLineCall::~TracePartLineCall() 0535 {} 0536 0537 0538 //--------------------------------------------------- 0539 // TracePartLine 0540 0541 TracePartLine::TracePartLine(TraceLine* line) 0542 : ProfileCostArray(ProfileContext::context(ProfileContext::PartLine)) 0543 { 0544 _dep = line; 0545 } 0546 0547 TracePartLine::~TracePartLine() 0548 {} 0549 0550 0551 0552 0553 //--------------------------------------------------- 0554 // TracePartCall 0555 0556 TracePartCall::TracePartCall(TraceCall* call) 0557 : TraceCallListCost(ProfileContext::context(ProfileContext::PartCall)) 0558 { 0559 _dep = call; 0560 0561 _firstFixCallCost = nullptr; 0562 } 0563 0564 TracePartCall::~TracePartCall() 0565 {} 0566 0567 bool TracePartCall::isRecursion() 0568 { 0569 return call()->isRecursion(); 0570 } 0571 0572 void TracePartCall::update() 0573 { 0574 #if !USE_FIXCOST 0575 TraceCallListCost::update(); 0576 #else 0577 0578 if (!_dirty) return; 0579 0580 #if TRACE_DEBUG 0581 qDebug("update %s", qPrintable( fullName() )); 0582 #endif 0583 0584 /* Without dependent cost items, assume fixed costs, 0585 * i.e. do not change cost */ 0586 if (_firstFixCallCost) { 0587 clear(); 0588 FixCallCost* item; 0589 for (item = _firstFixCallCost; item; item = item->nextCostOfPartCall()) 0590 item->addTo(this); 0591 } 0592 0593 _dirty = false; 0594 0595 #if TRACE_DEBUG 0596 qDebug(" > %s", qPrintable(costString(0))); 0597 #endif 0598 0599 #endif // USE_FIXCOST 0600 } 0601 0602 0603 //--------------------------------------------------- 0604 // TracePartFunction 0605 0606 TracePartFunction::TracePartFunction(TraceFunction* function, 0607 TracePartObject* partObject, 0608 TracePartFile *partFile) 0609 : TraceInclusiveCost(ProfileContext::context(ProfileContext::PartFunction)) 0610 { 0611 _dep = function; 0612 _partObject = partObject; 0613 _partFile = partFile; 0614 _partClass = nullptr; 0615 0616 _calledCount = 0; 0617 _callingCount = 0; 0618 _calledContexts = 0; 0619 _callingContexts = 0; 0620 0621 _firstFixCost = nullptr; 0622 _firstFixJump = nullptr; 0623 } 0624 0625 TracePartFunction::~TracePartFunction() 0626 {} 0627 0628 QString TracePartFunction::prettyCalledCount() 0629 { 0630 return _calledCount.pretty(); 0631 } 0632 0633 QString TracePartFunction::prettyCallingCount() 0634 { 0635 return _callingCount.pretty(); 0636 } 0637 0638 QString TracePartFunction::costString(EventTypeSet* m) 0639 { 0640 update(); 0641 0642 QString res = TraceInclusiveCost::costString(m); 0643 res += QStringLiteral(", called from %1: %2") 0644 .arg(_calledContexts).arg(prettyCalledCount()); 0645 res += QStringLiteral(", calling from %1: %2") 0646 .arg(_callingContexts).arg(prettyCallingCount()); 0647 0648 return res; 0649 } 0650 0651 0652 void TracePartFunction::addPartInstr(TracePartInstr* ref) 0653 { 0654 #if TRACE_ASSERTIONS 0655 if (_partInstr.contains(ref)) { 0656 qDebug("TracePartFunction::addPartInstr: %s already in list!", 0657 qPrintable(ref->name())); 0658 return; 0659 } 0660 #endif 0661 0662 _partInstr.append(ref); 0663 invalidate(); 0664 0665 #if TRACE_DEBUG 0666 qDebug("%s added\n %s (now %d)", 0667 qPrintable( fullName() ), qPrintable(ref->fullName()), 0668 _partInstr.count()); 0669 #endif 0670 } 0671 0672 0673 void TracePartFunction::addPartLine(TracePartLine* ref) 0674 { 0675 #if TRACE_ASSERTIONS 0676 if (_partLines.contains(ref)) { 0677 qDebug("TracePartFunction::addPartLine: %s already in list!", 0678 qPrintable(ref->name())); 0679 return; 0680 } 0681 #endif 0682 0683 _partLines.append(ref); 0684 invalidate(); 0685 0686 #if TRACE_DEBUG 0687 qDebug("%s added\n %s (now %d)", 0688 qPrintable( fullName() ), qPrintable(ref->fullName()), 0689 _partLines.count()); 0690 #endif 0691 } 0692 0693 0694 void TracePartFunction::addPartCaller(TracePartCall* ref) 0695 { 0696 #if TRACE_ASSERTIONS 0697 if (_partCallers.contains(ref)) { 0698 qDebug("TracePartFunction::addPartCaller: %s already in list!", 0699 qPrintable(ref->name())); 0700 return; 0701 } 0702 #endif 0703 0704 _partCallers.append(ref); 0705 invalidate(); 0706 0707 #if TRACE_DEBUG 0708 qDebug("%s added Caller\n %s (now %d)", 0709 qPrintable( fullName() ), qPrintable(ref->fullName()), 0710 _partCallers.count()); 0711 #endif 0712 } 0713 0714 0715 void TracePartFunction::addPartCalling(TracePartCall* ref) 0716 { 0717 #if TRACE_ASSERTIONS 0718 if (_partCallings.contains(ref)) { 0719 qDebug("TracePartFunction::addPartCalling: %s already in list!", 0720 qPrintable(ref->name())); 0721 return; 0722 } 0723 #endif 0724 0725 _partCallings.append(ref); 0726 invalidate(); 0727 0728 #if TRACE_DEBUG 0729 qDebug("%s added Calling\n %s (now %d)", 0730 qPrintable( fullName() ), qPrintable(ref->fullName()), 0731 _partCallings.count()); 0732 #endif 0733 } 0734 0735 SubCost TracePartFunction::calledCount() 0736 { 0737 if (_dirty) update(); 0738 0739 return _calledCount; 0740 } 0741 0742 int TracePartFunction::calledContexts() 0743 { 0744 if (_dirty) update(); 0745 0746 return _calledContexts; 0747 } 0748 0749 SubCost TracePartFunction::callingCount() 0750 { 0751 if (_dirty) update(); 0752 0753 return _callingCount; 0754 } 0755 0756 0757 int TracePartFunction::callingContexts() 0758 { 0759 if (_dirty) update(); 0760 0761 return _callingContexts; 0762 } 0763 0764 0765 void TracePartFunction::update() 0766 { 0767 if (!_dirty) return; 0768 0769 #if TRACE_DEBUG 0770 qDebug("TracePartFunction::update %s (Callers %d, Callings %d, lines %d)", 0771 qPrintable(name()), _partCallers.count(), _partCallings.count(), 0772 _partLines.count()); 0773 #endif 0774 0775 _calledCount = 0; 0776 _callingCount = 0; 0777 _calledContexts = 0; 0778 _callingContexts = 0; 0779 0780 // To calculate context counts, we just use first real event type (FIXME?) 0781 EventType* e = data() ? data()->eventTypes()->realType(0) : nullptr; 0782 0783 // calculate additional cost metrics 0784 foreach(TracePartCall* caller, _partCallers) { 0785 if (e && (caller->subCost(e) >0)) 0786 _calledContexts++; 0787 0788 SubCost c = caller->callCount(); 0789 if (c>0) { 0790 _calledCount += c; 0791 } 0792 } 0793 foreach(TracePartCall* calling, _partCallings) { 0794 if (e && (calling->subCost(e)>0)) 0795 _callingContexts++; 0796 0797 SubCost c = calling->callCount(); 0798 if (c>0) { 0799 _callingCount += c; 0800 } 0801 } 0802 0803 // self cost 0804 #if !USE_FIXCOST 0805 if (_partLines.count()>0) { 0806 ProfileCostArray::clear(); 0807 0808 foreach(TracePartLine* line, _partLines) 0809 addCost(line); 0810 } 0811 #else 0812 if (_firstFixCost) { 0813 ProfileCostArray::clear(); 0814 0815 FixCost* item; 0816 for (item = _firstFixCost; item; item = item->nextCostOfPartFunction()) 0817 item->addTo(this); 0818 } 0819 #endif 0820 0821 0822 /* There are two possibilities to calculate inclusive cost: 0823 * 1) sum of call costs to this function 0824 * 2) sum of call costs from this function + self cost 0825 * 0826 * 1) is wrong if a function was called spontaneous, but also by a call. 0827 * This eventually can happen with thread/process startup functions, 0828 * and signal handlers. 0829 * 0830 * 2) is wrong with "skipped PLT" and the calltree skin, because 0831 * cost of PLT is attributed to called function (?) 0832 * 0833 * For now, do 1) if there are callers, otherwise 2). 0834 * Should this be fixed to take the maximum of 1) and 2) ? 0835 */ 0836 _inclusive.clear(); 0837 if (_calledCount>0) { 0838 // inclusive cost: if possible, use caller sums 0839 foreach(TracePartCall* caller, _partCallers) { 0840 // detect simple recursion (no cycle) 0841 if (caller->isRecursion()) continue; 0842 0843 addInclusive(caller); 0844 } 0845 } 0846 else { 0847 // without caller info, use calling sum + line costs 0848 foreach(TracePartCall* calling, _partCallings) { 0849 // detect simple recursion (no cycle) 0850 if (calling->isRecursion()) continue; 0851 0852 addInclusive(calling); 0853 } 0854 _dirty = false; // do not recurse! 0855 addInclusive(this); 0856 } 0857 0858 _dirty = false; 0859 0860 #if TRACE_DEBUG 0861 qDebug(" > %s", qPrintable(costString(0))); 0862 #endif 0863 } 0864 0865 0866 0867 //--------------------------------------------------- 0868 // TracePartClass 0869 0870 TracePartClass::TracePartClass(TraceClass* cls) 0871 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartClass)) 0872 { 0873 _dep = cls; 0874 } 0875 0876 TracePartClass::~TracePartClass() 0877 {} 0878 0879 QString TracePartClass::prettyName() const 0880 { 0881 return QStringLiteral("%1 from %2") 0882 .arg( _dep->name().isEmpty() ? QStringLiteral("(global)") : _dep->name()) 0883 .arg(part()->name()); 0884 } 0885 0886 //--------------------------------------------------- 0887 // TracePartFile 0888 0889 TracePartFile::TracePartFile(TraceFile* file) 0890 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartFile)) 0891 { 0892 _dep = file; 0893 } 0894 0895 TracePartFile::~TracePartFile() 0896 {} 0897 0898 0899 //--------------------------------------------------- 0900 // TracePartObject 0901 0902 TracePartObject::TracePartObject(TraceObject* object) 0903 : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartObject)) 0904 { 0905 _dep = object; 0906 } 0907 0908 TracePartObject::~TracePartObject() 0909 {} 0910 0911 0912 0913 0914 //--------------------------------------------------- 0915 // TraceInstrJump 0916 0917 TraceInstrJump::TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo, 0918 bool isCondJump) 0919 : TraceJumpCost(ProfileContext::context(ProfileContext::InstrJump)) 0920 { 0921 _first = nullptr; 0922 0923 _instrFrom = instrFrom; 0924 _instrTo = instrTo; 0925 _isCondJump = isCondJump; 0926 } 0927 0928 TraceInstrJump::~TraceInstrJump() 0929 { 0930 // we are the owner of the TracePartInstrJump's generated in our factory 0931 TracePartInstrJump* item = _first, *next; 0932 while(item) { 0933 next = item->next(); 0934 delete item; 0935 item = next; 0936 } 0937 } 0938 0939 TracePartInstrJump* TraceInstrJump::partInstrJump(TracePart* part) 0940 { 0941 static TracePartInstrJump* item = nullptr; 0942 0943 // shortcut if recently used 0944 if (item && 0945 (item->instrJump()==this) && 0946 (item->part() == part)) return item; 0947 0948 for(item = _first; item; item = item->next()) 0949 if (item->part() == part) 0950 return item; 0951 0952 item = new TracePartInstrJump(this, _first); 0953 item->setPosition(part); 0954 _first = item; 0955 return item; 0956 } 0957 0958 void TraceInstrJump::update() 0959 { 0960 if (!_dirty) return; 0961 0962 clear(); 0963 TracePartInstrJump* item; 0964 for (item = _first; item; item = item->next()) { 0965 if (!item->part() || !item->part()->isActive()) continue; 0966 0967 addCost(item); 0968 } 0969 _dirty = false; 0970 0971 #if TRACE_DEBUG 0972 qDebug("updated %s", qPrintable( fullName() )); 0973 #endif 0974 0975 #if TRACE_DEBUG 0976 qDebug(" > %s", qPrintable(costString(0))); 0977 #endif 0978 } 0979 0980 QString TraceInstrJump::name() const 0981 { 0982 return QStringLiteral("jump at 0x%1 to 0x%2") 0983 .arg(_instrFrom->addr().toString()) 0984 .arg(_instrTo->addr().toString()); 0985 } 0986 0987 0988 //--------------------------------------------------- 0989 // TraceLineJump 0990 0991 TraceLineJump::TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo, 0992 bool isCondJump) 0993 : TraceJumpListCost(ProfileContext::context(ProfileContext::LineJump)) 0994 { 0995 _lineFrom = lineFrom; 0996 _lineTo = lineTo; 0997 _isCondJump = isCondJump; 0998 } 0999 1000 TraceLineJump::~TraceLineJump() 1001 { 1002 // we are the owner of TracePartLineJump's generated in our factory 1003 qDeleteAll(_deps); 1004 } 1005 1006 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 1019 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 1030 TraceInstrCall::TraceInstrCall(TraceCall* call, TraceInstr* instr) 1031 : TraceCallListCost(ProfileContext::context(ProfileContext::InstrCall)) 1032 { 1033 _call = call; 1034 _instr = instr; 1035 } 1036 1037 TraceInstrCall::~TraceInstrCall() 1038 { 1039 // we are the owner of TracePartInstrCall's generated in our factory 1040 qDeleteAll(_deps); 1041 } 1042 1043 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 1060 QString TraceInstrCall::name() const 1061 { 1062 return QStringLiteral("%1 at %2").arg(_call->name()).arg(_instr->name()); 1063 } 1064 1065 1066 //--------------------------------------------------- 1067 // TraceLineCall 1068 1069 TraceLineCall::TraceLineCall(TraceCall* call, TraceLine* line) 1070 : TraceCallListCost(ProfileContext::context(ProfileContext::LineCall)) 1071 { 1072 _call = call; 1073 1074 _line = line; 1075 } 1076 1077 TraceLineCall::~TraceLineCall() 1078 { 1079 // we are the owner of TracePartLineCall's generated in our factory 1080 qDeleteAll(_deps); 1081 } 1082 1083 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 1098 QString TraceLineCall::name() const 1099 { 1100 return QStringLiteral("%1 at %2").arg(_call->name()).arg(_line->name()); 1101 } 1102 1103 1104 //--------------------------------------------------- 1105 // TraceCall 1106 1107 TraceCall::TraceCall(TraceFunction* caller, TraceFunction* called) 1108 : TraceCallListCost(ProfileContext::context(ProfileContext::Call)) 1109 { 1110 _caller = caller; 1111 _called = called; 1112 } 1113 1114 1115 TraceCall::~TraceCall() 1116 { 1117 // we are the owner of all items generated in our factories 1118 qDeleteAll(_deps); 1119 qDeleteAll(_lineCalls); 1120 } 1121 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 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 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 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 1185 QString TraceCall::name() const 1186 { 1187 return QStringLiteral("%1 => %2") 1188 .arg(_caller->name()) 1189 .arg(_called->name()); 1190 } 1191 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 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 1220 TraceFunction* TraceCall::caller(bool /*skipCycle*/) const 1221 { 1222 return _caller; 1223 } 1224 1225 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 1238 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 1254 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 1276 TraceInstr::TraceInstr() 1277 : TraceListCost(ProfileContext::context(ProfileContext::Instr)) 1278 { 1279 _addr = 0; 1280 _line = nullptr; 1281 _function = nullptr; 1282 } 1283 1284 TraceInstr::~TraceInstr() 1285 { 1286 // we are the owner of items generated in our factories 1287 qDeleteAll(_deps); 1288 qDeleteAll(_instrJumps); 1289 } 1290 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 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 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 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 1356 QString TraceInstr::name() const 1357 { 1358 return QStringLiteral("0x%1").arg(_addr.toString()); 1359 } 1360 1361 QString TraceInstr::prettyName() const 1362 { 1363 return QStringLiteral("0x%1").arg(_addr.toString()); 1364 } 1365 1366 1367 //--------------------------------------------------- 1368 // TraceLine 1369 1370 TraceLine::TraceLine() 1371 : TraceListCost(ProfileContext::context(ProfileContext::Line)) 1372 { 1373 _lineno = 0; 1374 _sourceFile = nullptr; 1375 } 1376 1377 TraceLine::~TraceLine() 1378 { 1379 // we are the owner of items generated in our factories 1380 qDeleteAll(_deps); 1381 qDeleteAll(_lineJumps); 1382 } 1383 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 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 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 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 1465 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 1475 QString TraceLine::prettyName() const 1476 { 1477 return QStringLiteral("%1 [%2]") 1478 .arg(name()).arg(_sourceFile->function()->prettyName()); 1479 } 1480 1481 //--------------------------------------------------- 1482 // TraceCostItem 1483 1484 TraceCostItem::TraceCostItem(ProfileContext* context) 1485 : TraceInclusiveListCost(context) 1486 { 1487 } 1488 1489 TraceCostItem::~TraceCostItem() 1490 {} 1491 1492 1493 //--------------------------------------------------- 1494 // TraceFunctionSource 1495 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 1511 TraceFunctionSource::~TraceFunctionSource() 1512 { 1513 delete _lineMap; 1514 delete _line0; 1515 } 1516 1517 QString TraceFunctionSource::name() const 1518 { 1519 return QStringLiteral("%1 for %2").arg(_file->name()).arg(_function->name()); 1520 } 1521 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 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 */ 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 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 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 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 1727 TraceAssociation::TraceAssociation() 1728 { 1729 _function = nullptr; 1730 _valid = false; 1731 } 1732 1733 TraceAssociation::~TraceAssociation() 1734 { 1735 // do not delete from TraceFunction 1736 if (_function) _function->removeAssociation(this); 1737 } 1738 1739 bool TraceAssociation::isAssociated() 1740 { 1741 if (!_function) return false; 1742 1743 return _function->association(rtti())==this; 1744 } 1745 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 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 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 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 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! 1815 void TraceFunction::addAssociation(TraceAssociation* a) 1816 { 1817 if (!a) return; 1818 _associations.append(a); 1819 } 1820 1821 void TraceFunction::removeAssociation(TraceAssociation* a) 1822 { 1823 _associations.removeAll(a); 1824 } 1825 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 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 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 1879 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 1936 QString TraceFunction::formattedName() const 1937 { 1938 // produce a "rich" name only if templates are hidden 1939 if (!GlobalConfig::hideTemplates() || _name.isEmpty()) return QString(); 1940 1941 // bold, but inside template parameters normal, function arguments italic 1942 QString rich(QStringLiteral("<b>")); 1943 int d = 0; 1944 for(int i=0;i<_name.length();i++) { 1945 switch(_name[i].toLatin1()) { 1946 case '&': 1947 rich.append("&"); 1948 break; 1949 case '<': 1950 d++; 1951 rich.append("<"); 1952 if (d==1) 1953 rich.append("</b>"); 1954 break; 1955 case '>': 1956 d--; 1957 if (d==0) 1958 rich.append("<b>"); 1959 rich.append("> "); // add space to allow for line break 1960 break; 1961 case '(': 1962 rich.append("</b>(<i><b>"); 1963 break; 1964 case ')': 1965 rich.append("</b></i>)<b>"); 1966 break; 1967 default: 1968 rich.append(_name[i]); 1969 break; 1970 } 1971 } 1972 rich.append("</b>"); 1973 return rich; 1974 } 1975 1976 QString TraceFunction::prettyEmptyName() 1977 { 1978 return QObject::tr("(unknown)"); 1979 } 1980 1981 /* 1982 * Returns location string: ELF object and source file(s). 1983 */ 1984 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... 2037 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 2045 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 2053 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 2061 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 2072 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 2080 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 */ 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 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 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 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 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 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 2229 SubCost TraceFunction::calledCount() 2230 { 2231 if (_dirty) update(); 2232 2233 return _calledCount; 2234 } 2235 2236 int TraceFunction::calledContexts() 2237 { 2238 if (_dirty) update(); 2239 2240 return _calledContexts; 2241 } 2242 2243 SubCost TraceFunction::callingCount() 2244 { 2245 if (_dirty) update(); 2246 2247 return _callingCount; 2248 } 2249 2250 int TraceFunction::callingContexts() 2251 { 2252 if (_dirty) update(); 2253 2254 return _callingContexts; 2255 } 2256 2257 QString TraceFunction::prettyCalledCount() 2258 { 2259 return _calledCount.pretty(); 2260 } 2261 2262 QString TraceFunction::prettyCallingCount() 2263 { 2264 return _callingCount.pretty(); 2265 } 2266 2267 2268 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 2292 const TraceCallList& TraceFunction::callings(bool /* skipCycle */) const 2293 { 2294 return _callings; 2295 } 2296 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 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 2392 bool TraceFunction::isCycle() 2393 { 2394 return _cycle == this; 2395 } 2396 2397 bool TraceFunction::isCycleMember() 2398 { 2399 return _cycle && (_cycle != this); 2400 } 2401 2402 void TraceFunction::cycleReset() 2403 { 2404 _cycle = nullptr; 2405 _cycleStackDown = nullptr; 2406 _cycleLow = 0; 2407 } 2408 2409 // this does not mark functions calling themself ! 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 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 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 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 2658 void TraceFunctionCycle::add(TraceFunction* f) 2659 { 2660 _members.append(f); 2661 } 2662 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 2690 TraceClass::TraceClass() 2691 : TraceCostItem(ProfileContext::context(ProfileContext::Class)) 2692 {} 2693 2694 TraceClass::~TraceClass() 2695 { 2696 // we are the owner of items generated in our factory 2697 qDeleteAll(_deps); 2698 } 2699 2700 QString TraceClass::prettyName() const 2701 { 2702 if (_name.isEmpty()) 2703 return prettyEmptyName(); 2704 return _name; 2705 } 2706 2707 QString TraceClass::prettyEmptyName() 2708 { 2709 return QObject::tr("(global)"); 2710 } 2711 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 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 2750 TraceFile::TraceFile() 2751 : TraceCostItem(ProfileContext::context(ProfileContext::File)) 2752 {} 2753 2754 TraceFile::~TraceFile() 2755 { 2756 // we are the owner of items generated in our factory 2757 qDeleteAll(_deps); 2758 } 2759 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 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 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 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 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 2839 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 2848 QString TraceFile::prettyName() const 2849 { 2850 QString sn = shortName(); 2851 2852 if (sn.isEmpty()) 2853 return prettyEmptyName(); 2854 2855 return sn; 2856 } 2857 2858 QString TraceFile::prettyEmptyName() 2859 { 2860 return QObject::tr("(unknown)"); 2861 } 2862 2863 QString TraceFile::prettyLongName() const 2864 { 2865 if (_name.isEmpty()) 2866 return prettyEmptyName(); 2867 return _name; 2868 } 2869 2870 2871 //--------------------------------------------------- 2872 // TraceObject 2873 2874 TraceObject::TraceObject() 2875 : TraceCostItem(ProfileContext::context(ProfileContext::Object)) 2876 {} 2877 2878 TraceObject::~TraceObject() 2879 { 2880 // we are the owner of items generated in our factory 2881 qDeleteAll(_deps); 2882 } 2883 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 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 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 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 2940 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 2949 QString TraceObject::prettyName() const 2950 { 2951 QString sn = shortName(); 2952 2953 if (sn.isEmpty()) 2954 return prettyEmptyName(); 2955 2956 return sn; 2957 } 2958 2959 QString TraceObject::prettyEmptyName() 2960 { 2961 return QObject::tr("(unknown)"); 2962 } 2963 2964 //--------------------------------------------------- 2965 // TracePart 2966 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 2981 TracePart::~TracePart() 2982 { 2983 delete _eventTypeMapping; 2984 } 2985 2986 void TracePart::setPartNumber(int n) 2987 { 2988 if (data()->maxPartNumber() <n) data()->setMaxPartNumber(n); 2989 _number = n; 2990 } 2991 2992 void TracePart::setThreadID(int tid) 2993 { 2994 if (data()->maxThreadID() <tid) data()->setMaxThreadID(tid); 2995 _tid = tid; 2996 } 2997 2998 void TracePart::setProcessID(int pid) 2999 { 3000 _pid = pid; 3001 } 3002 3003 3004 3005 // strip path 3006 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 3015 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 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 3038 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 3057 TraceData::TraceData(Logger* l) 3058 : ProfileCostArray(ProfileContext::context(ProfileContext::Data)) 3059 { 3060 _logger = l; 3061 init(); 3062 } 3063 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 3077 TraceData::~TraceData() 3078 { 3079 qDeleteAll(_parts); 3080 3081 delete _fixPool; 3082 delete _dynPool; 3083 } 3084 3085 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 3094 FixPool* TraceData::fixPool() 3095 { 3096 if (!_fixPool) 3097 _fixPool = new FixPool(); 3098 3099 return _fixPool; 3100 } 3101 3102 DynPool* TraceData::dynPool() 3103 { 3104 if (!_dynPool) 3105 _dynPool = new DynPool(); 3106 3107 return _dynPool; 3108 } 3109 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 */ 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 3163 int TraceData::load(QString file) 3164 { 3165 return load(QStringList(file)); 3166 } 3167 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 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 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 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 3242 bool TraceData::activatePart(TracePart* p, bool active) 3243 { 3244 return p->activate(active); 3245 } 3246 3247 bool TraceData::activateAll(bool active) 3248 { 3249 return activateParts(_parts, active); 3250 } 3251 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 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 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 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 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 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() 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 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 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 3479 TraceFunctionMap::ConstIterator TraceData::functionBeginIterator() const 3480 { 3481 return _functionMap.begin(); 3482 } 3483 3484 TraceFunctionMap::ConstIterator TraceData::functionEndIterator() const 3485 { 3486 return _functionMap.end(); 3487 } 3488 3489 // _maxCallCount is maintained globally, and only updated at loading 3490 void TraceData::updateMaxCallCount(SubCost c) 3491 { 3492 if (_maxCallCount < c) 3493 _maxCallCount = c; 3494 } 3495 3496 void TraceData::resetSourceDirs() 3497 { 3498 TraceFileMap::Iterator fit; 3499 for ( fit = _fileMap.begin(); 3500 fit != _fileMap.end(); ++fit ) 3501 (*fit).resetDirectory(); 3502 } 3503 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 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 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 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 3729 void TraceData::updateObjectCycles() 3730 { 3731 } 3732 3733 3734 void TraceData::updateClassCycles() 3735 { 3736 } 3737 3738 3739 void TraceData::updateFileCycles() 3740 { 3741 } 3742