OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/browser_watcher/postmortem_report_collector.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #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/path_service.h" | |
14 #include "components/browser_watcher/postmortem_minidump_writer.h" | |
15 #include "third_party/crashpad/crashpad/client/settings.h" | |
16 #include "third_party/crashpad/crashpad/util/misc/uuid.h" | |
17 | |
18 using base::FilePath; | |
19 | |
20 namespace browser_watcher { | |
21 | |
22 using base::debug::GlobalActivityAnalyzer; | |
23 using base::debug::ThreadActivityAnalyzer; | |
24 using crashpad::CrashReportDatabase; | |
25 | |
26 namespace { | |
27 | |
28 // An object that deletes a file when it goes out of scope. | |
29 class FileDeleter { | |
30 public: | |
31 explicit FileDeleter(FilePath path) : file_path_(path) {} | |
32 ~FileDeleter() { | |
33 if (!base::DeleteFile(file_path_, false)) | |
34 LOG(ERROR) << "Failed to delete " << file_path_.value(); | |
35 } | |
36 | |
37 private: | |
38 FilePath file_path_; | |
39 }; | |
40 | |
41 } // namespace | |
42 | |
43 PostmortemReportCollector::PostmortemReportCollector( | |
44 const FilePath& debug_dir, | |
45 const FilePath::StringType& debug_file_pattern, | |
46 std::unique_ptr<crashpad::CrashReportDatabase> report_database) | |
47 : debug_state_dir_(debug_dir), | |
48 debug_file_pattern_(debug_file_pattern), | |
49 report_database_(std::move(report_database)) { | |
50 DCHECK_NE(true, debug_dir.empty()); | |
51 DCHECK_NE(true, debug_file_pattern.empty()); | |
52 DCHECK_NE(nullptr, report_database_.get()); | |
53 } | |
54 | |
55 // TODO(manzagop): throttling and graceful handling of too much data. | |
56 void PostmortemReportCollector::CollectAndSubmitForUpload( | |
57 const std::set<FilePath>& excluded_debug_files) { | |
58 DCHECK_NE(nullptr, report_database_.get()); | |
59 | |
60 // Collect the list of files to harvest. | |
61 std::vector<FilePath> debug_files = | |
62 GetDebugStateFilePaths(excluded_debug_files); | |
63 | |
64 // Determine the crashpad client id. | |
65 crashpad::UUID client_id; | |
66 crashpad::Settings* settings = report_database_->GetSettings(); | |
67 if (settings) { | |
68 // If GetSettings() or GetClientID() fails client_id will be left at its | |
69 // default value, all zeroes, which is appropriate. | |
70 settings->GetClientID(&client_id); | |
71 } | |
72 | |
73 // Note: the code below involves two notions of report: | |
74 // - crashpad reports | |
75 // - chrome internal state reports, which are reported wrapped inside a | |
76 // crashpad report | |
77 for (const FilePath& file : debug_files) { | |
78 FileDeleter debug_file_deleter(file); | |
Sigurður Ásgeirsson
2016/08/11 17:44:15
The intent here seems to be to make at most one re
manzagop (departed)
2016/08/12 21:23:22
Good point. Done.
| |
79 | |
80 // Collect the data from the debug file to a proto. | |
81 std::unique_ptr<StabilityReport> report_proto = Collect(file); | |
82 if (!report_proto) { | |
83 // The file was empty, or there was an error collecting the data. Detailed | |
84 // logging happens within the Collect function. | |
85 continue; | |
86 } | |
87 | |
88 // Prepare a crashpad report. | |
89 CrashReportDatabase::NewReport* new_report; | |
90 CrashReportDatabase::OperationStatus database_status = | |
91 report_database_->PrepareNewCrashReport(&new_report); | |
92 if (database_status != CrashReportDatabase::kNoError) { | |
93 // Note that we delete the report even in this case, to prevent report | |
94 // accumulation. | |
95 LOG(ERROR) << "PrepareNewCrashReport failed"; | |
96 continue; | |
97 } | |
98 CrashReportDatabase::CallErrorWritingCrashReport | |
99 call_error_writing_crash_report(report_database_.get(), new_report); | |
100 | |
101 // Write the report to a minidump. | |
102 if (!WriteReportToMinidump(*report_proto, client_id, new_report->uuid, | |
103 reinterpret_cast<FILE*>(new_report->handle))) { | |
104 // Note that we delete the report even in this case, to prevent report | |
105 // accumulation. Detailed logging happens within the function. | |
106 continue; | |
107 } | |
108 | |
109 // Finalize the report wrt the report database. | |
110 call_error_writing_crash_report.Disarm(); | |
111 crashpad::UUID unused_report_id; | |
112 database_status = report_database_->FinishedWritingCrashReport( | |
113 new_report, &unused_report_id); | |
114 if (database_status != CrashReportDatabase::kNoError) { | |
115 // Note that we delete the report even in this case, to prevent report | |
116 // accumulation. | |
117 LOG(ERROR) << "FinishedWritingCrashReport failed"; | |
118 continue; | |
119 } | |
120 } | |
121 } | |
122 | |
123 PostmortemReportCollector::PostmortemReportCollector() {} | |
124 | |
125 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths( | |
126 const std::set<FilePath>& excluded_debug_files) { | |
127 std::vector<FilePath> paths; | |
128 base::FileEnumerator enumerator(debug_state_dir_, false /* recursive */, | |
129 base::FileEnumerator::FILES, | |
130 debug_file_pattern_); | |
131 FilePath path; | |
132 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { | |
133 if (excluded_debug_files.find(path) == excluded_debug_files.end()) | |
134 paths.push_back(path); | |
135 } | |
136 return paths; | |
137 } | |
138 | |
139 std::unique_ptr<StabilityReport> PostmortemReportCollector::Collect( | |
140 const base::FilePath& debug_state_file) { | |
141 // Create a global analyzer. | |
142 std::unique_ptr<GlobalActivityAnalyzer> global_analyzer = | |
143 GlobalActivityAnalyzer::CreateWithFile(debug_state_file); | |
144 if (!global_analyzer) | |
145 return nullptr; | |
146 | |
147 // Early exit if there is no data. | |
148 ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer(); | |
149 if (!thread_analyzer) { | |
150 // No data. This case happens in the case of a clean exit. | |
151 return nullptr; | |
152 } | |
153 | |
154 // Iterate through the thread analyzers, fleshing out the report. | |
155 std::unique_ptr<StabilityReport> report(new StabilityReport()); | |
156 ProcessState* process_state = report->add_process_states(); | |
157 | |
158 while (thread_analyzer) { | |
159 // Only valid analyzers are expected. | |
160 DCHECK(thread_analyzer->IsValid()); | |
161 | |
162 ThreadState* thread_state = process_state->add_threads(); | |
163 thread_state->set_thread_name(thread_analyzer->GetThreadName()); | |
164 | |
165 // TODO(manzagop): flesh this out. | |
166 | |
167 thread_analyzer = global_analyzer->GetNextAnalyzer(); | |
168 } | |
169 | |
170 return report; | |
171 } | |
172 | |
173 bool PostmortemReportCollector::WriteReportToMinidump( | |
174 const StabilityReport& report, | |
175 const crashpad::UUID& client_id, | |
176 const crashpad::UUID& report_id, | |
177 FILE* minidump_file) { | |
178 PostmortemMinidumpWriter writer(minidump_file); | |
179 return writer.WriteDump(report, client_id, report_id); | |
180 } | |
181 | |
182 } // namespace browser_watcher | |
OLD | NEW |