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

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

Issue 1425533011: Support "shared" histograms between processes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shmem-alloc
Patch Set: extract common histogram FactoryGet code; extract histogram persistence into seperate files 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
(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.
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698