| OLD | NEW |
| 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/files/file_enumerator.h" | 9 #include "base/files/file_enumerator.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/path_service.h" | 13 #include "base/path_service.h" |
| 14 #include "base/strings/string_piece.h" | 14 #include "base/strings/string_piece.h" |
| 15 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 16 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 17 #include "components/browser_watcher/postmortem_minidump_writer.h" | 17 #include "components/browser_watcher/postmortem_minidump_writer.h" |
| 18 #include "components/browser_watcher/stability_data_names.h" | 18 #include "components/browser_watcher/stability_data_names.h" |
| 19 #include "third_party/crashpad/crashpad/client/settings.h" | 19 #include "third_party/crashpad/crashpad/client/settings.h" |
| 20 #include "third_party/crashpad/crashpad/util/misc/uuid.h" | 20 #include "third_party/crashpad/crashpad/util/misc/uuid.h" |
| 21 | 21 |
| 22 namespace browser_watcher { | 22 namespace browser_watcher { |
| 23 | 23 |
| 24 using base::FilePath; | 24 using base::FilePath; |
| 25 using crashpad::CrashReportDatabase; | 25 using crashpad::CrashReportDatabase; |
| 26 | 26 |
| 27 namespace { |
| 28 |
| 29 // DO NOT CHANGE VALUES. This is logged persistently in a histogram. |
| 30 enum SystemSessionAnalysisStatus { |
| 31 SYSTEM_SESSION_ANALYSIS_SUCCESS = 0, |
| 32 SYSTEM_SESSION_ANALYSIS_NO_TIMESTAMP = 1, |
| 33 SYSTEM_SESSION_ANALYSIS_NO_ANALYZER = 2, |
| 34 SYSTEM_SESSION_ANALYSIS_FAILED = 3, |
| 35 SYSTEM_SESSION_ANALYSIS_OUTSIDE_RANGE = 4, |
| 36 SYSTEM_SESSION_ANALYSIS_STATUS_MAX = 5 |
| 37 }; |
| 38 |
| 39 bool GetStartTimestamp( |
| 40 const google::protobuf::Map<std::string, TypedValue>& global_data, |
| 41 base::Time* time) { |
| 42 DCHECK(time); |
| 43 |
| 44 const auto& it = global_data.find(kStabilityStartTimestamp); |
| 45 if (it == global_data.end()) |
| 46 return false; |
| 47 |
| 48 const TypedValue& value = it->second; |
| 49 if (value.value_case() != TypedValue::kSignedValue) |
| 50 return false; |
| 51 |
| 52 *time = base::Time::FromInternalValue(value.signed_value()); |
| 53 return true; |
| 54 } |
| 55 |
| 56 } // namespace |
| 57 |
| 27 PostmortemReportCollector::PostmortemReportCollector( | 58 PostmortemReportCollector::PostmortemReportCollector( |
| 28 const std::string& product_name, | 59 const std::string& product_name, |
| 29 const std::string& version_number, | 60 const std::string& version_number, |
| 30 const std::string& channel_name) | 61 const std::string& channel_name, |
| 62 SystemSessionAnalyzer* analyzer) |
| 31 : product_name_(product_name), | 63 : product_name_(product_name), |
| 32 version_number_(version_number), | 64 version_number_(version_number), |
| 33 channel_name_(channel_name) {} | 65 channel_name_(channel_name), |
| 66 system_session_analyzer_(analyzer) {} |
| 34 | 67 |
| 35 PostmortemReportCollector::~PostmortemReportCollector() {} | 68 PostmortemReportCollector::~PostmortemReportCollector() {} |
| 36 | 69 |
| 37 int PostmortemReportCollector::CollectAndSubmitForUpload( | 70 int PostmortemReportCollector::CollectAndSubmitAllPendingReports( |
| 38 const FilePath& debug_info_dir, | 71 const base::FilePath& debug_info_dir, |
| 39 const FilePath::StringType& debug_file_pattern, | 72 const base::FilePath::StringType& debug_file_pattern, |
| 40 const std::set<FilePath>& excluded_debug_files, | 73 const std::set<base::FilePath>& excluded_debug_files, |
| 41 crashpad::CrashReportDatabase* report_database) { | 74 crashpad::CrashReportDatabase* report_database) { |
| 42 DCHECK_NE(true, debug_info_dir.empty()); | 75 DCHECK_NE(true, debug_info_dir.empty()); |
| 43 DCHECK_NE(true, debug_file_pattern.empty()); | 76 DCHECK_NE(true, debug_file_pattern.empty()); |
| 44 DCHECK_NE(nullptr, report_database); | 77 DCHECK_NE(nullptr, report_database); |
| 45 | 78 |
| 46 // Collect the list of files to harvest. | 79 // Collect the list of files to harvest. |
| 47 std::vector<FilePath> debug_files = GetDebugStateFilePaths( | 80 std::vector<FilePath> debug_files = GetDebugStateFilePaths( |
| 48 debug_info_dir, debug_file_pattern, excluded_debug_files); | 81 debug_info_dir, debug_file_pattern, excluded_debug_files); |
| 49 UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount", | 82 UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount", |
| 50 debug_files.size()); | 83 debug_files.size()); |
| 51 | 84 |
| 52 // Determine the crashpad client id. | 85 // Determine the crashpad client id. |
| 53 crashpad::UUID client_id; | 86 crashpad::UUID client_id; |
| 54 crashpad::Settings* settings = report_database->GetSettings(); | 87 crashpad::Settings* settings = report_database->GetSettings(); |
| 55 if (settings) { | 88 if (settings) { |
| 56 // If GetSettings() or GetClientID() fails client_id will be left at its | 89 // If GetSettings() or GetClientID() fails client_id will be left at its |
| 57 // default value, all zeroes, which is appropriate. | 90 // default value, all zeroes, which is appropriate. |
| 58 settings->GetClientID(&client_id); | 91 settings->GetClientID(&client_id); |
| 59 } | 92 } |
| 60 | 93 |
| 61 // Process each stability file. | 94 // Process each stability file. |
| 62 int success_cnt = 0; | 95 int success_cnt = 0; |
| 63 for (const FilePath& file : debug_files) { | 96 for (const FilePath& file : debug_files) { |
| 64 CollectionStatus status = | 97 CollectionStatus status = |
| 65 CollectAndSubmit(client_id, file, report_database); | 98 CollectAndSubmitOneReport(client_id, file, report_database); |
| 66 // TODO(manzagop): consider making this a stability metric. | 99 // TODO(manzagop): consider making this a stability metric. |
| 67 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status, | 100 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status, |
| 68 COLLECTION_STATUS_MAX); | 101 COLLECTION_STATUS_MAX); |
| 69 if (status == SUCCESS) | 102 if (status == SUCCESS) |
| 70 ++success_cnt; | 103 ++success_cnt; |
| 71 } | 104 } |
| 72 | 105 |
| 73 return success_cnt; | 106 return success_cnt; |
| 74 } | 107 } |
| 75 | 108 |
| 76 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths( | 109 std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths( |
| 77 const FilePath& debug_info_dir, | 110 const FilePath& debug_info_dir, |
| 78 const FilePath::StringType& debug_file_pattern, | 111 const FilePath::StringType& debug_file_pattern, |
| 79 const std::set<FilePath>& excluded_debug_files) { | 112 const std::set<FilePath>& excluded_debug_files) { |
| 80 DCHECK_NE(true, debug_info_dir.empty()); | 113 DCHECK_NE(true, debug_info_dir.empty()); |
| 81 DCHECK_NE(true, debug_file_pattern.empty()); | 114 DCHECK_NE(true, debug_file_pattern.empty()); |
| 82 | 115 |
| 83 std::vector<FilePath> paths; | 116 std::vector<FilePath> paths; |
| 84 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */, | 117 base::FileEnumerator enumerator(debug_info_dir, false /* recursive */, |
| 85 base::FileEnumerator::FILES, | 118 base::FileEnumerator::FILES, |
| 86 debug_file_pattern); | 119 debug_file_pattern); |
| 87 FilePath path; | 120 FilePath path; |
| 88 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { | 121 for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { |
| 89 if (excluded_debug_files.find(path) == excluded_debug_files.end()) | 122 if (excluded_debug_files.find(path) == excluded_debug_files.end()) |
| 90 paths.push_back(path); | 123 paths.push_back(path); |
| 91 } | 124 } |
| 92 return paths; | 125 return paths; |
| 93 } | 126 } |
| 94 | 127 |
| 95 CollectionStatus PostmortemReportCollector::CollectAndSubmit( | 128 CollectionStatus PostmortemReportCollector::CollectAndSubmitOneReport( |
| 96 const crashpad::UUID& client_id, | 129 const crashpad::UUID& client_id, |
| 97 const FilePath& file, | 130 const FilePath& file, |
| 98 crashpad::CrashReportDatabase* report_database) { | 131 crashpad::CrashReportDatabase* report_database) { |
| 99 DCHECK_NE(nullptr, report_database); | 132 DCHECK_NE(nullptr, report_database); |
| 100 | 133 |
| 101 // Note: the code below involves two notions of report: chrome internal state | 134 // Note: the code below involves two notions of report: chrome internal state |
| 102 // reports and the crashpad reports they get wrapped into. | 135 // reports and the crashpad reports they get wrapped into. |
| 103 | 136 |
| 104 // Collect the data from the debug file to a proto. Note: a non-empty report | 137 // Collect the data from the debug file to a proto. Note: a non-empty report |
| 105 // is interpreted here as an unclean exit. | 138 // is interpreted here as an unclean exit. |
| 106 StabilityReport report_proto; | 139 StabilityReport report_proto; |
| 107 CollectionStatus status = Collect(file, &report_proto); | 140 CollectionStatus status = CollectOneReport(file, &report_proto); |
| 108 if (status != SUCCESS) { | 141 if (status != SUCCESS) { |
| 109 // The file was empty, or there was an error collecting the data. Detailed | 142 // The file was empty, or there was an error collecting the data. Detailed |
| 110 // logging happens within the Collect function. | 143 // logging happens within the Collect function. |
| 111 if (!base::DeleteFile(file, false)) | 144 if (!base::DeleteFile(file, false)) |
| 112 DLOG(ERROR) << "Failed to delete " << file.value(); | 145 DLOG(ERROR) << "Failed to delete " << file.value(); |
| 113 return status; | 146 return status; |
| 114 } | 147 } |
| 115 | 148 |
| 116 // Prepare a crashpad report. | 149 // Prepare a crashpad report. |
| 117 CrashReportDatabase::NewReport* new_report = nullptr; | 150 CrashReportDatabase::NewReport* new_report = nullptr; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 database_status = report_database->FinishedWritingCrashReport( | 185 database_status = report_database->FinishedWritingCrashReport( |
| 153 new_report, &unused_report_id); | 186 new_report, &unused_report_id); |
| 154 if (database_status != CrashReportDatabase::kNoError) { | 187 if (database_status != CrashReportDatabase::kNoError) { |
| 155 DLOG(ERROR) << "FinishedWritingCrashReport failed"; | 188 DLOG(ERROR) << "FinishedWritingCrashReport failed"; |
| 156 return FINISHED_WRITING_CRASH_REPORT_FAILED; | 189 return FINISHED_WRITING_CRASH_REPORT_FAILED; |
| 157 } | 190 } |
| 158 | 191 |
| 159 return SUCCESS; | 192 return SUCCESS; |
| 160 } | 193 } |
| 161 | 194 |
| 162 CollectionStatus PostmortemReportCollector::Collect(const base::FilePath& file, | 195 CollectionStatus PostmortemReportCollector::CollectOneReport( |
| 163 StabilityReport* report) { | 196 const base::FilePath& file, |
| 197 StabilityReport* report) { |
| 164 DCHECK(report); | 198 DCHECK(report); |
| 199 |
| 165 CollectionStatus status = Extract(file, report); | 200 CollectionStatus status = Extract(file, report); |
| 166 if (status != SUCCESS) | 201 if (status != SUCCESS) |
| 167 return status; | 202 return status; |
| 168 | 203 |
| 169 // Add the reporter's details to the report. | 204 SetReporterDetails(report); |
| 205 RecordSystemShutdownState(report); |
| 206 |
| 207 return SUCCESS; |
| 208 } |
| 209 |
| 210 void PostmortemReportCollector::SetReporterDetails( |
| 211 StabilityReport* report) const { |
| 212 DCHECK(report); |
| 213 |
| 170 google::protobuf::Map<std::string, TypedValue>& global_data = | 214 google::protobuf::Map<std::string, TypedValue>& global_data = |
| 171 *(report->mutable_global_data()); | 215 *(report->mutable_global_data()); |
| 216 |
| 217 // Reporter version details. These are useful as the reporter may be of a |
| 218 // different version. |
| 172 global_data[kStabilityReporterChannel].set_string_value(channel_name()); | 219 global_data[kStabilityReporterChannel].set_string_value(channel_name()); |
| 173 #if defined(ARCH_CPU_X86) | 220 #if defined(ARCH_CPU_X86) |
| 174 global_data[kStabilityReporterPlatform].set_string_value( | 221 global_data[kStabilityReporterPlatform].set_string_value( |
| 175 std::string("Win32")); | 222 std::string("Win32")); |
| 176 #elif defined(ARCH_CPU_X86_64) | 223 #elif defined(ARCH_CPU_X86_64) |
| 177 global_data[kStabilityReporterPlatform].set_string_value( | 224 global_data[kStabilityReporterPlatform].set_string_value( |
| 178 std::string("Win64")); | 225 std::string("Win64")); |
| 179 #endif | 226 #endif |
| 180 global_data[kStabilityReporterProduct].set_string_value(product_name()); | 227 global_data[kStabilityReporterProduct].set_string_value(product_name()); |
| 181 global_data[kStabilityReporterVersion].set_string_value(version_number()); | 228 global_data[kStabilityReporterVersion].set_string_value(version_number()); |
| 229 } |
| 182 | 230 |
| 183 return SUCCESS; | 231 void PostmortemReportCollector::RecordSystemShutdownState( |
| 232 StabilityReport* report) const { |
| 233 DCHECK(report); |
| 234 |
| 235 // The session state for the stability report, recorded to provided visibility |
| 236 // into whether the system session was clean. |
| 237 SystemState::SessionState session_state = SystemState::UNKNOWN; |
| 238 // The status of the analysis, recorded to provide insight into the success |
| 239 // or failure of the analysis. |
| 240 SystemSessionAnalysisStatus status = SYSTEM_SESSION_ANALYSIS_SUCCESS; |
| 241 |
| 242 base::Time time; |
| 243 if (!GetStartTimestamp(report->global_data(), &time)) { |
| 244 status = SYSTEM_SESSION_ANALYSIS_NO_TIMESTAMP; |
| 245 } else if (!system_session_analyzer_) { |
| 246 status = SYSTEM_SESSION_ANALYSIS_NO_ANALYZER; |
| 247 } else { |
| 248 SystemSessionAnalyzer::Status analyzer_status = |
| 249 system_session_analyzer_->IsSessionUnclean(time); |
| 250 switch (analyzer_status) { |
| 251 case SystemSessionAnalyzer::FAILED: |
| 252 status = SYSTEM_SESSION_ANALYSIS_FAILED; |
| 253 break; |
| 254 case SystemSessionAnalyzer::CLEAN: |
| 255 session_state = SystemState::CLEAN; |
| 256 break; |
| 257 case SystemSessionAnalyzer::UNCLEAN: |
| 258 session_state = SystemState::UNCLEAN; |
| 259 break; |
| 260 case SystemSessionAnalyzer::OUTSIDE_RANGE: |
| 261 status = SYSTEM_SESSION_ANALYSIS_OUTSIDE_RANGE; |
| 262 break; |
| 263 } |
| 264 } |
| 265 |
| 266 report->mutable_system_state()->set_session_state(session_state); |
| 267 UMA_HISTOGRAM_ENUMERATION( |
| 268 "ActivityTracker.Collect.SystemSessionAnalysisStatus", status, |
| 269 SYSTEM_SESSION_ANALYSIS_STATUS_MAX); |
| 184 } | 270 } |
| 185 | 271 |
| 186 bool PostmortemReportCollector::WriteReportToMinidump( | 272 bool PostmortemReportCollector::WriteReportToMinidump( |
| 187 StabilityReport* report, | 273 StabilityReport* report, |
| 188 const crashpad::UUID& client_id, | 274 const crashpad::UUID& client_id, |
| 189 const crashpad::UUID& report_id, | 275 const crashpad::UUID& report_id, |
| 190 base::PlatformFile minidump_file) { | 276 base::PlatformFile minidump_file) { |
| 191 DCHECK(report); | 277 DCHECK(report); |
| 192 | 278 |
| 193 return WritePostmortemDump(minidump_file, client_id, report_id, report); | 279 return WritePostmortemDump(minidump_file, client_id, report_id, report); |
| 194 } | 280 } |
| 195 | 281 |
| 196 } // namespace browser_watcher | 282 } // namespace browser_watcher |
| OLD | NEW |