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

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 UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount", 49 UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount",
174 debug_files.size()); 50 debug_files.size());
(...skipping 16 matching lines...) Expand all
191 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status, 67 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status,
192 COLLECTION_STATUS_MAX); 68 COLLECTION_STATUS_MAX);
193 if (status == SUCCESS) 69 if (status == SUCCESS)
194 ++success_cnt; 70 ++success_cnt;
195 } 71 }
196 72
197 return success_cnt; 73 return success_cnt;
198 } 74 }
199 75
200 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths( 76 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
201 const base::FilePath& debug_info_dir, 77 const FilePath& debug_info_dir,
202 const base::FilePath::StringType& debug_file_pattern, 78 const FilePath::StringType& debug_file_pattern,
203 const std::set<FilePath>& excluded_debug_files) { 79 const std::set<FilePath>& excluded_debug_files) {
204 DCHECK_NE(true, debug_info_dir.empty()); 80 DCHECK_NE(true, debug_info_dir.empty());
205 DCHECK_NE(true, debug_file_pattern.empty()); 81 DCHECK_NE(true, debug_file_pattern.empty());
206 82
207 std::vector<FilePath> paths; 83 std::vector<FilePath> paths;
208 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */, 84 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */,
209 base::FileEnumerator::FILES, 85 base::FileEnumerator::FILES,
210 debug_file_pattern); 86 debug_file_pattern);
211 FilePath path; 87 FilePath path;
212 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { 88 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
213 if (excluded_debug_files.find(path) == excluded_debug_files.end()) 89 if (excluded_debug_files.find(path) == excluded_debug_files.end())
214 paths.push_back(path); 90 paths.push_back(path);
215 } 91 }
216 return paths; 92 return paths;
217 } 93 }
218 94
219 PostmortemReportCollector::CollectionStatus 95 CollectionStatus PostmortemReportCollector::CollectAndSubmit(
220 PostmortemReportCollector::CollectAndSubmit(
221 const crashpad::UUID& client_id, 96 const crashpad::UUID& client_id,
222 const FilePath& file, 97 const FilePath& file,
223 crashpad::CrashReportDatabase* report_database) { 98 crashpad::CrashReportDatabase* report_database) {
224 DCHECK_NE(nullptr, report_database); 99 DCHECK_NE(nullptr, report_database);
225 100
226 // Note: the code below involves two notions of report: chrome internal state 101 // Note: the code below involves two notions of report: chrome internal state
227 // reports and the crashpad reports they get wrapped into. 102 // reports and the crashpad reports they get wrapped into.
228 103
229 // Collect the data from the debug file to a proto. Note: a non-empty report 104 // Collect the data from the debug file to a proto. Note: a non-empty report
230 // is interpreted here as an unclean exit. 105 // is interpreted here as an unclean exit.
231 std::unique_ptr<StabilityReport> report_proto; 106 StabilityReport report_proto;
232 CollectionStatus status = Collect(file, &report_proto); 107 CollectionStatus status = Collect(file, &report_proto);
233 if (status != SUCCESS) { 108 if (status != SUCCESS) {
234 // The file was empty, or there was an error collecting the data. Detailed 109 // The file was empty, or there was an error collecting the data. Detailed
235 // logging happens within the Collect function. 110 // logging happens within the Collect function.
236 if (!base::DeleteFile(file, false)) 111 if (!base::DeleteFile(file, false))
237 DLOG(ERROR) << "Failed to delete " << file.value(); 112 DLOG(ERROR) << "Failed to delete " << file.value();
238 return status; 113 return status;
239 } 114 }
240 DCHECK_NE(nullptr, report_proto.get());
241 115
242 // Prepare a crashpad report. 116 // Prepare a crashpad report.
243 CrashReportDatabase::NewReport* new_report = nullptr; 117 CrashReportDatabase::NewReport* new_report = nullptr;
244 CrashReportDatabase::OperationStatus database_status = 118 CrashReportDatabase::OperationStatus database_status =
245 report_database->PrepareNewCrashReport(&new_report); 119 report_database->PrepareNewCrashReport(&new_report);
246 if (database_status != CrashReportDatabase::kNoError) { 120 if (database_status != CrashReportDatabase::kNoError) {
247 // Assume this is recoverable: not deleting the file. 121 // Assume this is recoverable: not deleting the file.
248 DLOG(ERROR) << "PrepareNewCrashReport failed"; 122 DLOG(ERROR) << "PrepareNewCrashReport failed";
249 return PREPARE_NEW_CRASH_REPORT_FAILED; 123 return PREPARE_NEW_CRASH_REPORT_FAILED;
250 } 124 }
251 CrashReportDatabase::CallErrorWritingCrashReport 125 CrashReportDatabase::CallErrorWritingCrashReport
252 call_error_writing_crash_report(report_database, new_report); 126 call_error_writing_crash_report(report_database, new_report);
253 127
254 // Write the report to a minidump. 128 // Write the report to a minidump.
255 if (!WriteReportToMinidump(report_proto.get(), client_id, new_report->uuid, 129 if (!WriteReportToMinidump(&report_proto, client_id, new_report->uuid,
256 reinterpret_cast<FILE*>(new_report->handle))) { 130 reinterpret_cast<FILE*>(new_report->handle))) {
257 // Assume this is not recoverable and delete the file. 131 // Assume this is not recoverable and delete the file.
258 if (!base::DeleteFile(file, false)) 132 if (!base::DeleteFile(file, false))
259 DLOG(ERROR) << "Failed to delete " << file.value(); 133 DLOG(ERROR) << "Failed to delete " << file.value();
260 return WRITE_TO_MINIDUMP_FAILED; 134 return WRITE_TO_MINIDUMP_FAILED;
261 } 135 }
262 136
263 // If the file cannot be deleted, do not report its contents. Note this can 137 // If the file cannot be deleted, do not report its contents. Note this can
264 // lead to under reporting and retries. However, under reporting is 138 // lead to under reporting and retries. However, under reporting is
265 // preferable to the over reporting that would happen with a file that 139 // preferable to the over reporting that would happen with a file that
(...skipping 12 matching lines...) Expand all
278 database_status = report_database->FinishedWritingCrashReport( 152 database_status = report_database->FinishedWritingCrashReport(
279 new_report, &unused_report_id); 153 new_report, &unused_report_id);
280 if (database_status != CrashReportDatabase::kNoError) { 154 if (database_status != CrashReportDatabase::kNoError) {
281 DLOG(ERROR) << "FinishedWritingCrashReport failed"; 155 DLOG(ERROR) << "FinishedWritingCrashReport failed";
282 return FINISHED_WRITING_CRASH_REPORT_FAILED; 156 return FINISHED_WRITING_CRASH_REPORT_FAILED;
283 } 157 }
284 158
285 return SUCCESS; 159 return SUCCESS;
286 } 160 }
287 161
288 PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect( 162 CollectionStatus PostmortemReportCollector::Collect(const base::FilePath& file,
289 const base::FilePath& debug_state_file, 163 StabilityReport* report) {
290 std::unique_ptr<StabilityReport>* report) { 164 DCHECK(report);
291 DCHECK_NE(nullptr, report); 165 CollectionStatus status = Extract(file, report);
292 report->reset(); 166 if (status != SUCCESS)
167 return status;
293 168
294 // Create a global analyzer. 169 // Add the reporter's details to the report.
295 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer =
296 GlobalActivityAnalyzer::CreateWithFile(debug_state_file);
297 if (!global_analyzer)
298 return ANALYZER_CREATION_FAILED;
299
300 // Early exit if there is no data.
301 std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
302 ActivityUserData::Snapshot global_data_snapshot =
303 global_analyzer->GetGlobalUserDataSnapshot();
304 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
305 if (log_messages.empty() && global_data_snapshot.empty() &&
306 !thread_analyzer) {
307 return DEBUG_FILE_NO_DATA;
308 }
309
310 // Create the report, then flesh it out.
311 report->reset(new StabilityReport());
312
313 // Collect log messages.
314 for (const std::string& message : log_messages) {
315 (*report)->add_log_messages(message);
316 }
317
318 // Collect global user data.
319 google::protobuf::Map<std::string, TypedValue>& global_data = 170 google::protobuf::Map<std::string, TypedValue>& global_data =
320 *(*report)->mutable_global_data(); 171 *(report->mutable_global_data());
321 CollectUserData(global_data_snapshot, &global_data, report->get());
322
323 // Add the reporting Chrome's details to the report.
324 global_data[kStabilityReporterChannel].set_string_value(channel_name()); 172 global_data[kStabilityReporterChannel].set_string_value(channel_name());
325 #if defined(ARCH_CPU_X86) 173 #if defined(ARCH_CPU_X86)
326 global_data[kStabilityReporterPlatform].set_string_value( 174 global_data[kStabilityReporterPlatform].set_string_value(
327 std::string("Win32")); 175 std::string("Win32"));
328 #elif defined(ARCH_CPU_X86_64) 176 #elif defined(ARCH_CPU_X86_64)
329 global_data[kStabilityReporterPlatform].set_string_value( 177 global_data[kStabilityReporterPlatform].set_string_value(
330 std::string("Win64")); 178 std::string("Win64"));
331 #endif 179 #endif
332 global_data[kStabilityReporterProduct].set_string_value(product_name()); 180 global_data[kStabilityReporterProduct].set_string_value(product_name());
333 global_data[kStabilityReporterVersion].set_string_value(version_number()); 181 global_data[kStabilityReporterVersion].set_string_value(version_number());
334 182
335 // Collect thread activity data.
336 // Note: a single process is instrumented.
337 ProcessState* process_state = (*report)->add_process_states();
338 for (; thread_analyzer != nullptr;
339 thread_analyzer = global_analyzer->GetNextAnalyzer()) {
340 // Only valid analyzers are expected per contract of GetFirstAnalyzer /
341 // GetNextAnalyzer.
342 DCHECK(thread_analyzer->IsValid());
343
344 if (!process_state->has_process_id()) {
345 process_state->set_process_id(
346 thread_analyzer->activity_snapshot().process_id);
347 }
348 DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
349 process_state->process_id());
350
351 ThreadState* thread_state = process_state->add_threads();
352 CollectThread(thread_analyzer->activity_snapshot(), thread_state);
353 }
354
355 // Collect module information.
356 CollectModuleInformation(global_analyzer->GetModules(), process_state);
357
358 return SUCCESS; 183 return SUCCESS;
359 } 184 }
360 185
361 void PostmortemReportCollector::CollectThread(
362 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
363 ThreadState* thread_state) {
364 DCHECK(thread_state);
365
366 thread_state->set_thread_name(snapshot.thread_name);
367 thread_state->set_thread_id(snapshot.thread_id);
368 thread_state->set_activity_count(snapshot.activity_stack_depth);
369
370 for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
371 const base::debug::Activity& recorded = snapshot.activity_stack[i];
372 Activity* collected = thread_state->add_activities();
373
374 // Collect activity
375 switch (recorded.activity_type) {
376 case base::debug::Activity::ACT_TASK_RUN:
377 collected->set_type(Activity::ACT_TASK_RUN);
378 collected->set_origin_address(recorded.origin_address);
379 collected->set_task_sequence_id(recorded.data.task.sequence_id);
380 break;
381 case base::debug::Activity::ACT_LOCK_ACQUIRE:
382 collected->set_type(Activity::ACT_LOCK_ACQUIRE);
383 collected->set_lock_address(recorded.data.lock.lock_address);
384 break;
385 case base::debug::Activity::ACT_EVENT_WAIT:
386 collected->set_type(Activity::ACT_EVENT_WAIT);
387 collected->set_event_address(recorded.data.event.event_address);
388 break;
389 case base::debug::Activity::ACT_THREAD_JOIN:
390 collected->set_type(Activity::ACT_THREAD_JOIN);
391 collected->set_thread_id(recorded.data.thread.thread_id);
392 break;
393 case base::debug::Activity::ACT_PROCESS_WAIT:
394 collected->set_type(Activity::ACT_PROCESS_WAIT);
395 collected->set_process_id(recorded.data.process.process_id);
396 break;
397 default:
398 break;
399 }
400
401 // Collect user data
402 if (i < snapshot.user_data_stack.size()) {
403 CollectUserData(snapshot.user_data_stack[i],
404 collected->mutable_user_data(), nullptr);
405 }
406 }
407 }
408
409 bool PostmortemReportCollector::WriteReportToMinidump( 186 bool PostmortemReportCollector::WriteReportToMinidump(
410 StabilityReport* report, 187 StabilityReport* report,
411 const crashpad::UUID& client_id, 188 const crashpad::UUID& client_id,
412 const crashpad::UUID& report_id, 189 const crashpad::UUID& report_id,
413 base::PlatformFile minidump_file) { 190 base::PlatformFile minidump_file) {
414 DCHECK(report); 191 DCHECK(report);
415 192
416 return WritePostmortemDump(minidump_file, client_id, report_id, report); 193 return WritePostmortemDump(minidump_file, client_id, report_id, report);
417 } 194 }
418 195
419 } // namespace browser_watcher 196 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698