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