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