Process mmap/munmap and smaps trace items in analyzer.
authorRuben Ayrapetyan <r.ayrapetyan@samsung.com>
Thu, 14 Sep 2017 13:29:33 +0000 (16:29 +0300)
committerRuben Ayrapetyan <r.ayrapetyan@samsung.com>
Thu, 14 Sep 2017 13:16:47 +0000 (16:16 +0300)
Maintain address ranges information in analyzer.

src/analyze/accumulatedtracedata.cpp
src/analyze/accumulatedtracedata.h

index 063e0ef..6b9dd59 100644 (file)
@@ -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;
+            }
+        }
+    }
+}
index 92ef69b..2024ec6 100644 (file)
@@ -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<uint64_t, AddressRangeInfo> AddressRangesMap;
+typedef std::pair<AddressRangesMap::iterator, AddressRangesMap::iterator> 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<StringIndex> stopIndices;
@@ -178,6 +316,8 @@ struct AccumulatedTraceData
     std::vector<IpIndex> opNewIpIndices;
 
     std::vector<AllocationInfo> allocationInfos;
+
+    AddressRangesMap addressRangeInfos;
 };
 
 #endif // ACCUMULATEDTRACEDATA_H