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/browser_watcher/activity_report_user_stream_data_source.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include <windows.h>
11 
12 // Must be included after windows.h.
13 #include <psapi.h>
14 
15 #include "base/files/file.h"
16 #include "base/files/file_util.h"
17 #include "base/memory/free_deleter.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/metrics/persistent_memory_allocator.h"
20 #include "base/process/memory.h"
21 #include "base/process/process.h"
22 #include "base/strings/string16.h"
23 #include "base/time/time.h"
24 #include "components/browser_watcher/activity_report_extractor.h"
25 #include "components/browser_watcher/activity_tracker_annotation.h"
26 #include "components/browser_watcher/extended_crash_reporting_metrics.h"
27 #include "components/browser_watcher/minidump_user_streams.h"
28 #include "third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h"
29 #include "third_party/crashpad/crashpad/snapshot/annotation_snapshot.h"
30 #include "third_party/crashpad/crashpad/snapshot/exception_snapshot.h"
31 #include "third_party/crashpad/crashpad/snapshot/module_snapshot.h"
32 #include "third_party/crashpad/crashpad/snapshot/process_snapshot.h"
33 #include "third_party/crashpad/crashpad/util/process/process_memory.h"
34 
35 namespace browser_watcher {
36 
37 namespace {
38 
39 // TODO(siggi): Refactor this to harmonize with the activity tracker setup.
40 const size_t kMaxActivityAnnotationSize = 2 << 20;
41 
42 using UniqueMallocPtr = std::unique_ptr<void, base::FreeDeleter>;
43 
UncheckedAllocate(size_t size)44 UniqueMallocPtr UncheckedAllocate(size_t size) {
45   void* raw_ptr = nullptr;
46   if (!base::UncheckedMalloc(size, &raw_ptr))
47     return UniqueMallocPtr();
48 
49   return UniqueMallocPtr(raw_ptr);
50 }
51 
52 // A PersistentMemoryAllocator subclass that can take ownership of a buffer
53 // that's allocated with a malloc-compatible allocation function.
54 class MallocMemoryAllocator : public base::PersistentMemoryAllocator {
55  public:
56   MallocMemoryAllocator(UniqueMallocPtr buffer, size_t size);
57   ~MallocMemoryAllocator() override;
58 };
59 
MallocMemoryAllocator(UniqueMallocPtr buffer,size_t size)60 MallocMemoryAllocator::MallocMemoryAllocator(UniqueMallocPtr buffer,
61                                              size_t size)
62     : base::PersistentMemoryAllocator(buffer.release(), size, 0, 0, "", true) {}
63 
~MallocMemoryAllocator()64 MallocMemoryAllocator::~MallocMemoryAllocator() {
65   free(const_cast<char*>(mem_base_));
66 }
67 
68 class BufferExtensionStreamDataSource final
69     : public crashpad::MinidumpUserExtensionStreamDataSource {
70  public:
71   explicit BufferExtensionStreamDataSource(uint32_t stream_type);
72 
73   bool Init(const StabilityReport& report);
74 
75   size_t StreamDataSize() override;
76   bool ReadStreamData(Delegate* delegate) override;
77 
78  private:
79   std::string data_;
80 
81   DISALLOW_COPY_AND_ASSIGN(BufferExtensionStreamDataSource);
82 };
83 
BufferExtensionStreamDataSource(uint32_t stream_type)84 BufferExtensionStreamDataSource::BufferExtensionStreamDataSource(
85     uint32_t stream_type)
86     : crashpad::MinidumpUserExtensionStreamDataSource(stream_type) {}
87 
Init(const StabilityReport & report)88 bool BufferExtensionStreamDataSource::Init(const StabilityReport& report) {
89   if (report.SerializeToString(&data_))
90     return true;
91   data_.clear();
92   return false;
93 }
94 
StreamDataSize()95 size_t BufferExtensionStreamDataSource::StreamDataSize() {
96   DCHECK(!data_.empty());
97   return data_.size();
98 }
99 
ReadStreamData(Delegate * delegate)100 bool BufferExtensionStreamDataSource::ReadStreamData(Delegate* delegate) {
101   DCHECK(!data_.empty());
102   return delegate->ExtensionStreamDataSourceRead(
103       data_.size() ? data_.data() : nullptr, data_.size());
104 }
105 
106 // TODO(manzagop): Collection should factor in whether this is a true crash or
107 // dump without crashing.
CollectStabilityReport(std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer,StabilityReport * report)108 bool CollectStabilityReport(
109     std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer,
110     StabilityReport* report) {
111   CollectionStatus status = ANALYZER_CREATION_FAILED;
112   if (global_analyzer)
113     status = Extract(std::move(global_analyzer), report);
114 
115   base::UmaHistogramEnumeration("ActivityTracker.CollectCrash.Status", status,
116                                 COLLECTION_STATUS_MAX);
117   if (status != SUCCESS)
118     return false;
119 
120   LogCollectOnCrashEvent(CollectOnCrashEvent::kReportExtractionSuccess);
121 
122   return true;
123 }
124 
CollectSystemPerformanceMetrics(StabilityReport * report)125 void CollectSystemPerformanceMetrics(StabilityReport* report) {
126   // Grab system commit memory. Also best effort.
127   PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)};
128   if (GetPerformanceInfo(&perf_info, sizeof(perf_info))) {
129     auto* memory_state =
130         report->mutable_system_memory_state()->mutable_windows_memory();
131 
132     memory_state->set_system_commit_limit(perf_info.CommitLimit);
133     memory_state->set_system_commit_remaining(perf_info.CommitLimit -
134                                               perf_info.CommitTotal);
135     memory_state->set_system_handle_count(perf_info.HandleCount);
136   }
137 }
138 
CollectProcessPerformanceMetrics(crashpad::ProcessSnapshot * process_snapshot,StabilityReport * report)139 void CollectProcessPerformanceMetrics(
140     crashpad::ProcessSnapshot* process_snapshot,
141     StabilityReport* report) {
142   const crashpad::ExceptionSnapshot* exception = process_snapshot->Exception();
143 
144   if (!exception)
145     return;
146 
147   // Find or create the ProcessState for the process in question.
148   base::ProcessId pid = process_snapshot->ProcessID();
149   ProcessState* process_state = nullptr;
150   for (int i = 0; i < report->process_states_size(); ++i) {
151     ProcessState* temp = report->mutable_process_states(i);
152 
153     if (temp->has_process_id() && temp->process_id() == pid) {
154       process_state = temp;
155       break;
156     }
157   }
158 
159   if (!process_state) {
160     process_state = report->add_process_states();
161     process_state->set_process_id(pid);
162   }
163 
164   auto* memory_state =
165       process_state->mutable_memory_state()->mutable_windows_memory();
166 
167   // Grab the requested allocation size in case of OOM exception.
168   if (exception->Exception() == base::win::kOomExceptionCode) {
169     const auto& codes = exception->Codes();
170     if (codes.size()) {
171       // The first parameter, if present, is the size of the allocation attempt.
172       memory_state->set_process_allocation_attempt(codes[0]);
173     }
174   }
175 
176   base::Process process(base::Process::OpenWithAccess(
177       pid, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ));
178 
179   if (process.IsValid()) {
180     PROCESS_MEMORY_COUNTERS_EX process_memory = {sizeof(process_memory)};
181     if (::GetProcessMemoryInfo(
182             process.Handle(),
183             reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&process_memory),
184             sizeof(process_memory))) {
185       // This is in units of bytes, re-scale to pages for consistency with
186       // system metrics.
187       const uint64_t kPageSize = 4096;
188       memory_state->set_process_private_usage(process_memory.PrivateUsage /
189                                               kPageSize);
190       memory_state->set_process_peak_workingset_size(
191           process_memory.PeakWorkingSetSize / kPageSize);
192       memory_state->set_process_peak_pagefile_usage(
193           process_memory.PeakPagefileUsage / kPageSize);
194     }
195 
196     DWORD process_handle_count = 0;
197     if (::GetProcessHandleCount(process.Handle(), &process_handle_count)) {
198       memory_state->set_process_handle_count(process_handle_count);
199     }
200   }
201 }
202 
203 // If the process has a beacon for in-memory activities, returns an analyzer
204 // for it.
205 std::unique_ptr<base::debug::GlobalActivityAnalyzer>
MaybeGetInMemoryActivityAnalyzer(crashpad::ProcessSnapshot * process_snapshot)206 MaybeGetInMemoryActivityAnalyzer(crashpad::ProcessSnapshot* process_snapshot) {
207   if (!process_snapshot->Memory())
208     return nullptr;
209 
210   auto modules = process_snapshot->Modules();
211   for (auto* module : modules) {
212     auto annotations = module->AnnotationObjects();
213     for (const auto& annotation : annotations) {
214       if (annotation.name == ActivityTrackerAnnotation::kAnnotationName &&
215           annotation.type == static_cast<uint16_t>(
216                                  ActivityTrackerAnnotation::kAnnotationType) &&
217           annotation.value.size() ==
218               sizeof(ActivityTrackerAnnotation::ValueType)) {
219         // Re-cast the annotation to its value type.
220         ActivityTrackerAnnotation::ValueType value;
221         memcpy(&value, annotation.value.data(), sizeof(value));
222 
223         // Check the size field for sanity.
224         if (value.size > kMaxActivityAnnotationSize)
225           continue;
226 
227         // Allocate the buffer with no terminate-on-exhaustion to make sure
228         // this can't be used to bring down the handler and thus elide
229         // crash reporting.
230         UniqueMallocPtr buffer = UncheckedAllocate(value.size);
231         if (!buffer || !base::PersistentMemoryAllocator::IsMemoryAcceptable(
232                            buffer.get(), value.size, 0, true)) {
233           continue;
234         }
235 
236         // Read the activity tracker data from the crashed process.
237         if (process_snapshot->Memory()->Read(value.address, value.size,
238                                              buffer.get())) {
239           // Success - wrap an allocator on the buffer, and an analyzer on that.
240           std::unique_ptr<MallocMemoryAllocator> allocator =
241               std::make_unique<MallocMemoryAllocator>(std::move(buffer),
242                                                       value.size);
243 
244           return base::debug::GlobalActivityAnalyzer::CreateWithAllocator(
245               std::move(allocator));
246         } else {
247           return nullptr;
248         }
249       }
250     }
251   }
252 
253   return nullptr;
254 }
255 
256 }  // namespace
257 
ActivityReportUserStreamDataSource(const base::FilePath & user_data_dir)258 ActivityReportUserStreamDataSource::ActivityReportUserStreamDataSource(
259     const base::FilePath& user_data_dir)
260     : user_data_dir_(user_data_dir) {}
261 
262 std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
ProduceStreamData(crashpad::ProcessSnapshot * process_snapshot)263 ActivityReportUserStreamDataSource::ProduceStreamData(
264     crashpad::ProcessSnapshot* process_snapshot) {
265   DCHECK(process_snapshot);
266   LogCollectOnCrashEvent(CollectOnCrashEvent::kCollectAttempt);
267 
268   StabilityReport report;
269 
270   // See whether there's an activity tracking report beacon in the process'
271   // annotations.
272   std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
273       MaybeGetInMemoryActivityAnalyzer(process_snapshot);
274   bool collected_report = false;
275   if (global_analyzer) {
276     LogCollectOnCrashEvent(CollectOnCrashEvent::kInMemoryAnnotationExists);
277 
278     collected_report =
279         CollectStabilityReport(std::move(global_analyzer), &report);
280   }
281 
282   CollectSystemPerformanceMetrics(&report);
283   CollectProcessPerformanceMetrics(process_snapshot, &report);
284 
285   std::unique_ptr<BufferExtensionStreamDataSource> source(
286       new BufferExtensionStreamDataSource(kActivityReportStreamType));
287   if (!source->Init(report))
288     return nullptr;
289 
290   if (collected_report)
291     LogCollectOnCrashEvent(CollectOnCrashEvent::kSuccess);
292 
293   return source;
294 }
295 
296 }  // namespace browser_watcher
297