Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(338)

Side by Side Diff: base/metrics/histogram_base.cc

Issue 1425533011: Support "shared" histograms between processes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shmem-alloc
Patch Set: added a couple tests (and fixed related issues) Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/histogram_base.h" 5 #include "base/metrics/histogram_base.h"
6 6
7 #include <climits> 7 #include <climits>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/json/json_string_value_serializer.h" 10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/metrics/histogram_samples.h" 14 #include "base/metrics/histogram_samples.h"
15 #include "base/metrics/sparse_histogram.h" 15 #include "base/metrics/sparse_histogram.h"
16 #include "base/metrics/statistics_recorder.h" 16 #include "base/metrics/statistics_recorder.h"
17 #include "base/pickle.h" 17 #include "base/pickle.h"
18 #include "base/process/process_handle.h" 18 #include "base/process/process_handle.h"
19 #include "base/strings/stringprintf.h" 19 #include "base/strings/stringprintf.h"
20 #include "base/synchronization/lock.h"
20 #include "base/values.h" 21 #include "base/values.h"
21 22
22 namespace base { 23 namespace base {
23 24
25 // Type identifiers used when storing in persistent memory so they can be
26 // identified during extraction. A "version number" is added to the base
27 // so that, if the structure of that object changes, stored older versions
28 // will be safely ignored.
29 enum : uint32_t {
30 kTypeIdHistogram = 0xF1645910 + 1, // SHA1(Histogram) v1
31 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
32 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
33 kTypeIdNameString = 0x117DF971 + 1, // SHA1(NameString) v1
34 };
35
36 // This data must be held in persistent memory in order for processes to
37 // locate and use histograms created elsewhere.
38 struct HistogramBase::PersistentHistogramData {
39 PersistentMemoryAllocator::Reference name_ref;
40 size_t name_length;
41 int histogram_type;
42 int flags;
43 int minimum;
44 int maximum;
45 size_t bucket_count;
46 PersistentMemoryAllocator::Reference ranges_ref;
47 uint32_t ranges_checksum;
48 PersistentMemoryAllocator::Reference counts_ref;
49 HistogramSamples::Metadata samples_metadata;
50 };
51
24 std::string HistogramTypeToString(HistogramType type) { 52 std::string HistogramTypeToString(HistogramType type) {
25 switch (type) { 53 switch (type) {
26 case HISTOGRAM: 54 case HISTOGRAM:
27 return "HISTOGRAM"; 55 return "HISTOGRAM";
28 case LINEAR_HISTOGRAM: 56 case LINEAR_HISTOGRAM:
29 return "LINEAR_HISTOGRAM"; 57 return "LINEAR_HISTOGRAM";
30 case BOOLEAN_HISTOGRAM: 58 case BOOLEAN_HISTOGRAM:
31 return "BOOLEAN_HISTOGRAM"; 59 return "BOOLEAN_HISTOGRAM";
32 case CUSTOM_HISTOGRAM: 60 case CUSTOM_HISTOGRAM:
33 return "CUSTOM_HISTOGRAM"; 61 return "CUSTOM_HISTOGRAM";
(...skipping 20 matching lines...) Expand all
54 case CUSTOM_HISTOGRAM: 82 case CUSTOM_HISTOGRAM:
55 return CustomHistogram::DeserializeInfoImpl(iter); 83 return CustomHistogram::DeserializeInfoImpl(iter);
56 case SPARSE_HISTOGRAM: 84 case SPARSE_HISTOGRAM:
57 return SparseHistogram::DeserializeInfoImpl(iter); 85 return SparseHistogram::DeserializeInfoImpl(iter);
58 default: 86 default:
59 return NULL; 87 return NULL;
60 } 88 }
61 } 89 }
62 90
63 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; 91 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
92 scoped_ptr<PersistentMemoryAllocator> HistogramBase::allocator_;
93
94 // Extract a histogram from persistent memory. Unfortunately, the above "pickle"
95 // methods cannot be used as part of the persistance because the deserialization
96 // methods always create local count data (these must referenced the persistent
97 // counts) and always add it to the local list of known histograms (these may
98 // be simple references to histograms in other processes).
99 // static
100 HistogramBase* HistogramBase::GetPersistentHistogram(
101 PersistentMemoryAllocator* allocator,
102 int32_t ref) {
103 PersistentHistogramData* histogram_data =
104 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram);
105 return CreatePersistentHistogram(allocator, histogram_data);
106 }
107
108 // static
109 HistogramBase* HistogramBase::GetNextPersistentHistogram(
110 PersistentMemoryAllocator* allocator,
111 PersistentMemoryAllocator::Iterator* iter) {
112 PersistentMemoryAllocator::Reference ref;
113 uint32_t type_id;
114 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) {
115 if (type_id == kTypeIdHistogram)
116 return GetPersistentHistogram(allocator, ref);
117 }
118 return nullptr;
119 }
120
121 // static
122 HistogramBase* HistogramBase::CreatePersistentHistogram(
123 PersistentMemoryAllocator* allocator,
124 PersistentHistogramData* histogram_data_ptr) {
125 if (!histogram_data_ptr) {
126 LOG(WARNING) << "Persistent histogram data was not valid; skipped.";
127 NOTREACHED();
128 return nullptr;
129 }
130
131 // Copy the histogram_data to local storage because anything in persistent
132 // memory cannot be trusted as it could be changed at any moment by a
133 // malicious actor that shares access. The contents of histogram_data are
134 // validated below; the local copy is to ensure that the contents cannot
135 // be externally changed between validation and use.
136 PersistentHistogramData histogram_data = *histogram_data_ptr;
137
138 char* name_data =
139 allocator->GetAsObject<char>(histogram_data.name_ref, kTypeIdNameString);
140 size_t name_length = histogram_data.name_length;
141 if (!name_data ||
142 name_length >= allocator->GetAllocSize(histogram_data.name_ref) ||
143 name_data[name_length] != 0) {
144 LOG(WARNING) << "Persistent histogram referenced invalid name string"
145 << "; skipped.";
146 NOTREACHED();
147 return nullptr;
148 }
149 std::string name(name_data, name_length);
150
151 HistogramBase::Sample* ranges_data =
152 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref,
153 kTypeIdRangesArray);
154 if (!ranges_data || histogram_data.bucket_count < 2 ||
155 histogram_data.bucket_count + 1 >
156 std::numeric_limits<size_t>::max() / sizeof(HistogramBase::Sample) ||
157 allocator->GetAllocSize(histogram_data.ranges_ref) <
158 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) {
159 LOG(WARNING) << "Persistent histogram referenced invalid ranges array"
160 << "; skipped.";
161 NOTREACHED();
162 return nullptr;
163 }
164 // To avoid racy destruction at shutdown, the following will be leaked.
165 BucketRanges* ranges = new BucketRanges(histogram_data.bucket_count + 1);
166 bool bad_ranges = false;
167 for (size_t i = 0; i < ranges->size(); ++i) {
168 if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
169 bad_ranges = true;
170 ranges->set_range(i, ranges_data[i]);
171 }
172 ranges->ResetChecksum();
173 if (bad_ranges || ranges->checksum() != histogram_data.ranges_checksum) {
174 LOG(WARNING) << "Persistent histogram referenced invalid ranges array"
175 << "; skipped.";
176 NOTREACHED();
177 return nullptr;
178 }
179 const BucketRanges* registered_ranges =
180 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
181
182 HistogramBase::AtomicCount* counts_data =
183 allocator->GetAsObject<HistogramBase::AtomicCount>(
184 histogram_data.counts_ref, kTypeIdCountsArray);
185 if (!counts_data ||
186 allocator->GetAllocSize(histogram_data.counts_ref) <
187 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) {
188 LOG(WARNING) << "Persistent histogram referenced invalid counts array"
189 << "; skipped.";
190 NOTREACHED();
191 return nullptr;
192 }
193
194 HistogramBase* histogram = nullptr;
195 switch (histogram_data.histogram_type) {
196 case HISTOGRAM:
197 histogram = new Histogram(
198 name,
199 histogram_data.minimum,
200 histogram_data.maximum,
201 registered_ranges,
202 counts_data,
203 histogram_data.bucket_count,
204 &histogram_data_ptr->samples_metadata);
205 break;
206 case LINEAR_HISTOGRAM:
207 histogram = new LinearHistogram(
208 name,
209 histogram_data.minimum,
210 histogram_data.maximum,
211 registered_ranges,
212 counts_data,
213 histogram_data.bucket_count,
214 &histogram_data_ptr->samples_metadata);
215 break;
216 case BOOLEAN_HISTOGRAM:
217 histogram = new BooleanHistogram(
218 name,
219 registered_ranges,
220 counts_data,
221 &histogram_data_ptr->samples_metadata);
222 break;
223 case CUSTOM_HISTOGRAM:
224 histogram = new CustomHistogram(
225 name,
226 registered_ranges,
227 counts_data,
228 histogram_data.bucket_count,
229 &histogram_data_ptr->samples_metadata);
230 break;
231 }
232
233 if (histogram) {
234 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType());
235 histogram->SetFlags(histogram_data.flags);
236 }
237
238 return histogram;
239 }
240
241 // static
242 HistogramBase* HistogramBase::AllocatePersistentHistogram(
243 PersistentMemoryAllocator* allocator,
244 HistogramType histogram_type,
245 const std::string& name,
246 int minimum,
247 int maximum,
248 const BucketRanges* bucket_ranges,
249 int32 flags,
250 PersistentMemoryAllocator::Reference* ref_ptr) {
251 if (allocator) {
252 size_t bucket_count = bucket_ranges->bucket_count();
253 CHECK(bucket_count <= std::numeric_limits<int32_t>::max() /
254 sizeof(HistogramBase::AtomicCount));
255 size_t counts_memory = bucket_count * sizeof(HistogramBase::AtomicCount);
256 size_t ranges_memory = (bucket_count + 1) * sizeof(HistogramBase::Sample);
257 PersistentMemoryAllocator::Reference name_ref =
258 allocator->Allocate(name.size() + 1, kTypeIdNameString);
259 PersistentMemoryAllocator::Reference ranges_ref =
260 allocator->Allocate(ranges_memory, kTypeIdRangesArray);
261 PersistentMemoryAllocator::Reference counts_ref =
262 allocator->Allocate(counts_memory, kTypeIdCountsArray);
263 PersistentMemoryAllocator::Reference histogram_ref =
264 allocator->Allocate(sizeof(PersistentHistogramData), kTypeIdHistogram);
265 char* name_data = allocator->GetAsObject<char>(name_ref, kTypeIdNameString);
266 HistogramBase::Sample* ranges_data =
267 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref,
268 kTypeIdRangesArray);
269 PersistentHistogramData* histogram_data =
270 allocator->GetAsObject<PersistentHistogramData>(histogram_ref,
271 kTypeIdHistogram);
272
273 // Only continue here if all allocations were successful.
274 if (counts_ref && name_data && ranges_data && histogram_data) {
275 strcpy(name_data, name.c_str());
276 for (size_t i = 0; i < bucket_ranges->size(); ++i)
277 ranges_data[i] = bucket_ranges->range(i);
278
279 histogram_data->name_ref = name_ref;
280 histogram_data->name_length = name.size();
281 histogram_data->histogram_type = histogram_type;
282 histogram_data->flags = flags;
283 histogram_data->minimum = minimum;
284 histogram_data->maximum = maximum;
285 histogram_data->bucket_count = bucket_count;
286 histogram_data->ranges_ref = ranges_ref;
287 histogram_data->ranges_checksum = bucket_ranges->checksum();
288 histogram_data->counts_ref = counts_ref;
289
290 // Create the histogram using resources in persistent memory. This ends up
291 // resolving the "ref" values stored in histogram_data instad of just
292 // using what is already known above but avoids duplicating the switch
293 // statement here and serves as a double-check that everything is
294 // correct before commiting the new histogram to persistent space.
295 HistogramBase* histogram =
296 CreatePersistentHistogram(allocator, histogram_data);
297 DCHECK(histogram);
298 if (ref_ptr != nullptr)
299 *ref_ptr = histogram_ref;
300 return histogram;
301 }
302
303 LOG(WARNING) << "Could not create histogram \"" << name
304 << "\" in persistent memory (full=" << allocator->IsFull()
305 << ", corrupt=" << allocator->IsCorrupt() << ")";
306 }
307
308 return nullptr;
309 }
310
311 // static
312 void HistogramBase::ImportPersistentHistograms() {
313 // Each call resumes from where it last left off so need persistant iterator.
314 // The lock protects against concurrent access to the iterator.
315 static PersistentMemoryAllocator::Iterator iter;
316 static base::Lock lock;
317
318 if (allocator_) {
319 base::AutoLock auto_lock(lock);
320 if (iter.is_clear())
321 allocator_->CreateIterator(&iter);
322
323 for (;;) {
324 HistogramBase* h = GetNextPersistentHistogram(allocator_.get(), &iter);
325 if (!h)
326 break;
327 StatisticsRecorder::RegisterOrDeleteDuplicate(h);
328 }
329 }
330 }
64 331
65 HistogramBase::HistogramBase(const std::string& name) 332 HistogramBase::HistogramBase(const std::string& name)
66 : histogram_name_(name), 333 : histogram_name_(name),
67 flags_(kNoFlags) {} 334 flags_(kNoFlags) {}
68 335
69 HistogramBase::~HistogramBase() {} 336 HistogramBase::~HistogramBase() {}
70 337
71 void HistogramBase::CheckName(const StringPiece& name) const { 338 void HistogramBase::CheckName(const StringPiece& name) const {
72 DCHECK_EQ(histogram_name(), name); 339 DCHECK_EQ(histogram_name(), name);
73 } 340 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const { 391 void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const {
125 if ((flags() & kCallbackExists) == 0) 392 if ((flags() & kCallbackExists) == 0)
126 return; 393 return;
127 394
128 StatisticsRecorder::OnSampleCallback cb = 395 StatisticsRecorder::OnSampleCallback cb =
129 StatisticsRecorder::FindCallback(histogram_name()); 396 StatisticsRecorder::FindCallback(histogram_name());
130 if (!cb.is_null()) 397 if (!cb.is_null())
131 cb.Run(sample); 398 cb.Run(sample);
132 } 399 }
133 400
401 // static
402 void HistogramBase::SetDefaultPersistentMemoryAllocator(
403 PersistentMemoryAllocator* allocator) {
404 // Releasing or changing an allocator is extremely dangerous because it
405 // likely has histograms stored within it. If the backing memory is also
406 // also released, future accesses to those histograms will seg-fault.
407 // It's not a fatal CHECK() because tests do this knowing that all
408 // such persistent histograms have already been forgotten.
409 if (allocator_) {
410 LOG(WARNING) << "Active PersistentMemoryAllocator has been released."
411 << " Some existing histogram pointers may be invalid.";
412 }
413 allocator_.reset(allocator);
414 }
415
134 void HistogramBase::WriteAsciiBucketGraph(double current_size, 416 void HistogramBase::WriteAsciiBucketGraph(double current_size,
135 double max_size, 417 double max_size,
136 std::string* output) const { 418 std::string* output) const {
137 const int k_line_length = 72; // Maximal horizontal width of graph. 419 const int k_line_length = 72; // Maximal horizontal width of graph.
138 int x_count = static_cast<int>(k_line_length * (current_size / max_size) 420 int x_count = static_cast<int>(k_line_length * (current_size / max_size)
139 + 0.5); 421 + 0.5);
140 int x_remainder = k_line_length - x_count; 422 int x_remainder = k_line_length - x_count;
141 423
142 while (0 < x_count--) 424 while (0 < x_count--)
143 output->append("-"); 425 output->append("-");
(...skipping 12 matching lines...) Expand all
156 return result; 438 return result;
157 } 439 }
158 440
159 void HistogramBase::WriteAsciiBucketValue(Count current, 441 void HistogramBase::WriteAsciiBucketValue(Count current,
160 double scaled_sum, 442 double scaled_sum,
161 std::string* output) const { 443 std::string* output) const {
162 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); 444 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
163 } 445 }
164 446
165 } // namespace base 447 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698