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