|  | /* | 
|  | * 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; | 
|  | } | 
|  | } | 
|  | } |