From 0bdf7c7dcefe97e0aa8c0408059e92c4076c094b Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Thu, 14 Sep 2017 16:29:33 +0300 Subject: [PATCH] Process mmap/munmap and smaps trace items in analyzer. Maintain address ranges information in analyzer. --- src/analyze/accumulatedtracedata.cpp | 260 +++++++++++++++++++++++++++++++++++ src/analyze/accumulatedtracedata.h | 140 +++++++++++++++++++ 2 files changed, 400 insertions(+) diff --git a/src/analyze/accumulatedtracedata.cpp b/src/analyze/accumulatedtracedata.cpp index 063e0ef..6b9dd59 100644 --- a/src/analyze/accumulatedtracedata.cpp +++ b/src/analyze/accumulatedtracedata.cpp @@ -172,7 +172,9 @@ bool AccumulatedTraceData::read(istream& in, const ParsePass pass) systemInfo = {}; peakRSS = 0; allocations.clear(); + addressRangeInfos.clear(); uint fileVersion = 0; + bool isSmapsChunkInProcess = false; // required for backwards compatibility // newer versions handle this in heaptrack_interpret already @@ -240,6 +242,115 @@ bool AccumulatedTraceData::read(istream& in, const ParsePass pass) index.index = instructionPointers.size(); opNewIpIndices.push_back(index); } + } else if (reader.mode() == '*') { + uint64_t length, ptr; + int prot, fd; + TraceIndex traceIndex; + + if (!(reader >> length) + || !(reader >> prot) + || !(reader >> fd) + || !(reader >> traceIndex.index) + || !(reader >> ptr)) { + cerr << "failed to parse line: " << reader.line() << endl; + continue; + } + + mapRemoveRanges(ptr, length); + + AddressRangesMapIteratorPair ranges = mapUpdateRange(ptr, length); + + assert (std::next(ranges.first) == ranges.second); + + AddressRangeInfo &rangeInfo = ranges.first->second; + + rangeInfo.setProt(prot); + rangeInfo.setFd(fd); + rangeInfo.setTraceIndex(traceIndex.index); + + combineContiguousSimilarRanges(); + } else if (reader.mode() == '/') { + uint64_t length, ptr; + + if (!(reader >> length) + || !(reader >> ptr)) { + cerr << "failed to parse line: " << reader.line() << endl; + continue; + } + + mapRemoveRanges(ptr, length); + } else if (reader.mode() == 'K') { + int isStart; + + if (!(reader >> isStart) || !(isStart == 1 || isStart == 0)) { + cerr << "failed to parse line: " << reader.line() << endl; + continue; + } + + if (isStart) + { + // smaps chunk start + if (isSmapsChunkInProcess) { + cerr << "wrong trace format (nested smaps chunks)" << endl; + continue; + } + + isSmapsChunkInProcess = true; + } + else + { + assert (!isStart); + + // smaps chunk end + if (!isSmapsChunkInProcess) { + cerr << "wrong trace format (nested smaps chunks?)" << endl; + continue; + } + + combineContiguousSimilarRanges(); + + isSmapsChunkInProcess = false; + } + } else if (reader.mode() == 'k') { + if (!isSmapsChunkInProcess) { + cerr << "wrong trace format (smaps data outside of smaps chunk)" << endl; + continue; + } + + uint64_t addr, diff, size, privateDirty, privateClean, sharedDirty, sharedClean; + int prot; + + constexpr uint64_t kilobyteSize = 1024; + + if (!(reader >> addr) + || !(reader >> diff) + || !(reader >> size) + || !(reader >> privateDirty) + || !(reader >> privateClean) + || !(reader >> sharedDirty) + || !(reader >> sharedClean) + || !(reader >> prot) + /* the value can be different on Tizen for [stack] area */ + /* || !(diff == size * kilobyteSize) */ + ) { + cerr << "failed to parse line: " << reader.line() << endl; + continue; + } + + AddressRangesMapIteratorPair ranges = mapUpdateRange(addr, diff); + + for (auto i = ranges.first; i != ranges.second; i++) + { + AddressRangeInfo &rangeInfo = i->second; + + rangeInfo.setProt(prot); + + rangeInfo.setPhysicalMemoryConsumption(diff, + privateDirty * kilobyteSize, + privateClean * kilobyteSize, + sharedDirty * kilobyteSize, + sharedClean * kilobyteSize); + } } else if (reader.mode() == '+') { AllocationInfo info; AllocationIndex allocationIndex; @@ -695,3 +806,152 @@ bool AccumulatedTraceData::isStopIndex(const StringIndex index) const { return find(stopIndices.begin(), stopIndices.end(), index) != stopIndices.end(); } + +AddressRangesMapIteratorPair AccumulatedTraceData::mapUpdateRange(const uint64_t start, + const uint64_t size) +{ + uint64_t ptr = start; + uint64_t endPtr = start + size; + + auto rangesIter = addressRangeInfos.lower_bound(ptr); + auto rangesEnd = addressRangeInfos.lower_bound(endPtr); + + if (rangesIter != addressRangeInfos.begin()) + { + auto prev = std::prev(rangesIter); + + if (prev->first + prev->second.size > start) + { + AddressRangeInfo newRange = prev->second.split(start - prev->first); + + addressRangeInfos.insert(std::make_pair(start, newRange)); + + rangesIter = std::next(prev); + + assert (rangesIter->first == ptr); + } + } + + while (ptr != endPtr) + { + if (rangesIter == rangesEnd) + { + addressRangeInfos.insert(std::make_pair(ptr, + AddressRangeInfo(ptr, + endPtr - ptr))); + + ptr = endPtr; + } + else + { + if (ptr != rangesIter->first) + { + assert(ptr < rangesIter->first); + + uint64_t newSubrangeStart = ptr; + uint64_t newSubrangeEnd = std::min(endPtr, rangesIter->first); + uint64_t newSubrangeSize = newSubrangeEnd - newSubrangeStart; + + addressRangeInfos.insert(std::make_pair(ptr, + AddressRangeInfo(newSubrangeStart, + newSubrangeSize))); + + ptr = newSubrangeEnd; + } + else + { + uint64_t subrangeStart = rangesIter->first; + uint64_t subrangeEnd = subrangeStart + rangesIter->second.size; + + if (subrangeEnd <= endPtr) + { + ptr = subrangeEnd; + + ++rangesIter; + } + else + { + assert (std::next(rangesIter) == rangesEnd); + + AddressRangeInfo newRange = rangesIter->second.split(endPtr - ptr); + + addressRangeInfos.insert(std::make_pair(endPtr, newRange)); + } + } + } + } + + return std::make_pair(addressRangeInfos.lower_bound(start), + addressRangeInfos.lower_bound(endPtr)); +} + +void AccumulatedTraceData::mapRemoveRanges(const uint64_t start, + const uint64_t size) +{ + auto i = addressRangeInfos.lower_bound(start); + auto e = addressRangeInfos.lower_bound(start + size); + + if (i != addressRangeInfos.begin()) + { + auto prev = std::prev(i); + + if (prev->first + prev->second.size > start) + { + AddressRangeInfo newRange = prev->second.split(start - prev->first); + + addressRangeInfos.insert(std::make_pair(start, newRange)); + + i = std::next(prev); + + assert(i->first == start); + } + } + + if (i != e) + { + auto last = std::prev(e); + + assert (last->first < start + size); + + if (last->first + last->second.size > start + size) + { + AddressRangeInfo newRange = last->second.split(start + size - last->first); + + addressRangeInfos.insert(std::make_pair(start + size, newRange)); + + e = std::next(last); + + assert(last->first + last->second.size == start + size); + } + } + + addressRangeInfos.erase(i, e); +} + +void AccumulatedTraceData::combineContiguousSimilarRanges() +{ + for (auto i = addressRangeInfos.begin (); i != addressRangeInfos.end(); ++i) + { + assert(i->first == i->second.start); + + auto j = std::next (i); + + assert (j == addressRangeInfos.end() + || i->second.start + i->second.size <= j->first); + + while (j != addressRangeInfos.end() + && i->first + i->second.size == j->first) + { + if (i->second.combineIfSimilar(j->second)) + { + addressRangeInfos.erase(j); + + j = std::next (i); + } + else + { + break; + } + } + } +} diff --git a/src/analyze/accumulatedtracedata.h b/src/analyze/accumulatedtracedata.h index 92ef69b..2024ec6 100644 --- a/src/analyze/accumulatedtracedata.h +++ b/src/analyze/accumulatedtracedata.h @@ -97,6 +97,139 @@ struct AllocationInfo } }; +/** + * Information for an virtual address range (usually created through mmap) + */ +struct AddressRangeInfo +{ + AddressRangeInfo(uint64_t vStart, uint64_t vSize) + : start(vStart), size(vSize), isProtSet(false), isPhysicalMemoryConsumptionSet(false), isFdSet(false) + { + assert(!traceIndex); + } + + mutable uint64_t start; + mutable uint64_t size; + + mutable double privateDirty; + mutable double privateClean; + mutable double sharedDirty; + mutable double sharedClean; + + mutable TraceIndex traceIndex; // allocation call site information (false means unknown call site) + + mutable int prot; + mutable int fd; + + mutable bool isProtSet; + mutable bool isPhysicalMemoryConsumptionSet; + mutable bool isFdSet; + + bool combineIfSimilar(const AddressRangeInfo &other) + { + assert (start + size == other.start); + + if ((isProtSet && !other.isProtSet) || (!isProtSet && other.isProtSet)) + return false; + + if ((isPhysicalMemoryConsumptionSet && !other.isPhysicalMemoryConsumptionSet) + || (!isPhysicalMemoryConsumptionSet && other.isPhysicalMemoryConsumptionSet)) + return false; + + if ((isFdSet && !other.isFdSet) || (!isFdSet && other.isFdSet)) + return false; + + if (isProtSet && prot != other.prot) + return false; + + if (isFdSet && fd != other.fd) + return false; + + if (traceIndex.index != other.traceIndex.index) + return false; + + size += other.size; + + if (isPhysicalMemoryConsumptionSet) + { + privateDirty += other.privateDirty; + privateClean += other.privateClean; + sharedDirty += other.sharedDirty; + sharedClean += other.sharedClean; + } + + return true; + } + + AddressRangeInfo split(uint64_t splitSize) + { + assert (splitSize < size); + + AddressRangeInfo newRange = *this; + + uint64_t initialSize = size; + + size = splitSize; + newRange.start = start + splitSize; + newRange.size = initialSize - splitSize; + + if (isPhysicalMemoryConsumptionSet) + { + double ratio = double(splitSize) / double(initialSize); + + privateDirty *= ratio; + privateClean *= ratio; + sharedDirty *= ratio; + sharedClean *= ratio; + + newRange.privateDirty -= privateDirty; + newRange.privateClean -= privateClean; + newRange.sharedDirty -= sharedDirty; + newRange.sharedClean -= sharedClean; + } + + return newRange; + } + + void setTraceIndex(int v) + { + traceIndex.index = v; + } + + void setProt(int v) + { + prot = v; + isProtSet = true; + } + + void setFd(int v) + { + fd = v; + isFdSet = true; + } + + void setPhysicalMemoryConsumption(uint64_t smapsRangeSize, + uint64_t vPrivateDirty, + uint64_t vPrivateClean, + uint64_t vSharedDirty, + uint64_t vSharedClean) + { + assert(size <= smapsRangeSize); + + double ratio = double(size) / double(smapsRangeSize); + + privateDirty = ratio * vPrivateDirty; + privateClean = ratio * vPrivateClean; + sharedDirty = ratio * vSharedDirty; + sharedClean = ratio * vSharedClean; + + isPhysicalMemoryConsumptionSet = true; + } +}; + +typedef std::map AddressRangesMap; +typedef std::pair AddressRangesMapIteratorPair; + struct AccumulatedTraceData { AccumulatedTraceData(); @@ -169,6 +302,11 @@ struct AccumulatedTraceData bool isStopIndex(const StringIndex index) const; + AddressRangesMapIteratorPair mapUpdateRange(const uint64_t start, const uint64_t size); + + void mapRemoveRanges(const uint64_t start, const uint64_t size); + void combineContiguousSimilarRanges(); + // indices of functions that should stop the backtrace, e.g. main or static // initialization std::vector stopIndices; @@ -178,6 +316,8 @@ struct AccumulatedTraceData std::vector opNewIpIndices; std::vector allocationInfos; + + AddressRangesMap addressRangeInfos; }; #endif // ACCUMULATEDTRACEDATA_H -- 2.7.4