// Copyright 2013 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 "base/debug/dump_without_crashing.h" #include "base/check.h" #include "base/metrics/histogram_functions.h" #include "base/no_destructor.h" #include "base/synchronization/lock.h" #include "base/trace_event/base_tracing.h" namespace { // Pointer to the function that's called by DumpWithoutCrashing* to dump the // process's memory. void(CDECL* dump_without_crashing_function_)() = nullptr; template bool ShouldDump(Map& map, Key& key, base::TimeDelta time_between_dumps) { static base::NoDestructor lock; base::AutoLock auto_lock(*lock); base::TimeTicks now = base::TimeTicks::Now(); auto [it, inserted] = map.emplace(key, now); if (inserted) { return true; } if (now - it->second >= time_between_dumps) { it->second = now; return true; } return false; } // This function takes `location` and `time_between_dumps` as an input // and checks if DumpWithoutCrashing() meets the requirements to take the dump // or not. bool ShouldDumpWithoutCrashWithLocation(const base::Location& location, base::TimeDelta time_between_dumps) { static base::NoDestructor> location_to_timestamp; return ShouldDump(*location_to_timestamp, location, time_between_dumps); } // Pair of `location` and `unique_identifier` creates a unique key and checks // if DumpWithoutCrashingWithUniqueId() meets the requirements to take dump or // not. bool ShouldDumpWithoutCrashWithLocationAndUniqueId( size_t unique_identifier, const base::Location& location, base::TimeDelta time_between_dumps) { static base::NoDestructor< std::map, base::TimeTicks>> location_and_unique_identifier_to_timestamp; std::pair key(location, unique_identifier); return ShouldDump(*location_and_unique_identifier_to_timestamp, key, time_between_dumps); } } // namespace namespace base { namespace debug { bool DumpWithoutCrashingUnthrottled() { TRACE_EVENT0("base", "DumpWithoutCrashingUnthrottled"); if (dump_without_crashing_function_) { (*dump_without_crashing_function_)(); return true; } return false; } bool DumpWithoutCrashing(const base::Location& location, base::TimeDelta time_between_dumps) { TRACE_EVENT0("base", "DumpWithoutCrashing"); if (dump_without_crashing_function_ && ShouldDumpWithoutCrashWithLocation(location, time_between_dumps)) { (*dump_without_crashing_function_)(); base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus", DumpWithoutCrashingStatus::kUploaded); return true; } base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus", DumpWithoutCrashingStatus::kThrottled); return false; } bool DumpWithoutCrashingWithUniqueId(size_t unique_identifier, const base::Location& location, base::TimeDelta time_between_dumps) { TRACE_EVENT0("base", "DumpWithoutCrashingWithUniqueId"); if (dump_without_crashing_function_ && ShouldDumpWithoutCrashWithLocationAndUniqueId(unique_identifier, location, time_between_dumps)) { (*dump_without_crashing_function_)(); base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus", DumpWithoutCrashingStatus::kUploaded); return true; } base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus", DumpWithoutCrashingStatus::kThrottled); return false; } void SetDumpWithoutCrashingFunction(void (CDECL *function)()) { #if !defined(COMPONENT_BUILD) // In component builds, the same base is shared between modules // so might be initialized several times. However in non- // component builds this should never happen. DCHECK(!dump_without_crashing_function_ || !function); #endif dump_without_crashing_function_ = function; } } // namespace debug } // namespace base