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 #include "costitem.h"
0010 
0011 #include <QObject>
0012 
0013 #include "tracedata.h"
0014 
0015 #define TRACE_DEBUG      0
0016 #define TRACE_ASSERTIONS 0
0017 
0018 //---------------------------------------------------
0019 // ProfileCost
0020 
0021 CostItem::CostItem(ProfileContext* c)
0022 {
0023     _position = nullptr;
0024     _dep = nullptr;
0025     _dirty = true;
0026 
0027     _context = c;
0028 }
0029 
0030 CostItem::~CostItem()
0031 {}
0032 
0033 
0034 void CostItem::clear()
0035 {
0036     invalidate();
0037 }
0038 
0039 
0040 QString CostItem::costString(EventTypeSet*)
0041 {
0042     return QStringLiteral("(no cost)");
0043 }
0044 
0045 QString CostItem::name() const
0046 {
0047     if (part()) {
0048         return QObject::tr("%1 from %2").arg(_dep->name()).arg(part()->name());
0049     }
0050 
0051     if (_dep)
0052         return _dep->name();
0053 
0054     return QObject::tr("(unknown)");
0055 }
0056 
0057 QString CostItem::prettyName() const
0058 {
0059     if (name().isEmpty()) return QObject::tr("(unknown)");
0060     return name();
0061 }
0062 
0063 QString CostItem::formattedName() const
0064 {
0065     return QString();
0066 }
0067 
0068 QString CostItem::fullName() const
0069 {
0070     return QStringLiteral("%1 %2")
0071             .arg(ProfileContext::typeName(type())).arg(prettyName());
0072 }
0073 
0074 QString CostItem::toString()
0075 {
0076     return QStringLiteral("%1\n  [%3]").arg(fullName()).arg(costString(nullptr));
0077 }
0078 
0079 void CostItem::invalidate()
0080 {
0081     if (_dirty) return;
0082     _dirty = true;
0083 
0084     if (_dep)
0085         _dep->invalidate();
0086 }
0087 
0088 void CostItem::update()
0089 {
0090     _dirty = false;
0091 }
0092 
0093 TracePart* CostItem::part()
0094 {
0095     return _position ? _position->part() : nullptr;
0096 }
0097 
0098 const TracePart* CostItem::part() const
0099 {
0100     return _position ? _position->part() : nullptr;
0101 }
0102 
0103 TraceData* CostItem::data()
0104 {
0105     return _position ? _position->data() : nullptr;
0106 }
0107 
0108 const TraceData* CostItem::data() const
0109 {
0110     return _position ? _position->data() : nullptr;
0111 }
0112 
0113 
0114 //---------------------------------------------------
0115 // ProfileCostArray
0116 
0117 
0118 const int ProfileCostArray::MaxRealIndex = MaxRealIndexValue;
0119 const int ProfileCostArray::InvalidIndex = -1;
0120 
0121 
0122 ProfileCostArray::ProfileCostArray(ProfileContext* context)
0123     : CostItem(context)
0124 {
0125     _cachedType = nullptr; // no virtual value cached
0126     _allocCount = 0;
0127     _count = 0;
0128     _cost = nullptr;
0129 }
0130 
0131 ProfileCostArray::ProfileCostArray()
0132     : CostItem(ProfileContext::context(ProfileContext::UnknownType))
0133 {
0134     _cachedType = nullptr; // no virtual value cached
0135     _allocCount = 0;
0136     _count = 0;
0137     _cost = nullptr;
0138 }
0139 
0140 ProfileCostArray::~ProfileCostArray()
0141 {
0142     if (_cost) delete[] _cost;
0143 }
0144 
0145 
0146 void ProfileCostArray::clear()
0147 {
0148     _count = 0;
0149     invalidate();
0150 }
0151 
0152 void ProfileCostArray::reserve(int count)
0153 {
0154     if (count <= _allocCount) return;
0155 
0156     SubCost* newcost = new SubCost[count];
0157     if (_cost) {
0158         /* first _count values are valid and have to be preserved */
0159         for(int i=0; i<_count; i++)
0160             newcost[i] = _cost[i];
0161         delete[] _cost;
0162     }
0163     _cost = newcost;
0164     _allocCount = count;
0165 }
0166 
0167 void ProfileCostArray::set(EventTypeMapping* mapping, const char* s)
0168 {
0169     if (!mapping) return;
0170     if (!s) {
0171         clear();
0172         return;
0173     }
0174 
0175     reserve(mapping->set()->realCount());
0176 
0177     while(*s == ' ') s++;
0178 
0179     if (mapping->isIdentity()) {
0180         int i = 0;
0181         while(i<mapping->count()) {
0182             if (!_cost[i].set(&s)) break;
0183             i++;
0184         }
0185         _count = i;
0186     }
0187     else {
0188         int i = 0, maxIndex = 0, index;
0189         while(1) {
0190             index = mapping->realIndex(i);
0191             if (maxIndex<index) maxIndex=index;
0192             if (index == ProfileCostArray::InvalidIndex) break;
0193             if (!_cost[index].set(&s)) break;
0194             i++;
0195         }
0196         // we have to set all costs of unused indexes till maxIndex to zero
0197         for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
0198             _cost[i] = 0;
0199         _count = maxIndex;
0200     }
0201     Q_ASSERT(_count <= _allocCount);
0202     // a cost change has to be propagated (esp. in subclasses)
0203     invalidate();
0204 }
0205 
0206 void ProfileCostArray::set(EventTypeMapping* mapping, FixString & s)
0207 {
0208     if (!mapping) return;
0209     s.stripSpaces();
0210     if (s.isEmpty()) {
0211         clear();
0212         return;
0213     }
0214 
0215     reserve(mapping->set()->realCount());
0216 
0217     if (mapping->isIdentity()) {
0218         int i = 0;
0219         while(i<mapping->count()) {
0220             if (!s.stripUInt64(_cost[i])) break;
0221             i++;
0222         }
0223         _count = i;
0224     }
0225     else {
0226         int i = 0, maxIndex = 0, index;
0227         while(1) {
0228             index = mapping->realIndex(i);
0229             if (maxIndex<index) maxIndex=index;
0230             if (index == ProfileCostArray::InvalidIndex) break;
0231             if (!s.stripUInt64(_cost[index])) break;
0232             i++;
0233         }
0234         // we have to set all costs of unused indexes till maxIndex to zero
0235         for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
0236             _cost[i] = 0;
0237         _count = maxIndex+1;
0238     }
0239     Q_ASSERT(_count <= _allocCount);
0240     invalidate();
0241 }
0242 
0243 
0244 void ProfileCostArray::addCost(EventTypeMapping* mapping, const char* s)
0245 {
0246     if (!mapping || !s) return;
0247     reserve(mapping->set()->realCount());
0248 
0249     SubCost v;
0250     if (mapping->isIdentity()) {
0251         int i = 0;
0252         while(i<mapping->count()) {
0253             if (!v.set(&s)) break;
0254             if (i<_count)
0255                 _cost[i] += v;
0256             else
0257                 _cost[i] = v;
0258             i++;
0259         }
0260         if (i > _count) _count = i;
0261     }
0262     else {
0263         int i = 0, maxIndex = 0, index;
0264         while(1) {
0265             if (!v.set(&s)) break;
0266             index = mapping->realIndex(i);
0267             if (maxIndex<index) maxIndex=index;
0268             if (index == ProfileCostArray::InvalidIndex) break;
0269             if (index<_count)
0270                 _cost[index] += v;
0271             else
0272                 _cost[index] = v;
0273             i++;
0274         }
0275         if (maxIndex >= _count) {
0276             /* we have to set all costs of unused indexes in the interval
0277              *  [_count;maxIndex] to zero */
0278             for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
0279                 _cost[i] = 0;
0280             _count = maxIndex+1;
0281         }
0282     }
0283 
0284     Q_ASSERT(_count <= _allocCount);
0285     invalidate();
0286 
0287 #if TRACE_DEBUG
0288     _dirty = false; // do not recurse !
0289     qDebug("%s\n now %s", qPrintable( fullName() ),
0290            qPrintable( ProfileCostArray::costString(0) ));
0291     _dirty = true; // because of invalidate()
0292 #endif
0293 }
0294 
0295 void ProfileCostArray::addCost(EventTypeMapping* mapping, FixString & s)
0296 {
0297     if (!mapping) return;
0298     s.stripSpaces();
0299     if (s.isEmpty()) return;
0300     reserve(mapping->set()->realCount());
0301 
0302     SubCost v;
0303     if (mapping->isIdentity()) {
0304         int i = 0;
0305         while(i<mapping->count()) {
0306             if (!s.stripUInt64(v)) break;
0307             if (i<_count)
0308                 _cost[i] += v;
0309             else
0310                 _cost[i] = v;
0311             i++;
0312         }
0313         if (i > _count) _count = i;
0314     }
0315     else {
0316         int i = 0, maxIndex = 0, index;
0317         while(1) {
0318             if (!s.stripUInt64(v)) break;
0319             index = mapping->realIndex(i);
0320             if (maxIndex<index) maxIndex=index;
0321             if (index == ProfileCostArray::InvalidIndex) break;
0322             if (index<_count)
0323                 _cost[index] += v;
0324             else
0325                 _cost[index] = v;
0326             i++;
0327         }
0328         if (maxIndex >= _count) {
0329             /* we have to set all costs of unused indexes in the interval
0330              *  [_count;maxIndex] to zero */
0331             for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
0332                 _cost[i] = 0;
0333             _count = maxIndex+1;
0334         }
0335     }
0336 
0337     Q_ASSERT(_count <= _allocCount);
0338     invalidate();
0339 
0340 #if TRACE_DEBUG
0341     _dirty = false; // do not recurse !
0342     qDebug("%s\n now %s", qPrintable( fullName() ),
0343            qPrintable( ProfileCostArray::costString(0 ) ) );
0344     _dirty = true; // because of invalidate()
0345 #endif
0346 }
0347 
0348 
0349 // update each subcost to be maximum of old and given costs
0350 void ProfileCostArray::maxCost(EventTypeMapping* mapping, FixString & s)
0351 {
0352     if (!mapping) return;
0353     s.stripSpaces();
0354     if (s.isEmpty()) return;
0355     reserve(mapping->set()->realCount());
0356 
0357     SubCost v;
0358     if (mapping->isIdentity()) {
0359         int i = 0;
0360         while(i<mapping->count()) {
0361             if (!s.stripUInt64(v)) break;
0362             if (i<_count) {
0363                 if (v>_cost[i]) _cost[i] = v;
0364             }
0365             else
0366                 _cost[i] = v;
0367             i++;
0368         }
0369         if (i > _count) _count = i;
0370     }
0371     else {
0372         int i = 0, maxIndex = 0, index;
0373         while(1) {
0374             if (!s.stripUInt64(v)) break;
0375             index = mapping->realIndex(i);
0376             if (maxIndex<index) maxIndex=index;
0377             if (index == ProfileCostArray::InvalidIndex) break;
0378             if (index<_count) {
0379                 if (v>_cost[index]) _cost[index] = v;
0380             }
0381             else
0382                 _cost[index] = v;
0383             i++;
0384         }
0385         if (maxIndex >= _count) {
0386             /* we have to set all costs of unused indexes in the interval
0387              *  [_count;maxIndex] to zero */
0388             for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
0389                 _cost[i] = 0;
0390             _count = maxIndex+1;
0391         }
0392     }
0393 
0394     Q_ASSERT(_count <= _allocCount);
0395     invalidate();
0396 
0397 #if TRACE_DEBUG
0398     _dirty = false; // do not recurse !
0399     qDebug("%s\n now %s", qPrintable( fullName() ),
0400            qPrintable(ProfileCostArray::costString(0)));
0401     _dirty = true; // because of invalidate()
0402 #endif
0403 }
0404 
0405 
0406 void ProfileCostArray::addCost(ProfileCostArray* item)
0407 {
0408     int i;
0409     if (!item) return;
0410 
0411     // we have to update the other item if needed
0412     // because we access the item costs directly
0413     if (item->_dirty) item->update();
0414 
0415     // make sure we have enough space allocated
0416     reserve(item->_count);
0417 
0418     if (item->_count < _count) {
0419         for (i = 0; i<item->_count; ++i)
0420             _cost[i] += item->_cost[i];
0421     }
0422     else {
0423         for (i = 0; i<_count; ++i)
0424             _cost[i] += item->_cost[i];
0425         for (; i<item->_count; ++i)
0426             _cost[i] = item->_cost[i];
0427         _count = item->_count;
0428     }
0429 
0430     Q_ASSERT(_count <= _allocCount);
0431     invalidate();
0432 
0433 #if TRACE_DEBUG
0434     _dirty = false; // do not recurse !
0435     qDebug("%s added cost item\n %s\n  now %s",
0436            qPrintable( fullName() ), qPrintable(item->fullName()),
0437            qPrintable(ProfileCostArray::costString(0)));
0438     _dirty = true; // because of invalidate()
0439 #endif
0440 }
0441 
0442 void ProfileCostArray::maxCost(ProfileCostArray* item)
0443 {
0444     int i;
0445 
0446     if (!item) return;
0447 
0448     // we have to update the other item if needed
0449     // because we access the item costs directly
0450     if (item->_dirty) item->update();
0451 
0452     // make sure we have enough space allocated
0453     reserve(item->_count);
0454 
0455     if (item->_count < _count) {
0456         for (i = 0; i<item->_count; ++i)
0457             if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
0458     }
0459     else {
0460         for (i = 0; i<_count; ++i)
0461             if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
0462         for (; i<item->_count; ++i)
0463             _cost[i] = item->_cost[i];
0464         _count = item->_count;
0465     }
0466 
0467     Q_ASSERT(_count <= _allocCount);
0468     invalidate();
0469 
0470 #if TRACE_DEBUG
0471     _dirty = false; // do not recurse !
0472     qDebug("%s added cost item\n %s\n  now %s",
0473            qPrintable( fullName() ), qPrintable(item->fullName()),
0474            qPrintable( ProfileCostArray::costString(0) ));
0475     _dirty = true; // because of invalidate()
0476 #endif
0477 }
0478 
0479 void ProfileCostArray::addCost(int realIndex, SubCost value)
0480 {
0481     if (realIndex<0 || realIndex>=MaxRealIndex) return;
0482     if (value == 0) return;
0483 
0484     reserve(realIndex+1);
0485     if (realIndex < _count)
0486         _cost[realIndex] += value;
0487     else {
0488         for(int i=_count;i<realIndex;i++)
0489             _cost[i] = 0;
0490         _cost[realIndex] = value;
0491         _count = realIndex+1;
0492     }
0493 
0494     Q_ASSERT(_count <= _allocCount);
0495     invalidate();
0496 }
0497 
0498 void ProfileCostArray::maxCost(int realIndex, SubCost value)
0499 {
0500     if (realIndex<0 || realIndex>=MaxRealIndex) return;
0501 
0502     reserve(realIndex+1);
0503     if (realIndex<_count) {
0504         if (value>_cost[realIndex]) _cost[realIndex] = value;
0505     }
0506     else {
0507         for(int i=_count;i<realIndex;i++)
0508             _cost[i] = 0;
0509         _cost[realIndex] = value;
0510         _count = realIndex+1;
0511     }
0512 
0513     Q_ASSERT(_count <= _allocCount);
0514     invalidate();
0515 }
0516 
0517 
0518 ProfileCostArray ProfileCostArray::diff(ProfileCostArray* item)
0519 {
0520     ProfileCostArray res(context());
0521 
0522     // we have to update the other item if needed
0523     // because we access the item costs directly
0524     if (item->_dirty) item->update();
0525 
0526     int maxCount = (item->_count > _count) ? item->_count : _count;
0527 
0528     res.reserve(maxCount);
0529     for (int i=0; i<maxCount;i++)
0530         res._cost[i] = item->subCost(i) - subCost(i);
0531     res._count = maxCount;
0532 
0533     Q_ASSERT(res._count <= res._allocCount);
0534     return res;
0535 }
0536 
0537 QString ProfileCostArray::costString(EventTypeSet* set)
0538 {
0539     QString res;
0540 
0541     if (_dirty) update();
0542 
0543     int maxIndex = set ? set->realCount() : ProfileCostArray::MaxRealIndex;
0544     for (int i = 0; i<maxIndex; i++) {
0545         if (!res.isEmpty()) res += QLatin1String(", ");
0546         if (set) res += set->type(i)->name() + ' ';
0547 
0548         res += subCost(i).pretty();
0549     }
0550     return res;
0551 }
0552 
0553 
0554 void ProfileCostArray::invalidate()
0555 {
0556     if (_dirty) return;
0557     _dirty = true;
0558     _cachedType = nullptr; // cached value is invalid, too
0559 
0560     if (_dep)
0561         _dep->invalidate();
0562 }
0563 
0564 void ProfileCostArray::update()
0565 {
0566     _dirty = false;
0567 }
0568 
0569 // this is only for real types
0570 SubCost ProfileCostArray::subCost(int idx)
0571 {
0572     if (idx<0) return 0;
0573 
0574     /* update if needed as cost could be calculated dynamically in subclasses
0575      * this can change _count !! */
0576     if (_dirty) update();
0577     if (idx>=_count) return 0;
0578 
0579     return _cost[idx];
0580 }
0581 
0582 
0583 SubCost ProfileCostArray::subCost(EventType* t)
0584 {
0585     if (!t) return 0;
0586     if (_cachedType != t) {
0587         _cachedType = t;
0588         _cachedCost = t->subCost(this);
0589     }
0590     return _cachedCost;
0591 }
0592 
0593 QString ProfileCostArray::prettySubCost(EventType* t)
0594 {
0595     return subCost(t).pretty();
0596 }
0597 
0598 QString ProfileCostArray::prettySubCostPerCall(EventType* t, uint64 calls)
0599 {
0600     if (calls == 0) {
0601         /* For callgrind, a call count of zero means that
0602          * a function was already active when measuring started
0603          * (even without that possibility, we never should crash).
0604          * To show full cost, set <calls> to 1.
0605          */
0606         calls = 1;
0607     }
0608     return SubCost(subCost(t) / calls).pretty();
0609 }
0610