Commit 72e80adc authored by Sigurdur Asgeirsson's avatar Sigurdur Asgeirsson Committed by Commit Bot

Remove unused persistent browser watcher stability debugging code.

It's been a couple of years since this code was used, and the oncoming
refactoring will be easier without this in the way.

Bug: 1044707
Change-Id: Ic6e07b8f3ce61312d2c5a89d8af9850ea7510a2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2032230Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarJesse Doherty <jwd@chromium.org>
Commit-Queue: Jesse Doherty <jwd@chromium.org>
Auto-Submit: Sigurður Ásgeirsson <siggi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737444}
parent 55dc3f03
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
#include "components/browser_watcher/stability_data_names.h" #include "components/browser_watcher/stability_data_names.h"
#include "components/browser_watcher/stability_debugging.h" #include "components/browser_watcher/stability_debugging.h"
#include "components/browser_watcher/stability_metrics.h" #include "components/browser_watcher/stability_metrics.h"
#include "components/browser_watcher/stability_paths.h"
#include "components/browser_watcher/stability_report.pb.h" #include "components/browser_watcher/stability_report.pb.h"
#endif #endif
...@@ -107,7 +106,7 @@ void RecordChromeModuleInfo( ...@@ -107,7 +106,7 @@ void RecordChromeModuleInfo(
global_tracker->RecordModuleInfo(module); global_tracker->RecordModuleInfo(module);
} }
void SetupStabilityDebugging() { void SetupExtendedCrashReporting() {
if (!base::FeatureList::IsEnabled( if (!base::FeatureList::IsEnabled(
browser_watcher::kExtendedCrashReportingFeature)) { browser_watcher::kExtendedCrashReportingFeature)) {
return; return;
...@@ -119,43 +118,14 @@ void SetupStabilityDebugging() { ...@@ -119,43 +118,14 @@ void SetupStabilityDebugging() {
const int kStackDepth = 4; const int kStackDepth = 4;
const uint64_t kAllocatorId = 0; const uint64_t kAllocatorId = 0;
// Check whether activities are to be recorded persistently or only in-memory. base::debug::GlobalActivityTracker::CreateWithAllocator(
const bool in_memory_only = base::GetFieldTrialParamByFeatureAsBool( std::make_unique<base::LocalPersistentMemoryAllocator>(
browser_watcher::kExtendedCrashReportingFeature, kMemorySize, kAllocatorId,
browser_watcher::kInMemoryOnlyParam, true); browser_watcher::kExtendedCrashReportingFeature.name),
if (in_memory_only) { kStackDepth, 0);
base::debug::GlobalActivityTracker::CreateWithAllocator(
std::make_unique<base::LocalPersistentMemoryAllocator>(
kMemorySize, kAllocatorId,
browser_watcher::kExtendedCrashReportingFeature.name),
kStackDepth, 0);
} else {
// Ensure the stability directory exists and determine the stability file's
// path.
base::FilePath user_data_dir;
if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir) ||
!base::CreateDirectory(
browser_watcher::GetStabilityDir(user_data_dir))) {
return;
}
browser_watcher::LogStabilityRecordEvent(
browser_watcher::StabilityRecordEvent::kStabilityDirectoryExists);
base::FilePath stability_file;
if (!browser_watcher::GetStabilityFileForProcess(
base::Process::Current(), user_data_dir, &stability_file)) {
return;
}
browser_watcher::LogStabilityRecordEvent(
browser_watcher::StabilityRecordEvent::kGotStabilityPath);
base::debug::GlobalActivityTracker::CreateWithFile(
stability_file, kMemorySize, kAllocatorId, "", kStackDepth);
}
// Track code activities (such as posting task, blocking on locks, and // Track code activities (such as posting task, blocking on locks, and
// joining threads) that can cause hanging threads and general instability // joining threads) that can cause hanging threads and general instability
base::debug::GlobalActivityTracker* global_tracker = base::debug::GlobalActivityTracker* global_tracker =
base::debug::GlobalActivityTracker::Get(); base::debug::GlobalActivityTracker::Get();
if (!global_tracker) if (!global_tracker)
...@@ -168,6 +138,9 @@ void SetupStabilityDebugging() { ...@@ -168,6 +138,9 @@ void SetupStabilityDebugging() {
static browser_watcher::ActivityTrackerAnnotation activity_tracker_annotation( static browser_watcher::ActivityTrackerAnnotation activity_tracker_annotation(
allocator->data(), allocator->size()); allocator->data(), allocator->size());
// Record the main DLL module info for easier symbolization.
RecordChromeModuleInfo(global_tracker);
browser_watcher::LogStabilityRecordEvent( browser_watcher::LogStabilityRecordEvent(
browser_watcher::StabilityRecordEvent::kGotTracker); browser_watcher::StabilityRecordEvent::kGotTracker);
// Record product, version, channel, special build and platform. // Record product, version, channel, special build and platform.
...@@ -197,32 +170,6 @@ void SetupStabilityDebugging() { ...@@ -197,32 +170,6 @@ void SetupStabilityDebugging() {
proc_data.SetInt(browser_watcher::kStabilityProcessType, proc_data.SetInt(browser_watcher::kStabilityProcessType,
browser_watcher::ProcessState::BROWSER_PROCESS); browser_watcher::ProcessState::BROWSER_PROCESS);
if (!in_memory_only) {
// Record information about chrome's module. We want this to be done early.
RecordChromeModuleInfo(global_tracker);
// Trigger a flush of the memory mapped file to maximize the chances of
// having a minimal amount of content in the stability file, even if
// the system crashes or loses power. Even running in the background,
// this is a potentially expensive operation, so done under an experiment
// to allow measuring the performance effects, if any.
const bool should_flush = base::GetFieldTrialParamByFeatureAsBool(
browser_watcher::kExtendedCrashReportingFeature,
browser_watcher::kInitFlushParam, false);
if (should_flush) {
base::PostTask(
FROM_HERE, {base::ThreadPool(), base::MayBlock()},
base::BindOnce(&base::PersistentMemoryAllocator::Flush,
base::Unretained(global_tracker->allocator()), true));
}
// Store a copy of the system profile in this allocator. There will be some
// delay before this gets populated, perhaps as much as a minute. Because
// of this, there is no need to flush it here.
metrics::GlobalPersistentSystemProfile::GetInstance()
->RegisterPersistentAllocator(global_tracker->allocator());
}
browser_watcher::RegisterStabilityVEH(); browser_watcher::RegisterStabilityVEH();
} }
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
...@@ -233,7 +180,7 @@ void SetupDesktopFieldTrials() { ...@@ -233,7 +180,7 @@ void SetupDesktopFieldTrials() {
prerender::ConfigureNoStatePrefetch(); prerender::ConfigureNoStatePrefetch();
SetupStunProbeTrial(); SetupStunProbeTrial();
#if defined(OS_WIN) #if defined(OS_WIN)
SetupStabilityDebugging(); SetupExtendedCrashReporting();
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
} }
......
...@@ -60,7 +60,6 @@ ...@@ -60,7 +60,6 @@
#include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "components/browser_watcher/stability_paths.h"
#include "components/crash/core/common/crash_keys.h" #include "components/crash/core/common/crash_keys.h"
#include "components/history/core/browser/history_service.h" #include "components/history/core/browser/history_service.h"
#include "components/metrics/call_stack_profile_metrics_provider.h" #include "components/metrics/call_stack_profile_metrics_provider.h"
...@@ -539,14 +538,6 @@ void ChromeMetricsServiceClient::OnEnvironmentUpdate(std::string* environment) { ...@@ -539,14 +538,6 @@ void ChromeMetricsServiceClient::OnEnvironmentUpdate(std::string* environment) {
#endif // OS_WIN || OS_MACOSX || OS_ANDROID #endif // OS_WIN || OS_MACOSX || OS_ANDROID
} }
void ChromeMetricsServiceClient::OnLogCleanShutdown() {
#if defined(OS_WIN)
base::FilePath user_data_dir;
if (base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
browser_watcher::MarkOwnStabilityFileDeleted(user_data_dir);
#endif // OS_WIN
}
void ChromeMetricsServiceClient::CollectFinalMetricsForLog( void ChromeMetricsServiceClient::CollectFinalMetricsForLog(
base::OnceClosure done_callback) { base::OnceClosure done_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
...@@ -67,7 +67,6 @@ class ChromeMetricsServiceClient : public metrics::MetricsServiceClient, ...@@ -67,7 +67,6 @@ class ChromeMetricsServiceClient : public metrics::MetricsServiceClient,
metrics::SystemProfileProto::Channel GetChannel() override; metrics::SystemProfileProto::Channel GetChannel() override;
std::string GetVersionString() override; std::string GetVersionString() override;
void OnEnvironmentUpdate(std::string* serialized_environment) override; void OnEnvironmentUpdate(std::string* serialized_environment) override;
void OnLogCleanShutdown() override;
void CollectFinalMetricsForLog(base::OnceClosure done_callback) override; void CollectFinalMetricsForLog(base::OnceClosure done_callback) override;
std::unique_ptr<metrics::MetricsLogUploader> CreateUploader( std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
const GURL& server_url, const GURL& server_url,
......
...@@ -30,34 +30,14 @@ if (is_win) { ...@@ -30,34 +30,14 @@ if (is_win) {
"watcher_metrics_provider_win.cc", "watcher_metrics_provider_win.cc",
"watcher_metrics_provider_win.h", "watcher_metrics_provider_win.h",
] ]
deps = [
":postmortem_stability",
":stability_client",
"//base",
"//components/metrics",
"//third_party/crashpad/crashpad/client",
]
}
static_library("postmortem_stability") {
sources = [
"postmortem_minidump_writer.h",
"postmortem_minidump_writer_win.cc",
"postmortem_report_collector.cc",
"postmortem_report_collector.h",
]
deps = [ deps = [
":stability_client", ":stability_client",
":stability_common", ":stability_common",
":stability_report_proto", ":stability_report_proto",
"//base", "//base",
"//components/metrics:metrics", "//components/metrics",
"//components/variations",
"//third_party/crashpad/crashpad/client", "//third_party/crashpad/crashpad/client",
"//third_party/crashpad/crashpad/minidump",
"//third_party/crashpad/crashpad/util",
] ]
ldflags = [ "/DELAYLOAD:wevtapi.dll" ] # Only used after unclean shutdowns.
} }
static_library("crash_stability") { static_library("crash_stability") {
...@@ -106,8 +86,6 @@ if (is_win) { ...@@ -106,8 +86,6 @@ if (is_win) {
"stability_debugging.h", "stability_debugging.h",
"stability_metrics.cc", "stability_metrics.cc",
"stability_metrics.h", "stability_metrics.h",
"stability_paths.cc",
"stability_paths.h",
] ]
deps = [ deps = [
"//base", "//base",
...@@ -121,10 +99,7 @@ if (is_win) { ...@@ -121,10 +99,7 @@ if (is_win) {
"activity_tracker_annotation_unittest.cc", "activity_tracker_annotation_unittest.cc",
"endsession_watcher_window_win_unittest.cc", "endsession_watcher_window_win_unittest.cc",
"exit_code_watcher_win_unittest.cc", "exit_code_watcher_win_unittest.cc",
"postmortem_minidump_writer_win_unittest.cc",
"postmortem_report_collector_unittest.cc",
"stability_debugging_win_unittest.cc", "stability_debugging_win_unittest.cc",
"stability_paths_unittest.cc",
"stability_report_extractor_unittest.cc", "stability_report_extractor_unittest.cc",
"watcher_client_win_unittest.cc", "watcher_client_win_unittest.cc",
"watcher_metrics_provider_win_unittest.cc", "watcher_metrics_provider_win_unittest.cc",
...@@ -134,7 +109,6 @@ if (is_win) { ...@@ -134,7 +109,6 @@ if (is_win) {
deps = [ deps = [
":browser_watcher", ":browser_watcher",
":browser_watcher_client", ":browser_watcher_client",
":postmortem_stability",
":stability_client", ":stability_client",
":stability_common", ":stability_common",
":stability_report_proto", ":stability_report_proto",
...@@ -164,7 +138,9 @@ if (is_win) { ...@@ -164,7 +138,9 @@ if (is_win) {
executable("fetch_system_session_events") { executable("fetch_system_session_events") {
sources = [ "fetch_system_session_events_main_win.cc" ] sources = [ "fetch_system_session_events_main_win.cc" ]
deps = [ deps = [
":postmortem_stability", ":stability_client",
":stability_common",
":stability_report_proto",
"//base", "//base",
"//components/metrics:metrics", "//components/metrics:metrics",
] ]
......
...@@ -11,8 +11,4 @@ const base::Feature kExtendedCrashReportingFeature{ ...@@ -11,8 +11,4 @@ const base::Feature kExtendedCrashReportingFeature{
const char kInMemoryOnlyParam[] = "in_memory_only"; const char kInMemoryOnlyParam[] = "in_memory_only";
const char kInitFlushParam[] = "init_flush";
const char kCollectPostmortemParam[] = "collect_postmortem";
} // namespace browser_watcher } // namespace browser_watcher
...@@ -17,14 +17,6 @@ extern const base::Feature kExtendedCrashReportingFeature; ...@@ -17,14 +17,6 @@ extern const base::Feature kExtendedCrashReportingFeature;
// activity in-memory only. // activity in-memory only.
extern const char kInMemoryOnlyParam[]; extern const char kInMemoryOnlyParam[];
// Name of an experiment parameter that controls whether to perform an initial
// flush.
extern const char kInitFlushParam[];
// Name of an experiment parameter that controls whether to collect postmortem
// reports.
extern const char kCollectPostmortemParam[];
} // namespace browser_watcher } // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_FEATURES_H_ #endif // COMPONENTS_BROWSER_WATCHER_FEATURES_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_POSTMORTEM_MINIDUMP_WRITER_H_
#define COMPONENTS_BROWSER_WATCHER_POSTMORTEM_MINIDUMP_WRITER_H_
#include <stdint.h>
#include <string>
#include "components/browser_watcher/stability_report.pb.h"
#include "third_party/crashpad/crashpad/util/file/file_writer.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
namespace browser_watcher {
// Write to |minidump_file| a minimal minidump that wraps |report|. Returns
// true on success, false otherwise.
// Note: the caller owns |minidump_file| and is responsible for keeping it valid
// for this function's duration. |minidump_file| is expected to be empty
// and a binary stream.
bool WritePostmortemDump(crashpad::FileWriterInterface* minidump_file,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
StabilityReport* report);
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_POSTMORTEM_MINIDUMP_WRITER_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Note: aside from using windows headers to obtain the definitions of minidump
// structures, nothing here is windows specific. This seems like the best
// approach given this code is for temporary experimentation on Windows.
// Longer term, Crashpad will take over the minidump writing in this case as
// well.
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include <windows.h> // NOLINT
#include <dbghelp.h>
#include <map>
#include <type_traits>
#include <vector>
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_math.h"
#include "base/strings/string_piece.h"
#include "components/browser_watcher/minidump_user_streams.h"
#include "components/browser_watcher/stability_data_names.h"
#include "third_party/crashpad/crashpad/minidump/minidump_extensions.h"
namespace browser_watcher {
namespace {
struct ProductDetails {
std::string product;
std::string channel;
std::string platform;
std::string version;
};
bool GetStringFromTypedValueMap(
const google::protobuf::Map<std::string, TypedValue>& map,
const std::string& key,
std::string* out) {
DCHECK(out);
auto it = map.find(key);
if (it == map.end())
return false;
const TypedValue& value = it->second;
if (value.value_case() != TypedValue::kStringValue)
return false;
*out = value.string_value();
return true;
}
bool GetProductDetails(
const google::protobuf::Map<std::string, TypedValue>& global_data,
ProductDetails* product_details) {
DCHECK(product_details);
if (!GetStringFromTypedValueMap(global_data, kStabilityProduct,
&(product_details->product)))
return false;
if (!GetStringFromTypedValueMap(global_data, kStabilityChannel,
&(product_details->channel)))
return false;
if (!GetStringFromTypedValueMap(global_data, kStabilityPlatform,
&(product_details->platform)))
return false;
return GetStringFromTypedValueMap(global_data, kStabilityVersion,
&(product_details->version));
}
// A class with functionality for writing minimal minidump containers to wrap
// postmortem stability reports.
// TODO(manzagop): remove this class once Crashpad takes over writing postmortem
// minidumps.
// TODO(manzagop): revisit where the module information should be transported,
// in the protocol buffer or in a module stream.
class PostmortemMinidumpWriter {
public:
// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
enum WriteStatus {
SUCCESS = 0,
FAILED = 1,
FAILED_MISSING_PRODUCT_DETAILS = 2,
WRITE_STATUS_MAX = 3
};
// |minidump_file| is expected to be empty and a binary stream.
PostmortemMinidumpWriter(crashpad::FileWriterInterface* minidump_file);
~PostmortemMinidumpWriter();
// Write to |minidump_file_| a minimal minidump that wraps |report|. Returns
// true on success, false otherwise.
bool WriteDump(const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
StabilityReport* report);
private:
// An offset within a minidump file. Note: using this type to avoid including
// windows.h and relying on the RVA type.
using FilePosition = uint32_t;
// The minidump header is always located at the head.
static const FilePosition kHeaderPos = 0U;
bool WriteDumpImpl(const StabilityReport& report,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
const ProductDetails& product_details);
bool AppendCrashpadInfo(const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
const std::map<std::string, std::string>& crash_keys);
bool AppendCrashpadDictionaryEntry(
const std::string& key,
const std::string& value,
std::vector<crashpad::MinidumpSimpleStringDictionaryEntry>* entries);
// Allocate |size_bytes| within the minidump. On success, |pos| contains the
// location of the allocation. Returns true on success, false otherwise.
bool Allocate(size_t size_bytes, FilePosition* pos);
// Seeks |cursor_|. The seek operation is kept separate from the write in
// order to make the call explicit. Seek operations can be costly and should
// be avoided.
bool SeekCursor(FilePosition destination);
// Write to pre-allocated space.
// Note: |pos| must match |cursor_|.
template <class DataType>
bool Write(FilePosition pos, const DataType& data);
bool WriteBytes(FilePosition pos, size_t size_bytes, const char* data);
// Allocate space for and write the contents of |data|. On success, |pos|
// contains the location of the write. Returns true on success, false
// otherwise.
template <class DataType>
bool Append(const DataType& data, FilePosition* pos);
template <class DataType>
bool AppendVec(const std::vector<DataType>& data, FilePosition* pos);
bool AppendUtf8String(base::StringPiece data, FilePosition* pos);
bool AppendBytes(base::StringPiece data, FilePosition* pos);
void RegisterDirectoryEntry(uint32_t stream_type,
FilePosition pos,
uint32_t size);
// The next allocatable FilePosition.
FilePosition next_available_byte_;
// Storage for the directory during writes.
std::vector<MINIDUMP_DIRECTORY> directory_;
// The file to write to.
crashpad::FileWriterInterface* minidump_file_;
DISALLOW_COPY_AND_ASSIGN(PostmortemMinidumpWriter);
};
void RecordWriteDumpStatus(PostmortemMinidumpWriter::WriteStatus status) {
base::UmaHistogramEnumeration("ActivityTracker.Collect.WriteDumpStatus",
status,
PostmortemMinidumpWriter::WRITE_STATUS_MAX);
}
PostmortemMinidumpWriter::PostmortemMinidumpWriter(
crashpad::FileWriterInterface* minidump_file)
: next_available_byte_(0U), minidump_file_(minidump_file) {
DCHECK_NE(nullptr, minidump_file_);
DCHECK_EQ(0LL, minidump_file_->SeekGet());
DCHECK_EQ(0LL, minidump_file_->Seek(0LL, SEEK_END));
}
PostmortemMinidumpWriter::~PostmortemMinidumpWriter() {}
bool PostmortemMinidumpWriter::WriteDump(
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
StabilityReport* report) {
DCHECK(report);
DCHECK_EQ(0U, next_available_byte_);
DCHECK(directory_.empty());
// Ensure the report contains the crasher's product details.
ProductDetails product_details = {};
if (!GetProductDetails(report->global_data(), &product_details)) {
// The report is missing the basic information to determine the affected
// version. Ignore the report.
RecordWriteDumpStatus(FAILED_MISSING_PRODUCT_DETAILS);
return false;
}
// No need to keep the version details inside the report.
report->mutable_global_data()->erase(kStabilityProduct);
report->mutable_global_data()->erase(kStabilityChannel);
report->mutable_global_data()->erase(kStabilityPlatform);
report->mutable_global_data()->erase(kStabilityVersion);
// Write the minidump, then reset members.
bool success = WriteDumpImpl(*report, client_id, report_id, product_details);
next_available_byte_ = 0U;
directory_.clear();
RecordWriteDumpStatus(success ? SUCCESS : FAILED);
return success;
}
bool PostmortemMinidumpWriter::WriteDumpImpl(
const StabilityReport& report,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
const ProductDetails& product_details) {
// Allocate space for the header and seek the cursor.
FilePosition pos = 0U;
if (!Allocate(sizeof(MINIDUMP_HEADER), &pos))
return false;
if (!SeekCursor(sizeof(MINIDUMP_HEADER)))
return false;
DCHECK_EQ(kHeaderPos, pos);
// Write the proto to the file.
std::string serialized_report;
if (!report.SerializeToString(&serialized_report))
return false;
FilePosition report_pos = 0U;
if (!AppendBytes(serialized_report, &report_pos))
return false;
// The directory entry for the stability report's stream.
RegisterDirectoryEntry(kStabilityReportStreamType, report_pos,
serialized_report.length());
// Write mandatory crash keys. These will be read by crashpad and used as
// http request parameters for the upload. Keys and values should match
// server side configuration.
// TODO(manzagop): use product and version from the stability report. The
// current executable's values are an (imperfect) proxy.
std::map<std::string, std::string> crash_keys = {
{"prod", product_details.product + "_Postmortem"},
{"ver", product_details.version},
{"channel", product_details.channel},
{"plat", product_details.platform}};
if (!AppendCrashpadInfo(client_id, report_id, crash_keys))
return false;
// Write the directory.
FilePosition directory_pos = 0U;
if (!AppendVec(directory_, &directory_pos))
return false;
// Write the header.
MINIDUMP_HEADER header;
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
header.NumberOfStreams = directory_.size();
header.StreamDirectoryRva = directory_pos;
if (!SeekCursor(0U))
return false;
return Write(kHeaderPos, header);
}
bool PostmortemMinidumpWriter::AppendCrashpadInfo(
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
const std::map<std::string, std::string>& crash_keys) {
// Write the crash keys as the contents of a crashpad dictionary.
std::vector<crashpad::MinidumpSimpleStringDictionaryEntry> entries;
for (const auto& crash_key : crash_keys) {
if (!AppendCrashpadDictionaryEntry(crash_key.first, crash_key.second,
&entries)) {
return false;
}
}
// Write the dictionary's index.
FilePosition dict_pos = 0U;
uint32_t entry_count = entries.size();
if (entry_count > 0) {
if (!Append(entry_count, &dict_pos))
return false;
FilePosition unused_pos = 0U;
if (!AppendVec(entries, &unused_pos))
return false;
}
MINIDUMP_LOCATION_DESCRIPTOR simple_annotations = {0};
simple_annotations.DataSize = 0U;
if (entry_count > 0)
simple_annotations.DataSize = next_available_byte_ - dict_pos;
// Note: an RVA of 0 indicates the absence of a dictionary.
simple_annotations.Rva = dict_pos;
// Write the crashpad info.
crashpad::MinidumpCrashpadInfo crashpad_info;
crashpad_info.version = crashpad::MinidumpCrashpadInfo::kVersion;
crashpad_info.report_id = report_id;
crashpad_info.client_id = client_id;
crashpad_info.simple_annotations = simple_annotations;
// Note: module_list is left at 0, which means none.
FilePosition crashpad_pos = 0U;
if (!Append(crashpad_info, &crashpad_pos))
return false;
// Append a directory entry for the crashpad info stream.
RegisterDirectoryEntry(crashpad::kMinidumpStreamTypeCrashpadInfo,
crashpad_pos, sizeof(crashpad::MinidumpCrashpadInfo));
return true;
}
bool PostmortemMinidumpWriter::AppendCrashpadDictionaryEntry(
const std::string& key,
const std::string& value,
std::vector<crashpad::MinidumpSimpleStringDictionaryEntry>* entries) {
DCHECK_NE(nullptr, entries);
FilePosition key_pos = 0U;
if (!AppendUtf8String(key, &key_pos))
return false;
FilePosition value_pos = 0U;
if (!AppendUtf8String(value, &value_pos))
return false;
crashpad::MinidumpSimpleStringDictionaryEntry entry = {0};
entry.key = key_pos;
entry.value = value_pos;
entries->push_back(entry);
return true;
}
bool PostmortemMinidumpWriter::Allocate(size_t size_bytes, FilePosition* pos) {
DCHECK(pos);
*pos = next_available_byte_;
base::CheckedNumeric<FilePosition> next = next_available_byte_;
next += size_bytes;
if (!next.IsValid())
return false;
next_available_byte_ += size_bytes;
return true;
}
bool PostmortemMinidumpWriter::SeekCursor(FilePosition destination) {
// Validate the write does not extend past the allocated space.
if (destination > next_available_byte_)
return false;
return minidump_file_->SeekSet(destination);
}
template <class DataType>
bool PostmortemMinidumpWriter::Write(FilePosition pos, const DataType& data) {
static_assert(std::is_trivially_copyable<DataType>::value,
"restricted to trivially copyable");
return WriteBytes(pos, sizeof(data), reinterpret_cast<const char*>(&data));
}
bool PostmortemMinidumpWriter::WriteBytes(FilePosition pos,
size_t size_bytes,
const char* data) {
DCHECK(data);
DCHECK_EQ(static_cast<int64_t>(pos), minidump_file_->SeekGet());
// Validate the write does not extend past the next available byte.
base::CheckedNumeric<FilePosition> pos_end = pos;
pos_end += size_bytes;
if (!pos_end.IsValid() || pos_end.ValueOrDie() > next_available_byte_)
return false;
return minidump_file_->Write(data, size_bytes);
}
template <class DataType>
bool PostmortemMinidumpWriter::Append(const DataType& data, FilePosition* pos) {
static_assert(std::is_trivially_copyable<DataType>::value,
"restricted to trivially copyable");
DCHECK(pos);
if (!Allocate(sizeof(data), pos))
return false;
return Write(*pos, data);
}
template <class DataType>
bool PostmortemMinidumpWriter::AppendVec(const std::vector<DataType>& data,
FilePosition* pos) {
static_assert(std::is_trivially_copyable<DataType>::value,
"restricted to trivially copyable");
DCHECK(!data.empty());
DCHECK(pos);
size_t size_bytes = sizeof(DataType) * data.size();
if (!Allocate(size_bytes, pos))
return false;
return WriteBytes(*pos, size_bytes,
reinterpret_cast<const char*>(&data.at(0)));
}
bool PostmortemMinidumpWriter::AppendUtf8String(base::StringPiece data,
FilePosition* pos) {
DCHECK(pos);
uint32_t string_size = data.size();
if (!Append(string_size, pos))
return false;
FilePosition unused_pos = 0U;
return AppendBytes(data, &unused_pos);
}
bool PostmortemMinidumpWriter::AppendBytes(base::StringPiece data,
FilePosition* pos) {
DCHECK(pos);
if (!Allocate(data.length(), pos))
return false;
return WriteBytes(*pos, data.length(), data.data());
}
void PostmortemMinidumpWriter::RegisterDirectoryEntry(uint32_t stream_type,
FilePosition pos,
uint32_t size) {
MINIDUMP_DIRECTORY entry = {0};
entry.StreamType = stream_type;
entry.Location.Rva = pos;
entry.Location.DataSize = size;
directory_.push_back(entry);
}
} // namespace
bool WritePostmortemDump(crashpad::FileWriterInterface* minidump_file,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
StabilityReport* report) {
DCHECK(report);
PostmortemMinidumpWriter writer(minidump_file);
return writer.WriteDump(client_id, report_id, report);
}
} // namespace browser_watcher
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include <windows.h> // NOLINT
#include <dbghelp.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/win/scoped_handle.h"
#include "components/browser_watcher/stability_data_names.h"
#include "components/browser_watcher/stability_report.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h"
#include "third_party/crashpad/crashpad/util/file/file_reader.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
namespace browser_watcher {
using crashpad::UUID;
const char kProductName[] = "some-product";
const char kExpectedProductName[] = "some-product_Postmortem";
const char kVersion[] = "51.0.2704.106";
const char kChannel[] = "some-channel";
const char kPlatform[] = "some-platform";
class WritePostmortemDumpTest : public testing::Test {
public:
void SetUp() override {
testing::Test::SetUp();
expected_client_id_.InitializeWithNew();
expected_report_id_.InitializeWithNew();
// Create a stability report.
// TODO(manzagop): flesh out the report once proto is more detailed.
ProcessState* process_state = expected_report_.add_process_states();
CodeModule* module = process_state->add_modules();
module->set_base_address(1024);
module->set_code_file("some_code_file.dll");
// Set up directory and path.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
minidump_path_ = temp_dir_.GetPath().AppendASCII("minidump.dmp");
}
bool WriteDump(bool add_product_details) {
// Make a copy of the expected report as product details are stripped from
// the proto written to the minidump stream.
StabilityReport report(expected_report_);
if (add_product_details) {
google::protobuf::Map<std::string, TypedValue>& global_data =
*report.mutable_global_data();
global_data[kStabilityChannel].set_string_value(kChannel);
global_data[kStabilityPlatform].set_string_value(kPlatform);
global_data[kStabilityProduct].set_string_value(kProductName);
global_data[kStabilityVersion].set_string_value(kVersion);
}
crashpad::FileWriter writer;
if (!writer.Open(minidump_path_, crashpad::FileWriteMode::kCreateOrFail,
crashpad::FilePermissions::kWorldReadable)) {
return false;
}
return WritePostmortemDump(&writer, expected_client_id_,
expected_report_id_, &report);
}
const base::FilePath& minidump_path() { return minidump_path_; }
const UUID& expected_client_id() { return expected_client_id_; }
const UUID& expected_report_id() { return expected_report_id_; }
const StabilityReport& expected_report() { return expected_report_; }
private:
base::ScopedTempDir temp_dir_;
base::FilePath minidump_path_;
UUID expected_client_id_;
UUID expected_report_id_;
StabilityReport expected_report_;
};
TEST_F(WritePostmortemDumpTest, MissingProductDetailsFailureTest) {
ASSERT_FALSE(WriteDump(false));
}
TEST_F(WritePostmortemDumpTest, ValidateStabilityReportTest) {
ASSERT_TRUE(WriteDump(true));
// Read back the minidump to extract the proto.
// TODO(manzagop): rely on crashpad for reading the proto once crashpad
// supports it (https://crashpad.chromium.org/bug/10).
base::ScopedFILE minidump_file;
minidump_file.reset(base::OpenFile(minidump_path(), "rb"));
ASSERT_TRUE(minidump_file.get());
MINIDUMP_HEADER header = {};
ASSERT_EQ(1U, fread(&header, sizeof(header), 1, minidump_file.get()));
ASSERT_EQ(static_cast<ULONG32>(MINIDUMP_SIGNATURE), header.Signature);
ASSERT_EQ(2U, header.NumberOfStreams);
RVA directory_rva = header.StreamDirectoryRva;
MINIDUMP_DIRECTORY directory = {};
ASSERT_EQ(0, fseek(minidump_file.get(), directory_rva, SEEK_SET));
ASSERT_EQ(1U, fread(&directory, sizeof(directory), 1, minidump_file.get()));
ASSERT_EQ(0x4B6B0002U, directory.StreamType);
RVA report_rva = directory.Location.Rva;
ULONG32 report_size_bytes = directory.Location.DataSize;
std::string recovered_serialized_report;
recovered_serialized_report.resize(report_size_bytes);
ASSERT_EQ(0, fseek(minidump_file.get(), report_rva, SEEK_SET));
ASSERT_EQ(report_size_bytes, fread(&recovered_serialized_report.at(0), 1,
report_size_bytes, minidump_file.get()));
// Validate the recovered report.
std::string expected_serialized_report;
expected_report().SerializeToString(&expected_serialized_report);
ASSERT_EQ(expected_serialized_report, recovered_serialized_report);
StabilityReport recovered_report;
ASSERT_TRUE(recovered_report.ParseFromString(recovered_serialized_report));
}
TEST_F(WritePostmortemDumpTest, CrashpadCanReadTest) {
ASSERT_TRUE(WriteDump(true));
// Validate crashpad can read the produced minidump.
crashpad::FileReader minidump_file_reader;
ASSERT_TRUE(minidump_file_reader.Open(minidump_path()));
crashpad::ProcessSnapshotMinidump minidump_process_snapshot;
ASSERT_TRUE(minidump_process_snapshot.Initialize(&minidump_file_reader));
// Validate the crashpadinfo.
UUID client_id;
minidump_process_snapshot.ClientID(&client_id);
ASSERT_EQ(expected_client_id(), client_id);
UUID report_id;
minidump_process_snapshot.ReportID(&report_id);
ASSERT_EQ(expected_report_id(), report_id);
std::map<std::string, std::string> parameters =
minidump_process_snapshot.AnnotationsSimpleMap();
auto it = parameters.find("prod");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kExpectedProductName, it->second);
it = parameters.find("ver");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kVersion, it->second);
it = parameters.find("channel");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kChannel, it->second);
it = parameters.find("plat");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kPlatform, it->second);
}
} // namespace browser_watcher
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/postmortem_report_collector.h"
#include <utility>
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include "components/browser_watcher/stability_data_names.h"
#include "third_party/crashpad/crashpad/client/settings.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
namespace browser_watcher {
using base::FilePath;
using crashpad::CrashReportDatabase;
namespace {
// 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::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(value.signed_value()));
return true;
}
void LogCollectionStatus(CollectionStatus status) {
base::UmaHistogramEnumeration("ActivityTracker.Collect.Status", status,
COLLECTION_STATUS_MAX);
}
} // namespace
PostmortemReportCollector::PostmortemReportCollector(
metrics::SystemSessionAnalyzer* analyzer)
: report_database_(nullptr), system_session_analyzer_(analyzer) {}
PostmortemReportCollector::PostmortemReportCollector(
const std::string& product_name,
const std::string& version_number,
const std::string& channel_name,
crashpad::CrashReportDatabase* report_database,
metrics::SystemSessionAnalyzer* analyzer)
: product_name_(product_name),
version_number_(version_number),
channel_name_(channel_name),
report_database_(report_database),
system_session_analyzer_(analyzer) {
DCHECK(!product_name_.empty());
DCHECK(!version_number.empty());
DCHECK(!channel_name.empty());
DCHECK_NE(nullptr, report_database);
}
PostmortemReportCollector::~PostmortemReportCollector() {}
void PostmortemReportCollector::Process(
const std::vector<base::FilePath>& stability_files) {
// Determine the crashpad client id.
crashpad::UUID client_id;
if (report_database_) {
crashpad::Settings* settings = report_database_->GetSettings();
if (settings) {
// If GetSettings() or GetClientID() fails client_id will be left at its
// default value, all zeroes, which is appropriate.
settings->GetClientID(&client_id);
}
}
for (const FilePath& file : stability_files) {
ProcessOneReport(client_id, file);
}
}
void PostmortemReportCollector::ProcessOneReport(
const crashpad::UUID& client_id,
const FilePath& file) {
LogCollectionStatus(COLLECTION_ATTEMPT);
// Note: the code below involves two notions of report: chrome internal state
// reports and the crashpad reports they get wrapped into.
// Collect the data from the debug file to a 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. This is
// not deemed an unclean shutdown. Detailed logging happens within the
// Collect function.
LogCollectionStatus(status);
if (!base::DeleteFile(file, false))
LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
return;
}
// Delete the stability file. If the file cannot be deleted, do not report its
// contents - it will be retried in a future processing run. Note that this
// approach can lead to under reporting and retries. However, under reporting
// is preferable to the over reporting that would happen with a file that
// cannot be deleted. Also note that the crash registration may still fail at
// this point: losing the report in such a case is deemed acceptable.
if (!base::DeleteFile(file, false)) {
DLOG(ERROR) << "Failed to delete " << file.value();
LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
return;
}
LogCollectionStatus(UNCLEAN_SHUTDOWN);
if (report_proto.system_state().session_state() == SystemState::UNCLEAN)
LogCollectionStatus(UNCLEAN_SESSION);
if (report_database_)
GenerateCrashReport(client_id, &report_proto);
}
CollectionStatus PostmortemReportCollector::CollectOneReport(
const base::FilePath& file,
StabilityReport* report) {
DCHECK(report);
std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
base::debug::GlobalActivityAnalyzer::CreateWithFile(file);
if (!global_analyzer)
return ANALYZER_CREATION_FAILED;
CollectionStatus status = Extract(std::move(global_analyzer), report);
if (status != SUCCESS)
return status;
SetReporterDetails(report);
RecordSystemShutdownState(report);
return SUCCESS;
}
void PostmortemReportCollector::SetReporterDetails(
StabilityReport* report) const {
DCHECK(report);
google::protobuf::Map<std::string, TypedValue>& global_data =
*(report->mutable_global_data());
// 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(
std::string("Win32"));
#elif defined(ARCH_CPU_X86_64)
global_data[kStabilityReporterPlatform].set_string_value(
std::string("Win64"));
#endif
global_data[kStabilityReporterProduct].set_string_value(product_name());
global_data[kStabilityReporterVersion].set_string_value(version_number());
}
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 {
metrics::SystemSessionAnalyzer::Status analyzer_status =
system_session_analyzer_->IsSessionUnclean(time);
switch (analyzer_status) {
case metrics::SystemSessionAnalyzer::FAILED:
status = SYSTEM_SESSION_ANALYSIS_FAILED;
break;
case metrics::SystemSessionAnalyzer::CLEAN:
session_state = SystemState::CLEAN;
break;
case metrics::SystemSessionAnalyzer::UNCLEAN:
session_state = SystemState::UNCLEAN;
break;
case metrics::SystemSessionAnalyzer::OUTSIDE_RANGE:
status = SYSTEM_SESSION_ANALYSIS_OUTSIDE_RANGE;
break;
}
}
report->mutable_system_state()->set_session_state(session_state);
base::UmaHistogramEnumeration(
"ActivityTracker.Collect.SystemSessionAnalysisStatus", status,
SYSTEM_SESSION_ANALYSIS_STATUS_MAX);
}
void PostmortemReportCollector::GenerateCrashReport(
const crashpad::UUID& client_id,
StabilityReport* report_proto) {
DCHECK_NE(nullptr, report_database_);
DCHECK(report_proto);
// Prepare a crashpad report.
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
CrashReportDatabase::OperationStatus database_status =
report_database_->PrepareNewCrashReport(&new_report);
if (database_status != CrashReportDatabase::kNoError) {
LogCollectionStatus(PREPARE_NEW_CRASH_REPORT_FAILED);
return;
}
// Write the report to a minidump.
if (!WriteReportToMinidump(report_proto, client_id, new_report->ReportID(),
new_report->Writer())) {
LogCollectionStatus(WRITE_TO_MINIDUMP_FAILED);
return;
}
// Finalize the report wrt the report database. Note that this doesn't trigger
// an immediate upload, but Crashpad will eventually upload the report (as of
// writing, the delay is on the order of up to 15 minutes).
crashpad::UUID unused_report_id;
database_status = report_database_->FinishedWritingCrashReport(
std::move(new_report), &unused_report_id);
if (database_status != CrashReportDatabase::kNoError) {
LogCollectionStatus(FINISHED_WRITING_CRASH_REPORT_FAILED);
return;
}
LogCollectionStatus(SUCCESS);
}
bool PostmortemReportCollector::WriteReportToMinidump(
StabilityReport* report,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
crashpad::FileWriterInterface* minidump_file) {
DCHECK(report);
return WritePostmortemDump(minidump_file, client_id, report_id, report);
}
} // namespace browser_watcher
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Following an unclean shutdown, a stability report can be collected and
// submitted for upload to a reporter.
#ifndef COMPONENTS_BROWSER_WATCHER_POSTMORTEM_REPORT_COLLECTOR_H_
#define COMPONENTS_BROWSER_WATCHER_POSTMORTEM_REPORT_COLLECTOR_H_
#include <stdio.h>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/debug/activity_analyzer.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "components/browser_watcher/stability_report.pb.h"
#include "components/browser_watcher/stability_report_extractor.h"
#include "components/metrics/system_session_analyzer_win.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
#include "third_party/crashpad/crashpad/util/file/file_writer.h"
namespace browser_watcher {
// Performs postmortem stability data collection and analysis. The data is then
// reported as user metrics (e.g. to estimate the number of unclean shutdowns,
// or those attributable to the system) and, optionally, as crash reports for
// a more detailed view.
class PostmortemReportCollector {
public:
// Creates a postmortem report collector. The |product_name|, |version_number|
// and |channel_name| are used to set reporter information in postmortem
// crash reports. If |report_database| is set, postmortem crash reports are
// generated and registered against it. If |analyzer| is set, it used to
// analyze the containing system session.
PostmortemReportCollector(metrics::SystemSessionAnalyzer* analyzer);
PostmortemReportCollector(const std::string& product_name,
const std::string& version_number,
const std::string& channel_name,
crashpad::CrashReportDatabase* report_database,
metrics::SystemSessionAnalyzer* analyzer);
~PostmortemReportCollector();
// Analyzes |stability_files|, logs postmortem user metrics and optionally
// generates postmortem crash reports.
void Process(const std::vector<base::FilePath>& stability_files);
const std::string& product_name() const { return product_name_; }
const std::string& version_number() const { return version_number_; }
const std::string& channel_name() const { return channel_name_; }
private:
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorTest,
GetDebugStateFilePaths);
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorTest, CollectEmptyFile);
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorTest, CollectRandomFile);
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorCollectionTest,
CollectSuccess);
FRIEND_TEST_ALL_PREFIXES(
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
LogCollection);
FRIEND_TEST_ALL_PREFIXES(
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
ProcessUserDataCollection);
FRIEND_TEST_ALL_PREFIXES(
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
FieldTrialCollection);
FRIEND_TEST_ALL_PREFIXES(
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
ModuleCollection);
FRIEND_TEST_ALL_PREFIXES(
PostmortemReportCollectorCollectionFromGlobalTrackerTest,
SystemStateTest);
// Processes a stability file, reports user metrics and optionally generates a
// crash report.
void ProcessOneReport(const crashpad::UUID& client_id,
const base::FilePath& file);
virtual CollectionStatus CollectOneReport(
const base::FilePath& stability_file,
StabilityReport* report);
void SetReporterDetails(StabilityReport* report) const;
void RecordSystemShutdownState(StabilityReport* report) const;
void GenerateCrashReport(const crashpad::UUID& client_id,
StabilityReport* report_proto);
virtual bool WriteReportToMinidump(
StabilityReport* report,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
crashpad::FileWriterInterface* minidump_file);
std::string product_name_;
std::string version_number_;
std::string channel_name_;
crashpad::CrashReportDatabase* report_database_; // Not owned.
metrics::SystemSessionAnalyzer* system_session_analyzer_; // Not owned.
DISALLOW_COPY_AND_ASSIGN(PostmortemReportCollector);
};
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_POSTMORTEM_REPORT_COLLECTOR_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/postmortem_report_collector.h"
#include <stdint.h>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/debug/activity_analyzer.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/threading/platform_thread.h"
#include "components/browser_watcher/stability_data_names.h"
#include "components/browser_watcher/stability_report_extractor.h"
#include "components/metrics/system_session_analyzer_win.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
namespace browser_watcher {
using base::debug::ActivityData;
using base::debug::ActivityTrackerMemoryAllocator;
using base::debug::ActivityUserData;
using base::debug::GlobalActivityTracker;
using base::debug::ThreadActivityTracker;
using base::File;
using base::FilePath;
using base::FilePersistentMemoryAllocator;
using base::MemoryMappedFile;
using base::PersistentMemoryAllocator;
using base::WrapUnique;
using crashpad::CrashReportDatabase;
using crashpad::Settings;
using crashpad::UUID;
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
namespace {
const char kProductName[] = "TestProduct";
const char kVersionNumber[] = "TestVersionNumber";
const char kChannelName[] = "TestChannel";
class MockPostmortemReportCollector final : public PostmortemReportCollector {
public:
explicit MockPostmortemReportCollector(CrashReportDatabase* crash_database)
: PostmortemReportCollector(kProductName,
kVersionNumber,
kChannelName,
crash_database,
nullptr) {}
MOCK_METHOD2(CollectOneReport,
CollectionStatus(const FilePath&, StabilityReport* report));
MOCK_METHOD4(WriteReportToMinidump,
bool(StabilityReport* report,
const crashpad::UUID& client_id,
const crashpad::UUID& report_id,
crashpad::FileWriterInterface* minidump_file));
};
class MockSystemSessionAnalyzer : public metrics::SystemSessionAnalyzer {
public:
MockSystemSessionAnalyzer() : SystemSessionAnalyzer(10U) {}
MOCK_METHOD1(IsSessionUnclean, Status(base::Time timestamp));
};
// Checks if two proto messages are the same based on their serializations. Note
// this only works if serialization is deterministic, which is not guaranteed.
// In practice, serialization is deterministic (even for protocol buffers with
// maps) and such matchers are common in the Chromium code base. Also note that
// in the context of this test, false positive matches are the problem and these
// are not possible (otherwise serialization would be ambiguous). False
// negatives would lead to test failure and developer action. Alternatives are:
// 1) a generic matcher (likely not possible without reflections, missing from
// lite runtime), 2) a specialized matcher or 3) implementing deterministic
// serialization.
// TODO(manzagop): switch a matcher with guarantees.
MATCHER_P(EqualsProto, message, "") {
std::string expected_serialized;
std::string actual_serialized;
message.SerializeToString(&expected_serialized);
arg.SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
} // namespace
class PostmortemReportCollectorProcessTest
: public ::testing::TestWithParam<bool> {
public:
void SetUpTest(bool system_session_clean,
bool expect_write_dump,
bool provide_crash_db) {
// Create a dummy debug file.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
debug_file_ = temp_dir_.GetPath().AppendASCII("foo-1.pma");
{
base::ScopedFILE file(base::OpenFile(debug_file_, "w"));
ASSERT_NE(file.get(), nullptr);
}
ASSERT_TRUE(base::PathExists(debug_file_));
if (provide_crash_db) {
database_ = CrashReportDatabase::Initialize(
temp_dir_.GetPath().AppendASCII("db"));
}
collector_.reset(new MockPostmortemReportCollector(database_.get()));
// Expect a single collection call.
StabilityReport report;
report.mutable_system_state()->set_session_state(
system_session_clean ? SystemState::CLEAN : SystemState::UNCLEAN);
EXPECT_CALL(*collector_, CollectOneReport(debug_file_, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<1>(report), Return(SUCCESS)));
if (expect_write_dump) {
EXPECT_CALL(*collector_, WriteReportToMinidump(_, _, _, _))
.Times(1)
.WillOnce(Return(true));
}
}
void ValidateHistograms(int unclean_cnt, int unclean_system_cnt) {
histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
UNCLEAN_SHUTDOWN, unclean_cnt);
histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
UNCLEAN_SESSION, unclean_system_cnt);
}
void CollectReports(bool is_session_clean, bool provide_crash_db) {
SetUpTest(is_session_clean, provide_crash_db, provide_crash_db);
// Run the test.
std::vector<FilePath> debug_files{debug_file_};
collector_->Process(debug_files);
ASSERT_FALSE(base::PathExists(debug_file_));
if (provide_crash_db) {
std::vector<CrashReportDatabase::Report> reports;
ASSERT_EQ(CrashReportDatabase::kNoError,
database_->GetPendingReports(&reports));
EXPECT_EQ(1u, reports.size());
}
}
protected:
base::HistogramTester histogram_tester_;
base::ScopedTempDir temp_dir_;
FilePath debug_file_;
std::unique_ptr<CrashReportDatabase> database_;
std::unique_ptr<MockPostmortemReportCollector> collector_;
};
TEST_P(PostmortemReportCollectorProcessTest, ProcessCleanSession) {
bool provide_crash_db = GetParam();
CollectReports(true, provide_crash_db);
int expected_unclean = 1;
int expected_system_unclean = 0;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
TEST_P(PostmortemReportCollectorProcessTest, ProcessUncleanSession) {
bool provide_crash_db = GetParam();
CollectReports(false, provide_crash_db);
int expected_unclean = 1;
int expected_system_unclean = 1;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
TEST_P(PostmortemReportCollectorProcessTest, ProcessStuckFile) {
bool provide_crash_db = GetParam();
bool system_session_clean = true;
bool expect_write_dump = false;
SetUpTest(system_session_clean, expect_write_dump, provide_crash_db);
// Open the stability debug file to prevent its deletion.
base::ScopedFILE file(base::OpenFile(debug_file_, "w"));
ASSERT_NE(file.get(), nullptr);
// Run the test.
std::vector<FilePath> debug_files{debug_file_};
collector_->Process(debug_files);
ASSERT_TRUE(base::PathExists(debug_file_));
histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
DEBUG_FILE_DELETION_FAILED, 1);
int expected_unclean = 0;
int expected_system_unclean = 0;
ValidateHistograms(expected_unclean, expected_system_unclean);
}
INSTANTIATE_TEST_SUITE_P(WithCrashDatabase,
PostmortemReportCollectorProcessTest,
::testing::Values(true));
INSTANTIATE_TEST_SUITE_P(WithoutCrashDatabase,
PostmortemReportCollectorProcessTest,
::testing::Values(true));
TEST(PostmortemReportCollectorTest, CollectEmptyFile) {
// Create an empty file.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.GetPath().AppendASCII("empty.pma");
{
base::ScopedFILE file(base::OpenFile(file_path, "w"));
ASSERT_NE(file.get(), nullptr);
}
ASSERT_TRUE(PathExists(file_path));
// Validate collection: an empty file cannot suppport an analyzer.
std::unique_ptr<CrashReportDatabase> crash_db(
CrashReportDatabase::Initialize(temp_dir.GetPath().AppendASCII("db")));
PostmortemReportCollector collector(kProductName, kVersionNumber,
kChannelName, crash_db.get(), nullptr);
StabilityReport report;
ASSERT_EQ(ANALYZER_CREATION_FAILED,
collector.CollectOneReport(file_path, &report));
}
TEST(PostmortemReportCollectorTest, CollectRandomFile) {
// Create a file with content we don't expect to be valid for a debug file.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.GetPath().AppendASCII("invalid_content.pma");
{
base::ScopedFILE file(base::OpenFile(file_path, "w"));
ASSERT_NE(file.get(), nullptr);
// Assuming this size is greater than the minimum size of a debug file.
std::vector<uint8_t> data(1024);
for (size_t i = 0; i < data.size(); ++i)
data[i] = i % UINT8_MAX;
ASSERT_EQ(data.size(),
fwrite(&data.at(0), sizeof(uint8_t), data.size(), file.get()));
}
ASSERT_TRUE(PathExists(file_path));
// Validate collection: random content appears as though there is not
// stability data.
std::unique_ptr<CrashReportDatabase> crash_db(
CrashReportDatabase::Initialize(temp_dir.GetPath().AppendASCII("db")));
PostmortemReportCollector collector(kProductName, kVersionNumber,
kChannelName, crash_db.get(), nullptr);
StabilityReport report;
ASSERT_NE(SUCCESS, collector.CollectOneReport(file_path, &report));
}
class PostmortemReportCollectorCollectionFromGlobalTrackerTest
: public testing::Test {
public:
const int kMemorySize = 1 << 20; // 1MiB
PostmortemReportCollectorCollectionFromGlobalTrackerTest() {}
~PostmortemReportCollectorCollectionFromGlobalTrackerTest() override {
GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
if (global_tracker) {
global_tracker->ReleaseTrackerForCurrentThreadForTesting();
delete global_tracker;
}
}
void SetUp() override {
testing::Test::SetUp();
// Set up a debug file path.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
}
const FilePath& debug_file_path() { return debug_file_path_; }
protected:
base::ScopedTempDir temp_dir_;
FilePath debug_file_path_;
};
TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
SystemStateTest) {
// Setup.
GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
"", 3);
ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
process_data.SetInt(kStabilityStartTimestamp, 12345LL);
// Collect.
MockSystemSessionAnalyzer analyzer;
EXPECT_CALL(analyzer,
IsSessionUnclean(base::Time::FromInternalValue(12345LL)))
.Times(1)
.WillOnce(Return(metrics::SystemSessionAnalyzer::CLEAN));
std::unique_ptr<CrashReportDatabase> crash_db(
CrashReportDatabase::Initialize(temp_dir_.GetPath().AppendASCII("db")));
PostmortemReportCollector collector(kProductName, kVersionNumber,
kChannelName, crash_db.get(), &analyzer);
StabilityReport report;
ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
// Validate the report.
ASSERT_EQ(SystemState::CLEAN, report.system_state().session_state());
}
} // namespace browser_watcher
...@@ -10,11 +10,11 @@ namespace browser_watcher { ...@@ -10,11 +10,11 @@ namespace browser_watcher {
// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram. // DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum class CollectOnCrashEvent { enum class CollectOnCrashEvent {
kCollectAttempt, kCollectAttempt,
kUserDataDirNotEmpty, kUserDataDirNotEmptyUnused, // No longer used.
kPathExists, kPathExistsUnused, // No longer used.
kReportExtractionSuccess, kReportExtractionSuccess,
kPmaSetDeletedFailed, kPmaSetDeletedFailedUnused, // No longer used.
kOpenForDeleteFailed, kOpenForDeleteFailedUnused, // No longer used.
kSuccess, kSuccess,
kInMemoryAnnotationExists, kInMemoryAnnotationExists,
// New values go here. // New values go here.
...@@ -24,12 +24,12 @@ enum class CollectOnCrashEvent { ...@@ -24,12 +24,12 @@ enum class CollectOnCrashEvent {
// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram. // DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
enum class StabilityRecordEvent { enum class StabilityRecordEvent {
kRecordAttempt, kRecordAttempt,
kStabilityDirectoryExists, kStabilityDirectoryExistsUnused, // No longer used.
kGotStabilityPath, kGotStabilityPathUnused, // No longer used.
kGotTracker, kGotTracker,
kMarkDeleted, kMarkDeletedUnused, // No longer used.
kMarkDeletedGotFile, kMarkDeletedGotFileUnused, // No longer used.
kOpenForDeleteFailed, kOpenForDeleteFailedUnused, // No longer used.
// New values go here. // New values go here.
kStabilityRecordEventMax kStabilityRecordEventMax
}; };
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/stability_paths.h"
#if defined(OS_WIN)
#include <windows.h>
#endif // defined(OS_WIN)
#include <memory>
#include <string>
#include <utility>
#include "base/debug/activity_tracker.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/memory_mapped_file.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/browser_watcher/features.h"
#include "components/browser_watcher/stability_metrics.h"
#if defined(OS_WIN)
#include "third_party/crashpad/crashpad/util/misc/time.h"
namespace browser_watcher {
using base::FilePath;
using base::FilePersistentMemoryAllocator;
using base::MemoryMappedFile;
using base::PersistentMemoryAllocator;
namespace {
bool GetCreationTime(const base::Process& process, FILETIME* creation_time) {
FILETIME ignore;
return ::GetProcessTimes(process.Handle(), creation_time, &ignore, &ignore,
&ignore) != 0;
}
bool SetPmaFileDeleted(const base::FilePath& file_path) {
// Map the file read-write so it can guarantee consistency between
// the analyzer and any trackers that may still be active.
std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
if (!mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE))
return false;
if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
return false;
FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0,
base::StringPiece(), true);
allocator.SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
return true;
}
} // namespace
FilePath GetStabilityDir(const FilePath& user_data_dir) {
return user_data_dir.AppendASCII("Stability");
}
FilePath GetStabilityFileForProcess(base::ProcessId pid,
timeval creation_time,
const FilePath& user_data_dir) {
FilePath stability_dir = GetStabilityDir(user_data_dir);
constexpr uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);
int64_t creation_time_us =
creation_time.tv_sec * kMicrosecondsPerSecond + creation_time.tv_usec;
std::string file_name =
base::StringPrintf("%" CrPRIdPid "-%lld", pid, creation_time_us);
return stability_dir.AppendASCII(file_name).AddExtension(
base::PersistentMemoryAllocator::kFileExtension);
}
bool GetStabilityFileForProcess(const base::Process& process,
const FilePath& user_data_dir,
FilePath* file_path) {
DCHECK(file_path);
FILETIME creation_time;
if (!GetCreationTime(process, &creation_time))
return false;
// We rely on Crashpad's conversion to ensure the resulting filename is the
// same as on crash, when the creation time is obtained via Crashpad.
*file_path = GetStabilityFileForProcess(
process.Pid(), crashpad::FiletimeToTimevalEpoch(creation_time),
user_data_dir);
return true;
}
FilePath::StringType GetStabilityFilePattern() {
return FilePath::StringType(FILE_PATH_LITERAL("*-*")) +
base::PersistentMemoryAllocator::kFileExtension;
}
std::vector<FilePath> GetStabilityFiles(
const FilePath& stability_dir,
const FilePath::StringType& stability_file_pattern,
const std::set<FilePath>& excluded_stability_files) {
DCHECK_NE(true, stability_dir.empty());
DCHECK_NE(true, stability_file_pattern.empty());
std::vector<FilePath> paths;
base::FileEnumerator enumerator(stability_dir, false /* recursive */,
base::FileEnumerator::FILES,
stability_file_pattern);
FilePath path;
for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
if (excluded_stability_files.find(path) == excluded_stability_files.end())
paths.push_back(path);
}
return paths;
}
void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir) {
base::debug::GlobalActivityTracker* global_tracker =
base::debug::GlobalActivityTracker::Get();
if (!global_tracker)
return; // No stability instrumentation.
global_tracker->MarkDeleted();
LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeleted);
// Open (with delete) and then immediately close the file by going out of
// scope. This should cause the stability debugging file to be deleted prior
// to the next execution.
base::FilePath stability_file;
if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir,
&stability_file)) {
return;
}
LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeletedGotFile);
base::File deleter(stability_file, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE);
if (!deleter.IsValid())
LogStabilityRecordEvent(StabilityRecordEvent::kOpenForDeleteFailed);
}
void MarkStabilityFileDeletedOnCrash(const base::FilePath& file_path) {
if (!SetPmaFileDeleted(file_path))
LogCollectOnCrashEvent(CollectOnCrashEvent::kPmaSetDeletedFailed);
base::File deleter(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE);
if (!deleter.IsValid())
LogCollectOnCrashEvent(CollectOnCrashEvent::kOpenForDeleteFailed);
}
} // namespace browser_watcher
#endif // defined(OS_WIN)
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
#define COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
#include <set>
#include <vector>
#include "base/files/file_path.h"
#include "base/process/process.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <winsock2.h>
#endif // defined(OS_WIN)
namespace browser_watcher {
#if defined(OS_WIN)
// Returns the stability debugging directory.
base::FilePath GetStabilityDir(const base::FilePath& user_data_dir);
// Returns the stability debugging path, which is based on pid and creation time
// to ensure uniqueness in the face of pid recycling.
base::FilePath GetStabilityFileForProcess(base::ProcessId pid,
timeval creation_time,
const base::FilePath& user_data_dir);
// On success, returns true and |path| contains the path to the stability file.
// On failure, returns false.
bool GetStabilityFileForProcess(const base::Process& process,
const base::FilePath& user_data_dir,
base::FilePath* path);
// Returns a pattern that matches file names returned by GetFileForProcess.
base::FilePath::StringType GetStabilityFilePattern();
// Returns files in |stability_dir| that match |stability_file_pattern|,
// excluding those in |excluded_stability_files|.
std::vector<base::FilePath> GetStabilityFiles(
const base::FilePath& stability_dir,
const base::FilePath::StringType& stability_file_pattern,
const std::set<base::FilePath>& excluded_stability_files);
// Sets the current process's stability file's state to deleted (via the
// GlobalActivityTracker) and opens the file for deletion. Metrics pertaining to
// stability recording are logged.
void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir);
// Sets another process's stability file's state to deleted, then opens it for
// deletion. This function is meant for use by the crashpad handler; it logs
// metrics labelled as in the context of crash collection.
void MarkStabilityFileDeletedOnCrash(const base::FilePath& file_path);
#endif // defined(OS_WIN)
} // namespace browser_watcher
#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/stability_paths.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/process/process.h"
#include "base/test/multiprocess_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace browser_watcher {
class StabilityPathsMultiProcTest : public base::MultiProcessTest {};
MULTIPROCESS_TEST_MAIN(DummyProcess) {
return 0;
}
TEST_F(StabilityPathsMultiProcTest, GetStabilityFileForProcessTest) {
const base::FilePath empty_path;
// Get the path for the current process.
base::FilePath stability_path;
ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
&stability_path));
// Ensure requesting a second time produces the same.
base::FilePath stability_path_two;
ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
&stability_path_two));
EXPECT_EQ(stability_path, stability_path_two);
// Ensure a different process has a different stability path.
base::Process process = SpawnChild("DummyProcess");
base::FilePath stability_path_other;
ASSERT_TRUE(
GetStabilityFileForProcess(process, empty_path, &stability_path_other));
EXPECT_NE(stability_path, stability_path_other);
}
TEST(StabilityPathsTest,
GetStabilityFilePatternMatchesGetStabilityFileForProcessResult) {
// GetStabilityFileForProcess file names must match GetStabilityFilePattern
// according to
// FileEnumerator's algorithm. We test this by writing out some files and
// validating what is matched.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath user_data_dir = temp_dir.GetPath();
// Create the stability directory.
base::FilePath stability_dir = GetStabilityDir(user_data_dir);
ASSERT_TRUE(base::CreateDirectory(stability_dir));
// Write a stability file.
base::FilePath stability_file;
ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(),
user_data_dir, &stability_file));
{
base::ScopedFILE file(base::OpenFile(stability_file, "w"));
ASSERT_TRUE(file.get());
}
// Write a file that shouldn't match.
base::FilePath non_matching_file =
stability_dir.AppendASCII("non_matching.foo");
{
base::ScopedFILE file(base::OpenFile(non_matching_file, "w"));
ASSERT_TRUE(file.get());
}
// Validate only the stability file matches.
base::FileEnumerator enumerator(stability_dir, false /* recursive */,
base::FileEnumerator::FILES,
GetStabilityFilePattern());
ASSERT_EQ(stability_file, enumerator.Next());
ASSERT_TRUE(enumerator.Next().empty());
}
TEST(StabilityPathsTest, GetStabilityFiles) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Create files.
std::vector<base::FilePath> expected_paths;
std::set<base::FilePath> excluded_paths;
{
// Matches the pattern.
base::FilePath path = temp_dir.GetPath().AppendASCII("foo1.pma");
base::ScopedFILE file(base::OpenFile(path, "w"));
ASSERT_NE(file.get(), nullptr);
expected_paths.push_back(path);
// Matches the pattern, but is excluded.
path = temp_dir.GetPath().AppendASCII("foo2.pma");
file.reset(base::OpenFile(path, "w"));
ASSERT_NE(file.get(), nullptr);
ASSERT_TRUE(excluded_paths.insert(path).second);
// Matches the pattern.
path = temp_dir.GetPath().AppendASCII("foo3.pma");
file.reset(base::OpenFile(path, "w"));
ASSERT_NE(file.get(), nullptr);
expected_paths.push_back(path);
// Does not match the pattern.
path = temp_dir.GetPath().AppendASCII("bar.baz");
file.reset(base::OpenFile(path, "w"));
ASSERT_NE(file.get(), nullptr);
}
EXPECT_THAT(GetStabilityFiles(temp_dir.GetPath(),
FILE_PATH_LITERAL("foo*.pma"), excluded_paths),
testing::UnorderedElementsAreArray(expected_paths));
}
} // namespace browser_watcher
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "components/browser_watcher/activity_tracker_annotation.h" #include "components/browser_watcher/activity_tracker_annotation.h"
#include "components/browser_watcher/minidump_user_streams.h" #include "components/browser_watcher/minidump_user_streams.h"
#include "components/browser_watcher/stability_metrics.h" #include "components/browser_watcher/stability_metrics.h"
#include "components/browser_watcher/stability_paths.h"
#include "components/browser_watcher/stability_report_extractor.h" #include "components/browser_watcher/stability_report_extractor.h"
#include "third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h" #include "third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h"
#include "third_party/crashpad/crashpad/snapshot/annotation_snapshot.h" #include "third_party/crashpad/crashpad/snapshot/annotation_snapshot.h"
...@@ -50,18 +49,6 @@ UniqueMallocPtr UncheckedAllocate(size_t size) { ...@@ -50,18 +49,6 @@ UniqueMallocPtr UncheckedAllocate(size_t size) {
return UniqueMallocPtr(raw_ptr); return UniqueMallocPtr(raw_ptr);
} }
base::FilePath GetStabilityFileName(
const base::FilePath& user_data_dir,
crashpad::ProcessSnapshot* process_snapshot) {
DCHECK(process_snapshot);
timeval creation_time = {};
process_snapshot->ProcessStartTime(&creation_time);
return GetStabilityFileForProcess(process_snapshot->ProcessID(),
creation_time, user_data_dir);
}
// A PersistentMemoryAllocator subclass that can take ownership of a buffer // A PersistentMemoryAllocator subclass that can take ownership of a buffer
// that's allocated with a malloc-compatible allocation function. // that's allocated with a malloc-compatible allocation function.
class MallocMemoryAllocator : public base::PersistentMemoryAllocator { class MallocMemoryAllocator : public base::PersistentMemoryAllocator {
...@@ -135,18 +122,6 @@ bool CollectStabilityReport( ...@@ -135,18 +122,6 @@ bool CollectStabilityReport(
return true; return true;
} }
bool CollectPersistentReport(const base::FilePath& path,
StabilityReport* report) {
std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
base::debug::GlobalActivityAnalyzer::CreateWithFile(path);
if (!CollectStabilityReport(std::move(global_analyzer), report))
return false;
MarkStabilityFileDeletedOnCrash(path);
return true;
}
void CollectSystemPerformanceMetrics(StabilityReport* report) { void CollectSystemPerformanceMetrics(StabilityReport* report) {
// Grab system commit memory. Also best effort. // Grab system commit memory. Also best effort.
PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)}; PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)};
...@@ -290,31 +265,18 @@ StabilityReportUserStreamDataSource::ProduceStreamData( ...@@ -290,31 +265,18 @@ StabilityReportUserStreamDataSource::ProduceStreamData(
DCHECK(process_snapshot); DCHECK(process_snapshot);
LogCollectOnCrashEvent(CollectOnCrashEvent::kCollectAttempt); LogCollectOnCrashEvent(CollectOnCrashEvent::kCollectAttempt);
// See whether there is a persistent stability file.
StabilityReport report; StabilityReport report;
bool collected_report = false;
if (!user_data_dir_.empty()) {
LogCollectOnCrashEvent(CollectOnCrashEvent::kUserDataDirNotEmpty);
base::FilePath stability_file = // See whether there's an activity tracking report beacon in the process'
GetStabilityFileName(user_data_dir_, process_snapshot); // annotations.
if (PathExists(stability_file)) { std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
LogCollectOnCrashEvent(CollectOnCrashEvent::kPathExists); MaybeGetInMemoryActivityAnalyzer(process_snapshot);
bool collected_report = false;
if (global_analyzer) {
LogCollectOnCrashEvent(CollectOnCrashEvent::kInMemoryAnnotationExists);
collected_report = CollectPersistentReport(stability_file, &report); collected_report =
} CollectStabilityReport(std::move(global_analyzer), &report);
}
// If there isn't a persistent report, see whether there's a non-persistent
// report beacon in the process' annotations.
if (!collected_report) {
std::unique_ptr<base::debug::GlobalActivityAnalyzer> global_analyzer =
MaybeGetInMemoryActivityAnalyzer(process_snapshot);
if (global_analyzer) {
LogCollectOnCrashEvent(CollectOnCrashEvent::kInMemoryAnnotationExists);
collected_report =
CollectStabilityReport(std::move(global_analyzer), &report);
}
} }
CollectSystemPerformanceMetrics(&report); CollectSystemPerformanceMetrics(&report);
......
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "components/browser_watcher/features.h" #include "components/browser_watcher/features.h"
#include "components/browser_watcher/postmortem_report_collector.h"
#include "components/browser_watcher/stability_paths.h"
#include "components/metrics/system_session_analyzer_win.h" #include "components/metrics/system_session_analyzer_win.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h" #include "third_party/crashpad/crashpad/client/crash_report_database.h"
...@@ -160,11 +158,6 @@ enum CollectionInitializationStatus { ...@@ -160,11 +158,6 @@ enum CollectionInitializationStatus {
INIT_STATUS_MAX = 4 INIT_STATUS_MAX = 4
}; };
void LogCollectionInitStatus(CollectionInitializationStatus status) {
base::UmaHistogramEnumeration("ActivityTracker.Collect.InitStatus", status,
INIT_STATUS_MAX);
}
// Returns a task runner appropriate for running background tasks that perform // Returns a task runner appropriate for running background tasks that perform
// file I/O. // file I/O.
scoped_refptr<base::TaskRunner> CreateBackgroundTaskRunner() { scoped_refptr<base::TaskRunner> CreateBackgroundTaskRunner() {
...@@ -222,82 +215,4 @@ void WatcherMetricsProviderWin::ProvideStabilityMetrics( ...@@ -222,82 +215,4 @@ void WatcherMetricsProviderWin::ProvideStabilityMetrics(
RecordExitCodes(registry_path_); RecordExitCodes(registry_path_);
} }
void WatcherMetricsProviderWin::AsyncInit(base::OnceClosure done_callback) {
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&WatcherMetricsProviderWin::CollectPostmortemReportsImpl,
weak_ptr_factory_.GetWeakPtr()),
std::move(done_callback));
}
// TODO(manzagop): consider mechanisms for partial collection if this is to be
// used on a critical path.
void WatcherMetricsProviderWin::CollectPostmortemReportsImpl() {
SCOPED_UMA_HISTOGRAM_TIMER("ActivityTracker.Collect.TotalTime");
bool is_stability_debugging_on = base::FeatureList::IsEnabled(
browser_watcher::kExtendedCrashReportingFeature);
if (!is_stability_debugging_on) {
return; // TODO(manzagop): scan for possible data to delete?
}
if (user_data_dir_.empty() || crash_dir_.empty()) {
LogCollectionInitStatus(UNKNOWN_DIR);
return;
}
// Determine which files to harvest.
base::FilePath stability_dir = GetStabilityDir(user_data_dir_);
base::FilePath current_stability_file;
if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir_,
&current_stability_file)) {
LogCollectionInitStatus(GET_STABILITY_FILE_PATH_FAILED);
return;
}
const std::set<base::FilePath>& excluded_stability_files = {
current_stability_file};
std::vector<base::FilePath> stability_files = GetStabilityFiles(
stability_dir, GetStabilityFilePattern(), excluded_stability_files);
base::UmaHistogramCounts100("ActivityTracker.Collect.StabilityFileCount",
stability_files.size());
// If postmortem collection is disabled, delete the files.
const bool should_collect = base::GetFieldTrialParamByFeatureAsBool(
browser_watcher::kExtendedCrashReportingFeature,
browser_watcher::kCollectPostmortemParam, false);
// Create a database. Note: Chrome already has a g_database in crashpad.cc but
// it has internal linkage. Create a new one.
std::unique_ptr<crashpad::CrashReportDatabase> crashpad_database;
if (should_collect) {
crashpad_database =
crashpad::CrashReportDatabase::InitializeWithoutCreating(crash_dir_);
if (!crashpad_database) {
LOG(ERROR) << "Failed to initialize a CrashPad database.";
LogCollectionInitStatus(CRASHPAD_DATABASE_INIT_FAILED);
// Note: continue to processing the files anyway.
}
}
// Note: this is logged even when Crashpad database initialization fails.
LogCollectionInitStatus(INIT_SUCCESS);
const size_t kSystemSessionsToInspect = 5U;
metrics::SystemSessionAnalyzer analyzer(kSystemSessionsToInspect);
if (should_collect) {
base::string16 product_name, version_number, channel_name;
exe_details_cb_.Run(&product_name, &version_number, &channel_name);
PostmortemReportCollector collector(
base::UTF16ToUTF8(product_name), base::UTF16ToUTF8(version_number),
base::UTF16ToUTF8(channel_name), crashpad_database.get(), &analyzer);
collector.Process(stability_files);
} else {
PostmortemReportCollector collector(&analyzer);
collector.Process(stability_files);
}
}
} // namespace browser_watcher } // namespace browser_watcher
...@@ -33,7 +33,6 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider { ...@@ -33,7 +33,6 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider {
~WatcherMetricsProviderWin() override; ~WatcherMetricsProviderWin() override;
// metrics::MetricsProvider implementation. // metrics::MetricsProvider implementation.
void AsyncInit(base::OnceClosure done_callback) override;
void OnRecordingEnabled() override; void OnRecordingEnabled() override;
void OnRecordingDisabled() override; void OnRecordingDisabled() override;
// Note: this function collects metrics, some of which are related to the // Note: this function collects metrics, some of which are related to the
...@@ -50,9 +49,6 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider { ...@@ -50,9 +49,6 @@ class WatcherMetricsProviderWin : public metrics::MetricsProvider {
metrics::SystemProfileProto* system_profile_proto) override; metrics::SystemProfileProto* system_profile_proto) override;
private: private:
// TODO(manzagop): avoid collecting reports for clean exits from the fast exit
// path.
void CollectPostmortemReportsImpl();
bool recording_enabled_; bool recording_enabled_;
bool cleanup_scheduled_; bool cleanup_scheduled_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment