1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/debug/dump_without_crashing.h"
6
7 #include "base/check.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/no_destructor.h"
10 #include "base/synchronization/lock.h"
11 #include "base/trace_event/base_tracing.h"
12
13 namespace {
14
15 // Pointer to the function that's called by DumpWithoutCrashing* to dump the
16 // process's memory.
17 void(CDECL* dump_without_crashing_function_)() = nullptr;
18
19 template <typename Map, typename Key>
ShouldDump(Map & map,Key & key,base::TimeDelta time_between_dumps)20 bool ShouldDump(Map& map, Key& key, base::TimeDelta time_between_dumps) {
21 static base::NoDestructor<base::Lock> lock;
22 base::AutoLock auto_lock(*lock);
23 base::TimeTicks now = base::TimeTicks::Now();
24 auto [it, inserted] = map.emplace(key, now);
25 if (inserted) {
26 return true;
27 }
28
29 if (now - it->second >= time_between_dumps) {
30 it->second = now;
31 return true;
32 }
33 return false;
34 }
35
36 // Map used to store the most recent time a location called
37 // ShouldDumpWithoutCrashWithLocation.
LocationToTimestampMap()38 std::map<base::Location, base::TimeTicks>& LocationToTimestampMap() {
39 static base::NoDestructor<std::map<base::Location, base::TimeTicks>>
40 location_to_timestamp;
41 return *location_to_timestamp;
42 }
43
44 // Map used to store the most recent time a pair of location and
45 // unique_identifier called ShouldDumpWithoutCrashWithLocationAndUniqueId.
46 std::map<std::pair<base::Location, size_t>, base::TimeTicks>&
LocationAndUniqueIdentifierToTimestampMap()47 LocationAndUniqueIdentifierToTimestampMap() {
48 static base::NoDestructor<
49 std::map<std::pair<base::Location, size_t>, base::TimeTicks>>
50 location_and_unique_identifier_to_timestamp;
51 return *location_and_unique_identifier_to_timestamp;
52 }
53
54 // This function takes `location` and `time_between_dumps` as an input
55 // and checks if DumpWithoutCrashing() meets the requirements to take the dump
56 // or not.
ShouldDumpWithoutCrashWithLocation(const base::Location & location,base::TimeDelta time_between_dumps)57 bool ShouldDumpWithoutCrashWithLocation(const base::Location& location,
58 base::TimeDelta time_between_dumps) {
59 return ShouldDump(LocationToTimestampMap(), location, time_between_dumps);
60 }
61
62 // Pair of `location` and `unique_identifier` creates a unique key and checks
63 // if DumpWithoutCrashingWithUniqueId() meets the requirements to take dump or
64 // not.
ShouldDumpWithoutCrashWithLocationAndUniqueId(size_t unique_identifier,const base::Location & location,base::TimeDelta time_between_dumps)65 bool ShouldDumpWithoutCrashWithLocationAndUniqueId(
66 size_t unique_identifier,
67 const base::Location& location,
68 base::TimeDelta time_between_dumps) {
69 std::pair<base::Location, size_t> key(location, unique_identifier);
70 return ShouldDump(LocationAndUniqueIdentifierToTimestampMap(), key,
71 time_between_dumps);
72 }
73
74 } // namespace
75
76 namespace base {
77
78 namespace debug {
79
DumpWithoutCrashingUnthrottled()80 bool DumpWithoutCrashingUnthrottled() {
81 TRACE_EVENT0("base", "DumpWithoutCrashingUnthrottled");
82 if (dump_without_crashing_function_) {
83 (*dump_without_crashing_function_)();
84 return true;
85 }
86 return false;
87 }
88
DumpWithoutCrashing(const base::Location & location,base::TimeDelta time_between_dumps)89 bool DumpWithoutCrashing(const base::Location& location,
90 base::TimeDelta time_between_dumps) {
91 TRACE_EVENT0("base", "DumpWithoutCrashing");
92 if (dump_without_crashing_function_ &&
93 ShouldDumpWithoutCrashWithLocation(location, time_between_dumps)) {
94 (*dump_without_crashing_function_)();
95 base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus",
96 DumpWithoutCrashingStatus::kUploaded);
97 return true;
98 }
99 base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus",
100 DumpWithoutCrashingStatus::kThrottled);
101 return false;
102 }
103
DumpWithoutCrashingWithUniqueId(size_t unique_identifier,const base::Location & location,base::TimeDelta time_between_dumps)104 bool DumpWithoutCrashingWithUniqueId(size_t unique_identifier,
105 const base::Location& location,
106 base::TimeDelta time_between_dumps) {
107 TRACE_EVENT0("base", "DumpWithoutCrashingWithUniqueId");
108 if (dump_without_crashing_function_ &&
109 ShouldDumpWithoutCrashWithLocationAndUniqueId(unique_identifier, location,
110 time_between_dumps)) {
111 (*dump_without_crashing_function_)();
112 base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus",
113 DumpWithoutCrashingStatus::kUploaded);
114 return true;
115 }
116 base::UmaHistogramEnumeration("Stability.DumpWithoutCrashingStatus",
117 DumpWithoutCrashingStatus::kThrottled);
118 return false;
119 }
120
SetDumpWithoutCrashingFunction(void (CDECL * function)())121 void SetDumpWithoutCrashingFunction(void (CDECL *function)()) {
122 #if !defined(COMPONENT_BUILD)
123 // In component builds, the same base is shared between modules
124 // so might be initialized several times. However in non-
125 // component builds this should never happen.
126 DCHECK(!dump_without_crashing_function_ || !function);
127 #endif
128 dump_without_crashing_function_ = function;
129 }
130
ClearMapsForTesting()131 void ClearMapsForTesting() {
132 LocationToTimestampMap().clear();
133 LocationAndUniqueIdentifierToTimestampMap().clear();
134 }
135
136 } // namespace debug
137 } // namespace base
138