summaryrefslogtreecommitdiff
path: root/chromium/components/browser_watcher/postmortem_report_collector.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/browser_watcher/postmortem_report_collector.cc')
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector.cc321
1 files changed, 92 insertions, 229 deletions
diff --git a/chromium/components/browser_watcher/postmortem_report_collector.cc b/chromium/components/browser_watcher/postmortem_report_collector.cc
index 837f0232974..35cc7b455b3 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector.cc
@@ -6,7 +6,6 @@
#include <utility>
-#include "base/debug/activity_analyzer.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
@@ -17,135 +16,41 @@
#include "base/strings/utf_string_conversions.h"
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include "components/browser_watcher/stability_data_names.h"
-#include "components/variations/active_field_trials.h"
#include "third_party/crashpad/crashpad/client/settings.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
-using base::FilePath;
-
namespace browser_watcher {
-using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot;
-using base::debug::ActivityUserData;
-using base::debug::GlobalActivityAnalyzer;
-using base::debug::GlobalActivityTracker;
-using base::debug::ThreadActivityAnalyzer;
+using base::FilePath;
using crashpad::CrashReportDatabase;
namespace {
-const char kFieldTrialKeyPrefix[] = "FieldTrial.";
-
-// Collects stability user data from the recorded format to the collected
-// format.
-void CollectUserData(
- const ActivityUserData::Snapshot& recorded_map,
- google::protobuf::Map<std::string, TypedValue>* collected_map,
- StabilityReport* report) {
- DCHECK(collected_map);
-
- for (const auto& name_and_value : recorded_map) {
- const std::string& key = name_and_value.first;
- const ActivityUserData::TypedValue& recorded_value = name_and_value.second;
- TypedValue collected_value;
-
- switch (recorded_value.type()) {
- case ActivityUserData::END_OF_VALUES:
- NOTREACHED();
- break;
- case ActivityUserData::RAW_VALUE: {
- base::StringPiece raw = recorded_value.Get();
- collected_value.set_bytes_value(raw.data(), raw.size());
- break;
- }
- case ActivityUserData::RAW_VALUE_REFERENCE: {
- base::StringPiece recorded_ref = recorded_value.GetReference();
- TypedValue::Reference* collected_ref =
- collected_value.mutable_bytes_reference();
- collected_ref->set_address(
- reinterpret_cast<uintptr_t>(recorded_ref.data()));
- collected_ref->set_size(recorded_ref.size());
- break;
- }
- case ActivityUserData::STRING_VALUE: {
- base::StringPiece value = recorded_value.GetString();
-
- if (report && base::StartsWith(key, kFieldTrialKeyPrefix,
- base::CompareCase::SENSITIVE)) {
- // This entry represents an active Field Trial.
- std::string trial_name =
- key.substr(std::strlen(kFieldTrialKeyPrefix));
- variations::ActiveGroupId group_id =
- variations::MakeActiveGroupId(trial_name, value.as_string());
- FieldTrial* field_trial = report->add_field_trials();
- field_trial->set_name_id(group_id.name);
- field_trial->set_group_id(group_id.group);
- continue;
- }
-
- collected_value.set_string_value(value.data(), value.size());
- break;
- }
- case ActivityUserData::STRING_VALUE_REFERENCE: {
- base::StringPiece recorded_ref = recorded_value.GetStringReference();
- TypedValue::Reference* collected_ref =
- collected_value.mutable_string_reference();
- collected_ref->set_address(
- reinterpret_cast<uintptr_t>(recorded_ref.data()));
- collected_ref->set_size(recorded_ref.size());
- break;
- }
- case ActivityUserData::CHAR_VALUE: {
- char char_value = recorded_value.GetChar();
- collected_value.set_char_value(&char_value, 1);
- break;
- }
- case ActivityUserData::BOOL_VALUE:
- collected_value.set_bool_value(recorded_value.GetBool());
- break;
- case ActivityUserData::SIGNED_VALUE:
- collected_value.set_signed_value(recorded_value.GetInt());
- break;
- case ActivityUserData::UNSIGNED_VALUE:
- collected_value.set_unsigned_value(recorded_value.GetUint());
- break;
- }
-
- (*collected_map)[key].Swap(&collected_value);
- }
-}
-
-void CollectModuleInformation(
- const std::vector<GlobalActivityTracker::ModuleInfo>& modules,
- ProcessState* process_state) {
- DCHECK(process_state);
-
- char code_identifier[17];
- char debug_identifier[41];
-
- for (const GlobalActivityTracker::ModuleInfo& recorded : modules) {
- CodeModule* collected = process_state->add_modules();
- collected->set_base_address(recorded.address);
- collected->set_size(recorded.size);
- collected->set_code_file(recorded.file);
-
- // Compute the code identifier using the required format.
- snprintf(code_identifier, sizeof(code_identifier), "%08X%zx",
- recorded.timestamp, recorded.size);
- collected->set_code_identifier(code_identifier);
- collected->set_debug_file(recorded.debug_file);
-
- // Compute the debug identifier using the required format.
- const crashpad::UUID* uuid =
- reinterpret_cast<const crashpad::UUID*>(recorded.identifier);
- snprintf(debug_identifier, sizeof(debug_identifier),
- "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", uuid->data_1,
- uuid->data_2, uuid->data_3, uuid->data_4[0], uuid->data_4[1],
- uuid->data_5[0], uuid->data_5[1], uuid->data_5[2], uuid->data_5[3],
- uuid->data_5[4], uuid->data_5[5], recorded.age);
- collected->set_debug_identifier(debug_identifier);
- collected->set_is_unloaded(!recorded.is_loaded);
- }
+// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
+enum SystemSessionAnalysisStatus {
+ SYSTEM_SESSION_ANALYSIS_SUCCESS = 0,
+ SYSTEM_SESSION_ANALYSIS_NO_TIMESTAMP = 1,
+ SYSTEM_SESSION_ANALYSIS_NO_ANALYZER = 2,
+ SYSTEM_SESSION_ANALYSIS_FAILED = 3,
+ SYSTEM_SESSION_ANALYSIS_OUTSIDE_RANGE = 4,
+ SYSTEM_SESSION_ANALYSIS_STATUS_MAX = 5
+};
+
+bool GetStartTimestamp(
+ const google::protobuf::Map<std::string, TypedValue>& global_data,
+ base::Time* time) {
+ DCHECK(time);
+
+ const auto& it = global_data.find(kStabilityStartTimestamp);
+ if (it == global_data.end())
+ return false;
+
+ const TypedValue& value = it->second;
+ if (value.value_case() != TypedValue::kSignedValue)
+ return false;
+
+ *time = base::Time::FromInternalValue(value.signed_value());
+ return true;
}
} // namespace
@@ -153,12 +58,16 @@ void CollectModuleInformation(
PostmortemReportCollector::PostmortemReportCollector(
const std::string& product_name,
const std::string& version_number,
- const std::string& channel_name)
+ const std::string& channel_name,
+ SystemSessionAnalyzer* analyzer)
: product_name_(product_name),
version_number_(version_number),
- channel_name_(channel_name) {}
+ channel_name_(channel_name),
+ system_session_analyzer_(analyzer) {}
-int PostmortemReportCollector::CollectAndSubmitForUpload(
+PostmortemReportCollector::~PostmortemReportCollector() {}
+
+int PostmortemReportCollector::CollectAndSubmitAllPendingReports(
const base::FilePath& debug_info_dir,
const base::FilePath::StringType& debug_file_pattern,
const std::set<base::FilePath>& excluded_debug_files,
@@ -186,7 +95,7 @@ int PostmortemReportCollector::CollectAndSubmitForUpload(
int success_cnt = 0;
for (const FilePath& file : debug_files) {
CollectionStatus status =
- CollectAndSubmit(client_id, file, report_database);
+ CollectAndSubmitOneReport(client_id, file, report_database);
// TODO(manzagop): consider making this a stability metric.
UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.Status", status,
COLLECTION_STATUS_MAX);
@@ -198,8 +107,8 @@ int PostmortemReportCollector::CollectAndSubmitForUpload(
}
std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
- const base::FilePath& debug_info_dir,
- const base::FilePath::StringType& debug_file_pattern,
+ const FilePath& debug_info_dir,
+ const FilePath::StringType& debug_file_pattern,
const std::set<FilePath>& excluded_debug_files) {
DCHECK_NE(true, debug_info_dir.empty());
DCHECK_NE(true, debug_file_pattern.empty());
@@ -216,8 +125,7 @@ std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
return paths;
}
-PostmortemReportCollector::CollectionStatus
-PostmortemReportCollector::CollectAndSubmit(
+CollectionStatus PostmortemReportCollector::CollectAndSubmitOneReport(
const crashpad::UUID& client_id,
const FilePath& file,
crashpad::CrashReportDatabase* report_database) {
@@ -228,8 +136,8 @@ PostmortemReportCollector::CollectAndSubmit(
// Collect the data from the debug file to a proto. Note: a non-empty report
// is interpreted here as an unclean exit.
- std::unique_ptr<StabilityReport> report_proto;
- CollectionStatus status = Collect(file, &report_proto);
+ StabilityReport report_proto;
+ CollectionStatus status = CollectOneReport(file, &report_proto);
if (status != SUCCESS) {
// The file was empty, or there was an error collecting the data. Detailed
// logging happens within the Collect function.
@@ -237,7 +145,6 @@ PostmortemReportCollector::CollectAndSubmit(
DLOG(ERROR) << "Failed to delete " << file.value();
return status;
}
- DCHECK_NE(nullptr, report_proto.get());
// Prepare a crashpad report.
CrashReportDatabase::NewReport* new_report = nullptr;
@@ -252,7 +159,7 @@ PostmortemReportCollector::CollectAndSubmit(
call_error_writing_crash_report(report_database, new_report);
// Write the report to a minidump.
- if (!WriteReportToMinidump(report_proto.get(), client_id, new_report->uuid,
+ if (!WriteReportToMinidump(&report_proto, client_id, new_report->uuid,
reinterpret_cast<FILE*>(new_report->handle))) {
// Assume this is not recoverable and delete the file.
if (!base::DeleteFile(file, false))
@@ -285,42 +192,30 @@ PostmortemReportCollector::CollectAndSubmit(
return SUCCESS;
}
-PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect(
- const base::FilePath& debug_state_file,
- std::unique_ptr<StabilityReport>* report) {
- DCHECK_NE(nullptr, report);
- report->reset();
-
- // Create a global analyzer.
- std::unique_ptr<GlobalActivityAnalyzer> global_analyzer =
- GlobalActivityAnalyzer::CreateWithFile(debug_state_file);
- if (!global_analyzer)
- return ANALYZER_CREATION_FAILED;
-
- // Early exit if there is no data.
- std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
- ActivityUserData::Snapshot global_data_snapshot =
- global_analyzer->GetGlobalUserDataSnapshot();
- ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
- if (log_messages.empty() && global_data_snapshot.empty() &&
- !thread_analyzer) {
- return DEBUG_FILE_NO_DATA;
- }
+CollectionStatus PostmortemReportCollector::CollectOneReport(
+ const base::FilePath& file,
+ StabilityReport* report) {
+ DCHECK(report);
- // Create the report, then flesh it out.
- report->reset(new StabilityReport());
+ CollectionStatus status = Extract(file, report);
+ if (status != SUCCESS)
+ return status;
- // Collect log messages.
- for (const std::string& message : log_messages) {
- (*report)->add_log_messages(message);
- }
+ SetReporterDetails(report);
+ RecordSystemShutdownState(report);
+
+ return SUCCESS;
+}
+
+void PostmortemReportCollector::SetReporterDetails(
+ StabilityReport* report) const {
+ DCHECK(report);
- // Collect global user data.
google::protobuf::Map<std::string, TypedValue>& global_data =
- *(*report)->mutable_global_data();
- CollectUserData(global_data_snapshot, &global_data, report->get());
+ *(report->mutable_global_data());
- // Add the reporting Chrome's details to the report.
+ // Reporter version details. These are useful as the reporter may be of a
+ // different version.
global_data[kStabilityReporterChannel].set_string_value(channel_name());
#if defined(ARCH_CPU_X86)
global_data[kStabilityReporterPlatform].set_string_value(
@@ -331,79 +226,47 @@ PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect(
#endif
global_data[kStabilityReporterProduct].set_string_value(product_name());
global_data[kStabilityReporterVersion].set_string_value(version_number());
-
- // Collect thread activity data.
- // Note: a single process is instrumented.
- ProcessState* process_state = (*report)->add_process_states();
- for (; thread_analyzer != nullptr;
- thread_analyzer = global_analyzer->GetNextAnalyzer()) {
- // Only valid analyzers are expected per contract of GetFirstAnalyzer /
- // GetNextAnalyzer.
- DCHECK(thread_analyzer->IsValid());
-
- if (!process_state->has_process_id()) {
- process_state->set_process_id(
- thread_analyzer->activity_snapshot().process_id);
- }
- DCHECK_EQ(thread_analyzer->activity_snapshot().process_id,
- process_state->process_id());
-
- ThreadState* thread_state = process_state->add_threads();
- CollectThread(thread_analyzer->activity_snapshot(), thread_state);
- }
-
- // Collect module information.
- CollectModuleInformation(global_analyzer->GetModules(), process_state);
-
- return SUCCESS;
}
-void PostmortemReportCollector::CollectThread(
- const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
- ThreadState* thread_state) {
- DCHECK(thread_state);
-
- thread_state->set_thread_name(snapshot.thread_name);
- thread_state->set_thread_id(snapshot.thread_id);
- thread_state->set_activity_count(snapshot.activity_stack_depth);
-
- for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
- const base::debug::Activity& recorded = snapshot.activity_stack[i];
- Activity* collected = thread_state->add_activities();
-
- // Collect activity
- switch (recorded.activity_type) {
- case base::debug::Activity::ACT_TASK_RUN:
- collected->set_type(Activity::ACT_TASK_RUN);
- collected->set_origin_address(recorded.origin_address);
- collected->set_task_sequence_id(recorded.data.task.sequence_id);
- break;
- case base::debug::Activity::ACT_LOCK_ACQUIRE:
- collected->set_type(Activity::ACT_LOCK_ACQUIRE);
- collected->set_lock_address(recorded.data.lock.lock_address);
- break;
- case base::debug::Activity::ACT_EVENT_WAIT:
- collected->set_type(Activity::ACT_EVENT_WAIT);
- collected->set_event_address(recorded.data.event.event_address);
+void PostmortemReportCollector::RecordSystemShutdownState(
+ StabilityReport* report) const {
+ DCHECK(report);
+
+ // The session state for the stability report, recorded to provided visibility
+ // into whether the system session was clean.
+ SystemState::SessionState session_state = SystemState::UNKNOWN;
+ // The status of the analysis, recorded to provide insight into the success
+ // or failure of the analysis.
+ SystemSessionAnalysisStatus status = SYSTEM_SESSION_ANALYSIS_SUCCESS;
+
+ base::Time time;
+ if (!GetStartTimestamp(report->global_data(), &time)) {
+ status = SYSTEM_SESSION_ANALYSIS_NO_TIMESTAMP;
+ } else if (!system_session_analyzer_) {
+ status = SYSTEM_SESSION_ANALYSIS_NO_ANALYZER;
+ } else {
+ SystemSessionAnalyzer::Status analyzer_status =
+ system_session_analyzer_->IsSessionUnclean(time);
+ switch (analyzer_status) {
+ case SystemSessionAnalyzer::FAILED:
+ status = SYSTEM_SESSION_ANALYSIS_FAILED;
break;
- case base::debug::Activity::ACT_THREAD_JOIN:
- collected->set_type(Activity::ACT_THREAD_JOIN);
- collected->set_thread_id(recorded.data.thread.thread_id);
+ case SystemSessionAnalyzer::CLEAN:
+ session_state = SystemState::CLEAN;
break;
- case base::debug::Activity::ACT_PROCESS_WAIT:
- collected->set_type(Activity::ACT_PROCESS_WAIT);
- collected->set_process_id(recorded.data.process.process_id);
+ case SystemSessionAnalyzer::UNCLEAN:
+ session_state = SystemState::UNCLEAN;
break;
- default:
+ case SystemSessionAnalyzer::OUTSIDE_RANGE:
+ status = SYSTEM_SESSION_ANALYSIS_OUTSIDE_RANGE;
break;
}
-
- // Collect user data
- if (i < snapshot.user_data_stack.size()) {
- CollectUserData(snapshot.user_data_stack[i],
- collected->mutable_user_data(), nullptr);
- }
}
+
+ report->mutable_system_state()->set_session_state(session_state);
+ UMA_HISTOGRAM_ENUMERATION(
+ "ActivityTracker.Collect.SystemSessionAnalysisStatus", status,
+ SYSTEM_SESSION_ANALYSIS_STATUS_MAX);
}
bool PostmortemReportCollector::WriteReportToMinidump(