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