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