blob: 9ba42a84fd3d0af75f847ba703fb1d8cf5dad148 [file]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <err.h>
#include <inttypes.h>
#include <malloc.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stack>
#include <string>
#include <unordered_map>
#include <memory_trace/MemoryTrace.h>
#include <memory_trace/TraceInfo.h>
#include "File.h"
#include "Utils.h"
namespace memory_trace {
size_t TraceInfo::GetIndex(std::stack<size_t>& free_indices) {
if (free_indices.empty()) {
return num_ptrs_++;
}
size_t index = free_indices.top();
free_indices.pop();
return index;
}
void TraceInfo::Init(const std::string& filename) {
filename_ = filename;
GetUnwindInfo(filename_.c_str(), &entries_, &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).
num_ptrs_ = 0;
std::stack<size_t> free_indices;
std::unordered_map<uint64_t, size_t> ptr_to_index;
for (size_t i = 0; i < num_entries_; i++) {
memory_trace::Entry* entry = &entries_[i];
switch (entry->type) {
case memory_trace::MALLOC:
case memory_trace::CALLOC:
case memory_trace::MEMALIGN: {
size_t idx = GetIndex(free_indices);
ptr_to_index[entry->ptr] = idx;
entry->ptr = idx;
break;
}
case memory_trace::REALLOC: {
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 %" PRIx64, entry->u.old_ptr);
}
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);
ptr_to_index[entry->ptr] = idx;
entry->ptr = idx;
break;
}
case memory_trace::FREE:
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 %" PRIx64, entry->ptr);
}
free_indices.push(idx_entry->second);
entry->ptr = idx_entry->second + 1;
ptr_to_index.erase(idx_entry);
}
break;
case memory_trace::THREAD_DONE:
case memory_trace::UNKNOWN:
break;
}
}
void* map = mmap(nullptr, sizeof(void*) * num_ptrs_, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (map == MAP_FAILED) {
err(1, "mmap failed");
}
ptrs_ = reinterpret_cast<void**>(map);
}
void TraceInfo::Clear() {
if (ptrs_ != nullptr) {
munmap(ptrs_, sizeof(void*) * num_ptrs_);
}
if (entries_ != nullptr) {
FreeEntries(entries_, num_entries_);
}
}
uint64_t TraceInfo::RunSingleThread() {
int pagesize = getpagesize();
uint64_t total_ns = 0;
for (size_t i = 0; i < num_entries_; i++) {
void* ptr;
const memory_trace::Entry& entry = entries_[i];
uint64_t start_ns;
switch (entry.type) {
case memory_trace::MALLOC:
start_ns = Nanotime();
ptr = malloc(entry.size);
if (ptr == nullptr) {
errx(1, "malloc returned nullptr");
}
MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
total_ns += Nanotime() - start_ns;
if (ptrs_[entry.ptr] != nullptr) {
errx(1, "Internal Error: malloc pointer being replaced is not nullptr");
}
ptrs_[entry.ptr] = ptr;
break;
case memory_trace::CALLOC:
start_ns = Nanotime();
ptr = calloc(entry.u.n_elements, entry.size);
if (ptr == nullptr) {
errx(1, "calloc returned nullptr");
}
MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
total_ns += Nanotime() - start_ns;
if (ptrs_[entry.ptr] != nullptr) {
errx(1, "Internal Error: calloc pointer being replaced is not nullptr");
}
ptrs_[entry.ptr] = ptr;
break;
case memory_trace::MEMALIGN:
start_ns = Nanotime();
ptr = memalign(entry.u.align, entry.size);
if (ptr == nullptr) {
errx(1, "memalign returned nullptr");
}
MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
total_ns += Nanotime() - start_ns;
if (ptrs_[entry.ptr] != nullptr) {
errx(1, "Internal Error: memalign pointer being replaced is not nullptr");
}
ptrs_[entry.ptr] = ptr;
break;
case memory_trace::REALLOC:
start_ns = Nanotime();
if (entry.u.old_ptr == 0) {
ptr = realloc(nullptr, entry.size);
} else {
ptr = realloc(ptrs_[entry.u.old_ptr - 1], entry.size);
ptrs_[entry.u.old_ptr - 1] = nullptr;
}
if (entry.size > 0) {
if (ptr == nullptr) {
errx(1, "realloc returned nullptr");
}
MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
}
total_ns += Nanotime() - start_ns;
if (ptrs_[entry.ptr] != nullptr) {
errx(1, "Internal Error: realloc pointer being replaced is not nullptr");
}
ptrs_[entry.ptr] = ptr;
break;
case memory_trace::FREE:
if (entry.ptr != 0) {
ptr = ptrs_[entry.ptr - 1];
ptrs_[entry.ptr - 1] = nullptr;
} else {
ptr = nullptr;
}
start_ns = Nanotime();
free(ptr);
total_ns += Nanotime() - start_ns;
break;
case memory_trace::THREAD_DONE:
case memory_trace::UNKNOWN:
break;
}
}
for (size_t i = 0; i < num_ptrs_; i++) {
void* ptr = ptrs_[i];
if (ptr != nullptr) {
free(ptr);
ptrs_[i] = nullptr;
}
}
return total_ns;
}
} // namespace memory_trace