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