persistent_system_profile.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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
  3. // found in the LICENSE file.
  4. #include "components/metrics/persistent_system_profile.h"
  5. #include <set>
  6. #include "base/atomicops.h"
  7. #include "base/bits.h"
  8. #include "base/containers/contains.h"
  9. #include "base/containers/cxx20_erase.h"
  10. #include "base/memory/singleton.h"
  11. #include "base/metrics/persistent_memory_allocator.h"
  12. #include "base/notreached.h"
  13. #include "base/pickle.h"
  14. #include "components/variations/active_field_trials.h"
  15. namespace metrics {
  16. namespace {
  17. // To provide atomic addition of records so that there is no confusion between
  18. // writers and readers, all of the metadata about a record is contained in a
  19. // structure that can be stored as a single atomic 32-bit word.
  20. union RecordHeader {
  21. struct {
  22. unsigned continued : 1; // Flag indicating if there is more after this.
  23. unsigned type : 7; // The type of this record.
  24. unsigned amount : 24; // The amount of data to follow.
  25. } as_parts;
  26. base::subtle::Atomic32 as_atomic;
  27. };
  28. constexpr uint32_t kTypeIdSystemProfile = 0x330A7150; // SHA1(SystemProfile)
  29. constexpr size_t kSystemProfileAllocSize = 4 << 10; // 4 KiB
  30. constexpr size_t kMaxRecordSize = (1 << 24) - sizeof(RecordHeader);
  31. static_assert(sizeof(RecordHeader) == sizeof(base::subtle::Atomic32),
  32. "bad RecordHeader size");
  33. // Calculate the size of a record based on the amount of data. This adds room
  34. // for the record header and rounds up to the next multiple of the record-header
  35. // size.
  36. size_t CalculateRecordSize(size_t data_amount) {
  37. return base::bits::AlignUp(data_amount + sizeof(RecordHeader),
  38. sizeof(RecordHeader));
  39. }
  40. } // namespace
  41. PersistentSystemProfile::RecordAllocator::RecordAllocator(
  42. base::PersistentMemoryAllocator* memory_allocator,
  43. size_t min_size)
  44. : allocator_(memory_allocator),
  45. has_complete_profile_(false),
  46. alloc_reference_(0),
  47. alloc_size_(0),
  48. end_offset_(0) {
  49. AddSegment(min_size);
  50. }
  51. PersistentSystemProfile::RecordAllocator::RecordAllocator(
  52. const base::PersistentMemoryAllocator* memory_allocator)
  53. : allocator_(
  54. const_cast<base::PersistentMemoryAllocator*>(memory_allocator)),
  55. alloc_reference_(0),
  56. alloc_size_(0),
  57. end_offset_(0) {}
  58. void PersistentSystemProfile::RecordAllocator::Reset() {
  59. // Clear the first word of all blocks so they're known to be "empty".
  60. alloc_reference_ = 0;
  61. while (NextSegment()) {
  62. // Get the block as a char* and cast it. It can't be fetched directly as
  63. // an array of RecordHeader because that's not a fundamental type and only
  64. // arrays of fundamental types are allowed.
  65. RecordHeader* header =
  66. reinterpret_cast<RecordHeader*>(allocator_->GetAsArray<char>(
  67. alloc_reference_, kTypeIdSystemProfile, sizeof(RecordHeader)));
  68. DCHECK(header);
  69. base::subtle::NoBarrier_Store(&header->as_atomic, 0);
  70. }
  71. // Reset member variables.
  72. has_complete_profile_ = false;
  73. alloc_reference_ = 0;
  74. alloc_size_ = 0;
  75. end_offset_ = 0;
  76. }
  77. bool PersistentSystemProfile::RecordAllocator::Write(RecordType type,
  78. base::StringPiece record) {
  79. const char* data = record.data();
  80. size_t remaining_size = record.size();
  81. // Allocate space and write records until everything has been stored.
  82. do {
  83. if (end_offset_ == alloc_size_) {
  84. if (!AddSegment(remaining_size))
  85. return false;
  86. }
  87. // Write out as much of the data as possible. |data| and |remaining_size|
  88. // are updated in place.
  89. if (!WriteData(type, &data, &remaining_size))
  90. return false;
  91. } while (remaining_size > 0);
  92. return true;
  93. }
  94. bool PersistentSystemProfile::RecordAllocator::HasMoreData() const {
  95. if (alloc_reference_ == 0 && !NextSegment())
  96. return false;
  97. char* block =
  98. allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
  99. base::PersistentMemoryAllocator::kSizeAny);
  100. if (!block)
  101. return false;
  102. RecordHeader header;
  103. header.as_atomic = base::subtle::Acquire_Load(
  104. reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_));
  105. return header.as_parts.type != kUnusedSpace;
  106. }
  107. bool PersistentSystemProfile::RecordAllocator::Read(RecordType* type,
  108. std::string* record) const {
  109. *type = kUnusedSpace;
  110. record->clear();
  111. // Access data and read records until everything has been loaded.
  112. while (true) {
  113. if (end_offset_ == alloc_size_) {
  114. if (!NextSegment())
  115. return false;
  116. }
  117. if (ReadData(type, record))
  118. return *type != kUnusedSpace;
  119. }
  120. }
  121. bool PersistentSystemProfile::RecordAllocator::NextSegment() const {
  122. base::PersistentMemoryAllocator::Iterator iter(allocator_, alloc_reference_);
  123. alloc_reference_ = iter.GetNextOfType(kTypeIdSystemProfile);
  124. alloc_size_ = allocator_->GetAllocSize(alloc_reference_);
  125. end_offset_ = 0;
  126. return alloc_reference_ != 0;
  127. }
  128. bool PersistentSystemProfile::RecordAllocator::AddSegment(size_t min_size) {
  129. if (NextSegment()) {
  130. // The first record-header should have been zeroed as part of the allocation
  131. // or by the "reset" procedure.
  132. DCHECK_EQ(0, base::subtle::NoBarrier_Load(
  133. allocator_->GetAsArray<base::subtle::Atomic32>(
  134. alloc_reference_, kTypeIdSystemProfile, 1)));
  135. return true;
  136. }
  137. DCHECK_EQ(0U, alloc_reference_);
  138. DCHECK_EQ(0U, end_offset_);
  139. size_t size =
  140. std::max(CalculateRecordSize(min_size), kSystemProfileAllocSize);
  141. uint32_t ref = allocator_->Allocate(size, kTypeIdSystemProfile);
  142. if (!ref)
  143. return false; // Allocator must be full.
  144. allocator_->MakeIterable(ref);
  145. alloc_reference_ = ref;
  146. alloc_size_ = allocator_->GetAllocSize(ref);
  147. return true;
  148. }
  149. bool PersistentSystemProfile::RecordAllocator::WriteData(RecordType type,
  150. const char** data,
  151. size_t* data_size) {
  152. char* block =
  153. allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
  154. base::PersistentMemoryAllocator::kSizeAny);
  155. if (!block)
  156. return false; // It's bad if there is no accessible block.
  157. const size_t max_write_size = std::min(
  158. kMaxRecordSize, alloc_size_ - end_offset_ - sizeof(RecordHeader));
  159. const size_t write_size = std::min(*data_size, max_write_size);
  160. const size_t record_size = CalculateRecordSize(write_size);
  161. DCHECK_LT(write_size, record_size);
  162. // Write the data and the record header.
  163. RecordHeader header;
  164. header.as_atomic = 0;
  165. header.as_parts.type = type;
  166. header.as_parts.amount = write_size;
  167. header.as_parts.continued = (write_size < *data_size);
  168. size_t offset = end_offset_;
  169. end_offset_ += record_size;
  170. DCHECK_GE(alloc_size_, end_offset_);
  171. if (end_offset_ < alloc_size_) {
  172. // An empty record header has to be next before this one gets written.
  173. base::subtle::NoBarrier_Store(
  174. reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_), 0);
  175. }
  176. memcpy(block + offset + sizeof(header), *data, write_size);
  177. base::subtle::Release_Store(
  178. reinterpret_cast<base::subtle::Atomic32*>(block + offset),
  179. header.as_atomic);
  180. // Account for what was stored and prepare for follow-on records with any
  181. // remaining data.
  182. *data += write_size;
  183. *data_size -= write_size;
  184. return true;
  185. }
  186. bool PersistentSystemProfile::RecordAllocator::ReadData(
  187. RecordType* type,
  188. std::string* record) const {
  189. DCHECK_GT(alloc_size_, end_offset_);
  190. char* block =
  191. allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
  192. base::PersistentMemoryAllocator::kSizeAny);
  193. if (!block) {
  194. *type = kUnusedSpace;
  195. return true; // No more data.
  196. }
  197. // Get and validate the record header.
  198. RecordHeader header;
  199. header.as_atomic = base::subtle::Acquire_Load(
  200. reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_));
  201. bool continued = !!header.as_parts.continued;
  202. if (header.as_parts.type == kUnusedSpace) {
  203. *type = kUnusedSpace;
  204. return true; // End of all records.
  205. } else if (*type == kUnusedSpace) {
  206. *type = static_cast<RecordType>(header.as_parts.type);
  207. } else if (*type != header.as_parts.type) {
  208. NOTREACHED(); // Continuation didn't match start of record.
  209. *type = kUnusedSpace;
  210. record->clear();
  211. return false;
  212. }
  213. size_t read_size = header.as_parts.amount;
  214. if (end_offset_ + sizeof(header) + read_size > alloc_size_) {
  215. NOTREACHED(); // Invalid header amount.
  216. *type = kUnusedSpace;
  217. return true; // Don't try again.
  218. }
  219. // Append the record data to the output string.
  220. record->append(block + end_offset_ + sizeof(header), read_size);
  221. end_offset_ += CalculateRecordSize(read_size);
  222. DCHECK_GE(alloc_size_, end_offset_);
  223. return !continued;
  224. }
  225. PersistentSystemProfile::PersistentSystemProfile() {}
  226. PersistentSystemProfile::~PersistentSystemProfile() {}
  227. void PersistentSystemProfile::RegisterPersistentAllocator(
  228. base::PersistentMemoryAllocator* memory_allocator) {
  229. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  230. // Create and store the allocator. A |min_size| of "1" ensures that a memory
  231. // block is reserved now.
  232. RecordAllocator allocator(memory_allocator, 1);
  233. allocators_.push_back(std::move(allocator));
  234. all_have_complete_profile_ = false;
  235. }
  236. void PersistentSystemProfile::DeregisterPersistentAllocator(
  237. base::PersistentMemoryAllocator* memory_allocator) {
  238. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  239. // This would be more efficient with a std::map but it's not expected that
  240. // allocators will get deregistered with any frequency, if at all.
  241. base::EraseIf(allocators_, [=](RecordAllocator& records) {
  242. return records.allocator() == memory_allocator;
  243. });
  244. }
  245. void PersistentSystemProfile::SetSystemProfile(
  246. const std::string& serialized_profile,
  247. bool complete) {
  248. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  249. if (allocators_.empty() || serialized_profile.empty())
  250. return;
  251. for (auto& allocator : allocators_) {
  252. // Don't overwrite a complete profile with an incomplete one.
  253. if (!complete && allocator.has_complete_profile())
  254. continue;
  255. // System profile always starts fresh.
  256. allocator.Reset();
  257. // Write out the serialized profile.
  258. allocator.Write(kSystemProfileProto, serialized_profile);
  259. // Indicate if this is a complete profile.
  260. if (complete)
  261. allocator.set_complete_profile();
  262. }
  263. if (complete)
  264. all_have_complete_profile_ = true;
  265. }
  266. void PersistentSystemProfile::SetSystemProfile(
  267. const SystemProfileProto& profile,
  268. bool complete) {
  269. // Avoid serialization if passed profile is not complete and all allocators
  270. // already have complete ones.
  271. if (!complete && all_have_complete_profile_)
  272. return;
  273. std::string serialized_profile;
  274. if (!profile.SerializeToString(&serialized_profile))
  275. return;
  276. SetSystemProfile(serialized_profile, complete);
  277. }
  278. void PersistentSystemProfile::AddFieldTrial(base::StringPiece trial,
  279. base::StringPiece group) {
  280. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  281. DCHECK(!trial.empty());
  282. DCHECK(!group.empty());
  283. base::Pickle pickler;
  284. pickler.WriteString(trial);
  285. pickler.WriteString(group);
  286. WriteToAll(kFieldTrialInfo,
  287. base::StringPiece(static_cast<const char*>(pickler.data()),
  288. pickler.size()));
  289. }
  290. // static
  291. bool PersistentSystemProfile::HasSystemProfile(
  292. const base::PersistentMemoryAllocator& memory_allocator) {
  293. const RecordAllocator records(&memory_allocator);
  294. return records.HasMoreData();
  295. }
  296. // static
  297. bool PersistentSystemProfile::GetSystemProfile(
  298. const base::PersistentMemoryAllocator& memory_allocator,
  299. SystemProfileProto* system_profile) {
  300. const RecordAllocator records(&memory_allocator);
  301. RecordType type;
  302. std::string record;
  303. do {
  304. if (!records.Read(&type, &record))
  305. return false;
  306. } while (type != kSystemProfileProto);
  307. if (!system_profile)
  308. return true;
  309. if (!system_profile->ParseFromString(record))
  310. return false;
  311. MergeUpdateRecords(memory_allocator, system_profile);
  312. return true;
  313. }
  314. // static
  315. void PersistentSystemProfile::MergeUpdateRecords(
  316. const base::PersistentMemoryAllocator& memory_allocator,
  317. SystemProfileProto* system_profile) {
  318. const RecordAllocator records(&memory_allocator);
  319. RecordType type;
  320. std::string record;
  321. std::set<uint32_t> known_field_trial_ids;
  322. // This is done separate from the code that gets the profile because it
  323. // compartmentalizes the code and makes it possible to reuse this section
  324. // should it be needed to merge "update" records into a new "complete"
  325. // system profile that somehow didn't get all the updates.
  326. while (records.Read(&type, &record)) {
  327. switch (type) {
  328. case kUnusedSpace:
  329. // These should never be returned.
  330. NOTREACHED();
  331. break;
  332. case kSystemProfileProto:
  333. // Profile was passed in; ignore this one.
  334. break;
  335. case kFieldTrialInfo: {
  336. // Get the set of known trial IDs so duplicates don't get added.
  337. if (known_field_trial_ids.empty()) {
  338. for (int i = 0; i < system_profile->field_trial_size(); ++i) {
  339. known_field_trial_ids.insert(
  340. system_profile->field_trial(i).name_id());
  341. }
  342. }
  343. base::Pickle pickler(record.data(), record.size());
  344. base::PickleIterator iter(pickler);
  345. base::StringPiece trial;
  346. base::StringPiece group;
  347. if (iter.ReadStringPiece(&trial) && iter.ReadStringPiece(&group)) {
  348. variations::ActiveGroupId field_ids =
  349. variations::MakeActiveGroupId(trial, group);
  350. if (!base::Contains(known_field_trial_ids, field_ids.name)) {
  351. SystemProfileProto::FieldTrial* field_trial =
  352. system_profile->add_field_trial();
  353. field_trial->set_name_id(field_ids.name);
  354. field_trial->set_group_id(field_ids.group);
  355. known_field_trial_ids.insert(field_ids.name);
  356. }
  357. }
  358. } break;
  359. }
  360. }
  361. }
  362. void PersistentSystemProfile::WriteToAll(RecordType type,
  363. base::StringPiece record) {
  364. for (auto& allocator : allocators_)
  365. allocator.Write(type, record);
  366. }
  367. GlobalPersistentSystemProfile* GlobalPersistentSystemProfile::GetInstance() {
  368. return base::Singleton<
  369. GlobalPersistentSystemProfile,
  370. base::LeakySingletonTraits<GlobalPersistentSystemProfile>>::get();
  371. }
  372. } // namespace metrics