activity_report_user_stream_data_source.cc 10 KB

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