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