summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2020-02-02 10:03:39 -0800
committerChristopher Ferris <cferris@google.com>2020-02-03 13:50:44 -0800
commitb846e6e2013dec36f1bf37ceaa0aa333fbbf5f64 (patch)
tree87764736b710a7418abf2cbb15ba6dfb30b48cf4
parent18f8a1a981a5aeac889b80f52e83e171d9db3781 (diff)
downloadextras-b846e6e2013dec36f1bf37ceaa0aa333fbbf5f64.tar.gz
Update the code to avoid memory issues.
Instead of creating a different structure for running the trace, re-use the AllocEntry structure. This avoids having to allocate a whole new copy of the allocation entries, especially, for really long traces. Test: Ran traces. Change-Id: I74b5140823f36da511694fbb05425c3b86f0c7cd
-rw-r--r--memory_replay/TraceBenchmark.cpp196
1 files changed, 97 insertions, 99 deletions
diff --git a/memory_replay/TraceBenchmark.cpp b/memory_replay/TraceBenchmark.cpp
index 21fd7298..fc393f09 100644
--- a/memory_replay/TraceBenchmark.cpp
+++ b/memory_replay/TraceBenchmark.cpp
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <unistd.h>
#include <algorithm>
@@ -38,19 +39,11 @@
#include "File.h"
#include "Utils.h"
-struct TraceAllocEntry {
- TraceAllocEntry(AllocEnum type, size_t idx, size_t size, size_t last_arg)
- : type(type), idx(idx), size(size) {
- u.old_idx = last_arg;
- }
- AllocEnum type;
- size_t idx;
- size_t size;
- union {
- size_t old_idx = 0;
- size_t align;
- size_t n_elements;
- } u;
+struct TraceDataType {
+ AllocEntry* entries = nullptr;
+ size_t num_entries = 0;
+ void** ptrs = nullptr;
+ size_t num_ptrs = 0;
};
static size_t GetIndex(std::stack<size_t>& free_indices, size_t* max_index) {
@@ -62,103 +55,108 @@ static size_t GetIndex(std::stack<size_t>& free_indices, size_t* max_index) {
return index;
}
-static std::vector<TraceAllocEntry>* GetTraceData(const char* filename, size_t* max_ptrs) {
- // Only keep last trace encountered cached.
- static std::string cached_filename;
- static std::vector<TraceAllocEntry> cached_entries;
- static size_t cached_max_ptrs;
+static void FreePtrs(TraceDataType* trace_data) {
+ for (size_t i = 0; i < trace_data->num_ptrs; i++) {
+ void* ptr = trace_data->ptrs[i];
+ if (ptr != nullptr) {
+ free(ptr);
+ trace_data->ptrs[i] = nullptr;
+ }
+ }
+}
- if (cached_filename == filename) {
- *max_ptrs = cached_max_ptrs;
- return &cached_entries;
+static void FreeTraceData(TraceDataType* trace_data) {
+ if (trace_data->ptrs == nullptr) {
+ return;
}
- cached_entries.clear();
- cached_max_ptrs = 0;
- cached_filename = filename;
+ munmap(trace_data->ptrs, sizeof(void*) * trace_data->num_ptrs);
+ FreeEntries(trace_data->entries, trace_data->num_entries);
+}
- std::string content(ZipGetContents(filename));
- if (content.empty()) {
- errx(1, "Internal Error: Empty zip file %s", filename);
+static void GetTraceData(const std::string& filename, TraceDataType* trace_data) {
+ // Only keep last trace encountered cached.
+ static std::string cached_filename;
+ static TraceDataType cached_trace_data;
+ if (cached_filename == filename) {
+ *trace_data = cached_trace_data;
+ return;
+ } else {
+ FreeTraceData(&cached_trace_data);
}
- std::vector<std::string> lines(android::base::Split(content, "\n"));
- *max_ptrs = 0;
+ cached_filename = filename;
+ GetUnwindInfo(filename.c_str(), &trace_data->entries, &trace_data->num_entries);
+
+ // This loop will convert the ptr field into an index into the ptrs array.
+ // Creating this index allows the trace run to quickly store or retrieve the
+ // allocation.
+ // For free, the ptr field will be index + one, where a zero represents
+ // a free(nullptr) call.
+ // For realloc, the old_pointer field will be index + one, where a zero
+ // represents a realloc(nullptr, XX).
+ trace_data->num_ptrs = 0;
std::stack<size_t> free_indices;
std::unordered_map<uint64_t, size_t> ptr_to_index;
- std::vector<TraceAllocEntry>* entries = &cached_entries;
- for (const std::string& line : lines) {
- if (line.empty()) {
- continue;
- }
- AllocEntry entry;
- AllocGetData(line, &entry);
-
- switch (entry.type) {
- case MALLOC: {
- size_t idx = GetIndex(free_indices, max_ptrs);
- ptr_to_index[entry.ptr] = idx;
- entries->emplace_back(MALLOC, idx, entry.size, 0);
- break;
- }
- case CALLOC: {
- size_t idx = GetIndex(free_indices, max_ptrs);
- ptr_to_index[entry.ptr] = idx;
- entries->emplace_back(CALLOC, idx, entry.u.n_elements, entry.size);
- break;
- }
+ for (size_t i = 0; i < trace_data->num_entries; i++) {
+ AllocEntry* entry = &trace_data->entries[i];
+ switch (entry->type) {
+ case MALLOC:
+ case CALLOC:
case MEMALIGN: {
- size_t idx = GetIndex(free_indices, max_ptrs);
- ptr_to_index[entry.ptr] = idx;
- entries->emplace_back(MEMALIGN, idx, entry.size, entry.u.align);
+ size_t idx = GetIndex(free_indices, &trace_data->num_ptrs);
+ ptr_to_index[entry->ptr] = idx;
+ entry->ptr = idx;
break;
}
case REALLOC: {
- size_t old_pointer_idx = 0;
- if (entry.u.old_ptr != 0) {
- auto idx_entry = ptr_to_index.find(entry.u.old_ptr);
+ if (entry->u.old_ptr != 0) {
+ auto idx_entry = ptr_to_index.find(entry->u.old_ptr);
if (idx_entry == ptr_to_index.end()) {
- errx(1, "File Error: Failed to find realloc pointer %" PRIu64, entry.u.old_ptr);
+ errx(1, "File Error: Failed to find realloc pointer %" PRIx64, entry->u.old_ptr);
}
- old_pointer_idx = idx_entry->second;
+ size_t old_pointer_idx = idx_entry->second;
free_indices.push(old_pointer_idx);
+ ptr_to_index.erase(idx_entry);
+ entry->u.old_ptr = old_pointer_idx + 1;
}
- size_t idx = GetIndex(free_indices, max_ptrs);
- ptr_to_index[entry.ptr] = idx;
- entries->emplace_back(REALLOC, idx, entry.size, old_pointer_idx + 1);
+ size_t idx = GetIndex(free_indices, &trace_data->num_ptrs);
+ ptr_to_index[entry->ptr] = idx;
+ entry->ptr = idx;
break;
}
case FREE:
- if (entry.ptr != 0) {
- auto idx_entry = ptr_to_index.find(entry.ptr);
+ if (entry->ptr != 0) {
+ auto idx_entry = ptr_to_index.find(entry->ptr);
if (idx_entry == ptr_to_index.end()) {
- errx(1, "File Error: Unable to find free pointer %" PRIu64, entry.ptr);
+ errx(1, "File Error: Unable to find free pointer %" PRIx64, entry->ptr);
}
free_indices.push(idx_entry->second);
- entries->emplace_back(FREE, idx_entry->second + 1, 0, 0);
- } else {
- entries->emplace_back(FREE, 0, 0, 0);
+ ptr_to_index.erase(idx_entry);
+ entry->ptr = idx_entry->second + 1;
}
break;
case THREAD_DONE:
- // Ignore these.
break;
}
}
+ void* map = mmap(nullptr, sizeof(void*) * trace_data->num_ptrs, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (map == MAP_FAILED) {
+ err(1, "mmap failed\n");
+ }
+ trace_data->ptrs = reinterpret_cast<void**>(map);
- cached_max_ptrs = *max_ptrs;
- return entries;
+ cached_trace_data = *trace_data;
}
-static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entries,
- size_t max_ptrs) {
- std::vector<void*> ptrs(max_ptrs, nullptr);
-
+static void RunTrace(benchmark::State& state, TraceDataType* trace_data) {
int pagesize = getpagesize();
- void* ptr;
uint64_t total_ns = 0;
uint64_t start_ns;
- for (auto& entry : entries) {
+ void** ptrs = trace_data->ptrs;
+ for (size_t i = 0; i < trace_data->num_entries; i++) {
+ void* ptr;
+ const AllocEntry& entry = trace_data->entries[i];
switch (entry.type) {
case MALLOC:
start_ns = Nanotime();
@@ -169,10 +167,10 @@ static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entr
MakeAllocationResident(ptr, entry.size, pagesize);
total_ns += Nanotime() - start_ns;
- if (ptrs[entry.idx] != nullptr) {
+ if (ptrs[entry.ptr] != nullptr) {
errx(1, "Internal Error: malloc pointer being replaced is not nullptr");
}
- ptrs[entry.idx] = ptr;
+ ptrs[entry.ptr] = ptr;
break;
case CALLOC:
@@ -184,10 +182,10 @@ static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entr
MakeAllocationResident(ptr, entry.size, pagesize);
total_ns += Nanotime() - start_ns;
- if (ptrs[entry.idx] != nullptr) {
+ if (ptrs[entry.ptr] != nullptr) {
errx(1, "Internal Error: calloc pointer being replaced is not nullptr");
}
- ptrs[entry.idx] = ptr;
+ ptrs[entry.ptr] = ptr;
break;
case MEMALIGN:
@@ -199,19 +197,19 @@ static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entr
MakeAllocationResident(ptr, entry.size, pagesize);
total_ns += Nanotime() - start_ns;
- if (ptrs[entry.idx] != nullptr) {
+ if (ptrs[entry.ptr] != nullptr) {
errx(1, "Internal Error: memalign pointer being replaced is not nullptr");
}
- ptrs[entry.idx] = ptr;
+ ptrs[entry.ptr] = ptr;
break;
case REALLOC:
start_ns = Nanotime();
- if (entry.u.old_idx == 0) {
+ if (entry.u.old_ptr == 0) {
ptr = realloc(nullptr, entry.size);
} else {
- ptr = realloc(ptrs[entry.u.old_idx - 1], entry.size);
- ptrs[entry.u.old_idx - 1] = nullptr;
+ ptr = realloc(ptrs[entry.u.old_ptr - 1], entry.size);
+ ptrs[entry.u.old_ptr - 1] = nullptr;
}
if (entry.size > 0) {
if (ptr == nullptr) {
@@ -221,16 +219,16 @@ static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entr
}
total_ns += Nanotime() - start_ns;
- if (ptrs[entry.idx] != nullptr) {
+ if (ptrs[entry.ptr] != nullptr) {
errx(1, "Internal Error: realloc pointer being replaced is not nullptr");
}
- ptrs[entry.idx] = ptr;
+ ptrs[entry.ptr] = ptr;
break;
case FREE:
- if (entry.idx != 0) {
- ptr = ptrs[entry.idx - 1];
- ptrs[entry.idx - 1] = nullptr;
+ if (entry.ptr != 0) {
+ ptr = ptrs[entry.ptr - 1];
+ ptrs[entry.ptr - 1] = nullptr;
} else {
ptr = nullptr;
}
@@ -245,20 +243,13 @@ static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entr
}
state.SetIterationTime(total_ns / double(1000000000.0));
- std::for_each(ptrs.begin(), ptrs.end(), [](void* ptr) { free(ptr); });
+ FreePtrs(trace_data);
}
// Run a trace as if all of the allocations occurred in a single thread.
// This is not completely realistic, but it is a possible worst case that
// could happen in an app.
static void BenchmarkTrace(benchmark::State& state, const char* filename, bool enable_decay_time) {
- std::string full_filename(android::base::GetExecutableDirectory() + "/traces/" + filename);
- size_t max_ptrs;
- std::vector<TraceAllocEntry>* entries = GetTraceData(full_filename.c_str(), &max_ptrs);
- if (entries == nullptr) {
- errx(1, "ERROR: Failed to get trace data for %s.", full_filename.c_str());
- }
-
#if defined(__BIONIC__)
if (enable_decay_time) {
mallopt(M_DECAY_TIME, 1);
@@ -266,10 +257,17 @@ static void BenchmarkTrace(benchmark::State& state, const char* filename, bool e
mallopt(M_DECAY_TIME, 0);
}
#endif
+ std::string full_filename(android::base::GetExecutableDirectory() + "/traces/" + filename);
+
+ TraceDataType trace_data;
+ GetTraceData(full_filename, &trace_data);
for (auto _ : state) {
- RunTrace(state, *entries, max_ptrs);
+ RunTrace(state, &trace_data);
}
+
+ // Don't free the trace_data, it is cached. The last set of trace data
+ // will be leaked away.
}
#define BENCH_OPTIONS \