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

Side by Side Diff: components/metrics/persistent_system_profile.cc

Issue 2950173003: Support add-on 'field trial' records. (Closed)
Patch Set: renamed method Created 3 years, 5 months 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 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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698