Reuse PointerMap for backwards compatibility.
authorMilian Wolff <mail@milianw.de>
Tue, 15 Dec 2015 17:34:30 +0000 (18:34 +0100)
committerMilian Wolff <mail@milianw.de>
Tue, 15 Dec 2015 17:34:30 +0000 (18:34 +0100)
This also makes the size histogram work with older data files.

accumulatedtracedata.cpp
accumulatedtracedata.h
gui/parser.cpp
heaptrack_interpret.cpp
heaptrack_print.cpp
pointermap.h

index e7ac38d..0b452af 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "linereader.h"
 #include "config.h"
+#include "pointermap.h"
 
 using namespace std;
 
@@ -56,7 +57,6 @@ AccumulatedTraceData::AccumulatedTraceData()
     traces.reserve(65536);
     strings.reserve(4096);
     allocations.reserve(16384);
-    activeSmallAllocations.reserve(65536);
     stopIndices.reserve(4);
     opNewIpIndices.reserve(16);
 }
@@ -157,9 +157,14 @@ bool AccumulatedTraceData::read(istream& in)
     peak = 0;
     leaked = 0;
     allocations.clear();
-    uint64_t lastAllocationPtr = 0;
     uint fileVersion = 0;
 
+    // required for backwards compatibility
+    // newer versions handle this in heaptrack_interpret already
+    AllocationInfoSet allocationInfoSet;
+    PointerMap pointers;
+    uint64_t lastAllocationPtr = 0;
+
     while (reader.getLine(in)) {
         if (reader.mode() == 's') {
             if (reparsing) {
@@ -209,7 +214,7 @@ bool AccumulatedTraceData::read(istream& in)
                 opNewIpIndices.push_back(index);
             }
         } else if (reader.mode() == '+') {
-            BigAllocationInfo info;
+            AllocationInfo info;
             AllocationIndex allocationIndex;
             if (fileVersion >= 0x010000) {
                 if (!(reader >> allocationIndex.index)) {
@@ -217,21 +222,16 @@ bool AccumulatedTraceData::read(istream& in)
                     continue;
                 }
                 info = allocationInfos[allocationIndex.index];
-            } else {
-                // TODO: allocationInfoIndex
+            } else { // backwards compatibility
                 uint64_t ptr = 0;
                 if (!(reader >> info.size) || !(reader >> info.traceIndex) || !(reader >> ptr)) {
                     cerr << "failed to parse line: " << reader.line() << endl;
                     continue;
                 }
-
-                if (info.size <= std::numeric_limits<uint32_t>::max()) {
-                    // save memory by storing this allocation in the list of small allocations
-                    activeSmallAllocations[ptr] = {static_cast<uint32_t>(info.size), info.traceIndex};
-                } else {
-                    // these rare allocations consume more memory to track, but that's fine
-                    activeBigAllocations[ptr] = info;
+                if (allocationInfoSet.add(info.size, info.traceIndex, &allocationIndex)) {
+                    allocationInfos.push_back(info);
                 }
+                pointers.addPointer(ptr, allocationIndex);
                 lastAllocationPtr = ptr;
             }
 
@@ -252,33 +252,33 @@ bool AccumulatedTraceData::read(istream& in)
 
             handleAllocation(info, allocationIndex);
         } else if (reader.mode() == '-') {
-            BigAllocationInfo info;
+            AllocationIndex allocationInfoIndex;
             bool temporary = false;
             if (fileVersion >= 0x010000) {
-                uint32_t allocationInfoIndex = 0;
-                if (!(reader >> allocationInfoIndex)) {
+                if (!(reader >> allocationInfoIndex.index)) {
                     cerr << "failed to parse line: " << reader.line() << endl;
                     continue;
                 }
-                info = allocationInfos[allocationInfoIndex];
                 // optional, and thus may fail.
                 // but that's OK since we default to false anyways
                 reader >> temporary;
-            } else {
+            } else { // backwards compatibility
                 uint64_t ptr = 0;
                 if (!(reader >> ptr)) {
                     cerr << "failed to parse line: " << reader.line() << endl;
                     continue;
                 }
-                info = takeActiveAllocation(ptr);
-                if (!info.traceIndex) {
+                auto taken = pointers.takePointer(ptr);
+                if (!taken.second) {
                     // happens when we attached to a running application
                     continue;
                 }
+                allocationInfoIndex = taken.first;
                 temporary = lastAllocationPtr == ptr;
                 lastAllocationPtr = 0;
             }
 
+            const auto& info = allocationInfos[allocationInfoIndex.index];
             auto& allocation = findAllocation(info.traceIndex);
             if (!allocation.allocations || allocation.leaked < info.size) {
                 if (!fromAttached) {
@@ -295,7 +295,7 @@ bool AccumulatedTraceData::read(istream& in)
                 ++totalTemporary;
             }
         } else if (reader.mode() == 'a') {
-            BigAllocationInfo info;
+            AllocationInfo info;
             if (!(reader >> info.size) || !(reader >> info.traceIndex)) {
                 cerr << "failed to parse line: " << reader.line() << endl;
                 continue;
@@ -330,10 +330,6 @@ bool AccumulatedTraceData::read(istream& in)
         }
     }
 
-    /// these are leaks, but we now have the same data in \c allocations as well
-    activeSmallAllocations.clear();
-    activeBigAllocations.clear();
-
     if (!reparsing) {
         totalTime = timeStamp + 1;
     }
@@ -389,24 +385,3 @@ bool AccumulatedTraceData::isStopIndex(const StringIndex index) const
 {
     return find(stopIndices.begin(), stopIndices.end(), index) != stopIndices.end();
 }
-
-// only used for backwards compatibility with older data files
-// newer files do this in heaptrack_interpret already with some more magic
-BigAllocationInfo AccumulatedTraceData::takeActiveAllocation(uint64_t ptr)
-{
-    auto small = activeSmallAllocations.find(ptr);
-    if (small != activeSmallAllocations.end()) {
-        auto ret = small->second;
-        activeSmallAllocations.erase(small);
-        return {ret.size, ret.traceIndex};
-    }
-    // rare
-    auto big = activeBigAllocations.find(ptr);
-    if (big != activeBigAllocations.end()) {
-        auto ret = big->second;
-        activeBigAllocations.erase(big);
-        return ret;
-    }
-    // happens esp. when we runtime-attached
-    return {};
-}
index 1b34be0..b3c6c88 100644 (file)
@@ -90,30 +90,15 @@ struct MergedAllocation : public AllocationData
     IpIndex ipIndex;
 };
 
-/**
- * Information for a single call to an allocation function for small allocations.
- *
- * The split between small and big allocations is done to save memory. Most of
- * the time apps will only do small allocations, and tons of them. With this
- * split we can reduce the memory footprint of the active allocation tracker
- * below by a factor of 2. This is especially notable for apps that do tons
- * of small allocations and don't free them. A notable example for such an
- * application is heaptrack_print/heaptrack_gui itself!
- */
-struct SmallAllocationInfo
-{
-    uint32_t size;
-    TraceIndex traceIndex;
-};
 
 /**
- * Information for a single call to an allocation function for big allocations.
+ * Information for a single call to an allocation function.
  */
-struct BigAllocationInfo
+struct AllocationInfo
 {
     uint64_t size;
     TraceIndex traceIndex;
-    bool operator==(const BigAllocationInfo& rhs) const
+    bool operator==(const AllocationInfo& rhs) const
     {
         return rhs.traceIndex == traceIndex && rhs.size == size;
     }
@@ -125,7 +110,7 @@ struct AccumulatedTraceData
     virtual ~AccumulatedTraceData() = default;
 
     virtual void handleTimeStamp(uint64_t oldStamp, uint64_t newStamp) = 0;
-    virtual void handleAllocation(const BigAllocationInfo& info, const AllocationIndex index) = 0;
+    virtual void handleAllocation(const AllocationInfo& info, const AllocationIndex index) = 0;
     virtual void handleDebuggee(const char* command) = 0;
 
     const std::string& stringify(const StringIndex stringId) const;
@@ -159,8 +144,6 @@ struct AccumulatedTraceData
 
     bool isStopIndex(const StringIndex index) const;
 
-    BigAllocationInfo takeActiveAllocation(uint64_t ptr);
-
     // indices of functions that should stop the backtrace, e.g. main or static initialization
     std::vector<StringIndex> stopIndices;
     std::vector<InstructionPointer> instructionPointers;
@@ -168,11 +151,7 @@ struct AccumulatedTraceData
     std::vector<std::string> strings;
     std::vector<IpIndex> opNewIpIndices;
 
-    std::vector<BigAllocationInfo> allocationInfos;
-
-    // backwards compatibility
-    std::unordered_map<uint64_t, SmallAllocationInfo> activeSmallAllocations;
-    std::unordered_map<uint64_t, BigAllocationInfo> activeBigAllocations;
+    std::vector<AllocationInfo> allocationInfos;
 };
 
 #endif // ACCUMULATEDTRACEDATA_H
index 52fdad9..c12de77 100644 (file)
@@ -240,7 +240,7 @@ struct ParserData final : public AccumulatedTraceData
         temporaryChartData.rows << temporary;
     }
 
-    void handleAllocation(const BigAllocationInfo& info, const AllocationIndex index)
+    void handleAllocation(const AllocationInfo& info, const AllocationIndex index)
     {
         maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
 
@@ -260,7 +260,7 @@ struct ParserData final : public AccumulatedTraceData
 
     struct CountedAllocationInfo
     {
-        BigAllocationInfo info;
+        AllocationInfo info;
         uint64_t allocations;
         bool operator<(const CountedAllocationInfo& rhs) const
         {
index 96002d1..3e289fd 100644 (file)
@@ -26,7 +26,6 @@
 #include <iostream>
 #include <sstream>
 #include <unordered_map>
-#include <unordered_set>
 #include <vector>
 #include <tuple>
 #include <algorithm>
@@ -35,7 +34,6 @@
 #include <cxxabi.h>
 
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/functional/hash.hpp>
 
 #include "libbacktrace/backtrace.h"
 #include "libbacktrace/internal.h"
@@ -313,36 +311,6 @@ private:
     unordered_map<uintptr_t, size_t> m_encounteredIps;
 };
 
-/**
- * Information for a single call to an allocation function for big allocations.
- */
-struct AllocationInfo
-{
-    uint64_t size;
-    TraceIndex traceIndex;
-    AllocationIndex allocationInfoIndex;
-    bool operator==(const AllocationInfo& rhs) const
-    {
-        return rhs.traceIndex == traceIndex
-            && rhs.size == size;
-            // allocationInfoIndex not compared to allow to look it up
-    }
-};
-
-}
-
-namespace std {
-template<>
-struct hash<AllocationInfo> {
-    size_t operator()(const AllocationInfo &info) const
-    {
-        size_t seed = 0;
-        boost::hash_combine(seed, info.size);
-        boost::hash_combine(seed, info.traceIndex.index);
-        // allocationInfoIndex not hashed to allow to look it up
-        return seed;
-    }
-};
 }
 
 int main(int /*argc*/, char** /*argv*/)
@@ -358,10 +326,9 @@ int main(int /*argc*/, char** /*argv*/)
 
     string exe;
 
-    unordered_set<AllocationInfo> allocationInfos;
     PointerMap ptrToIndex;
     uint64_t lastPtr = 0;
-    allocationInfos.reserve(625000);
+    AllocationInfoSet allocationInfos;
 
     while (reader.getLine(cin)) {
         if (reader.mode() == 'x') {
@@ -410,18 +377,12 @@ int main(int /*argc*/, char** /*argv*/)
                 continue;
             }
             AllocationIndex index;
-            index.index = allocationInfos.size();
-            AllocationInfo info = {size, traceId, index};
-            auto it = allocationInfos.find(info);
-            if (it != allocationInfos.end()) {
-                info = *it;
-            } else {
-                allocationInfos.insert(it, info);
-                fprintf(stdout, "a %zx %x\n", info.size, info.traceIndex.index);
+            if (allocationInfos.add(size, traceId, &index)) {
+                fprintf(stdout, "a %zx %x\n", size, traceId.index);
             }
-            ptrToIndex.addPointer(ptr, info.allocationInfoIndex);
+            ptrToIndex.addPointer(ptr, index);
             lastPtr = ptr;
-            fprintf(stdout, "+ %x\n", info.allocationInfoIndex.index);
+            fprintf(stdout, "+ %x\n", index.index);
         } else if (reader.mode() == '-') {
             uint64_t ptr = 0;
             if (!(reader >> ptr)) {
index 5e6d0e5..b942ba8 100644 (file)
@@ -430,7 +430,7 @@ struct Printer final : public AccumulatedTraceData
         }
     }
 
-    void handleAllocation(const BigAllocationInfo& info, const AllocationIndex /*index*/) override
+    void handleAllocation(const AllocationInfo& info, const AllocationIndex /*index*/) override
     {
         if (printHistogram) {
             ++sizeHistogram[info.size];
index ab6b62b..30eb395 100644 (file)
 
 #include <vector>
 #include <unordered_map>
+#include <unordered_set>
 #include <map>
 #include <limits>
 #include <iostream>
 #include <algorithm>
 
+#include <boost/functional/hash.hpp>
+
 #include "indices.h"
 
 /**
+ * Information for a single call to an allocation function for big allocations.
+ */
+struct IndexedAllocationInfo
+{
+    uint64_t size;
+    TraceIndex traceIndex;
+    AllocationIndex allocationIndex;
+    bool operator==(const IndexedAllocationInfo& rhs) const
+    {
+        return rhs.traceIndex == traceIndex
+            && rhs.size == size;
+            // allocationInfoIndex not compared to allow to look it up
+    }
+};
+
+namespace std {
+template<>
+struct hash<IndexedAllocationInfo> {
+    size_t operator()(const IndexedAllocationInfo &info) const
+    {
+        size_t seed = 0;
+        boost::hash_combine(seed, info.size);
+        boost::hash_combine(seed, info.traceIndex.index);
+        // allocationInfoIndex not hashed to allow to look it up
+        return seed;
+    }
+};
+}
+
+struct AllocationInfoSet
+{
+    AllocationInfoSet()
+    {
+        set.reserve(625000);
+    }
+
+    bool add(uint64_t size, TraceIndex traceIndex, AllocationIndex* allocationIndex)
+    {
+        allocationIndex->index = set.size();
+        IndexedAllocationInfo info = {size, traceIndex, *allocationIndex};
+        auto it = set.find(info);
+        if (it != set.end()) {
+            *allocationIndex = it->allocationIndex;
+            return false;
+        } else {
+            set.insert(it, info);
+            return true;
+        }
+    }
+    std::unordered_set<IndexedAllocationInfo> set;
+};
+
+/**
  * A low-memory-overhead map of 64bit pointer addresses to 32bit allocation indices.
  *
  * We leverage the fact that pointers are allocated in pages, i.e. close to each