blob: ec9b78699ce635bf95eb2e1c712e688bd1f395b8 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metrics/leak_detector/call_stack_table.h"
#include <algorithm>
#include "components/metrics/leak_detector/call_stack_manager.h"
namespace metrics {
namespace leak_detector {
namespace {
using ValueType = LeakDetectorValueType;
// During leak analysis, we only want to examine the top
// |kMaxCountOfSuspciousStacks| entries.
const int kMaxCountOfSuspciousStacks = 16;
const int kInitialHashTableSize = 1999;
} // namespace
size_t CallStackTable::StoredHash::operator()(
const CallStack* call_stack) const {
// The call stack object should already have a hash computed when it was
// created.
//
// This is NOT the actual hash computation function for a new call stack.
return call_stack->hash;
}
CallStackTable::CallStackTable(int call_stack_suspicion_threshold)
: num_allocs_(0),
num_frees_(0),
entry_map_(kInitialHashTableSize),
leak_analyzer_(kMaxCountOfSuspciousStacks,
call_stack_suspicion_threshold) {}
CallStackTable::~CallStackTable() {}
void CallStackTable::Add(const CallStack* call_stack) {
++entry_map_[call_stack].count;
++num_allocs_;
}
void CallStackTable::Remove(const CallStack* call_stack) {
auto iter = entry_map_.find(call_stack);
if (iter == entry_map_.end())
return;
uint32_t& count_for_call_stack = iter->second.count;
--count_for_call_stack;
++num_frees_;
// Delete zero-alloc entries to free up space.
if (count_for_call_stack == 0)
entry_map_.erase(iter);
}
void CallStackTable::TestForLeaks() {
// Add all entries to the ranked list.
RankedSet ranked_entries(kMaxCountOfSuspciousStacks);
GetTopCallStacks(&ranked_entries);
leak_analyzer_.AddSample(std::move(ranked_entries));
}
void CallStackTable::GetTopCallStacks(RankedSet* top_entries) const {
for (const auto& call_stack_count_info : entry_map_) {
top_entries->AddCallStack(call_stack_count_info.first,
call_stack_count_info.second.count);
}
}
void CallStackTable::UpdateLastDropInfo(size_t timestamp) {
for (auto& call_stack_and_info : entry_map_) {
auto& count_info = call_stack_and_info.second;
// If the |previous_count| is 0 then we need to initialize info.
if (count_info.previous_count == 0 ||
count_info.count < count_info.previous_count) {
count_info.last_drop_timestamp = timestamp;
count_info.last_drop_count = count_info.count;
}
count_info.previous_count = count_info.count;
}
}
void CallStackTable::GetLastUptrendInfo(
const CallStack* call_stack, size_t timestamp, size_t* timestamp_delta,
uint32_t* count_delta) const {
const auto& call_stack_count_info_iter = entry_map_.find(call_stack);
if (call_stack_count_info_iter != entry_map_.end()) {
const auto& count_info = call_stack_count_info_iter->second;
*timestamp_delta = timestamp - count_info.last_drop_timestamp;
*count_delta = count_info.count - count_info.last_drop_count;
} else {
DLOG(WARNING) << "Accessing information about a call stack that has not "
<< "been recorded";
*timestamp_delta = timestamp;
*count_delta = 0;
}
}
} // namespace leak_detector
} // namespace metrics