Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: components/browser_watcher/postmortem_report_collector.cc

Issue 2722223002: Separate collection logic from the extraction of the report (Closed)
Patch Set: Address Siggi's comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/browser_watcher/postmortem_report_collector.h" 5 #include "components/browser_watcher/postmortem_report_collector.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/debug/activity_analyzer.h"
10 #include "base/files/file_enumerator.h" 9 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h" 10 #include "base/files/file_util.h"
12 #include "base/logging.h" 11 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h" 12 #include "base/metrics/histogram_macros.h"
14 #include "base/path_service.h" 13 #include "base/path_service.h"
15 #include "base/strings/string_piece.h" 14 #include "base/strings/string_piece.h"
16 #include "base/strings/string_util.h" 15 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h" 16 #include "base/strings/utf_string_conversions.h"
18 #include "components/browser_watcher/postmortem_minidump_writer.h" 17 #include "components/browser_watcher/postmortem_minidump_writer.h"
19 #include "components/browser_watcher/stability_data_names.h" 18 #include "components/browser_watcher/stability_data_names.h"
20 #include "components/variations/active_field_trials.h"
21 #include "third_party/crashpad/crashpad/client/settings.h" 19 #include "third_party/crashpad/crashpad/client/settings.h"
22 #include "third_party/crashpad/crashpad/util/misc/uuid.h" 20 #include "third_party/crashpad/crashpad/util/misc/uuid.h"
23 21
24 using base::FilePath;
25
26 namespace browser_watcher { 22 namespace browser_watcher {
27 23
28 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot; 24 using base::FilePath;
29 using base::debug::ActivityUserData;
30 using base::debug::GlobalActivityAnalyzer;
31 using base::debug::GlobalActivityTracker;
32 using base::debug::ThreadActivityAnalyzer;
33 using crashpad::CrashReportDatabase; 25 using crashpad::CrashReportDatabase;
34 26
35 namespace {
36
37 const char kFieldTrialKeyPrefix[] = "FieldTrial.";
38
39 // Collects stability user data from the recorded format to the collected
40 // format.
41 void CollectUserData(
42 const ActivityUserData::Snapshot& recorded_map,
43 google::protobuf::Map<std::string, TypedValue>* collected_map,
44 StabilityReport* report) {
45 DCHECK(collected_map);
46
47 for (const auto& name_and_value : recorded_map) {
48 const std::string& key = name_and_value.first;
49 const ActivityUserData::TypedValue& recorded_value = name_and_value.second;
50 TypedValue collected_value;
51
52 switch (recorded_value.type()) {
53 case ActivityUserData::END_OF_VALUES:
54 NOTREACHED();
55 break;
56 case ActivityUserData::RAW_VALUE: {
57 base::StringPiece raw = recorded_value.Get();
58 collected_value.set_bytes_value(raw.data(), raw.size());
59 break;
60 }
61 case ActivityUserData::RAW_VALUE_REFERENCE: {
62 base::StringPiece recorded_ref = recorded_value.GetReference();
63 TypedValue::Reference* collected_ref =
64 collected_value.mutable_bytes_reference();
65 collected_ref->set_address(
66 reinterpret_cast<uintptr_t>(recorded_ref.data()));
67 collected_ref->set_size(recorded_ref.size());
68 break;
69 }
70 case ActivityUserData::STRING_VALUE: {
71 base::StringPiece value = recorded_value.GetString();
72
73 if (report && base::StartsWith(key, kFieldTrialKeyPrefix,
74 base::CompareCase::SENSITIVE)) {
75 // This entry represents an active Field Trial.
76 std::string trial_name =
77 key.substr(std::strlen(kFieldTrialKeyPrefix));
78 variations::ActiveGroupId group_id =
79 variations::MakeActiveGroupId(trial_name, value.as_string());
80 FieldTrial* field_trial = report->add_field_trials();
81 field_trial->set_name_id(group_id.name);
82 field_trial->set_group_id(group_id.group);
83 continue;
84 }
85
86 collected_value.set_string_value(value.data(), value.size());
87 break;
88 }
89 case ActivityUserData::STRING_VALUE_REFERENCE: {
90 base::StringPiece recorded_ref = recorded_value.GetStringReference();
91 TypedValue::Reference* collected_ref =
92 collected_value.mutable_string_reference();
93 collected_ref->set_address(
94 reinterpret_cast<uintptr_t>(recorded_ref.data()));
95 collected_ref->set_size(recorded_ref.size());
96 break;
97 }
98 case ActivityUserData::CHAR_VALUE: {
99 char char_value = recorded_value.GetChar();
100 collected_value.set_char_value(&char_value, 1);
101 break;
102 }
103 case ActivityUserData::BOOL_VALUE:
104 collected_value.set_bool_value(recorded_value.GetBool());
105 break;
106 case ActivityUserData::SIGNED_VALUE:
107 collected_value.set_signed_value(recorded_value.GetInt());
108 break;
109 case ActivityUserData::UNSIGNED_VALUE:
110 collected_value.set_unsigned_value(recorded_value.GetUint());
111 break;
112 }
113
114 (*collected_map)[key].Swap(&collected_value);
115 }
116 }
117
118 void CollectModuleInformation(
119 const std::vector<GlobalActivityTracker::ModuleInfo>& modules,
120 ProcessState* process_state) {
121 DCHECK(process_state);
122
123 char code_identifier[17];
124 char debug_identifier[41];
125
126 for (const GlobalActivityTracker::ModuleInfo& recorded : modules) {
127 CodeModule* collected = process_state->add_modules();
128 collected->set_base_address(recorded.address);
129 collected->set_size(recorded.size);
130 collected->set_code_file(recorded.file);
131
132 // Compute the code identifier using the required format.
133 snprintf(code_identifier, sizeof(code_identifier), "%08X%zx",
134 recorded.timestamp, recorded.size);
135 collected->set_code_identifier(code_identifier);
136 collected->set_debug_file(recorded.debug_file);
137
138 // Compute the debug identifier using the required format.
139 const crashpad::UUID* uuid =
140 reinterpret_cast<const crashpad::UUID*>(recorded.identifier);
141 snprintf(debug_identifier, sizeof(debug_identifier),
142 "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", uuid->data_1,
143 uuid->data_2, uuid->data_3, uuid->data_4[0], uuid->data_4[1],
144 uuid->data_5[0], uuid->data_5[1], uuid->data_5[2], uuid->data_5[3],
145 uuid->data_5[4], uuid->data_5[5], recorded.age);
146 collected->set_debug_identifier(debug_identifier);
147 collected->set_is_unloaded(!recorded.is_loaded);
148 }
149 }
150
151 } // namespace
152
153 PostmortemReportCollector::PostmortemReportCollector( 27 PostmortemReportCollector::PostmortemReportCollector(
154 const std::string& product_name, 28 const std::string& product_name,
155 const std::string& version_number, 29 const std::string& version_number,
156 const std::string& channel_name) 30 const std::string& channel_name)
157 : product_name_(product_name), 31 : product_name_(product_name),
158 version_number_(version_number), 32 version_number_(version_number),
159 channel_name_(channel_name) {} 33 channel_name_(channel_name) {}
160 34
35 PostmortemReportCollector::~PostmortemReportCollector() {}
36
161 int PostmortemReportCollector::CollectAndSubmitForUpload( 37 int PostmortemReportCollector::CollectAndSubmitForUpload(
162 const base::FilePath& debug_info_dir, 38 const FilePath& debug_info_dir,
163 const base::FilePath::StringType& debug_file_pattern, 39 const FilePath::StringType& debug_file_pattern,
164 const std::set<base::FilePath>& excluded_debug_files, 40 const std::set<FilePath>& excluded_debug_files,
165 crashpad::CrashReportDatabase* report_database) { 41 crashpad::CrashReportDatabase* report_database) {
166 DCHECK_NE(true, debug_info_dir.empty()); 42 DCHECK_NE(true, debug_info_dir.empty());
167 DCHECK_NE(true, debug_file_pattern.empty()); 43 DCHECK_NE(true, debug_file_pattern.empty());
168 DCHECK_NE(nullptr, report_database); 44 DCHECK_NE(nullptr, report_database);
169 45
170 // Collect the list of files to harvest. 46 // Collect the list of files to harvest.
171 std::vector<FilePath> debug_files = GetDebugStateFilePaths( 47 std::vector<FilePath> debug_files = GetDebugStateFilePaths(
172 debug_info_dir, debug_file_pattern, excluded_debug_files); 48 debug_info_dir, debug_file_pattern, excluded_debug_files);
173 49
174 // Determine the crashpad client id. 50 // Determine the crashpad client id.
(...skipping 14 matching lines...) Expand all
189 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status, 65 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status,
190 COLLECTION_STATUS_MAX); 66 COLLECTION_STATUS_MAX);
191 if (status == SUCCESS) 67 if (status == SUCCESS)
192 ++success_cnt; 68 ++success_cnt;
193 } 69 }
194 70
195 return success_cnt; 71 return success_cnt;
196 } 72 }
197 73
198 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths( 74 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
199 const base::FilePath& debug_info_dir, 75 const FilePath& debug_info_dir,
200 const base::FilePath::StringType& debug_file_pattern, 76 const FilePath::StringType& debug_file_pattern,
201 const std::set<FilePath>& excluded_debug_files) { 77 const std::set<FilePath>& excluded_debug_files) {
202 DCHECK_NE(true, debug_info_dir.empty()); 78 DCHECK_NE(true, debug_info_dir.empty());
203 DCHECK_NE(true, debug_file_pattern.empty()); 79 DCHECK_NE(true, debug_file_pattern.empty());
204 80
205 std::vector<FilePath> paths; 81 std::vector<FilePath> paths;
206 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */, 82 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */,
207 base::FileEnumerator::FILES, 83 base::FileEnumerator::FILES,
208 debug_file_pattern); 84 debug_file_pattern);
209 FilePath path; 85 FilePath path;
210 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { 86 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
211 if (excluded_debug_files.find(path) == excluded_debug_files.end()) 87 if (excluded_debug_files.find(path) == excluded_debug_files.end())
212 paths.push_back(path); 88 paths.push_back(path);
213 } 89 }
214 return paths; 90 return paths;
215 } 91 }
216 92
217 PostmortemReportCollector::CollectionStatus 93 CollectionStatus PostmortemReportCollector::CollectAndSubmit(
218 PostmortemReportCollector::CollectAndSubmit(
219 const crashpad::UUID& client_id, 94 const crashpad::UUID& client_id,
220 const FilePath& file, 95 const FilePath& file,
221 crashpad::CrashReportDatabase* report_database) { 96 crashpad::CrashReportDatabase* report_database) {
222 DCHECK_NE(nullptr, report_database); 97 DCHECK_NE(nullptr, report_database);
223 98
224 // Note: the code below involves two notions of report: chrome internal state 99 // Note: the code below involves two notions of report: chrome internal state
225 // reports and the crashpad reports they get wrapped into. 100 // reports and the crashpad reports they get wrapped into.
226 101
227 // Collect the data from the debug file to a proto. Note: a non-empty report 102 // Collect the data from the debug file to a proto. Note: a non-empty report
228 // is interpreted here as an unclean exit. 103 // is interpreted here as an unclean exit.
229 std::unique_ptr<StabilityReport> report_proto; 104 StabilityReport report_proto;
230 CollectionStatus status = Collect(file, &report_proto); 105 CollectionStatus status = Collect(file, &report_proto);
231 if (status != SUCCESS) { 106 if (status != SUCCESS) {
232 // The file was empty, or there was an error collecting the data. Detailed 107 // The file was empty, or there was an error collecting the data. Detailed
233 // logging happens within the Collect function. 108 // logging happens within the Collect function.
234 if (!base::DeleteFile(file, false)) 109 if (!base::DeleteFile(file, false))
235 LOG(ERROR) << "Failed to delete " << file.value(); 110 LOG(ERROR) << "Failed to delete " << file.value();
236 return status; 111 return status;
237 } 112 }
238 DCHECK_NE(nullptr, report_proto.get());
239 113
240 // Prepare a crashpad report. 114 // Prepare a crashpad report.
241 CrashReportDatabase::NewReport* new_report = nullptr; 115 CrashReportDatabase::NewReport* new_report = nullptr;
242 CrashReportDatabase::OperationStatus database_status = 116 CrashReportDatabase::OperationStatus database_status =
243 report_database->PrepareNewCrashReport(&new_report); 117 report_database->PrepareNewCrashReport(&new_report);
244 if (database_status != CrashReportDatabase::kNoError) { 118 if (database_status != CrashReportDatabase::kNoError) {
245 LOG(ERROR) << "PrepareNewCrashReport failed"; 119 LOG(ERROR) << "PrepareNewCrashReport failed";
246 return PREPARE_NEW_CRASH_REPORT_FAILED; 120 return PREPARE_NEW_CRASH_REPORT_FAILED;
247 } 121 }
248 CrashReportDatabase::CallErrorWritingCrashReport 122 CrashReportDatabase::CallErrorWritingCrashReport
249 call_error_writing_crash_report(report_database, new_report); 123 call_error_writing_crash_report(report_database, new_report);
250 124
251 // Write the report to a minidump. 125 // Write the report to a minidump.
252 if (!WriteReportToMinidump(report_proto.get(), client_id, new_report->uuid, 126 if (!WriteReportToMinidump(&report_proto, client_id, new_report->uuid,
253 reinterpret_cast<FILE*>(new_report->handle))) { 127 reinterpret_cast<FILE*>(new_report->handle))) {
254 return WRITE_TO_MINIDUMP_FAILED; 128 return WRITE_TO_MINIDUMP_FAILED;
255 } 129 }
256 130
257 // If the file cannot be deleted, do not report its contents. Note this can 131 // If the file cannot be deleted, do not report its contents. Note this can
258 // lead to under reporting and retries. However, under reporting is 132 // lead to under reporting and retries. However, under reporting is
259 // preferable to the over reporting that would happen with a file that 133 // preferable to the over reporting that would happen with a file that
260 // cannot be deleted. 134 // cannot be deleted.
261 // TODO(manzagop): metrics for the number of non-deletable files. 135 // TODO(manzagop): metrics for the number of non-deletable files.
262 if (!base::DeleteFile(file, false)) { 136 if (!base::DeleteFile(file, false)) {
263 LOG(ERROR) << "Failed to delete " << file.value(); 137 LOG(ERROR) << "Failed to delete " << file.value();
264 return DEBUG_FILE_DELETION_FAILED; 138 return DEBUG_FILE_DELETION_FAILED;
265 } 139 }
266 140
267 // Finalize the report wrt the report database. Note that this doesn't trigger 141 // Finalize the report wrt the report database. Note that this doesn't trigger
268 // an immediate upload, but Crashpad will eventually upload the report (as of 142 // an immediate upload, but Crashpad will eventually upload the report (as of
269 // writing, the delay is on the order of up to 15 minutes). 143 // writing, the delay is on the order of up to 15 minutes).
270 call_error_writing_crash_report.Disarm(); 144 call_error_writing_crash_report.Disarm();
271 crashpad::UUID unused_report_id; 145 crashpad::UUID unused_report_id;
272 database_status = report_database->FinishedWritingCrashReport( 146 database_status = report_database->FinishedWritingCrashReport(
273 new_report, &unused_report_id); 147 new_report, &unused_report_id);
274 if (database_status != CrashReportDatabase::kNoError) { 148 if (database_status != CrashReportDatabase::kNoError) {
275 LOG(ERROR) << "FinishedWritingCrashReport failed"; 149 LOG(ERROR) << "FinishedWritingCrashReport failed";
276 return FINISHED_WRITING_CRASH_REPORT_FAILED; 150 return FINISHED_WRITING_CRASH_REPORT_FAILED;
277 } 151 }
278 152
279 return SUCCESS; 153 return SUCCESS;
280 } 154 }
281 155
282 PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect( 156 CollectionStatus PostmortemReportCollector::Collect(const base::FilePath& file,
283 const base::FilePath& debug_state_file, 157 StabilityReport* report) {
284 std::unique_ptr<StabilityReport>* report) { 158 DCHECK(report);
285 DCHECK_NE(nullptr, report); 159 CollectionStatus status = Extract(file, report);
286 report->reset(); 160 if (status != SUCCESS)
161 return status;
287 162
288 // Create a global analyzer. 163 // Add the reporter's details to the report.
289 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer =
290 GlobalActivityAnalyzer::CreateWithFile(debug_state_file);
291 if (!global_analyzer)
292 return ANALYZER_CREATION_FAILED;
293
294 // Early exit if there is no data.
295 std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
296 ActivityUserData::Snapshot global_data_snapshot =
297 global_analyzer->GetGlobalUserDataSnapshot();
298 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
299 if (log_messages.empty() && global_data_snapshot.empty() &&
300 !thread_analyzer) {
301 return DEBUG_FILE_NO_DATA;
302 }
303
304 // Create the report, then flesh it out.
305 report->reset(new StabilityReport());
306
307 // Collect log messages.
308 for (const std::string& message : log_messages) {
309 (*report)->add_log_messages(message);
310 }
311
312 // Collect global user data.
313 google::protobuf::Map<std::string, TypedValue>& global_data = 164 google::protobuf::Map<std::string, TypedValue>& global_data =
314 *(*report)->mutable_global_data(); 165 *(report->mutable_global_data());
315 CollectUserData(global_data_snapshot, &global_data, report->get());
316
317 // Add the reporting Chrome's details to the report.
318 global_data[kStabilityReporterChannel].set_string_value(channel_name()); 166 global_data[kStabilityReporterChannel].set_string_value(channel_name());
319 #if defined(ARCH_CPU_X86) 167 #if defined(ARCH_CPU_X86)
320 global_data[kStabilityReporterPlatform].set_string_value( 168 global_data[kStabilityReporterPlatform].set_string_value(
321 std::string("Win32")); 169 std::string("Win32"));
322 #elif defined(ARCH_CPU_X86_64) 170 #elif defined(ARCH_CPU_X86_64)
323 global_data[kStabilityReporterPlatform].set_string_value( 171 global_data[kStabilityReporterPlatform].set_string_value(
324 std::string("Win64")); 172 std::string("Win64"));
325 #endif 173 #endif
326 global_data[kStabilityReporterProduct].set_string_value(product_name()); 174 global_data[kStabilityReporterProduct].set_string_value(product_name());
327 global_data[kStabilityReporterVersion].set_string_value(version_number()); 175 global_data[kStabilityReporterVersion].set_string_value(version_number());
328 176
329 // Collect thread activity data.
330 // Note: a single process is instrumented.
331 ProcessState* process_state = (*report)->add_process_states();
332 for (; thread_analyzer != nullptr;
333 thread_analyzer = global_analyzer->GetNextAnalyzer()) {
334 // Only valid analyzers are expected per contract of GetFirstAnalyzer /
335 // GetNextAnalyzer.
336 DCHECK(thread_analyzer->IsValid());
337
338 if (!process_state->has_process_id()) {
339 process_state->set_process_id(
340 thread_analyzer->activity_snapshot().process_id);
341 }
342 DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
343 process_state->process_id());
344
345 ThreadState* thread_state = process_state->add_threads();
346 CollectThread(thread_analyzer->activity_snapshot(), thread_state);
347 }
348
349 // Collect module information.
350 CollectModuleInformation(global_analyzer->GetModules(), process_state);
351
352 return SUCCESS; 177 return SUCCESS;
353 } 178 }
354 179
355 void PostmortemReportCollector::CollectThread(
356 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
357 ThreadState* thread_state) {
358 DCHECK(thread_state);
359
360 thread_state->set_thread_name(snapshot.thread_name);
361 thread_state->set_thread_id(snapshot.thread_id);
362 thread_state->set_activity_count(snapshot.activity_stack_depth);
363
364 for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
365 const base::debug::Activity& recorded = snapshot.activity_stack[i];
366 Activity* collected = thread_state->add_activities();
367
368 // Collect activity
369 switch (recorded.activity_type) {
370 case base::debug::Activity::ACT_TASK_RUN:
371 collected->set_type(Activity::ACT_TASK_RUN);
372 collected->set_origin_address(recorded.origin_address);
373 collected->set_task_sequence_id(recorded.data.task.sequence_id);
374 break;
375 case base::debug::Activity::ACT_LOCK_ACQUIRE:
376 collected->set_type(Activity::ACT_LOCK_ACQUIRE);
377 collected->set_lock_address(recorded.data.lock.lock_address);
378 break;
379 case base::debug::Activity::ACT_EVENT_WAIT:
380 collected->set_type(Activity::ACT_EVENT_WAIT);
381 collected->set_event_address(recorded.data.event.event_address);
382 break;
383 case base::debug::Activity::ACT_THREAD_JOIN:
384 collected->set_type(Activity::ACT_THREAD_JOIN);
385 collected->set_thread_id(recorded.data.thread.thread_id);
386 break;
387 case base::debug::Activity::ACT_PROCESS_WAIT:
388 collected->set_type(Activity::ACT_PROCESS_WAIT);
389 collected->set_process_id(recorded.data.process.process_id);
390 break;
391 default:
392 break;
393 }
394
395 // Collect user data
396 if (i < snapshot.user_data_stack.size()) {
397 CollectUserData(snapshot.user_data_stack[i],
398 collected->mutable_user_data(), nullptr);
399 }
400 }
401 }
402
403 bool PostmortemReportCollector::WriteReportToMinidump( 180 bool PostmortemReportCollector::WriteReportToMinidump(
404 StabilityReport* report, 181 StabilityReport* report,
405 const crashpad::UUID& client_id, 182 const crashpad::UUID& client_id,
406 const crashpad::UUID& report_id, 183 const crashpad::UUID& report_id,
407 base::PlatformFile minidump_file) { 184 base::PlatformFile minidump_file) {
408 DCHECK(report); 185 DCHECK(report);
409 186
410 return WritePostmortemDump(minidump_file, client_id, report_id, report); 187 return WritePostmortemDump(minidump_file, client_id, report_id, report);
411 } 188 }
412 189
413 } // namespace browser_watcher 190 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698