OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "components/metrics/persistent_system_profile.h" | 5 #include "components/metrics/persistent_system_profile.h" |
6 | 6 |
7 #include <set> | |
8 | |
7 #include "base/atomicops.h" | 9 #include "base/atomicops.h" |
8 #include "base/memory/singleton.h" | 10 #include "base/memory/singleton.h" |
9 #include "base/metrics/persistent_memory_allocator.h" | 11 #include "base/metrics/persistent_memory_allocator.h" |
12 #include "base/pickle.h" | |
10 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
14 #include "components/variations/active_field_trials.h" | |
11 | 15 |
12 namespace metrics { | 16 namespace metrics { |
13 | 17 |
14 namespace { | 18 namespace { |
15 | 19 |
16 // To provide atomic addition of records so that there is no confusion between | 20 // To provide atomic addition of records so that there is no confusion between |
17 // writers and readers, all of the metadata about a record is contained in a | 21 // writers and readers, all of the metadata about a record is contained in a |
18 // structure that can be stored as a single atomic 32-bit word. | 22 // structure that can be stored as a single atomic 32-bit word. |
19 union RecordHeader { | 23 union RecordHeader { |
20 struct { | 24 struct { |
21 unsigned continued : 1; // Flag indicating if there is more after this. | 25 unsigned continued : 1; // Flag indicating if there is more after this. |
22 unsigned type : 7; // The type of this record. | 26 unsigned type : 7; // The type of this record. |
23 unsigned amount : 24; // The amount of data to follow. | 27 unsigned amount : 24; // The amount of data to follow. |
24 } as_parts; | 28 } as_parts; |
25 base::subtle::Atomic32 as_atomic; | 29 base::subtle::Atomic32 as_atomic; |
26 }; | 30 }; |
27 | 31 |
28 constexpr uint32_t kTypeIdSystemProfile = 0x330A7150; // SHA1(SystemProfile) | 32 constexpr uint32_t kTypeIdSystemProfile = 0x330A7150; // SHA1(SystemProfile) |
29 constexpr size_t kSystemProfileAllocSize = 4 << 10; // 4 KiB | 33 constexpr size_t kSystemProfileAllocSize = 4 << 10; // 4 KiB |
30 constexpr size_t kMaxRecordSize = (1 << 24) - sizeof(RecordHeader); | 34 constexpr size_t kMaxRecordSize = (1 << 24) - sizeof(RecordHeader); |
31 | 35 |
32 static_assert(sizeof(RecordHeader) == sizeof(base::subtle::Atomic32), | 36 static_assert(sizeof(RecordHeader) == sizeof(base::subtle::Atomic32), |
33 "bad RecordHeader size"); | 37 "bad RecordHeader size"); |
34 | 38 |
35 // Calculate the size of a record based on the amount of data. This adds room | 39 // Calculate the size of a record based on the amount of data. This adds room |
36 // for the record header and rounds up to the next multiple of the record-header | 40 // for the record header and rounds up to the next multiple of the record-header |
37 // size. | 41 // size. |
38 size_t CalculateRecordSize(size_t data_amount) { | 42 size_t CalculateRecordSize(size_t data_amount) { |
39 return (data_amount + sizeof(RecordHeader) + sizeof(RecordHeader) - 1) & | 43 return (data_amount + sizeof(RecordHeader) + sizeof(RecordHeader) - 1) & |
40 ~(sizeof(RecordHeader) - 1); | 44 ~(sizeof(RecordHeader) - 1); |
Alexei Svitkine (slow)
2017/06/28 14:59:00
I recently noticed we have base::Align():
https:/
bcwhite
2017/06/28 16:43:31
Done.
| |
41 } | 45 } |
42 | 46 |
43 } // namespace | 47 } // namespace |
44 | 48 |
45 PersistentSystemProfile::RecordAllocator::RecordAllocator( | 49 PersistentSystemProfile::RecordAllocator::RecordAllocator( |
46 base::PersistentMemoryAllocator* memory_allocator, | 50 base::PersistentMemoryAllocator* memory_allocator, |
47 size_t min_size) | 51 size_t min_size) |
48 : allocator_(memory_allocator), | 52 : allocator_(memory_allocator), |
49 has_complete_profile_(false), | 53 has_complete_profile_(false), |
50 alloc_reference_(0), | 54 alloc_reference_(0), |
(...skipping 24 matching lines...) Expand all Loading... | |
75 base::subtle::NoBarrier_Store(&header->as_atomic, 0); | 79 base::subtle::NoBarrier_Store(&header->as_atomic, 0); |
76 } | 80 } |
77 | 81 |
78 // Reset member variables. | 82 // Reset member variables. |
79 has_complete_profile_ = false; | 83 has_complete_profile_ = false; |
80 alloc_reference_ = 0; | 84 alloc_reference_ = 0; |
81 alloc_size_ = 0; | 85 alloc_size_ = 0; |
82 end_offset_ = 0; | 86 end_offset_ = 0; |
83 } | 87 } |
84 | 88 |
85 bool PersistentSystemProfile::RecordAllocator::Write( | 89 bool PersistentSystemProfile::RecordAllocator::Write(RecordType type, |
86 RecordType type, | 90 base::StringPiece record) { |
87 const std::string& record) { | |
88 const char* data = record.data(); | 91 const char* data = record.data(); |
89 size_t remaining_size = record.size(); | 92 size_t remaining_size = record.size(); |
90 | 93 |
91 // Allocate space and write records until everything has been stored. | 94 // Allocate space and write records until everything has been stored. |
92 do { | 95 do { |
93 if (end_offset_ == alloc_size_) { | 96 if (end_offset_ == alloc_size_) { |
94 if (!AddSegment(remaining_size)) | 97 if (!AddSegment(remaining_size)) |
95 return false; | 98 return false; |
96 } | 99 } |
97 // Write out as much of the data as possible. |data| and |remaining_size| | 100 // Write out as much of the data as possible. |data| and |remaining_size| |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 allocator_->MakeIterable(ref); | 168 allocator_->MakeIterable(ref); |
166 | 169 |
167 alloc_reference_ = ref; | 170 alloc_reference_ = ref; |
168 alloc_size_ = allocator_->GetAllocSize(ref); | 171 alloc_size_ = allocator_->GetAllocSize(ref); |
169 return true; | 172 return true; |
170 } | 173 } |
171 | 174 |
172 bool PersistentSystemProfile::RecordAllocator::WriteData( | 175 bool PersistentSystemProfile::RecordAllocator::WriteData( |
173 RecordType type, | 176 RecordType type, |
174 const char** data, | 177 const char** data, |
175 size_t* remaining_size) { | 178 size_t* remaining_size) { |
Alexei Svitkine (slow)
2017/06/28 14:59:01
Reading the code again, I think it would be cleare
bcwhite
2017/06/28 16:43:31
Done.
| |
176 char* block = | 179 char* block = |
177 allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile, | 180 allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile, |
178 base::PersistentMemoryAllocator::kSizeAny); | 181 base::PersistentMemoryAllocator::kSizeAny); |
179 if (!block) | 182 if (!block) |
180 return false; // It's bad if there is no accessible block. | 183 return false; // It's bad if there is no accessible block. |
181 | 184 |
182 size_t write_size = std::min(*remaining_size, kMaxRecordSize); | 185 size_t write_size = std::min(*remaining_size, kMaxRecordSize); |
183 write_size = | 186 write_size = |
184 std::min(write_size, alloc_size_ - end_offset_ - sizeof(RecordHeader)); | 187 std::min(write_size, alloc_size_ - end_offset_ - sizeof(RecordHeader)); |
185 | 188 |
186 // Write the data and the record header. | 189 // Write the data and the record header. |
187 RecordHeader header; | 190 RecordHeader header; |
188 header.as_atomic = 0; | 191 header.as_atomic = 0; |
189 header.as_parts.type = type; | 192 header.as_parts.type = type; |
190 header.as_parts.amount = write_size; | 193 header.as_parts.amount = write_size; |
191 header.as_parts.continued = (write_size < *remaining_size); | 194 header.as_parts.continued = (write_size < *remaining_size); |
192 size_t offset = end_offset_; | 195 size_t offset = end_offset_; |
193 end_offset_ += CalculateRecordSize(write_size); | 196 end_offset_ += CalculateRecordSize(write_size); |
194 DCHECK_GE(alloc_size_, end_offset_); | 197 DCHECK_GE(alloc_size_, end_offset_); |
195 if (end_offset_ < alloc_size_) { | 198 if (end_offset_ < alloc_size_) { |
196 // An empty record header has to be next before this one gets written. | 199 // An empty record header has to be next before this one gets written. |
197 base::subtle::NoBarrier_Store( | 200 base::subtle::NoBarrier_Store( |
198 reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_), 0); | 201 reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_), 0); |
199 } | 202 } |
200 memcpy(block + offset + sizeof(header), *data, write_size); | 203 memcpy(block + offset + sizeof(header), *data, write_size); |
201 base::subtle::Release_Store(reinterpret_cast<base::subtle::Atomic32*>(block), | 204 base::subtle::Release_Store( |
202 header.as_atomic); | 205 reinterpret_cast<base::subtle::Atomic32*>(block + offset), |
Alexei Svitkine (slow)
2017/06/28 14:59:00
Wait, so this is now writing the header at block+o
bcwhite
2017/06/28 16:43:30
It was an error where each additional record had i
| |
206 header.as_atomic); | |
203 | 207 |
204 // Account for what was stored and prepare for follow-on records with any | 208 // Account for what was stored and prepare for follow-on records with any |
205 // remaining data. | 209 // remaining data. |
206 *data += write_size; | 210 *data += write_size; |
207 *remaining_size -= write_size; | 211 *remaining_size -= write_size; |
208 | 212 |
209 return true; | 213 return true; |
210 } | 214 } |
211 | 215 |
212 bool PersistentSystemProfile::RecordAllocator::ReadData( | 216 bool PersistentSystemProfile::RecordAllocator::ReadData( |
(...skipping 19 matching lines...) Expand all Loading... | |
232 return true; // End of all records. | 236 return true; // End of all records. |
233 } else if (*type == kUnusedSpace) { | 237 } else if (*type == kUnusedSpace) { |
234 *type = static_cast<RecordType>(header.as_parts.type); | 238 *type = static_cast<RecordType>(header.as_parts.type); |
235 } else if (*type != header.as_parts.type) { | 239 } else if (*type != header.as_parts.type) { |
236 NOTREACHED(); // Continuation didn't match start of record. | 240 NOTREACHED(); // Continuation didn't match start of record. |
237 *type = kUnusedSpace; | 241 *type = kUnusedSpace; |
238 record->clear(); | 242 record->clear(); |
239 return false; | 243 return false; |
240 } | 244 } |
241 size_t read_size = header.as_parts.amount; | 245 size_t read_size = header.as_parts.amount; |
242 if (read_size < sizeof(header) || | 246 if (end_offset_ + sizeof(header) + read_size > alloc_size_) { |
243 end_offset_ + sizeof(header) + read_size > alloc_size_) { | |
244 NOTREACHED(); // Invalid header amount. | 247 NOTREACHED(); // Invalid header amount. |
245 *type = kUnusedSpace; | 248 *type = kUnusedSpace; |
246 return true; // Don't try again. | 249 return true; // Don't try again. |
247 } | 250 } |
248 | 251 |
249 // Append the record data to the output string. | 252 // Append the record data to the output string. |
250 record->append(block + sizeof(header), read_size); | 253 record->append(block + end_offset_ + sizeof(header), read_size); |
251 end_offset_ += CalculateRecordSize(read_size); | 254 end_offset_ += CalculateRecordSize(read_size); |
252 DCHECK_GE(alloc_size_, end_offset_); | 255 DCHECK_GE(alloc_size_, end_offset_); |
253 | 256 |
254 return !continued; | 257 return !continued; |
255 } | 258 } |
256 | 259 |
257 PersistentSystemProfile::PersistentSystemProfile() {} | 260 PersistentSystemProfile::PersistentSystemProfile() {} |
258 | 261 |
259 PersistentSystemProfile::~PersistentSystemProfile() {} | 262 PersistentSystemProfile::~PersistentSystemProfile() {} |
260 | 263 |
(...skipping 24 matching lines...) Expand all Loading... | |
285 bool complete) { | 288 bool complete) { |
286 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 289 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
287 | 290 |
288 if (allocators_.empty() || serialized_profile.empty()) | 291 if (allocators_.empty() || serialized_profile.empty()) |
289 return; | 292 return; |
290 | 293 |
291 for (auto& allocator : allocators_) { | 294 for (auto& allocator : allocators_) { |
292 // Don't overwrite a complete profile with an incomplete one. | 295 // Don't overwrite a complete profile with an incomplete one. |
293 if (!complete && allocator.has_complete_profile()) | 296 if (!complete && allocator.has_complete_profile()) |
294 continue; | 297 continue; |
295 // A full system profile always starts fresh. | 298 // A full system profile always starts fresh. Incomplete keeps existing |
296 allocator.Reset(); | 299 // records for merging. |
300 if (complete) | |
301 allocator.Reset(); | |
297 // Write out the serialized profile. | 302 // Write out the serialized profile. |
298 allocator.Write(kSystemProfileProto, serialized_profile); | 303 allocator.Write(kSystemProfileProto, serialized_profile); |
299 // Indicate if this is a complete profile. | 304 // Indicate if this is a complete profile. |
300 if (complete) | 305 if (complete) |
301 allocator.set_complete_profile(); | 306 allocator.set_complete_profile(); |
302 } | 307 } |
303 | 308 |
304 if (complete) | 309 if (complete) |
305 all_have_complete_profile_ = true; | 310 all_have_complete_profile_ = true; |
306 } | 311 } |
307 | 312 |
308 void PersistentSystemProfile::SetSystemProfile( | 313 void PersistentSystemProfile::SetSystemProfile( |
309 const SystemProfileProto& profile, | 314 const SystemProfileProto& profile, |
310 bool complete) { | 315 bool complete) { |
311 // Avoid serialization if passed profile is not complete and all allocators | 316 // Avoid serialization if passed profile is not complete and all allocators |
312 // already have complete ones. | 317 // already have complete ones. |
313 if (!complete && all_have_complete_profile_) | 318 if (!complete && all_have_complete_profile_) |
314 return; | 319 return; |
315 | 320 |
316 std::string serialized_profile; | 321 std::string serialized_profile; |
317 if (!profile.SerializeToString(&serialized_profile)) | 322 if (!profile.SerializeToString(&serialized_profile)) |
318 return; | 323 return; |
319 SetSystemProfile(serialized_profile, complete); | 324 SetSystemProfile(serialized_profile, complete); |
320 } | 325 } |
321 | 326 |
327 void PersistentSystemProfile::SetFieldTrial(base::StringPiece trial, | |
328 base::StringPiece group) { | |
329 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
330 | |
331 if (trial.empty() || group.empty()) | |
Alexei Svitkine (slow)
2017/06/28 14:59:00
This shouldn't be possible. Can these be DCHECKs i
bcwhite
2017/06/28 16:43:30
Done.
| |
332 return; | |
333 | |
334 base::Pickle pickler; | |
335 if (!pickler.WriteString(trial) || !pickler.WriteString(group)) | |
336 return; | |
337 | |
338 WriteToAll(kFieldTrialInfo, | |
339 base::StringPiece(static_cast<const char*>(pickler.data()), | |
340 pickler.size())); | |
341 } | |
342 | |
322 // static | 343 // static |
323 bool PersistentSystemProfile::HasSystemProfile( | 344 bool PersistentSystemProfile::HasSystemProfile( |
324 const base::PersistentMemoryAllocator& memory_allocator) { | 345 const base::PersistentMemoryAllocator& memory_allocator) { |
325 const RecordAllocator records(&memory_allocator); | 346 const RecordAllocator records(&memory_allocator); |
326 return records.HasMoreData(); | 347 return records.HasMoreData(); |
327 } | 348 } |
328 | 349 |
329 // static | 350 // static |
330 bool PersistentSystemProfile::GetSystemProfile( | 351 bool PersistentSystemProfile::GetSystemProfile( |
331 const base::PersistentMemoryAllocator& memory_allocator, | 352 const base::PersistentMemoryAllocator& memory_allocator, |
332 SystemProfileProto* system_profile) { | 353 SystemProfileProto* system_profile) { |
333 const RecordAllocator records(&memory_allocator); | 354 const RecordAllocator records(&memory_allocator); |
334 | 355 |
335 RecordType type; | 356 RecordType type; |
336 std::string record; | 357 std::string record; |
337 if (!records.Read(&type, &record)) | 358 do { |
338 return false; | 359 if (!records.Read(&type, &record)) |
339 if (type != kSystemProfileProto) | 360 return false; |
361 } while (type != kSystemProfileProto); | |
362 | |
363 if (!system_profile->ParseFromString(record)) | |
340 return false; | 364 return false; |
341 | 365 |
342 return system_profile->ParseFromString(record); | 366 MergeUpdateRecords(memory_allocator, system_profile); |
367 return true; | |
368 } | |
369 | |
370 // static | |
371 void PersistentSystemProfile::MergeUpdateRecords( | |
372 const base::PersistentMemoryAllocator& memory_allocator, | |
373 SystemProfileProto* system_profile) { | |
374 const RecordAllocator records(&memory_allocator); | |
375 | |
376 RecordType type; | |
377 std::string record; | |
378 std::set<uint32_t> known_field_trial_ids; | |
379 | |
380 while (records.Read(&type, &record)) { | |
Alexei Svitkine (slow)
2017/06/28 14:59:01
Maybe add a comment about why this can't be done i
bcwhite
2017/06/28 16:43:31
Mostly it just made a nice, independent block of c
| |
381 switch (type) { | |
382 case kUnusedSpace: | |
383 // These should never be returned. | |
384 NOTREACHED(); | |
385 break; | |
386 | |
387 case kSystemProfileProto: | |
388 // Profile was passed in; ignore this one. | |
389 break; | |
390 | |
391 case kFieldTrialInfo: { | |
392 // Get the set of known trial IDs so duplicates don't get added. | |
393 if (known_field_trial_ids.empty()) { | |
394 for (int i = 0; i < system_profile->field_trial_size(); ++i) { | |
395 const SystemProfileProto::FieldTrial& field_trial = | |
396 system_profile->field_trial(i); | |
Alexei Svitkine (slow)
2017/06/28 14:59:00
Nit: Inline this into the line below.
bcwhite
2017/06/28 16:43:31
Done.
| |
397 known_field_trial_ids.insert(field_trial.name_id()); | |
398 } | |
399 } | |
400 | |
401 base::Pickle pickler(record.data(), record.size()); | |
402 base::PickleIterator iter(pickler); | |
403 base::StringPiece trial; | |
404 base::StringPiece group; | |
405 if (iter.ReadStringPiece(&trial) && iter.ReadStringPiece(&group)) { | |
406 variations::ActiveGroupId field_ids = | |
407 variations::MakeActiveGroupId(trial, group); | |
Alexei Svitkine (slow)
2017/06/28 14:59:00
Hmm, maybe we can just store ActiveGroupId in the
bcwhite
2017/06/28 16:43:31
Yes. However, that means calculating the two SHA1
| |
408 if (!base::ContainsKey(known_field_trial_ids, field_ids.name)) { | |
409 SystemProfileProto::FieldTrial* field_trial = | |
410 system_profile->add_field_trial(); | |
411 field_trial->set_name_id(field_ids.name); | |
412 field_trial->set_group_id(field_ids.group); | |
413 known_field_trial_ids.insert(field_ids.name); | |
414 } | |
415 } | |
416 } break; | |
417 } | |
418 } | |
419 } | |
420 | |
421 void PersistentSystemProfile::WriteToAll(RecordType type, | |
422 base::StringPiece record) { | |
423 for (auto& allocator : allocators_) | |
424 allocator.Write(type, record); | |
343 } | 425 } |
344 | 426 |
345 GlobalPersistentSystemProfile* GlobalPersistentSystemProfile::GetInstance() { | 427 GlobalPersistentSystemProfile* GlobalPersistentSystemProfile::GetInstance() { |
346 return base::Singleton< | 428 return base::Singleton< |
347 GlobalPersistentSystemProfile, | 429 GlobalPersistentSystemProfile, |
348 base::LeakySingletonTraits<GlobalPersistentSystemProfile>>::get(); | 430 base::LeakySingletonTraits<GlobalPersistentSystemProfile>>::get(); |
349 } | 431 } |
350 | 432 |
351 } // namespace metrics | 433 } // namespace metrics |
OLD | NEW |