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

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

Issue 2722223002: Separate collection logic from the extraction of the report (Closed)
Patch Set: 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_impl.h"
6 6
7 #include <memory>
8 #include <string>
7 #include <utility> 9 #include <utility>
10 #include <vector>
8 11
9 #include "base/debug/activity_analyzer.h" 12 #include "base/debug/activity_analyzer.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #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"
19 #include "components/browser_watcher/stability_data_names.h" 17 #include "components/browser_watcher/stability_data_names.h"
20 #include "components/variations/active_field_trials.h" 18 #include "components/variations/active_field_trials.h"
21 #include "third_party/crashpad/crashpad/client/settings.h"
22 #include "third_party/crashpad/crashpad/util/misc/uuid.h" 19 #include "third_party/crashpad/crashpad/util/misc/uuid.h"
23 20
24 using base::FilePath;
25
26 namespace browser_watcher { 21 namespace browser_watcher {
22 namespace impl {
27 23
28 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot; 24 using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot;
29 using base::debug::ActivityUserData; 25 using base::debug::ActivityUserData;
30 using base::debug::GlobalActivityAnalyzer; 26 using base::debug::GlobalActivityAnalyzer;
31 using base::debug::GlobalActivityTracker; 27 using base::debug::GlobalActivityTracker;
32 using base::debug::ThreadActivityAnalyzer; 28 using base::debug::ThreadActivityAnalyzer;
33 using crashpad::CrashReportDatabase;
34 29
35 namespace { 30 namespace {
36 31
37 const char kFieldTrialKeyPrefix[] = "FieldTrial."; 32 const char kFieldTrialKeyPrefix[] = "FieldTrial.";
38 33
39 // Collects stability user data from the recorded format to the collected 34 // Collects stability user data from the recorded format to the collected
40 // format. 35 // format.
41 void CollectUserData( 36 void CollectUserData(
42 const ActivityUserData::Snapshot& recorded_map, 37 const ActivityUserData::Snapshot& recorded_map,
43 google::protobuf::Map<std::string, TypedValue>* collected_map, 38 google::protobuf::Map<std::string, TypedValue>* collected_map,
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 uuid->data_2, uuid->data_3, uuid->data_4[0], uuid->data_4[1], 138 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], 139 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); 140 uuid->data_5[4], uuid->data_5[5], recorded.age);
146 collected->set_debug_identifier(debug_identifier); 141 collected->set_debug_identifier(debug_identifier);
147 collected->set_is_unloaded(!recorded.is_loaded); 142 collected->set_is_unloaded(!recorded.is_loaded);
148 } 143 }
149 } 144 }
150 145
151 } // namespace 146 } // namespace
152 147
153 PostmortemReportCollector::PostmortemReportCollector( 148 // DO NOT SUBMIT: review diff stepping stone. Move the function after 1st round.
154 const std::string& product_name, 149 void CollectThread(
155 const std::string& version_number, 150 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
156 const std::string& channel_name) 151 ThreadState* thread_state);
157 : product_name_(product_name),
158 version_number_(version_number),
159 channel_name_(channel_name) {}
160 152
161 int PostmortemReportCollector::CollectAndSubmitForUpload( 153 CollectionStatus Collect(const base::FilePath& stability_file,
162 const base::FilePath& debug_info_dir, 154 StabilityReport* report) {
163 const base::FilePath::StringType& debug_file_pattern, 155 DCHECK(report);
164 const std::set<base::FilePath>& excluded_debug_files,
165 crashpad::CrashReportDatabase* report_database) {
166 DCHECK_NE(true, debug_info_dir.empty());
167 DCHECK_NE(true, debug_file_pattern.empty());
168 DCHECK_NE(nullptr, report_database);
169
170 // Collect the list of files to harvest.
171 std::vector<FilePath> debug_files = GetDebugStateFilePaths(
172 debug_info_dir, debug_file_pattern, excluded_debug_files);
173
174 // Determine the crashpad client id.
175 crashpad::UUID client_id;
176 crashpad::Settings* settings = report_database->GetSettings();
177 if (settings) {
178 // If GetSettings() or GetClientID() fails client_id will be left at its
179 // default value, all zeroes, which is appropriate.
180 settings->GetClientID(&client_id);
181 }
182
183 // Process each stability file.
184 int success_cnt = 0;
185 for (const FilePath& file : debug_files) {
186 CollectionStatus status =
187 CollectAndSubmit(client_id, file, report_database);
188 // TODO(manzagop): consider making this a stability metric.
189 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status,
190 COLLECTION_STATUS_MAX);
191 if (status == SUCCESS)
192 ++success_cnt;
193 }
194
195 return success_cnt;
196 }
197
198 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
199 const base::FilePath& debug_info_dir,
200 const base::FilePath::StringType& debug_file_pattern,
201 const std::set<FilePath>& excluded_debug_files) {
202 DCHECK_NE(true, debug_info_dir.empty());
203 DCHECK_NE(true, debug_file_pattern.empty());
204
205 std::vector<FilePath> paths;
206 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */,
207 base::FileEnumerator::FILES,
208 debug_file_pattern);
209 FilePath path;
210 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
211 if (excluded_debug_files.find(path) == excluded_debug_files.end())
212 paths.push_back(path);
213 }
214 return paths;
215 }
216
217 PostmortemReportCollector::CollectionStatus
218 PostmortemReportCollector::CollectAndSubmit(
219 const crashpad::UUID& client_id,
220 const FilePath& file,
221 crashpad::CrashReportDatabase* report_database) {
222 DCHECK_NE(nullptr, report_database);
223
224 // Note: the code below involves two notions of report: chrome internal state
225 // reports and the crashpad reports they get wrapped into.
226
227 // Collect the data from the debug file to a proto. Note: a non-empty report
228 // is interpreted here as an unclean exit.
229 std::unique_ptr<StabilityReport> report_proto;
230 CollectionStatus status = Collect(file, &report_proto);
231 if (status != SUCCESS) {
232 // The file was empty, or there was an error collecting the data. Detailed
233 // logging happens within the Collect function.
234 if (!base::DeleteFile(file, false))
235 LOG(ERROR) << "Failed to delete " << file.value();
236 return status;
237 }
238 DCHECK_NE(nullptr, report_proto.get());
239
240 // Prepare a crashpad report.
241 CrashReportDatabase::NewReport* new_report = nullptr;
242 CrashReportDatabase::OperationStatus database_status =
243 report_database->PrepareNewCrashReport(&new_report);
244 if (database_status != CrashReportDatabase::kNoError) {
245 LOG(ERROR) << "PrepareNewCrashReport failed";
246 return PREPARE_NEW_CRASH_REPORT_FAILED;
247 }
248 CrashReportDatabase::CallErrorWritingCrashReport
249 call_error_writing_crash_report(report_database, new_report);
250
251 // Write the report to a minidump.
252 if (!WriteReportToMinidump(report_proto.get(), client_id, new_report->uuid,
253 reinterpret_cast<FILE*>(new_report->handle))) {
254 return WRITE_TO_MINIDUMP_FAILED;
255 }
256
257 // 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
259 // preferable to the over reporting that would happen with a file that
260 // cannot be deleted.
261 // TODO(manzagop): metrics for the number of non-deletable files.
262 if (!base::DeleteFile(file, false)) {
263 LOG(ERROR) << "Failed to delete " << file.value();
264 return DEBUG_FILE_DELETION_FAILED;
265 }
266
267 // 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
269 // writing, the delay is on the order of up to 15 minutes).
270 call_error_writing_crash_report.Disarm();
271 crashpad::UUID unused_report_id;
272 database_status = report_database->FinishedWritingCrashReport(
273 new_report, &unused_report_id);
274 if (database_status != CrashReportDatabase::kNoError) {
275 LOG(ERROR) << "FinishedWritingCrashReport failed";
276 return FINISHED_WRITING_CRASH_REPORT_FAILED;
277 }
278
279 return SUCCESS;
280 }
281
282 PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect(
283 const base::FilePath& debug_state_file,
284 std::unique_ptr<StabilityReport>* report) {
285 DCHECK_NE(nullptr, report);
286 report->reset();
287 156
288 // Create a global analyzer. 157 // Create a global analyzer.
289 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer = 158 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer =
290 GlobalActivityAnalyzer::CreateWithFile(debug_state_file); 159 GlobalActivityAnalyzer::CreateWithFile(stability_file);
291 if (!global_analyzer) 160 if (!global_analyzer)
292 return ANALYZER_CREATION_FAILED; 161 return ANALYZER_CREATION_FAILED;
293 162
294 // Early exit if there is no data. 163 // Early exit if there is no data.
295 std::vector<std::string> log_messages = global_analyzer->GetLogMessages(); 164 std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
296 ActivityUserData::Snapshot global_data_snapshot = 165 ActivityUserData::Snapshot global_data_snapshot =
297 global_analyzer->GetGlobalUserDataSnapshot(); 166 global_analyzer->GetGlobalUserDataSnapshot();
298 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer(); 167 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
299 if (log_messages.empty() && global_data_snapshot.empty() && 168 if (log_messages.empty() && global_data_snapshot.empty() &&
300 !thread_analyzer) { 169 !thread_analyzer) {
301 return DEBUG_FILE_NO_DATA; 170 return DEBUG_FILE_NO_DATA;
302 } 171 }
303 172
304 // Create the report, then flesh it out.
305 report->reset(new StabilityReport());
306
307 // Collect log messages. 173 // Collect log messages.
308 for (const std::string& message : log_messages) { 174 for (const std::string& message : log_messages) {
309 (*report)->add_log_messages(message); 175 report->add_log_messages(message);
310 } 176 }
311 177
312 // Collect global user data. 178 // Collect global user data.
313 google::protobuf::Map<std::string, TypedValue>& global_data = 179 google::protobuf::Map<std::string, TypedValue>& global_data =
314 *(*report)->mutable_global_data(); 180 *(report->mutable_global_data());
315 CollectUserData(global_data_snapshot, &global_data, report->get()); 181 CollectUserData(global_data_snapshot, &global_data, report);
316
317 // Add the reporting Chrome's details to the report.
318 global_data[kStabilityReporterChannel].set_string_value(channel_name());
319 #if defined(ARCH_CPU_X86)
320 global_data[kStabilityReporterPlatform].set_string_value(
321 std::string("Win32"));
322 #elif defined(ARCH_CPU_X86_64)
323 global_data[kStabilityReporterPlatform].set_string_value(
324 std::string("Win64"));
325 #endif
326 global_data[kStabilityReporterProduct].set_string_value(product_name());
327 global_data[kStabilityReporterVersion].set_string_value(version_number());
328 182
329 // Collect thread activity data. 183 // Collect thread activity data.
330 // Note: a single process is instrumented. 184 // Note: a single process is instrumented.
331 ProcessState* process_state = (*report)->add_process_states(); 185 ProcessState* process_state = report->add_process_states();
332 for (; thread_analyzer != nullptr; 186 for (; thread_analyzer != nullptr;
333 thread_analyzer = global_analyzer->GetNextAnalyzer()) { 187 thread_analyzer = global_analyzer->GetNextAnalyzer()) {
334 // Only valid analyzers are expected per contract of GetFirstAnalyzer / 188 // Only valid analyzers are expected per contract of GetFirstAnalyzer /
335 // GetNextAnalyzer. 189 // GetNextAnalyzer.
336 DCHECK(thread_analyzer->IsValid()); 190 DCHECK(thread_analyzer->IsValid());
337 191
338 if (!process_state->has_process_id()) { 192 if (!process_state->has_process_id()) {
339 process_state->set_process_id( 193 process_state->set_process_id(
340 thread_analyzer->activity_snapshot().process_id); 194 thread_analyzer->activity_snapshot().process_id);
341 } 195 }
342 DCHECK_EQ(thread_analyzer->activity_snapshot().process_id, 196 DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
343 process_state->process_id()); 197 process_state->process_id());
344 198
345 ThreadState* thread_state = process_state->add_threads(); 199 ThreadState* thread_state = process_state->add_threads();
346 CollectThread(thread_analyzer->activity_snapshot(), thread_state); 200 CollectThread(thread_analyzer->activity_snapshot(), thread_state);
347 } 201 }
348 202
349 // Collect module information. 203 // Collect module information.
350 CollectModuleInformation(global_analyzer->GetModules(), process_state); 204 CollectModuleInformation(global_analyzer->GetModules(), process_state);
351 205
352 return SUCCESS; 206 return SUCCESS;
353 } 207 }
354 208
355 void PostmortemReportCollector::CollectThread( 209 // DO NOT SUBMIT: review diff stepping stone. Move the function after 1st round.
210 void CollectThread(
356 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot, 211 const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
357 ThreadState* thread_state) { 212 ThreadState* thread_state) {
358 DCHECK(thread_state); 213 DCHECK(thread_state);
359 214
360 thread_state->set_thread_name(snapshot.thread_name); 215 thread_state->set_thread_name(snapshot.thread_name);
361 thread_state->set_thread_id(snapshot.thread_id); 216 thread_state->set_thread_id(snapshot.thread_id);
362 thread_state->set_activity_count(snapshot.activity_stack_depth); 217 thread_state->set_activity_count(snapshot.activity_stack_depth);
363 218
364 for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) { 219 for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
365 const base::debug::Activity& recorded = snapshot.activity_stack[i]; 220 const base::debug::Activity& recorded = snapshot.activity_stack[i];
(...skipping 27 matching lines...) Expand all
393 } 248 }
394 249
395 // Collect user data 250 // Collect user data
396 if (i < snapshot.user_data_stack.size()) { 251 if (i < snapshot.user_data_stack.size()) {
397 CollectUserData(snapshot.user_data_stack[i], 252 CollectUserData(snapshot.user_data_stack[i],
398 collected->mutable_user_data(), nullptr); 253 collected->mutable_user_data(), nullptr);
399 } 254 }
400 } 255 }
401 } 256 }
402 257
403 bool PostmortemReportCollector::WriteReportToMinidump( 258 } // namespace impl
404 StabilityReport* report,
405 const crashpad::UUID& client_id,
406 const crashpad::UUID& report_id,
407 base::PlatformFile minidump_file) {
408 DCHECK(report);
409
410 return WritePostmortemDump(minidump_file, client_id, report_id, report);
411 }
412
413 } // namespace browser_watcher 259 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698