This also makes the size histogram work with older data files.
#include "linereader.h"
#include "config.h"
+#include "pointermap.h"
using namespace std;
traces.reserve(65536);
strings.reserve(4096);
allocations.reserve(16384);
- activeSmallAllocations.reserve(65536);
stopIndices.reserve(4);
opNewIpIndices.reserve(16);
}
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) {
opNewIpIndices.push_back(index);
}
} else if (reader.mode() == '+') {
- BigAllocationInfo info;
+ AllocationInfo info;
AllocationIndex allocationIndex;
if (fileVersion >= 0x010000) {
if (!(reader >> allocationIndex.index)) {
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;
}
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) {
++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;
}
}
- /// these are leaks, but we now have the same data in \c allocations as well
- activeSmallAllocations.clear();
- activeBigAllocations.clear();
-
if (!reparsing) {
totalTime = timeStamp + 1;
}
{
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 {};
-}
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;
}
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;
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;
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
temporaryChartData.rows << temporary;
}
- void handleAllocation(const BigAllocationInfo& info, const AllocationIndex index)
+ void handleAllocation(const AllocationInfo& info, const AllocationIndex index)
{
maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
struct CountedAllocationInfo
{
- BigAllocationInfo info;
+ AllocationInfo info;
uint64_t allocations;
bool operator<(const CountedAllocationInfo& rhs) const
{
#include <iostream>
#include <sstream>
#include <unordered_map>
-#include <unordered_set>
#include <vector>
#include <tuple>
#include <algorithm>
#include <cxxabi.h>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/functional/hash.hpp>
#include "libbacktrace/backtrace.h"
#include "libbacktrace/internal.h"
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*/)
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') {
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)) {
}
}
- void handleAllocation(const BigAllocationInfo& info, const AllocationIndex /*index*/) override
+ void handleAllocation(const AllocationInfo& info, const AllocationIndex /*index*/) override
{
if (printHistogram) {
++sizeHistogram[info.size];
#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