diff options
author | Christopher Ferris <cferris@google.com> | 2020-02-02 10:03:39 -0800 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2020-02-03 13:50:44 -0800 |
commit | b846e6e2013dec36f1bf37ceaa0aa333fbbf5f64 (patch) | |
tree | 87764736b710a7418abf2cbb15ba6dfb30b48cf4 | |
parent | 18f8a1a981a5aeac889b80f52e83e171d9db3781 (diff) | |
download | extras-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.cpp | 196 |
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 \ |