• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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