File indexing completed on 2024-05-05 05:44:22
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 * Function Coverage Analysis 0011 */ 0012 0013 #include "coverage.h" 0014 0015 //#define DEBUG_COVERAGE 1 0016 0017 EventType* Coverage::_costType; 0018 0019 const int Coverage::maxHistogramDepth = maxHistogramDepthValue; 0020 const int Coverage::Rtti = 1; 0021 0022 Coverage::Coverage() 0023 { 0024 } 0025 0026 void Coverage::init() 0027 { 0028 _self = 0.0; 0029 _incl = 0.0; 0030 _callCount = 0.0; 0031 // should always be overwritten before usage 0032 _firstPercentage = 1.0; 0033 _minDistance = 9999; 0034 _maxDistance = 0; 0035 _active = false; 0036 _inRecursion = false; 0037 for (int i = 0;i<maxHistogramDepth;i++) { 0038 _selfHisto[i] = 0.0; 0039 _inclHisto[i] = 0.0; 0040 } 0041 0042 _valid = true; 0043 } 0044 0045 int Coverage::inclusiveMedian() 0046 { 0047 double maxP = _inclHisto[0]; 0048 int medD = 0; 0049 for (int i = 1;i<maxHistogramDepth;i++) 0050 if (_inclHisto[i]>maxP) { 0051 maxP = _inclHisto[i]; 0052 medD = i; 0053 } 0054 0055 return medD; 0056 } 0057 0058 int Coverage::selfMedian() 0059 { 0060 double maxP = _selfHisto[0]; 0061 int medD = 0; 0062 for (int i = 1;i<maxHistogramDepth;i++) 0063 if (_selfHisto[i]>maxP) { 0064 maxP = _selfHisto[i]; 0065 medD = i; 0066 } 0067 0068 return medD; 0069 } 0070 0071 TraceFunctionList Coverage::coverage(TraceFunction* f, CoverageMode m, 0072 EventType* ct) 0073 { 0074 invalidate(f->data(), Coverage::Rtti); 0075 0076 _costType = ct; 0077 0078 // function f takes ownership over c! 0079 Coverage* c = new Coverage(); 0080 c->setFunction(f); 0081 c->init(); 0082 0083 TraceFunctionList l; 0084 0085 if (m == Caller) 0086 c->addCallerCoverage(l, 1.0, 0); 0087 else 0088 c->addCallingCoverage(l, 1.0, 1.0, 0); 0089 0090 return l; 0091 } 0092 0093 void Coverage::addCallerCoverage(TraceFunctionList& fList, 0094 double pBack, int d) 0095 { 0096 if (_inRecursion) return; 0097 0098 double incl; 0099 incl = (double) (_function->inclusive()->subCost(_costType)); 0100 0101 if (_active) { 0102 #ifdef DEBUG_COVERAGE 0103 qDebug("CallerCov: D %d, %s (was active, incl %f, self %f): newP %f", d, 0104 qPrintable(_function->prettyName()), _incl, _self, pBack); 0105 #endif 0106 _inRecursion = true; 0107 } 0108 else { 0109 _active = true; 0110 0111 // only add cost if this is no recursion 0112 0113 _incl += pBack; 0114 _firstPercentage = pBack; 0115 0116 if (_minDistance > d) _minDistance = d; 0117 if (_maxDistance < d) _maxDistance = d; 0118 if (d<maxHistogramDepth) { 0119 _inclHisto[d] += pBack; 0120 } 0121 else { 0122 _inclHisto[maxHistogramDepth-1] += pBack; 0123 } 0124 0125 #ifdef DEBUG_COVERAGE 0126 qDebug("CallerCov: D %d, %s (now active, new incl %f): newP %f", 0127 d, qPrintable(_function->prettyName()), _incl, pBack); 0128 #endif 0129 } 0130 0131 double callVal, pBackNew; 0132 0133 foreach(TraceCall* call, _function->callers()) { 0134 if (call->inCycle()>0) continue; 0135 if (call->isRecursion()) continue; 0136 0137 if (call->subCost(_costType)>0) { 0138 TraceFunction* caller = call->caller(); 0139 0140 Coverage* c = (Coverage*) caller->association(rtti()); 0141 if (!c) { 0142 c = new Coverage(); 0143 c->setFunction(caller); 0144 } 0145 if (!c->isValid()) { 0146 c->init(); 0147 fList.append(caller); 0148 } 0149 0150 if (c->isActive()) continue; 0151 if (c->inRecursion()) continue; 0152 0153 callVal = (double) call->subCost(_costType); 0154 pBackNew = pBack * (callVal / incl); 0155 0156 // FIXME ?!? 0157 0158 if (!c->isActive()) { 0159 if (d>=0) 0160 c->callCount() += (double)call->callCount(); 0161 else 0162 c->callCount() += _callCount; 0163 } 0164 else { 0165 // adjust pNew by sum of geometric series of recursion factor. 0166 // Thus we can avoid endless recursion here 0167 pBackNew *= 1.0 / (1.0 - pBackNew / c->firstPercentage()); 0168 } 0169 0170 // Limit depth 0171 if (pBackNew > 0.0001) 0172 c->addCallerCoverage(fList, pBackNew, d+1); 0173 } 0174 } 0175 0176 if (_inRecursion) 0177 _inRecursion = false; 0178 else if (_active) 0179 _active = false; 0180 } 0181 0182 /** 0183 * pForward is time on percent used, 0184 * pBack is given to allow for calculation of call counts 0185 */ 0186 void Coverage::addCallingCoverage(TraceFunctionList& fList, 0187 double pForward, double pBack, int d) 0188 { 0189 if (_inRecursion) return; 0190 0191 #ifdef DEBUG_COVERAGE 0192 static const char* spaces = " "; 0193 #endif 0194 0195 double self, incl; 0196 incl = (double) (_function->inclusive()->subCost(_costType)); 0197 0198 #ifdef DEBUG_COVERAGE 0199 qDebug("CngCov:%s - %s (incl %f, self %f): forward %f, back %f", 0200 spaces+strlen(spaces)-d, 0201 qPrintable(_function->prettyName()), _incl, _self, pForward, pBack); 0202 #endif 0203 0204 0205 if (_active) { 0206 _inRecursion = true; 0207 0208 #ifdef DEBUG_COVERAGE 0209 qDebug("CngCov:%s < %s: STOP (is active)", 0210 spaces+strlen(spaces)-d, 0211 qPrintable(_function->prettyName())); 0212 #endif 0213 0214 } 0215 else { 0216 _active = true; 0217 0218 // only add cost if this is no recursion 0219 self = pForward * (_function->subCost(_costType)) / incl; 0220 _incl += pForward; 0221 _self += self; 0222 _firstPercentage = pForward; 0223 0224 if (_minDistance > d) _minDistance = d; 0225 if (_maxDistance < d) _maxDistance = d; 0226 if (d<maxHistogramDepth) { 0227 _inclHisto[d] += pForward; 0228 _selfHisto[d] += self; 0229 } 0230 else { 0231 _inclHisto[maxHistogramDepth-1] += pForward; 0232 _selfHisto[maxHistogramDepth-1] += self; 0233 } 0234 0235 #ifdef DEBUG_COVERAGE 0236 qDebug("CngCov:%s < %s (incl %f, self %f)", 0237 spaces+strlen(spaces)-d, 0238 qPrintable(_function->prettyName()), _incl, _self); 0239 #endif 0240 } 0241 0242 double callVal, pForwardNew, pBackNew; 0243 0244 foreach(TraceCall* call, _function->callings()) { 0245 if (call->inCycle()>0) continue; 0246 if (call->isRecursion()) continue; 0247 0248 if (call->subCost(_costType)>0) { 0249 TraceFunction* calling = call->called(); 0250 0251 Coverage* c = (Coverage*) calling->association(rtti()); 0252 if (!c) { 0253 c = new Coverage(); 0254 c->setFunction(calling); 0255 } 0256 if (!c->isValid()) { 0257 c->init(); 0258 fList.append(calling); 0259 } 0260 0261 if (c->isActive()) continue; 0262 if (c->inRecursion()) continue; 0263 0264 callVal = (double) call->subCost(_costType); 0265 pForwardNew = pForward * (callVal / incl); 0266 pBackNew = pBack * (callVal / 0267 calling->inclusive()->subCost(_costType)); 0268 0269 if (!c->isActive()) { 0270 c->callCount() += pBack * call->callCount(); 0271 0272 #ifdef DEBUG_COVERAGE 0273 qDebug("CngCov:%s > %s: forward %f, back %f, calls %f -> %f, now %f", 0274 spaces+strlen(spaces)-d, 0275 qPrintable(calling->prettyName()), 0276 pForwardNew, pBackNew, 0277 (double)call->callCount(), 0278 pBack * call->callCount(), 0279 c->callCount()); 0280 #endif 0281 } 0282 else { 0283 // adjust pNew by sum of geometric series of recursion factor. 0284 // Thus we can avoid endless recursion here 0285 double fFactor = 1.0 / (1.0 - pForwardNew / c->firstPercentage()); 0286 double bFactor = 1.0 / (1.0 - pBackNew); 0287 #ifdef DEBUG_COVERAGE 0288 qDebug("CngCov:%s Recursion - origP %f, actP %f => factor %f, newP %f", 0289 spaces+strlen(spaces)-d, 0290 c->firstPercentage(), pForwardNew, 0291 fFactor, pForwardNew * fFactor); 0292 #endif 0293 pForwardNew *= fFactor; 0294 pBackNew *= bFactor; 0295 0296 } 0297 0298 // Limit depth 0299 if (pForwardNew > 0.0001) 0300 c->addCallingCoverage(fList, pForwardNew, pBackNew, d+1); 0301 } 0302 } 0303 0304 if (_inRecursion) 0305 _inRecursion = false; 0306 else if (_active) 0307 _active = false; 0308 } 0309