diff --git a/src/heap-profile-table.cc b/src/heap-profile-table.cc index 3cbc103..c295ddb 100644 --- a/src/heap-profile-table.cc +++ b/src/heap-profile-table.cc @@ -337,141 +337,3 @@ void HeapProfileTable::CleanupOldProfiles(const char* prefix) { RAW_LOG(WARNING, "Unable to remove old heap profiles (can't run glob())"); #endif } - -HeapProfileTable::Snapshot* HeapProfileTable::TakeSnapshot() { - Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_); - address_map_->Iterate([s] (const void* ptr, AllocValue* v) { - s->Add(ptr, *v); - }); - return s; -} - -void HeapProfileTable::ReleaseSnapshot(Snapshot* s) { - s->~Snapshot(); - dealloc_(s); -} - -HeapProfileTable::Snapshot* HeapProfileTable::NonLiveSnapshot( - Snapshot* base) { - RAW_VLOG(2, "NonLiveSnapshot input: %" PRId64 " %" PRId64 "\n", - total_.allocs - total_.frees, - total_.alloc_size - total_.free_size); - - Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_); - address_map_->Iterate([&] (const void* ptr, AllocValue* v) { - if (v->live()) { - v->set_live(false); - } else { - if (base != nullptr && base->map_.Find(ptr) != nullptr) { - // Present in arg->base, so do not save - } else { - s->Add(ptr, *v); - } - } - }); - RAW_VLOG(2, "NonLiveSnapshot output: %" PRId64 " %" PRId64 "\n", - s->total_.allocs - s->total_.frees, - s->total_.alloc_size - s->total_.free_size); - return s; -} - -// Information kept per unique bucket seen -struct HeapProfileTable::Snapshot::Entry { - int count; - size_t bytes; - Bucket* bucket; - Entry() : count(0), bytes(0) { } - - // Order by decreasing bytes - bool operator<(const Entry& x) const { - return this->bytes > x.bytes; - } -}; - -void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name, - const char* filename, - bool should_symbolize) { - // This is only used by the heap leak checker, but is intimately - // tied to the allocation map that belongs in this module and is - // therefore placed here. - RAW_LOG(ERROR, "Leak check %s detected leaks of %zu bytes " - "in %zu objects", - checker_name, - size_t(total_.alloc_size), - size_t(total_.allocs)); - - // Group objects by Bucket - std::map buckets; - map_.Iterate([&] (const void* ptr, AllocValue* v) { - Entry* e = &buckets[v->bucket()]; // Creates empty Entry first time - e->bucket = v->bucket(); - e->count++; - e->bytes += v->bytes; - }); - - // Sort buckets by decreasing leaked size - const int n = buckets.size(); - Entry* entries = new Entry[n]; - int dst = 0; - for (std::map::const_iterator iter = buckets.begin(); - iter != buckets.end(); - ++iter) { - entries[dst++] = iter->second; - } - std::sort(entries, entries + n); - - // Report a bounded number of leaks to keep the leak report from - // growing too long. - const int to_report = - (FLAGS_heap_check_max_leaks > 0 && - n > FLAGS_heap_check_max_leaks) ? FLAGS_heap_check_max_leaks : n; - RAW_LOG(ERROR, "The %d largest leaks:", to_report); - - // Print - SymbolTable symbolization_table; - for (int i = 0; i < to_report; i++) { - const Entry& e = entries[i]; - for (int j = 0; j < e.bucket->depth; j++) { - symbolization_table.Add(e.bucket->stack[j]); - } - } - if (should_symbolize) - symbolization_table.Symbolize(); - - { - auto do_log = +[] (const char* buf, size_t amt) { - RAW_LOG(ERROR, "%.*s", amt, buf); - }; - constexpr int kBufSize = 2<<10; - tcmalloc::WriteFnWriter printer{do_log}; - - for (int i = 0; i < to_report; i++) { - const Entry& e = entries[i]; - printer.AppendF("Leak of %zu bytes in %d objects allocated from:\n", - e.bytes, e.count); - for (int j = 0; j < e.bucket->depth; j++) { - const void* pc = e.bucket->stack[j]; - printer.AppendF("\t@ %" PRIxPTR " %s\n", - reinterpret_cast(pc), symbolization_table.GetSymbol(pc)); - } - } - } - - if (to_report < n) { - RAW_LOG(ERROR, "Skipping leaks numbered %d..%d", - to_report, n-1); - } - delete[] entries; - - if (!HeapProfileTable::WriteProfile(filename, total_, &map_)) { - RAW_LOG(ERROR, "Could not write pprof profile to %s", filename); - } -} - -void HeapProfileTable::Snapshot::ReportIndividualObjects() { - map_.Iterate([] (const void* ptr, AllocValue* v) { - // Perhaps also log the allocation stack trace (unsymbolized) - // on this line in case somebody finds it useful. - RAW_LOG(ERROR, "leaked %zu byte object %p", v->bytes, ptr); - }); -} diff --git a/src/heap-profile-table.h b/src/heap-profile-table.h index 7c266c3..44a40d8 100644 --- a/src/heap-profile-table.h +++ b/src/heap-profile-table.h @@ -137,26 +137,7 @@ class HeapProfileTable { // Cleanup any old profile files matching prefix + ".*" + kFileExt. static void CleanupOldProfiles(const char* prefix); - // Return a snapshot of the current contents of *this. - // Caller must call ReleaseSnapshot() on result when no longer needed. - // The result is only valid while this exists and until - // the snapshot is discarded by calling ReleaseSnapshot(). - class Snapshot; - Snapshot* TakeSnapshot(); - - // Release a previously taken snapshot. snapshot must not - // be used after this call. - void ReleaseSnapshot(Snapshot* snapshot); - - // Return a snapshot of every non-live, non-ignored object in *this. - // If "base" is non-nullptr, skip any objects present in "base". As - // a side-effect, clears the "live" bit on every live object in - // *this. Caller must call ReleaseSnapshot() on result when no - // longer needed. - Snapshot* NonLiveSnapshot(Snapshot* base); - private: - // data types ---------------------------- // Hash table bucket to hold (de)allocation stats @@ -246,53 +227,4 @@ class HeapProfileTable { DISALLOW_COPY_AND_ASSIGN(HeapProfileTable); }; -class HeapProfileTable::Snapshot { - public: - const Stats& total() const { return total_; } - - // Report anything in this snapshot as a leak. - // May use new/delete for temporary storage. - // If should_symbolize is true, will fork (which is not threadsafe) - // to turn addresses into symbol names. Set to false for maximum safety. - // Also writes a heap profile to "filename" that contains - // all of the objects in this snapshot. - void ReportLeaks(const char* checker_name, const char* filename, - bool should_symbolize); - - // Report the addresses of all leaked objects. - // May use new/delete for temporary storage. - void ReportIndividualObjects(); - - bool Empty() const { - return (total_.allocs == 0) && (total_.alloc_size == 0); - } - - private: - friend class HeapProfileTable; - - // Total count/size are stored in a Bucket so we can reuse UnparseBucket - Bucket total_; - - // We share the Buckets managed by the parent table, but have our - // own object->bucket map. - AllocationMap map_; - - Snapshot(Allocator alloc, DeAllocator dealloc) : map_(alloc, dealloc) { - memset(&total_, 0, sizeof(total_)); - } - - // Callback used to populate a Snapshot object with entries found - // in another allocation map. - inline void Add(const void* ptr, const AllocValue& v) { - map_.Insert(ptr, v); - total_.allocs++; - total_.alloc_size += v.bytes; - } - - // Helpers for sorting and generating leak reports - struct Entry; - - DISALLOW_COPY_AND_ASSIGN(Snapshot); -}; - #endif // BASE_HEAP_PROFILE_TABLE_H_