1 // Copyright 2016 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 "base/debug/activity_analyzer.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/files/file.h"
11 #include "base/files/file_path.h"
12 #include "base/files/memory_mapped_file.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19
20 namespace base {
21 namespace debug {
22
23 namespace {
24 // An empty snapshot that can be returned when there otherwise is none.
25 LazyInstance<ActivityUserData::Snapshot>::Leaky g_empty_user_data_snapshot;
26
27 // DO NOT CHANGE VALUES. This is logged persistently in a histogram.
28 enum AnalyzerCreationError {
29 kInvalidMemoryMappedFile,
30 kPmaBadFile,
31 kPmaUninitialized,
32 kPmaDeleted,
33 kPmaCorrupt,
34 kAnalyzerCreationErrorMax // Keep this last.
35 };
36
LogAnalyzerCreationError(AnalyzerCreationError error)37 void LogAnalyzerCreationError(AnalyzerCreationError error) {
38 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.AnalyzerCreationError",
39 error, kAnalyzerCreationErrorMax);
40 }
41
42 } // namespace
43
44 ThreadActivityAnalyzer::Snapshot::Snapshot() = default;
45 ThreadActivityAnalyzer::Snapshot::~Snapshot() = default;
46
ThreadActivityAnalyzer(const ThreadActivityTracker & tracker)47 ThreadActivityAnalyzer::ThreadActivityAnalyzer(
48 const ThreadActivityTracker& tracker)
49 : activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {}
50
ThreadActivityAnalyzer(void * base,size_t size)51 ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size)
52 : ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {}
53
ThreadActivityAnalyzer(PersistentMemoryAllocator * allocator,PersistentMemoryAllocator::Reference reference)54 ThreadActivityAnalyzer::ThreadActivityAnalyzer(
55 PersistentMemoryAllocator* allocator,
56 PersistentMemoryAllocator::Reference reference)
57 : ThreadActivityAnalyzer(allocator->GetAsArray<char>(
58 reference,
59 GlobalActivityTracker::kTypeIdActivityTracker,
60 PersistentMemoryAllocator::kSizeAny),
61 allocator->GetAllocSize(reference)) {}
62
63 ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default;
64
AddGlobalInformation(GlobalActivityAnalyzer * global)65 void ThreadActivityAnalyzer::AddGlobalInformation(
66 GlobalActivityAnalyzer* global) {
67 if (!IsValid())
68 return;
69
70 // User-data is held at the global scope even though it's referenced at the
71 // thread scope.
72 activity_snapshot_.user_data_stack.clear();
73 for (auto& activity : activity_snapshot_.activity_stack) {
74 // The global GetUserDataSnapshot will return an empty snapshot if the ref
75 // or id is not valid.
76 activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot(
77 activity_snapshot_.process_id, activity.user_data_ref,
78 activity.user_data_id));
79 }
80 }
81
GlobalActivityAnalyzer(std::unique_ptr<PersistentMemoryAllocator> allocator)82 GlobalActivityAnalyzer::GlobalActivityAnalyzer(
83 std::unique_ptr<PersistentMemoryAllocator> allocator)
84 : allocator_(std::move(allocator)),
85 analysis_stamp_(0LL),
86 allocator_iterator_(allocator_.get()) {
87 DCHECK(allocator_);
88 }
89
90 GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default;
91
92 // static
93 std::unique_ptr<GlobalActivityAnalyzer>
CreateWithAllocator(std::unique_ptr<PersistentMemoryAllocator> allocator)94 GlobalActivityAnalyzer::CreateWithAllocator(
95 std::unique_ptr<PersistentMemoryAllocator> allocator) {
96 if (allocator->GetMemoryState() ==
97 PersistentMemoryAllocator::MEMORY_UNINITIALIZED) {
98 LogAnalyzerCreationError(kPmaUninitialized);
99 return nullptr;
100 }
101 if (allocator->GetMemoryState() ==
102 PersistentMemoryAllocator::MEMORY_DELETED) {
103 LogAnalyzerCreationError(kPmaDeleted);
104 return nullptr;
105 }
106 if (allocator->IsCorrupt()) {
107 LogAnalyzerCreationError(kPmaCorrupt);
108 return nullptr;
109 }
110
111 return WrapUnique(new GlobalActivityAnalyzer(std::move(allocator)));
112 }
113
114 #if !defined(OS_NACL)
115 // static
CreateWithFile(const FilePath & file_path)116 std::unique_ptr<GlobalActivityAnalyzer> GlobalActivityAnalyzer::CreateWithFile(
117 const FilePath& file_path) {
118 // Map the file read-write so it can guarantee consistency between
119 // the analyzer and any trackers that my still be active.
120 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
121 mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE);
122 if (!mmfile->IsValid()) {
123 LogAnalyzerCreationError(kInvalidMemoryMappedFile);
124 return nullptr;
125 }
126
127 if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
128 LogAnalyzerCreationError(kPmaBadFile);
129 return nullptr;
130 }
131
132 return CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
133 std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true));
134 }
135 #endif // !defined(OS_NACL)
136
137 // static
138 std::unique_ptr<GlobalActivityAnalyzer>
CreateWithSharedMemory(std::unique_ptr<SharedMemory> shm)139 GlobalActivityAnalyzer::CreateWithSharedMemory(
140 std::unique_ptr<SharedMemory> shm) {
141 if (shm->mapped_size() == 0 ||
142 !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
143 return nullptr;
144 }
145 return CreateWithAllocator(std::make_unique<SharedPersistentMemoryAllocator>(
146 std::move(shm), 0, StringPiece(), /*readonly=*/true));
147 }
148
149 // static
150 std::unique_ptr<GlobalActivityAnalyzer>
CreateWithSharedMemoryHandle(const SharedMemoryHandle & handle,size_t size)151 GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(
152 const SharedMemoryHandle& handle,
153 size_t size) {
154 std::unique_ptr<SharedMemory> shm(
155 new SharedMemory(handle, /*readonly=*/true));
156 if (!shm->Map(size))
157 return nullptr;
158 return CreateWithSharedMemory(std::move(shm));
159 }
160
GetFirstProcess()161 int64_t GlobalActivityAnalyzer::GetFirstProcess() {
162 PrepareAllAnalyzers();
163 return GetNextProcess();
164 }
165
GetNextProcess()166 int64_t GlobalActivityAnalyzer::GetNextProcess() {
167 if (process_ids_.empty())
168 return 0;
169 int64_t pid = process_ids_.back();
170 process_ids_.pop_back();
171 return pid;
172 }
173
GetFirstAnalyzer(int64_t pid)174 ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(int64_t pid) {
175 analyzers_iterator_ = analyzers_.begin();
176 analyzers_iterator_pid_ = pid;
177 if (analyzers_iterator_ == analyzers_.end())
178 return nullptr;
179 int64_t create_stamp;
180 if (analyzers_iterator_->second->GetProcessId(&create_stamp) == pid &&
181 create_stamp <= analysis_stamp_) {
182 return analyzers_iterator_->second.get();
183 }
184 return GetNextAnalyzer();
185 }
186
GetNextAnalyzer()187 ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetNextAnalyzer() {
188 DCHECK(analyzers_iterator_ != analyzers_.end());
189 int64_t create_stamp;
190 do {
191 ++analyzers_iterator_;
192 if (analyzers_iterator_ == analyzers_.end())
193 return nullptr;
194 } while (analyzers_iterator_->second->GetProcessId(&create_stamp) !=
195 analyzers_iterator_pid_ ||
196 create_stamp > analysis_stamp_);
197 return analyzers_iterator_->second.get();
198 }
199
GetAnalyzerForThread(const ThreadKey & key)200 ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread(
201 const ThreadKey& key) {
202 auto found = analyzers_.find(key);
203 if (found == analyzers_.end())
204 return nullptr;
205 return found->second.get();
206 }
207
GetUserDataSnapshot(int64_t pid,uint32_t ref,uint32_t id)208 ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
209 int64_t pid,
210 uint32_t ref,
211 uint32_t id) {
212 ActivityUserData::Snapshot snapshot;
213
214 void* memory = allocator_->GetAsArray<char>(
215 ref, GlobalActivityTracker::kTypeIdUserDataRecord,
216 PersistentMemoryAllocator::kSizeAny);
217 if (memory) {
218 size_t size = allocator_->GetAllocSize(ref);
219 const ActivityUserData user_data(memory, size);
220 user_data.CreateSnapshot(&snapshot);
221 int64_t process_id;
222 int64_t create_stamp;
223 if (!ActivityUserData::GetOwningProcessId(memory, &process_id,
224 &create_stamp) ||
225 process_id != pid || user_data.id() != id) {
226 // This allocation has been overwritten since it was created. Return an
227 // empty snapshot because whatever was captured is incorrect.
228 snapshot.clear();
229 }
230 }
231
232 return snapshot;
233 }
234
235 const ActivityUserData::Snapshot&
GetProcessDataSnapshot(int64_t pid)236 GlobalActivityAnalyzer::GetProcessDataSnapshot(int64_t pid) {
237 auto iter = process_data_.find(pid);
238 if (iter == process_data_.end())
239 return g_empty_user_data_snapshot.Get();
240 if (iter->second.create_stamp > analysis_stamp_)
241 return g_empty_user_data_snapshot.Get();
242 DCHECK_EQ(pid, iter->second.process_id);
243 return iter->second.data;
244 }
245
GetLogMessages()246 std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
247 std::vector<std::string> messages;
248 PersistentMemoryAllocator::Reference ref;
249
250 PersistentMemoryAllocator::Iterator iter(allocator_.get());
251 while ((ref = iter.GetNextOfType(
252 GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) {
253 const char* message = allocator_->GetAsArray<char>(
254 ref, GlobalActivityTracker::kTypeIdGlobalLogMessage,
255 PersistentMemoryAllocator::kSizeAny);
256 if (message)
257 messages.push_back(message);
258 }
259
260 return messages;
261 }
262
263 std::vector<GlobalActivityTracker::ModuleInfo>
GetModules(int64_t pid)264 GlobalActivityAnalyzer::GetModules(int64_t pid) {
265 std::vector<GlobalActivityTracker::ModuleInfo> modules;
266
267 PersistentMemoryAllocator::Iterator iter(allocator_.get());
268 const GlobalActivityTracker::ModuleInfoRecord* record;
269 while (
270 (record =
271 iter.GetNextOfObject<GlobalActivityTracker::ModuleInfoRecord>()) !=
272 nullptr) {
273 int64_t process_id;
274 int64_t create_stamp;
275 if (!OwningProcess::GetOwningProcessId(&record->owner, &process_id,
276 &create_stamp) ||
277 pid != process_id || create_stamp > analysis_stamp_) {
278 continue;
279 }
280 GlobalActivityTracker::ModuleInfo info;
281 if (record->DecodeTo(&info, allocator_->GetAllocSize(
282 allocator_->GetAsReference(record)))) {
283 modules.push_back(std::move(info));
284 }
285 }
286
287 return modules;
288 }
289
290 GlobalActivityAnalyzer::ProgramLocation
GetProgramLocationFromAddress(uint64_t address)291 GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) {
292 // TODO(bcwhite): Implement this.
293 return { 0, 0 };
294 }
295
IsDataComplete() const296 bool GlobalActivityAnalyzer::IsDataComplete() const {
297 DCHECK(allocator_);
298 return !allocator_->IsFull();
299 }
300
301 GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() = default;
302 GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
303 const UserDataSnapshot& rhs) = default;
304 GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
305 UserDataSnapshot&& rhs) = default;
306 GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() = default;
307
PrepareAllAnalyzers()308 void GlobalActivityAnalyzer::PrepareAllAnalyzers() {
309 // Record the time when analysis started.
310 analysis_stamp_ = base::Time::Now().ToInternalValue();
311
312 // Fetch all the records. This will retrieve only ones created since the
313 // last run since the PMA iterator will continue from where it left off.
314 uint32_t type;
315 PersistentMemoryAllocator::Reference ref;
316 while ((ref = allocator_iterator_.GetNext(&type)) != 0) {
317 switch (type) {
318 case GlobalActivityTracker::kTypeIdActivityTracker:
319 case GlobalActivityTracker::kTypeIdActivityTrackerFree:
320 case GlobalActivityTracker::kTypeIdProcessDataRecord:
321 case GlobalActivityTracker::kTypeIdProcessDataRecordFree:
322 case PersistentMemoryAllocator::kTypeIdTransitioning:
323 // Active, free, or transitioning: add it to the list of references
324 // for later analysis.
325 memory_references_.insert(ref);
326 break;
327 }
328 }
329
330 // Clear out any old information.
331 analyzers_.clear();
332 process_data_.clear();
333 process_ids_.clear();
334 std::set<int64_t> seen_pids;
335
336 // Go through all the known references and create objects for them with
337 // snapshots of the current state.
338 for (PersistentMemoryAllocator::Reference memory_ref : memory_references_) {
339 // Get the actual data segment for the tracker. Any type will do since it
340 // is checked below.
341 void* const base = allocator_->GetAsArray<char>(
342 memory_ref, PersistentMemoryAllocator::kTypeIdAny,
343 PersistentMemoryAllocator::kSizeAny);
344 const size_t size = allocator_->GetAllocSize(memory_ref);
345 if (!base)
346 continue;
347
348 switch (allocator_->GetType(memory_ref)) {
349 case GlobalActivityTracker::kTypeIdActivityTracker: {
350 // Create the analyzer on the data. This will capture a snapshot of the
351 // tracker state. This can fail if the tracker is somehow corrupted or
352 // is in the process of shutting down.
353 std::unique_ptr<ThreadActivityAnalyzer> analyzer(
354 new ThreadActivityAnalyzer(base, size));
355 if (!analyzer->IsValid())
356 continue;
357 analyzer->AddGlobalInformation(this);
358
359 // Track PIDs.
360 int64_t pid = analyzer->GetProcessId();
361 if (seen_pids.find(pid) == seen_pids.end()) {
362 process_ids_.push_back(pid);
363 seen_pids.insert(pid);
364 }
365
366 // Add this analyzer to the map of known ones, indexed by a unique
367 // thread
368 // identifier.
369 DCHECK(!base::ContainsKey(analyzers_, analyzer->GetThreadKey()));
370 analyzer->allocator_reference_ = ref;
371 analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
372 } break;
373
374 case GlobalActivityTracker::kTypeIdProcessDataRecord: {
375 // Get the PID associated with this data record.
376 int64_t process_id;
377 int64_t create_stamp;
378 ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
379 DCHECK(!base::ContainsKey(process_data_, process_id));
380
381 // Create a snapshot of the data. This can fail if the data is somehow
382 // corrupted or the process shutdown and the memory being released.
383 UserDataSnapshot& snapshot = process_data_[process_id];
384 snapshot.process_id = process_id;
385 snapshot.create_stamp = create_stamp;
386 const ActivityUserData process_data(base, size);
387 if (!process_data.CreateSnapshot(&snapshot.data))
388 break;
389
390 // Check that nothing changed. If it did, forget what was recorded.
391 ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
392 if (process_id != snapshot.process_id ||
393 create_stamp != snapshot.create_stamp) {
394 process_data_.erase(process_id);
395 break;
396 }
397
398 // Track PIDs.
399 if (seen_pids.find(process_id) == seen_pids.end()) {
400 process_ids_.push_back(process_id);
401 seen_pids.insert(process_id);
402 }
403 } break;
404 }
405 }
406
407 // Reverse the list of PIDs so that they get popped in the order found.
408 std::reverse(process_ids_.begin(), process_ids_.end());
409 }
410
411 } // namespace debug
412 } // namespace base
413