OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
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/metrics/histogram_persistence.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/metrics/histogram.h" | |
10 #include "base/metrics/histogram_base.h" | |
11 #include "base/metrics/histogram_samples.h" | |
12 #include "base/metrics/statistics_recorder.h" | |
13 #include "base/synchronization/lock.h" | |
14 | |
15 namespace base { | |
16 | |
17 // Type identifiers used when storing in persistent memory so they can be | |
18 // identified during extraction. A "version number" is added to the base | |
19 // so that, if the structure of that object changes, stored older versions | |
20 // will be safely ignored. | |
21 enum : uint32_t { | |
22 kTypeIdHistogram = 0xF1645910 + 1, // SHA1(Histogram) v1 | |
23 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 | |
24 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 | |
25 kTypeIdNameString = 0x117DF971 + 1, // SHA1(NameString) v1 | |
26 }; | |
27 | |
28 // This data must be held in persistent memory in order for processes to | |
29 // locate and use histograms created elsewhere. | |
30 struct PersistentHistogramData { | |
31 PersistentMemoryAllocator::Reference name_ref; | |
32 size_t name_length; | |
33 int histogram_type; | |
34 int flags; | |
35 int minimum; | |
36 int maximum; | |
37 size_t bucket_count; | |
38 PersistentMemoryAllocator::Reference ranges_ref; | |
39 uint32_t ranges_checksum; | |
40 PersistentMemoryAllocator::Reference counts_ref; | |
41 HistogramSamples::Metadata samples_metadata; | |
42 }; | |
43 | |
44 scoped_ptr<PersistentMemoryAllocator> allocator_; | |
45 | |
46 void SetPersistentHistogramMemoryAllocator( | |
47 PersistentMemoryAllocator* allocator) { | |
48 // Releasing or changing an allocator is extremely dangerous because it | |
49 // likely has histograms stored within it. If the backing memory is also | |
50 // also released, future accesses to those histograms will seg-fault. | |
51 // It's not a fatal CHECK() because tests do this knowing that all | |
52 // such persistent histograms have already been forgotten. | |
53 if (allocator_) { | |
54 LOG(WARNING) << "Active PersistentMemoryAllocator has been released." | |
55 << " Some existing histogram pointers may be invalid."; | |
56 } | |
57 allocator_.reset(allocator); | |
58 } | |
59 | |
60 PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator() { | |
61 return allocator_.get(); | |
62 } | |
63 | |
64 PersistentMemoryAllocator* ReleasePersistentHistogramMemoryAllocator() { | |
65 return allocator_.release(); | |
66 }; | |
67 | |
68 // Extract a histogram from persistent memory. Unfortunately, the above "pickle" | |
69 // methods cannot be used as part of the persistance because the deserialization | |
70 // methods always create local count data (these must referenced the persistent | |
71 // counts) and always add it to the local list of known histograms (these may | |
72 // be simple references to histograms in other processes). | |
73 HistogramBase* GetPersistentHistogram( | |
74 PersistentMemoryAllocator* allocator, | |
75 int32_t ref) { | |
76 PersistentHistogramData* histogram_data = | |
77 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram); | |
78 return CreatePersistentHistogram(allocator, histogram_data); | |
79 } | |
80 | |
81 HistogramBase* GetNextPersistentHistogram( | |
82 PersistentMemoryAllocator* allocator, | |
83 PersistentMemoryAllocator::Iterator* iter) { | |
84 PersistentMemoryAllocator::Reference ref; | |
85 uint32_t type_id; | |
86 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) { | |
87 if (type_id == kTypeIdHistogram) | |
88 return GetPersistentHistogram(allocator, ref); | |
89 } | |
90 return nullptr; | |
91 } | |
92 | |
93 HistogramBase* CreatePersistentHistogram( | |
94 PersistentMemoryAllocator* allocator, | |
95 PersistentHistogramData* histogram_data_ptr) { | |
96 if (!histogram_data_ptr) { | |
97 LOG(WARNING) << "Persistent histogram data was not valid; skipped."; | |
98 NOTREACHED(); | |
99 return nullptr; | |
100 } | |
101 | |
102 // Copy the histogram_data to local storage because anything in persistent | |
103 // memory cannot be trusted as it could be changed at any moment by a | |
104 // malicious actor that shares access. The contents of histogram_data are | |
105 // validated below; the local copy is to ensure that the contents cannot | |
106 // be externally changed between validation and use. | |
107 PersistentHistogramData histogram_data = *histogram_data_ptr; | |
108 | |
109 char* name_data = | |
110 allocator->GetAsObject<char>(histogram_data.name_ref, kTypeIdNameString); | |
111 size_t name_length = histogram_data.name_length; | |
112 if (!name_data || | |
113 name_length >= allocator->GetAllocSize(histogram_data.name_ref) || | |
114 name_data[name_length] != 0) { | |
115 LOG(WARNING) << "Persistent histogram referenced invalid name string" | |
116 << "; skipped."; | |
117 NOTREACHED(); | |
118 return nullptr; | |
119 } | |
120 std::string name(name_data, name_length); | |
121 | |
122 HistogramBase::Sample* ranges_data = | |
123 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, | |
124 kTypeIdRangesArray); | |
125 if (!ranges_data || histogram_data.bucket_count < 2 || | |
126 histogram_data.bucket_count + 1 > | |
127 std::numeric_limits<size_t>::max() / sizeof(HistogramBase::Sample) || | |
128 allocator->GetAllocSize(histogram_data.ranges_ref) < | |
129 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) { | |
130 LOG(WARNING) << "Persistent histogram referenced invalid ranges array" | |
131 << "; skipped."; | |
132 NOTREACHED(); | |
133 return nullptr; | |
134 } | |
135 // To avoid racy destruction at shutdown, the following will be leaked. | |
136 BucketRanges* ranges = new BucketRanges(histogram_data.bucket_count + 1); | |
137 bool bad_ranges = false; | |
138 for (size_t i = 0; i < ranges->size(); ++i) { | |
139 if (i > 0 && ranges_data[i] <= ranges_data[i - 1]) | |
140 bad_ranges = true; | |
141 ranges->set_range(i, ranges_data[i]); | |
142 } | |
143 ranges->ResetChecksum(); | |
144 if (bad_ranges || ranges->checksum() != histogram_data.ranges_checksum) { | |
145 LOG(WARNING) << "Persistent histogram referenced invalid ranges array" | |
146 << "; skipped."; | |
147 NOTREACHED(); | |
148 return nullptr; | |
149 } | |
150 const BucketRanges* registered_ranges = | |
151 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); | |
152 | |
153 HistogramBase::AtomicCount* counts_data = | |
154 allocator->GetAsObject<HistogramBase::AtomicCount>( | |
155 histogram_data.counts_ref, kTypeIdCountsArray); | |
156 if (!counts_data || | |
157 allocator->GetAllocSize(histogram_data.counts_ref) < | |
158 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) { | |
159 LOG(WARNING) << "Persistent histogram referenced invalid counts array" | |
160 << "; skipped."; | |
161 NOTREACHED(); | |
162 return nullptr; | |
163 } | |
164 | |
165 HistogramBase* histogram = nullptr; | |
166 switch (histogram_data.histogram_type) { | |
167 case HISTOGRAM: | |
168 histogram = new Histogram( | |
169 name, | |
170 histogram_data.minimum, | |
171 histogram_data.maximum, | |
172 registered_ranges, | |
173 counts_data, | |
174 histogram_data.bucket_count, | |
175 &histogram_data_ptr->samples_metadata); | |
176 break; | |
177 case LINEAR_HISTOGRAM: | |
178 histogram = new LinearHistogram( | |
179 name, | |
180 histogram_data.minimum, | |
181 histogram_data.maximum, | |
182 registered_ranges, | |
183 counts_data, | |
184 histogram_data.bucket_count, | |
185 &histogram_data_ptr->samples_metadata); | |
186 break; | |
187 case BOOLEAN_HISTOGRAM: | |
188 histogram = new BooleanHistogram( | |
189 name, | |
190 registered_ranges, | |
191 counts_data, | |
192 &histogram_data_ptr->samples_metadata); | |
193 break; | |
194 case CUSTOM_HISTOGRAM: | |
195 histogram = new CustomHistogram( | |
196 name, | |
197 registered_ranges, | |
198 counts_data, | |
199 histogram_data.bucket_count, | |
200 &histogram_data_ptr->samples_metadata); | |
201 break; | |
202 } | |
203 | |
204 if (histogram) { | |
205 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); | |
206 histogram->SetFlags(histogram_data.flags); | |
207 } | |
208 | |
209 return histogram; | |
210 } | |
211 | |
212 HistogramBase* AllocatePersistentHistogram( | |
213 PersistentMemoryAllocator* allocator, | |
214 HistogramType histogram_type, | |
215 const std::string& name, | |
216 int minimum, | |
217 int maximum, | |
218 const BucketRanges* bucket_ranges, | |
219 int32 flags, | |
220 PersistentMemoryAllocator::Reference* ref_ptr) { | |
221 if (allocator) { | |
222 size_t bucket_count = bucket_ranges->bucket_count(); | |
223 CHECK(bucket_count <= std::numeric_limits<int32_t>::max() / | |
224 sizeof(HistogramBase::AtomicCount)); | |
225 size_t counts_memory = bucket_count * sizeof(HistogramBase::AtomicCount); | |
226 size_t ranges_memory = (bucket_count + 1) * sizeof(HistogramBase::Sample); | |
227 PersistentMemoryAllocator::Reference name_ref = | |
228 allocator->Allocate(name.size() + 1, kTypeIdNameString); | |
229 PersistentMemoryAllocator::Reference ranges_ref = | |
230 allocator->Allocate(ranges_memory, kTypeIdRangesArray); | |
231 PersistentMemoryAllocator::Reference counts_ref = | |
232 allocator->Allocate(counts_memory, kTypeIdCountsArray); | |
233 PersistentMemoryAllocator::Reference histogram_ref = | |
234 allocator->Allocate(sizeof(PersistentHistogramData), kTypeIdHistogram); | |
235 char* name_data = allocator->GetAsObject<char>(name_ref, kTypeIdNameString); | |
236 HistogramBase::Sample* ranges_data = | |
237 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, | |
238 kTypeIdRangesArray); | |
239 PersistentHistogramData* histogram_data = | |
240 allocator->GetAsObject<PersistentHistogramData>(histogram_ref, | |
241 kTypeIdHistogram); | |
242 | |
243 // Only continue here if all allocations were successful. | |
Alexei Svitkine (slow)
2015/12/15 22:54:03
If only some of them were successful, what handles
bcwhite
2015/12/16 13:54:29
It's not possible to free anything. The memory wo
Alexei Svitkine (slow)
2015/12/16 16:33:49
Okay, that's fair. Please add a comment about that
bcwhite
2015/12/17 15:55:48
Done. Though I think I'm going to look at embeddi
| |
244 if (counts_ref && name_data && ranges_data && histogram_data) { | |
245 strcpy(name_data, name.c_str()); | |
246 for (size_t i = 0; i < bucket_ranges->size(); ++i) | |
247 ranges_data[i] = bucket_ranges->range(i); | |
248 | |
249 histogram_data->name_ref = name_ref; | |
250 histogram_data->name_length = name.size(); | |
251 histogram_data->histogram_type = histogram_type; | |
252 histogram_data->flags = flags; | |
253 histogram_data->minimum = minimum; | |
254 histogram_data->maximum = maximum; | |
255 histogram_data->bucket_count = bucket_count; | |
256 histogram_data->ranges_ref = ranges_ref; | |
257 histogram_data->ranges_checksum = bucket_ranges->checksum(); | |
258 histogram_data->counts_ref = counts_ref; | |
259 | |
260 // Create the histogram using resources in persistent memory. This ends up | |
261 // resolving the "ref" values stored in histogram_data instad of just | |
262 // using what is already known above but avoids duplicating the switch | |
263 // statement here and serves as a double-check that everything is | |
264 // correct before commiting the new histogram to persistent space. | |
265 HistogramBase* histogram = | |
266 CreatePersistentHistogram(allocator, histogram_data); | |
267 DCHECK(histogram); | |
268 if (ref_ptr != nullptr) | |
269 *ref_ptr = histogram_ref; | |
270 return histogram; | |
271 } | |
272 | |
273 LOG(WARNING) << "Could not create histogram \"" << name | |
274 << "\" in persistent memory (full=" << allocator->IsFull() | |
275 << ", corrupt=" << allocator->IsCorrupt() << ")"; | |
276 } | |
277 | |
278 return nullptr; | |
279 } | |
280 | |
281 void ImportPersistentHistograms() { | |
282 // Each call resumes from where it last left off so need persistant iterator. | |
283 // The lock protects against concurrent access to the iterator. | |
284 static PersistentMemoryAllocator::Iterator iter; | |
285 static base::Lock lock; | |
286 | |
287 if (allocator_) { | |
288 base::AutoLock auto_lock(lock); | |
289 if (iter.is_clear()) | |
290 allocator_->CreateIterator(&iter); | |
291 | |
292 for (;;) { | |
293 HistogramBase* h = GetNextPersistentHistogram(allocator_.get(), &iter); | |
294 if (!h) | |
295 break; | |
296 StatisticsRecorder::RegisterOrDeleteDuplicate(h); | |
297 } | |
298 } | |
299 } | |
300 | |
301 } // namespace base | |
OLD | NEW |