platform_bionic/libc/malloc_debug/TrackData.cpp
Christopher Ferris 426b00a29b Fix the way malloc debug returns info.
When I rewrote malloc debug, I accidentally thought that each
value returned in the info buffer contained the number of backtrace
frames in the backtrace. This was incorrect, it should have been
the total number of allocations with the same backtrace.

This is a temporary fix that sets that value to 1. The better fix is
to de-dupe backtraces and then return all allocations of the same size
with the same backtrace.

I updated the documents to describe this.

Bug: 31854476

Test: Unit tests pass.
Change-Id: Idf9efaa3d363923b5d7543d90dc7c65a0ed553d9
2017-03-09 13:47:37 -08:00

140 lines
4.5 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <private/ScopedPthreadMutexLocker.h>
#include "backtrace.h"
#include "BacktraceData.h"
#include "Config.h"
#include "DebugData.h"
#include "debug_disable.h"
#include "debug_log.h"
#include "malloc_debug.h"
#include "TrackData.h"
TrackData::TrackData(DebugData* debug_data) : OptionData(debug_data) {
}
void TrackData::GetList(std::vector<const Header*>* list) {
for (const auto& header : headers_) {
list->push_back(header);
}
// Sort by the size of the allocation.
std::sort(list->begin(), list->end(), [](const Header* a, const Header* b) {
if (a->size == b->size) return a < b;
return a->size > b->size;
});
}
void TrackData::Add(const Header* header, bool backtrace_found) {
pthread_mutex_lock(&mutex_);
if (backtrace_found) {
total_backtrace_allocs_++;
}
headers_.insert(header);
pthread_mutex_unlock(&mutex_);
}
void TrackData::Remove(const Header* header, bool backtrace_found) {
pthread_mutex_lock(&mutex_);
headers_.erase(header);
if (backtrace_found) {
total_backtrace_allocs_--;
}
pthread_mutex_unlock(&mutex_);
}
bool TrackData::Contains(const Header* header) {
pthread_mutex_lock(&mutex_);
bool found = headers_.count(header);
pthread_mutex_unlock(&mutex_);
return found;
}
void TrackData::DisplayLeaks() {
std::vector<const Header*> list;
GetList(&list);
size_t track_count = 0;
for (const auto& header : list) {
error_log("+++ %s leaked block of size %zu at %p (leak %zu of %zu)", getprogname(),
header->real_size(), debug_->GetPointer(header), ++track_count, list.size());
if (debug_->config().options & BACKTRACE) {
BacktraceHeader* back_header = debug_->GetAllocBacktrace(header);
if (back_header->num_frames > 0) {
error_log("Backtrace at time of allocation:");
backtrace_log(&back_header->frames[0], back_header->num_frames);
}
}
g_dispatch->free(header->orig_pointer);
}
}
void TrackData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size,
size_t* total_memory, size_t* backtrace_size) {
ScopedPthreadMutexLocker scoped(&mutex_);
if (headers_.size() == 0 || total_backtrace_allocs_ == 0) {
return;
}
*backtrace_size = debug_->config().backtrace_frames;
*info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size;
*info = reinterpret_cast<uint8_t*>(g_dispatch->calloc(*info_size, total_backtrace_allocs_));
if (*info == nullptr) {
return;
}
*overall_size = *info_size * total_backtrace_allocs_;
std::vector<const Header*> list;
GetList(&list);
uint8_t* data = *info;
size_t num_allocations = 1;
for (const auto& header : list) {
BacktraceHeader* back_header = debug_->GetAllocBacktrace(header);
if (back_header->num_frames > 0) {
memcpy(data, &header->size, sizeof(size_t));
memcpy(&data[sizeof(size_t)], &num_allocations, sizeof(size_t));
memcpy(&data[2 * sizeof(size_t)], &back_header->frames[0],
back_header->num_frames * sizeof(uintptr_t));
*total_memory += header->real_size();
data += *info_size;
}
}
}