Merge "Add the timestamps for each alloc data" am: 08af69ab7f
Original change: https://android-review.googlesource.com/c/platform/bionic/+/2292945 Change-Id: Ia524fb82c657c29e984b42ea217d1be1aab7421a Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
bb3242aa02
4 changed files with 257 additions and 99 deletions
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
@ -52,40 +53,51 @@ bool ThreadCompleteEntry::Write(int fd) const {
|
|||
return dprintf(fd, "%d: thread_done 0x0\n", tid_) > 0;
|
||||
}
|
||||
|
||||
AllocEntry::AllocEntry(void* pointer) : pointer_(pointer) {}
|
||||
AllocEntry::AllocEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
|
||||
: pointer_(pointer), start_ns_(start_ns), end_ns_(end_ns) {}
|
||||
|
||||
MallocEntry::MallocEntry(void* pointer, size_t size) : AllocEntry(pointer), size_(size) {}
|
||||
MallocEntry::MallocEntry(void* pointer, size_t size, uint64_t start_ns, uint64_t end_ns)
|
||||
: AllocEntry(pointer, start_ns, end_ns), size_(size) {}
|
||||
|
||||
bool MallocEntry::Write(int fd) const {
|
||||
return dprintf(fd, "%d: malloc %p %zu\n", tid_, pointer_, size_) > 0;
|
||||
return dprintf(fd, "%d: malloc %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, size_,
|
||||
start_ns_, end_ns_) > 0;
|
||||
}
|
||||
|
||||
FreeEntry::FreeEntry(void* pointer) : AllocEntry(pointer) {}
|
||||
FreeEntry::FreeEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
|
||||
: AllocEntry(pointer, start_ns, end_ns) {}
|
||||
|
||||
bool FreeEntry::Write(int fd) const {
|
||||
return dprintf(fd, "%d: free %p\n", tid_, pointer_) > 0;
|
||||
return dprintf(fd, "%d: free %p %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, start_ns_, end_ns_) >
|
||||
0;
|
||||
}
|
||||
|
||||
CallocEntry::CallocEntry(void* pointer, size_t nmemb, size_t size)
|
||||
: MallocEntry(pointer, size), nmemb_(nmemb) {}
|
||||
CallocEntry::CallocEntry(void* pointer, size_t nmemb, size_t size, uint64_t start_ns,
|
||||
uint64_t end_ns)
|
||||
: MallocEntry(pointer, size, start_ns, end_ns), nmemb_(nmemb) {}
|
||||
|
||||
bool CallocEntry::Write(int fd) const {
|
||||
return dprintf(fd, "%d: calloc %p %zu %zu\n", tid_, pointer_, nmemb_, size_) > 0;
|
||||
return dprintf(fd, "%d: calloc %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, nmemb_,
|
||||
size_, start_ns_, end_ns_) > 0;
|
||||
}
|
||||
|
||||
ReallocEntry::ReallocEntry(void* pointer, size_t size, void* old_pointer)
|
||||
: MallocEntry(pointer, size), old_pointer_(old_pointer) {}
|
||||
ReallocEntry::ReallocEntry(void* pointer, size_t size, void* old_pointer, uint64_t start_ns,
|
||||
uint64_t end_ns)
|
||||
: MallocEntry(pointer, size, start_ns, end_ns), old_pointer_(old_pointer) {}
|
||||
|
||||
bool ReallocEntry::Write(int fd) const {
|
||||
return dprintf(fd, "%d: realloc %p %p %zu\n", tid_, pointer_, old_pointer_, size_) > 0;
|
||||
return dprintf(fd, "%d: realloc %p %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
|
||||
old_pointer_, size_, start_ns_, end_ns_) > 0;
|
||||
}
|
||||
|
||||
// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
|
||||
MemalignEntry::MemalignEntry(void* pointer, size_t size, size_t alignment)
|
||||
: MallocEntry(pointer, size), alignment_(alignment) {}
|
||||
MemalignEntry::MemalignEntry(void* pointer, size_t size, size_t alignment, uint64_t start_ns,
|
||||
uint64_t end_ns)
|
||||
: MallocEntry(pointer, size, start_ns, end_ns), alignment_(alignment) {}
|
||||
|
||||
bool MemalignEntry::Write(int fd) const {
|
||||
return dprintf(fd, "%d: memalign %p %zu %zu\n", tid_, pointer_, alignment_, size_) > 0;
|
||||
return dprintf(fd, "%d: memalign %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
|
||||
alignment_, size_, start_ns_, end_ns_) > 0;
|
||||
}
|
||||
|
||||
struct ThreadData {
|
||||
|
|
|
@ -68,19 +68,23 @@ class ThreadCompleteEntry : public RecordEntry {
|
|||
|
||||
class AllocEntry : public RecordEntry {
|
||||
public:
|
||||
explicit AllocEntry(void* pointer);
|
||||
explicit AllocEntry(void* pointer, uint64_t st, uint64_t et);
|
||||
virtual ~AllocEntry() = default;
|
||||
|
||||
protected:
|
||||
void* pointer_;
|
||||
|
||||
// The start/end time of this operation.
|
||||
uint64_t start_ns_;
|
||||
uint64_t end_ns_;
|
||||
|
||||
private:
|
||||
BIONIC_DISALLOW_COPY_AND_ASSIGN(AllocEntry);
|
||||
};
|
||||
|
||||
class MallocEntry : public AllocEntry {
|
||||
public:
|
||||
MallocEntry(void* pointer, size_t size);
|
||||
MallocEntry(void* pointer, size_t size, uint64_t st, uint64_t et);
|
||||
virtual ~MallocEntry() = default;
|
||||
|
||||
bool Write(int fd) const override;
|
||||
|
@ -94,7 +98,7 @@ class MallocEntry : public AllocEntry {
|
|||
|
||||
class FreeEntry : public AllocEntry {
|
||||
public:
|
||||
explicit FreeEntry(void* pointer);
|
||||
explicit FreeEntry(void* pointer, uint64_t st, uint64_t et);
|
||||
virtual ~FreeEntry() = default;
|
||||
|
||||
bool Write(int fd) const override;
|
||||
|
@ -105,7 +109,7 @@ class FreeEntry : public AllocEntry {
|
|||
|
||||
class CallocEntry : public MallocEntry {
|
||||
public:
|
||||
CallocEntry(void* pointer, size_t size, size_t nmemb);
|
||||
CallocEntry(void* pointer, size_t size, size_t nmemb, uint64_t st, uint64_t et);
|
||||
virtual ~CallocEntry() = default;
|
||||
|
||||
bool Write(int fd) const override;
|
||||
|
@ -119,7 +123,7 @@ class CallocEntry : public MallocEntry {
|
|||
|
||||
class ReallocEntry : public MallocEntry {
|
||||
public:
|
||||
ReallocEntry(void* pointer, size_t size, void* old_pointer);
|
||||
ReallocEntry(void* pointer, size_t size, void* old_pointer, uint64_t st, uint64_t et);
|
||||
virtual ~ReallocEntry() = default;
|
||||
|
||||
bool Write(int fd) const override;
|
||||
|
@ -134,7 +138,7 @@ class ReallocEntry : public MallocEntry {
|
|||
// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
|
||||
class MemalignEntry : public MallocEntry {
|
||||
public:
|
||||
MemalignEntry(void* pointer, size_t size, size_t alignment);
|
||||
MemalignEntry(void* pointer, size_t size, size_t alignment, uint64_t st, uint64_t et);
|
||||
virtual ~MemalignEntry() = default;
|
||||
|
||||
bool Write(int fd) const override;
|
||||
|
|
|
@ -68,6 +68,100 @@ DebugData* g_debug;
|
|||
bool* g_zygote_child;
|
||||
|
||||
const MallocDispatch* g_dispatch;
|
||||
|
||||
static __always_inline uint64_t Nanotime() {
|
||||
struct timespec t = {};
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A TimedResult contains the result of from malloc end_ns al. functions and the
|
||||
// start/end timestamps.
|
||||
struct TimedResult {
|
||||
uint64_t start_ns = 0;
|
||||
uint64_t end_ns = 0;
|
||||
union {
|
||||
size_t s;
|
||||
int i;
|
||||
void* p;
|
||||
} v;
|
||||
|
||||
uint64_t GetStartTimeNS() const { return start_ns; }
|
||||
uint64_t GetEndTimeNS() const { return end_ns; }
|
||||
void SetStartTimeNS(uint64_t t) { start_ns = t; }
|
||||
void SetEndTimeNS(uint64_t t) { end_ns = t; }
|
||||
|
||||
template <typename T>
|
||||
void setValue(T);
|
||||
template <>
|
||||
void setValue(size_t s) {
|
||||
v.s = s;
|
||||
}
|
||||
template <>
|
||||
void setValue(int i) {
|
||||
v.i = i;
|
||||
}
|
||||
template <>
|
||||
void setValue(void* p) {
|
||||
v.p = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T getValue() const;
|
||||
template <>
|
||||
size_t getValue<size_t>() const {
|
||||
return v.s;
|
||||
}
|
||||
template <>
|
||||
int getValue<int>() const {
|
||||
return v.i;
|
||||
}
|
||||
template <>
|
||||
void* getValue<void*>() const {
|
||||
return v.p;
|
||||
}
|
||||
};
|
||||
|
||||
class ScopedTimer {
|
||||
public:
|
||||
ScopedTimer(TimedResult& res) : res_(res) { res_.start_ns = Nanotime(); }
|
||||
|
||||
~ScopedTimer() { res_.end_ns = Nanotime(); }
|
||||
|
||||
private:
|
||||
TimedResult& res_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename MallocFn, typename... Args>
|
||||
static TimedResult TimerCall(MallocFn fn, Args... args) {
|
||||
TimedResult ret;
|
||||
decltype((g_dispatch->*fn)(args...)) r;
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
ScopedTimer t(ret);
|
||||
r = (g_dispatch->*fn)(args...);
|
||||
} else {
|
||||
r = (g_dispatch->*fn)(args...);
|
||||
}
|
||||
ret.setValue<decltype(r)>(r);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename MallocFn, typename... Args>
|
||||
static TimedResult TimerCallVoid(MallocFn fn, Args... args) {
|
||||
TimedResult ret;
|
||||
{
|
||||
ScopedTimer t(ret);
|
||||
(g_dispatch->*fn)(args...);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TCALL(FUNC, ...) TimerCall(&MallocDispatch::FUNC, __VA_ARGS__);
|
||||
#define TCALLVOID(FUNC, ...) TimerCallVoid(&MallocDispatch::FUNC, __VA_ARGS__);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -419,7 +513,7 @@ size_t debug_malloc_usable_size(void* pointer) {
|
|||
return InternalMallocUsableSize(pointer);
|
||||
}
|
||||
|
||||
static void* InternalMalloc(size_t size) {
|
||||
static TimedResult InternalMalloc(size_t size) {
|
||||
if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
debug_dump_heap(android::base::StringPrintf(
|
||||
"%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
|
||||
|
@ -430,30 +524,35 @@ static void* InternalMalloc(size_t size) {
|
|||
size = 1;
|
||||
}
|
||||
|
||||
TimedResult result;
|
||||
|
||||
size_t real_size = size + g_debug->extra_bytes();
|
||||
if (real_size < size) {
|
||||
// Overflow.
|
||||
errno = ENOMEM;
|
||||
return nullptr;
|
||||
result.setValue<void*>(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (size > PointerInfoType::MaxSize()) {
|
||||
errno = ENOMEM;
|
||||
return nullptr;
|
||||
result.setValue<void*>(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* pointer;
|
||||
if (g_debug->HeaderEnabled()) {
|
||||
Header* header =
|
||||
reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
|
||||
result = TCALL(memalign, MINIMUM_ALIGNMENT_BYTES, real_size);
|
||||
Header* header = reinterpret_cast<Header*>(result.getValue<void*>());
|
||||
if (header == nullptr) {
|
||||
return nullptr;
|
||||
return result;
|
||||
}
|
||||
pointer = InitHeader(header, header, size);
|
||||
result.setValue<void*>(InitHeader(header, header, size));
|
||||
} else {
|
||||
pointer = g_dispatch->malloc(real_size);
|
||||
result = TCALL(malloc, real_size);
|
||||
}
|
||||
|
||||
void* pointer = result.getValue<void*>();
|
||||
|
||||
if (pointer != nullptr) {
|
||||
if (g_debug->TrackPointers()) {
|
||||
PointerData::Add(pointer, size);
|
||||
|
@ -466,7 +565,8 @@ static void* InternalMalloc(size_t size) {
|
|||
memset(pointer, g_debug->config().fill_alloc_value(), bytes);
|
||||
}
|
||||
}
|
||||
return pointer;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void* debug_malloc(size_t size) {
|
||||
|
@ -479,16 +579,17 @@ void* debug_malloc(size_t size) {
|
|||
ScopedDisableDebugCalls disable;
|
||||
ScopedBacktraceSignalBlocker blocked;
|
||||
|
||||
void* pointer = InternalMalloc(size);
|
||||
TimedResult result = InternalMalloc(size);
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new MallocEntry(pointer, size));
|
||||
g_debug->record->AddEntry(new MallocEntry(result.getValue<void*>(), size,
|
||||
result.GetStartTimeNS(), result.GetEndTimeNS()));
|
||||
}
|
||||
|
||||
return pointer;
|
||||
return result.getValue<void*>();
|
||||
}
|
||||
|
||||
static void InternalFree(void* pointer) {
|
||||
static TimedResult InternalFree(void* pointer) {
|
||||
if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
debug_dump_heap(android::base::StringPrintf(
|
||||
"%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
|
||||
|
@ -530,6 +631,7 @@ static void InternalFree(void* pointer) {
|
|||
PointerData::Remove(pointer);
|
||||
}
|
||||
|
||||
TimedResult result;
|
||||
if (g_debug->config().options() & FREE_TRACK) {
|
||||
// Do not add the allocation until we are done modifying the pointer
|
||||
// itself. This avoids a race if a lot of threads are all doing
|
||||
|
@ -537,15 +639,15 @@ static void InternalFree(void* pointer) {
|
|||
// pointer from another thread, while still trying to free it in
|
||||
// this function.
|
||||
pointer = PointerData::AddFreed(pointer, bytes);
|
||||
if (pointer != nullptr) {
|
||||
if (g_debug->HeaderEnabled()) {
|
||||
if (pointer != nullptr && g_debug->HeaderEnabled()) {
|
||||
pointer = g_debug->GetHeader(pointer)->orig_pointer;
|
||||
}
|
||||
g_dispatch->free(pointer);
|
||||
}
|
||||
result = TCALLVOID(free, pointer);
|
||||
} else {
|
||||
g_dispatch->free(free_pointer);
|
||||
result = TCALLVOID(free, free_pointer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void debug_free(void* pointer) {
|
||||
|
@ -558,15 +660,16 @@ void debug_free(void* pointer) {
|
|||
ScopedDisableDebugCalls disable;
|
||||
ScopedBacktraceSignalBlocker blocked;
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new FreeEntry(pointer));
|
||||
}
|
||||
|
||||
if (!VerifyPointer(pointer, "free")) {
|
||||
return;
|
||||
}
|
||||
|
||||
InternalFree(pointer);
|
||||
TimedResult result = InternalFree(pointer);
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(
|
||||
new FreeEntry(pointer, result.GetStartTimeNS(), result.GetEndTimeNS()));
|
||||
}
|
||||
}
|
||||
|
||||
void* debug_memalign(size_t alignment, size_t bytes) {
|
||||
|
@ -588,6 +691,7 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
TimedResult result;
|
||||
void* pointer;
|
||||
if (g_debug->HeaderEnabled()) {
|
||||
// Make the alignment a power of two.
|
||||
|
@ -610,7 +714,8 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
pointer = g_dispatch->malloc(real_size);
|
||||
result = TCALL(malloc, real_size);
|
||||
pointer = result.getValue<void*>();
|
||||
if (pointer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -620,6 +725,7 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
value += (-value % alignment);
|
||||
|
||||
Header* header = g_debug->GetHeader(reinterpret_cast<void*>(value));
|
||||
// Don't need to update `result` here because we only need the timestamps.
|
||||
pointer = InitHeader(header, pointer, bytes);
|
||||
} else {
|
||||
size_t real_size = bytes + g_debug->extra_bytes();
|
||||
|
@ -628,7 +734,8 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
errno = ENOMEM;
|
||||
return nullptr;
|
||||
}
|
||||
pointer = g_dispatch->memalign(alignment, real_size);
|
||||
result = TCALL(memalign, alignment, real_size);
|
||||
pointer = result.getValue<void*>();
|
||||
}
|
||||
|
||||
if (pointer != nullptr) {
|
||||
|
@ -644,7 +751,8 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
}
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment));
|
||||
g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment,
|
||||
result.GetStartTimeNS(), result.GetEndTimeNS()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,10 +770,12 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
ScopedBacktraceSignalBlocker blocked;
|
||||
|
||||
if (pointer == nullptr) {
|
||||
pointer = InternalMalloc(bytes);
|
||||
TimedResult result = InternalMalloc(bytes);
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new ReallocEntry(pointer, bytes, nullptr));
|
||||
g_debug->record->AddEntry(new ReallocEntry(result.getValue<void*>(), bytes, nullptr,
|
||||
result.GetStartTimeNS(), result.GetEndTimeNS()));
|
||||
}
|
||||
pointer = result.getValue<void*>();
|
||||
return pointer;
|
||||
}
|
||||
|
||||
|
@ -674,11 +784,13 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
TimedResult result = InternalFree(pointer);
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new ReallocEntry(nullptr, bytes, pointer));
|
||||
g_debug->record->AddEntry(new ReallocEntry(nullptr, bytes, pointer, result.GetStartTimeNS(),
|
||||
result.GetEndTimeNS()));
|
||||
}
|
||||
|
||||
InternalFree(pointer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -697,6 +809,7 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
TimedResult result;
|
||||
void* new_pointer;
|
||||
size_t prev_size;
|
||||
if (g_debug->HeaderEnabled()) {
|
||||
|
@ -730,7 +843,8 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
}
|
||||
|
||||
// Allocate the new size.
|
||||
new_pointer = InternalMalloc(bytes);
|
||||
result = InternalMalloc(bytes);
|
||||
new_pointer = result.getValue<void*>();
|
||||
if (new_pointer == nullptr) {
|
||||
errno = ENOMEM;
|
||||
return nullptr;
|
||||
|
@ -738,14 +852,18 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
|
||||
prev_size = header->usable_size;
|
||||
memcpy(new_pointer, pointer, prev_size);
|
||||
InternalFree(pointer);
|
||||
TimedResult free_time = InternalFree(pointer);
|
||||
// `realloc` is split into two steps, update the end time to the finish time
|
||||
// of the second operation.
|
||||
result.SetEndTimeNS(free_time.GetEndTimeNS());
|
||||
} else {
|
||||
if (g_debug->TrackPointers()) {
|
||||
PointerData::Remove(pointer);
|
||||
}
|
||||
|
||||
prev_size = g_dispatch->malloc_usable_size(pointer);
|
||||
new_pointer = g_dispatch->realloc(pointer, real_size);
|
||||
result = TCALL(realloc, pointer, real_size);
|
||||
new_pointer = result.getValue<void*>();
|
||||
if (new_pointer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -767,7 +885,8 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
}
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new ReallocEntry(new_pointer, bytes, pointer));
|
||||
g_debug->record->AddEntry(new ReallocEntry(new_pointer, bytes, pointer, result.GetStartTimeNS(),
|
||||
result.GetEndTimeNS()));
|
||||
}
|
||||
|
||||
return new_pointer;
|
||||
|
@ -807,21 +926,24 @@ void* debug_calloc(size_t nmemb, size_t bytes) {
|
|||
}
|
||||
|
||||
void* pointer;
|
||||
TimedResult result;
|
||||
if (g_debug->HeaderEnabled()) {
|
||||
// Need to guarantee the alignment of the header.
|
||||
Header* header =
|
||||
reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
|
||||
result = TCALL(memalign, MINIMUM_ALIGNMENT_BYTES, real_size);
|
||||
Header* header = reinterpret_cast<Header*>(result.getValue<void*>());
|
||||
if (header == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
memset(header, 0, g_dispatch->malloc_usable_size(header));
|
||||
pointer = InitHeader(header, header, size);
|
||||
} else {
|
||||
pointer = g_dispatch->calloc(1, real_size);
|
||||
result = TCALL(calloc, 1, real_size);
|
||||
pointer = result.getValue<void*>();
|
||||
}
|
||||
|
||||
if (g_debug->config().options() & RECORD_ALLOCS) {
|
||||
g_debug->record->AddEntry(new CallocEntry(pointer, bytes, nmemb));
|
||||
g_debug->record->AddEntry(
|
||||
new CallocEntry(pointer, bytes, nmemb, result.GetStartTimeNS(), result.GetEndTimeNS()));
|
||||
}
|
||||
|
||||
if (pointer != nullptr && g_debug->TrackPointers()) {
|
||||
|
|
|
@ -184,6 +184,23 @@ std::string ShowDiffs(uint8_t* a, uint8_t* b, size_t size) {
|
|||
return diff;
|
||||
}
|
||||
|
||||
static void VerifyRecords(std::vector<std::string>& expected, std::string& actual) {
|
||||
size_t offset = 0;
|
||||
for (std::string& str : expected) {
|
||||
ASSERT_STREQ(str.c_str(), actual.substr(offset, str.size()).c_str());
|
||||
if (str.find("thread_done") != std::string::npos) {
|
||||
offset = actual.find_first_of("\n", offset) + 1;
|
||||
continue;
|
||||
}
|
||||
offset += str.size() + 1;
|
||||
uint64_t st = strtoull(&actual[offset], nullptr, 10);
|
||||
offset = actual.find_first_of(" ", offset) + 1;
|
||||
uint64_t et = strtoull(&actual[offset], nullptr, 10);
|
||||
ASSERT_GT(et, st);
|
||||
offset = actual.find_first_of("\n", offset) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyAllocCalls(bool all_options) {
|
||||
size_t alloc_size = 1024;
|
||||
|
||||
|
@ -2171,61 +2188,61 @@ TEST_F(MallocDebugTest, debug_valloc) {
|
|||
#endif
|
||||
|
||||
void VerifyRecordAllocs(const std::string& record_filename) {
|
||||
std::string expected;
|
||||
std::vector<std::string> expected;
|
||||
|
||||
void* pointer = debug_malloc(10);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 10\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_calloc(1, 20);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: calloc %p 20 1\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: calloc %p 20 1", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_realloc(nullptr, 30);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: realloc %p 0x0 30\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: realloc %p 0x0 30", getpid(), pointer));
|
||||
void* old_pointer = pointer;
|
||||
pointer = debug_realloc(pointer, 2048);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: realloc %p %p 2048\n", getpid(),
|
||||
pointer, old_pointer);
|
||||
expected.push_back(
|
||||
android::base::StringPrintf("%d: realloc %p %p 2048", getpid(), pointer, old_pointer));
|
||||
debug_realloc(pointer, 0);
|
||||
expected += android::base::StringPrintf("%d: realloc 0x0 %p 0\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: realloc 0x0 %p 0", getpid(), pointer));
|
||||
|
||||
pointer = debug_memalign(16, 40);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: memalign %p 16 40\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: memalign %p 16 40", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_aligned_alloc(32, 64);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: memalign %p 32 64\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: memalign %p 32 64", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 50));
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: memalign %p 32 50\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: memalign %p 32 50", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
|
||||
pointer = debug_pvalloc(60);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: memalign %p 4096 4096\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: memalign %p 4096 4096", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_valloc(70);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: memalign %p 4096 70\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: memalign %p 4096 70", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
#endif
|
||||
|
||||
// Dump all of the data accumulated so far.
|
||||
|
@ -2235,7 +2252,7 @@ void VerifyRecordAllocs(const std::string& record_filename) {
|
|||
std::string actual;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
|
||||
|
||||
ASSERT_STREQ(expected.c_str(), actual.c_str());
|
||||
VerifyRecords(expected, actual);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ("", getFakeLogPrint().c_str());
|
||||
|
@ -2256,23 +2273,23 @@ TEST_F(MallocDebugTest, record_allocs_with_header) {
|
|||
TEST_F(MallocDebugTest, record_allocs_max) {
|
||||
InitRecordAllocs("record_allocs=5");
|
||||
|
||||
std::string expected;
|
||||
std::vector<std::string> expected;
|
||||
|
||||
void* pointer = debug_malloc(10);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 10\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_malloc(20);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 20\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 20", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
pointer = debug_malloc(1024);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 1024\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 1024", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
|
||||
// Dump all of the data accumulated so far.
|
||||
|
@ -2282,7 +2299,7 @@ TEST_F(MallocDebugTest, record_allocs_max) {
|
|||
std::string actual;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
|
||||
|
||||
ASSERT_STREQ(expected.c_str(), actual.c_str());
|
||||
VerifyRecords(expected, actual);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ(
|
||||
|
@ -2303,9 +2320,10 @@ TEST_F(MallocDebugTest, record_allocs_thread_done) {
|
|||
});
|
||||
thread.join();
|
||||
|
||||
std::string expected = android::base::StringPrintf("%d: malloc %p 100\n", tid, pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", tid, pointer);
|
||||
expected += android::base::StringPrintf("%d: thread_done 0x0\n", tid);
|
||||
std::vector<std::string> expected;
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 100", tid, pointer));
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", tid, pointer));
|
||||
expected.push_back(android::base::StringPrintf("%d: thread_done 0x0", tid));
|
||||
|
||||
// Dump all of the data accumulated so far.
|
||||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0);
|
||||
|
@ -2314,7 +2332,7 @@ TEST_F(MallocDebugTest, record_allocs_thread_done) {
|
|||
std::string actual;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
|
||||
|
||||
ASSERT_STREQ(expected.c_str(), actual.c_str());
|
||||
VerifyRecords(expected, actual);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ("", getFakeLogPrint().c_str());
|
||||
|
@ -2329,13 +2347,13 @@ TEST_F(MallocDebugTest, record_allocs_file_name_fail) {
|
|||
|
||||
ASSERT_EQ(0, symlink("/data/local/tmp/does_not_exist", record_filename.c_str()));
|
||||
|
||||
std::string expected;
|
||||
std::vector<std::string> expected;
|
||||
|
||||
void* pointer = debug_malloc(10);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 10\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
// Dump all of the data accumulated so far.
|
||||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0);
|
||||
|
@ -2351,7 +2369,8 @@ TEST_F(MallocDebugTest, record_allocs_file_name_fail) {
|
|||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0);
|
||||
|
||||
ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
|
||||
ASSERT_STREQ(expected.c_str(), actual.c_str());
|
||||
|
||||
VerifyRecords(expected, actual);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
std::string expected_log = android::base::StringPrintf(
|
||||
|
@ -2375,13 +2394,13 @@ TEST_F(MallocDebugTest, record_allocs_no_entries_to_write) {
|
|||
TEST_F(MallocDebugTest, record_allocs_write_entries_does_not_allocate) {
|
||||
InitRecordAllocs("record_allocs=5");
|
||||
|
||||
std::string expected;
|
||||
std::vector<std::string> expected;
|
||||
|
||||
void* pointer = debug_malloc(10);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
expected += android::base::StringPrintf("%d: malloc %p 10\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer));
|
||||
debug_free(pointer);
|
||||
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
|
||||
expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer));
|
||||
|
||||
malloc_disable();
|
||||
kill(getpid(), SIGRTMAX - 18);
|
||||
|
@ -2389,7 +2408,8 @@ TEST_F(MallocDebugTest, record_allocs_write_entries_does_not_allocate) {
|
|||
|
||||
std::string actual;
|
||||
ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
|
||||
ASSERT_STREQ(expected.c_str(), actual.c_str());
|
||||
|
||||
VerifyRecords(expected, actual);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ("", getFakeLogPrint().c_str());
|
||||
|
|
Loading…
Reference in a new issue