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

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

Issue 2907543003: Support persistent system profiles. (Closed)
Patch Set: addressed review comments by asvitkine Created 3 years, 6 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
(Empty)
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
5 #include "components/metrics/persistent_system_profile.h"
6
7 #include "base/atomicops.h"
8 #include "base/memory/singleton.h"
9 #include "base/metrics/persistent_memory_allocator.h"
10 #include "base/stl_util.h"
11
12 namespace metrics {
13
14 namespace {
15
16 // 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
18 // structure that can be stored as a single atomic 32-bit word.
19 union RecordHeader {
20 struct {
21 unsigned continued : 1; // Flag indicating if there is more after this.
22 unsigned type : 7; // The type of this record.
23 unsigned amount : 24; // The amount of data to follow.
24 } as_parts;
25 base::subtle::Atomic32 as_atomic;
26 };
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
32 static_assert(sizeof(RecordHeader) == sizeof(base::subtle::Atomic32),
33 "bad RecordHeader size");
34
35 // 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
37 // size.
38 size_t CalculateRecordSize(size_t data_amount) {
39 return (data_amount + sizeof(RecordHeader) + sizeof(RecordHeader) - 1) &
40 ~(sizeof(RecordHeader) - 1);
41 }
42
43 } // namespace
44
45 PersistentSystemProfile::RecordAllocator::RecordAllocator(
46 base::PersistentMemoryAllocator* memory_allocator,
47 size_t min_size)
48 : allocator_(memory_allocator),
49 alloc_reference_(0),
50 alloc_size_(0),
51 end_offset_(0) {
52 AddSegment(min_size);
53 }
54
55 PersistentSystemProfile::RecordAllocator::RecordAllocator(
56 const base::PersistentMemoryAllocator* memory_allocator)
57 : allocator_(
58 const_cast<base::PersistentMemoryAllocator*>(memory_allocator)),
59 alloc_reference_(0),
60 alloc_size_(0),
61 end_offset_(0) {}
62
63 void PersistentSystemProfile::RecordAllocator::Reset() {
64 // Clear the first word of all blocks so they're known to be "empty".
65 alloc_reference_ = 0;
66 while (NextSegment()) {
67 // Get the block as a char* and cast it. It can't be fetched directly as
68 // an array of RecordHeader because that's not a fundamental type and only
69 // arrays of fundamental types are allowed.
70 RecordHeader* header =
71 reinterpret_cast<RecordHeader*>(allocator_->GetAsArray<char>(
72 alloc_reference_, kTypeIdSystemProfile, sizeof(RecordHeader)));
73 DCHECK(header);
74 base::subtle::NoBarrier_Store(&header->as_atomic, 0);
75 }
76
77 // Reset member variables.
78 alloc_reference_ = 0;
79 alloc_size_ = 0;
80 end_offset_ = 0;
81 }
82
83 bool PersistentSystemProfile::RecordAllocator::Write(
84 RecordType type,
85 const std::string& record) {
86 const char* data = record.data();
87 size_t remaining_size = record.size();
88
89 // Allocate space and write records until everything has been stored.
90 do {
91 if (end_offset_ == alloc_size_) {
92 if (!AddSegment(remaining_size))
93 return false;
94 }
95 // Write out as much of the data as possible. |data| and |remaining_size|
96 // are updated in place.
97 if (!WriteData(type, &data, &remaining_size))
98 return false;
99 } while (remaining_size > 0);
100
101 return true;
102 }
103
104 bool PersistentSystemProfile::RecordAllocator::Read(RecordType* type,
105 std::string* record) const {
106 *type = kUnusedSpace;
107 record->clear();
108
109 // Access data and read records until everything has been loaded.
110 while (true) {
111 if (end_offset_ == alloc_size_) {
112 if (!NextSegment())
113 return false;
114 }
115 if (ReadData(type, record))
116 return *type != kUnusedSpace;
117 }
118 }
119
120 bool PersistentSystemProfile::RecordAllocator::NextSegment() const {
121 base::PersistentMemoryAllocator::Iterator iter(allocator_, alloc_reference_);
122 alloc_reference_ = iter.GetNextOfType(kTypeIdSystemProfile);
123 alloc_size_ = allocator_->GetAllocSize(alloc_reference_);
124 end_offset_ = 0;
125 return alloc_reference_ != 0;
126 }
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
138 DCHECK_EQ(0U, alloc_reference_);
139 DCHECK_EQ(0U, end_offset_);
140
141 size_t size =
142 std::max(CalculateRecordSize(min_size), kSystemProfileAllocSize);
143
144 uint32_t ref = allocator_->Allocate(size, kTypeIdSystemProfile);
145 if (!ref)
146 return false; // Allocator must be full.
147 allocator_->MakeIterable(ref);
148
149 alloc_reference_ = ref;
150 alloc_size_ = allocator_->GetAllocSize(ref);
151 return true;
152 }
153
154 bool PersistentSystemProfile::RecordAllocator::WriteData(
155 RecordType type,
156 const char** data,
157 size_t* remaining_size) {
158 char* block =
159 allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
160 base::PersistentMemoryAllocator::kSizeAny);
161 if (!block)
162 return false; // It's bad if there is no accessible block.
163
164 size_t write_size = std::min(*remaining_size, kMaxRecordSize);
165 write_size =
166 std::min(write_size, alloc_size_ - end_offset_ - sizeof(RecordHeader));
167
168 // Write the data and the record header.
169 RecordHeader header;
170 header.as_atomic = 0;
171 header.as_parts.type = type;
172 header.as_parts.amount = write_size;
173 header.as_parts.continued = (write_size < *remaining_size);
174 size_t offset = end_offset_;
175 end_offset_ += CalculateRecordSize(write_size);
176 DCHECK_GE(alloc_size_, end_offset_);
177 if (end_offset_ < alloc_size_) {
178 // An empty record header has to be next before this one gets written.
179 base::subtle::NoBarrier_Store(
180 reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_), 0);
181 }
182 memcpy(block + offset + sizeof(header), *data, write_size);
183 base::subtle::Release_Store(reinterpret_cast<base::subtle::Atomic32*>(block),
184 header.as_atomic);
185
186 // Account for what was stored and prepare for follow-on records with any
187 // remaining data.
188 *data += write_size;
189 *remaining_size -= write_size;
190
191 return true;
192 }
193
194 bool PersistentSystemProfile::RecordAllocator::ReadData(
195 RecordType* type,
196 std::string* record) const {
197 DCHECK_GT(alloc_size_, end_offset_);
198
199 char* block =
200 allocator_->GetAsArray<char>(alloc_reference_, kTypeIdSystemProfile,
201 base::PersistentMemoryAllocator::kSizeAny);
202 if (!block) {
203 *type = kUnusedSpace;
204 return true; // No more data.
205 }
206
207 // Get and validate the record header.
208 RecordHeader header;
209 header.as_atomic = base::subtle::Acquire_Load(
210 reinterpret_cast<base::subtle::Atomic32*>(block + end_offset_));
211 bool continued = !!header.as_parts.continued;
212 if (header.as_parts.type == kUnusedSpace) {
213 *type = kUnusedSpace;
214 return true; // End of all records.
215 } else if (*type == kUnusedSpace) {
216 *type = static_cast<RecordType>(header.as_parts.type);
217 } else if (*type != header.as_parts.type) {
218 NOTREACHED(); // Continuation didn't match start of record.
219 *type = kUnusedSpace;
220 record->clear();
221 return false;
222 }
223 size_t read_size = header.as_parts.amount;
224 if (read_size < sizeof(header) ||
225 end_offset_ + sizeof(header) + read_size > alloc_size_) {
226 NOTREACHED(); // Invalid header amount.
227 *type = kUnusedSpace;
228 return true; // Don't try again.
229 }
230
231 // Append the record data to the output string.
232 record->append(block + sizeof(header), read_size);
233 end_offset_ += CalculateRecordSize(read_size);
234 DCHECK_GE(alloc_size_, end_offset_);
235
236 return !continued;
237 }
238
239 PersistentSystemProfile::PersistentSystemProfile() {}
240
241 PersistentSystemProfile::~PersistentSystemProfile() {}
242
243 void PersistentSystemProfile::RegisterPersistentAllocator(
244 base::PersistentMemoryAllocator* memory_allocator) {
245 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
246
247 // Create and store the allocator. A |min_size| of "1" ensures that a memory
248 // block is reserved now.
249 RecordAllocator allocator(memory_allocator, 1);
250 allocators_.push_back(std::move(allocator));
251 }
252
253 void PersistentSystemProfile::DeregisterPersistentAllocator(
254 base::PersistentMemoryAllocator* memory_allocator) {
255 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
256
257 // This would be more efficient with a std::map but it's not expected that
258 // allocators will get deregistered with any frequency, if at all.
259 base::EraseIf(allocators_, [=](RecordAllocator& records) {
260 return records.allocator() == memory_allocator;
261 });
262 }
263
264 void PersistentSystemProfile::SetSystemProfile(
265 const std::string& serialized_profile) {
266 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
267
268 if (allocators_.empty() || serialized_profile.empty())
Alexei Svitkine (slow) 2017/05/29 22:27:24 If serialized_profile is empty, I think this shoul
bcwhite 2017/05/30 01:19:12 The string can be empty if there is a serializatio
269 return;
270
271 for (auto& allocator : allocators_) {
272 // A full system profile always starts fresh.
273 allocator.Reset();
274 // Write out the serialized profile.
275 allocator.Write(kSystemProfileProto, serialized_profile);
276 }
277 }
278
279 // static
280 bool PersistentSystemProfile::GetSystemProfile(
281 const base::PersistentMemoryAllocator& memory_allocator,
282 SystemProfileProto* system_profile) {
283 const RecordAllocator records(&memory_allocator);
284
285 RecordType type;
286 std::string record;
287 if (!records.Read(&type, &record))
288 return false;
289 if (type != kSystemProfileProto)
290 return false;
291
292 return system_profile->ParseFromString(record);
293 }
294
295 GlobalPersistentSystemProfile* GlobalPersistentSystemProfile::GetInstance() {
296 return base::Singleton<
297 GlobalPersistentSystemProfile,
298 base::LeakySingletonTraits<GlobalPersistentSystemProfile>>::get();
299 }
300
301 } // namespace metrics
OLDNEW
« no previous file with comments | « components/metrics/persistent_system_profile.h ('k') | components/metrics/persistent_system_profile_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698