Commit 0962c66c authored by Paul Meyer's avatar Paul Meyer Committed by Commit Bot

Implement crash report prototype (behind flag)

This patch implements crash reporting, as detailed in the
reporting spec: https://w3c.github.io/reporting/#crash-report

Change-Id: I209880150193910a0947608e6b08a6eeea29206c
Reviewed-on: https://chromium-review.googlesource.com/1174965
Commit-Queue: Paul Meyer <paulmeyer@chromium.org>
Reviewed-by: default avatarNick Carter <nick@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589941}
parent e2b20c65
...@@ -5,16 +5,22 @@ ...@@ -5,16 +5,22 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/guid.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h" #include "base/test/values_test_util.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/ssl/cert_verifier_browser_test.h" #include "chrome/browser/ssl/cert_verifier_browser_test.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h" #include "net/dns/mock_host_resolver.h"
#include "net/reporting/reporting_policy.h" #include "net/reporting/reporting_policy.h"
#include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/controllable_http_response.h"
...@@ -77,7 +83,8 @@ class ReportingBrowserTest : public CertVerifierBrowserTest { ...@@ -77,7 +83,8 @@ class ReportingBrowserTest : public CertVerifierBrowserTest {
void ReportingBrowserTest::SetUp() { void ReportingBrowserTest::SetUp() {
scoped_feature_list_.InitWithFeatures( scoped_feature_list_.InitWithFeatures(
{network::features::kReporting, network::features::kNetworkErrorLogging}, {network::features::kReporting, network::features::kNetworkErrorLogging,
features::kCrashReporting},
{}); {});
CertVerifierBrowserTest::SetUp(); CertVerifierBrowserTest::SetUp();
...@@ -101,7 +108,7 @@ void ReportingBrowserTest::SetUpOnMainThread() { ...@@ -101,7 +108,7 @@ void ReportingBrowserTest::SetUpOnMainThread() {
// Reporting and NEL will ignore configurations headers if the response // Reporting and NEL will ignore configurations headers if the response
// doesn't come from an HTTPS origin, or if the origin's certificate is // doesn't come from an HTTPS origin, or if the origin's certificate is
// invalud. Our test certs are valid, so we need a mock certificate verifier // invalid. Our test certs are valid, so we need a mock certificate verifier
// to trick the Reporting stack into paying attention to our test headers. // to trick the Reporting stack into paying attention to our test headers.
mock_cert_verifier()->set_default_result(net::OK); mock_cert_verifier()->set_default_result(net::OK);
ASSERT_TRUE(server()->Start()); ASSERT_TRUE(server()->Start());
...@@ -166,3 +173,49 @@ IN_PROC_BROWSER_TEST_F(ReportingBrowserTest, TestReportingHeadersProcessed) { ...@@ -166,3 +173,49 @@ IN_PROC_BROWSER_TEST_F(ReportingBrowserTest, TestReportingHeadersProcessed) {
port())); port()));
EXPECT_EQ(*expected, *actual); EXPECT_EQ(*expected, *actual);
} }
// TODO(paulmeyer): Test currently fails on Linux and Windows bots.
#if !defined(OS_LINUX) && !defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(ReportingBrowserTest, CrashReport) {
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver navigation_observer(contents);
// Navigate to reporting-enabled page.
NavigateParams params(browser(), GetReportingEnabledURL(),
ui::PAGE_TRANSITION_LINK);
Navigate(&params);
original_response()->WaitForRequest();
original_response()->Send("HTTP/1.1 200 OK\r\n");
original_response()->Send(GetReportToHeader());
original_response()->Send("\r\n");
original_response()->Done();
navigation_observer.Wait();
// Simulate a crash on the page.
contents->GetController().LoadURL(GURL(content::kChromeUICrashURL),
content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
upload_response()->WaitForRequest();
auto response = ParseReportUpload(upload_response()->http_request()->content);
upload_response()->Send("HTTP/1.1 200 OK\r\n");
upload_response()->Send("\r\n");
upload_response()->Done();
// Verify the contents of the report that we received.
EXPECT_TRUE(response != nullptr);
auto report = response->GetList().begin();
auto* type = report->FindKeyOfType("type", base::Value::Type::STRING);
auto* url = report->FindKeyOfType("url", base::Value::Type::STRING);
auto* id =
report->FindPathOfType({"body", "crashId"}, base::Value::Type::STRING);
EXPECT_EQ("crash", type->GetString());
EXPECT_EQ(base::StringPrintf("https://example.com:%d/original", port()),
url->GetString());
EXPECT_TRUE(base::IsValidGUID(id->GetString()));
}
#endif
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/debug/alias.h" #include "base/debug/alias.h"
#include "base/feature_list.h" #include "base/guid.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
...@@ -152,6 +152,10 @@ ...@@ -152,6 +152,10 @@
#include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/data_pipe.h"
#include "net/reporting/reporting_report.h"
#include "net/reporting/reporting_service.h"
#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "services/device/public/cpp/device_features.h" #include "services/device/public/cpp/device_features.h"
#include "services/device/public/mojom/sensor_provider.mojom.h" #include "services/device/public/mojom/sensor_provider.mojom.h"
#include "services/device/public/mojom/wake_lock.mojom.h" #include "services/device/public/mojom/wake_lock.mojom.h"
...@@ -193,6 +197,11 @@ ...@@ -193,6 +197,11 @@
using base::TimeDelta; using base::TimeDelta;
namespace features {
extern const base::Feature kCrashReporting{"CrashReporting",
base::FEATURE_DISABLED_BY_DEFAULT};
}
namespace content { namespace content {
namespace { namespace {
...@@ -425,6 +434,36 @@ void LogRendererKillCrashKeys(const GURL& site_url) { ...@@ -425,6 +434,36 @@ void LogRendererKillCrashKeys(const GURL& site_url) {
base::debug::SetCrashKeyString(site_url_key, site_url.spec()); base::debug::SetCrashKeyString(site_url_key, site_url.spec());
} }
// Sends a crash report to the Reporting API. Must only be called on the IO
// thread.
void SendCrashReport(RenderProcessHost* rph,
const std::string& crash_id,
const GURL& url,
std::unique_ptr<base::DictionaryValue> body) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Get the ReportingService.
StoragePartition* storage_partition = rph->GetStoragePartition();
scoped_refptr<net::URLRequestContextGetter> request_context_getter(
storage_partition->GetURLRequestContext());
net::URLRequestContext* request_context =
request_context_getter->GetURLRequestContext();
net::ReportingService* reporting_service =
request_context->reporting_service();
if (!reporting_service) {
net::ReportingReport::RecordReportDiscardedForNoReportingService();
return;
}
// Send the crash report to the Reporting API.
std::string user_agent;
if (request_context->http_user_agent_settings() != nullptr)
user_agent = request_context->http_user_agent_settings()->GetUserAgent();
reporting_service->QueueReport(url, user_agent, "default" /* group */,
"crash" /* type */, std::move(body),
0 /* depth */);
}
} // namespace } // namespace
class RenderFrameHostImpl::DroppedInterfaceRequestLogger class RenderFrameHostImpl::DroppedInterfaceRequestLogger
...@@ -2112,6 +2151,9 @@ void RenderFrameHostImpl::OnRenderProcessGone(int status, int exit_code) { ...@@ -2112,6 +2151,9 @@ void RenderFrameHostImpl::OnRenderProcessGone(int status, int exit_code) {
static_cast<base::TerminationStatus>(status); static_cast<base::TerminationStatus>(status);
} }
if (base::FeatureList::IsEnabled(features::kCrashReporting))
MaybeGenerateCrashReport(status);
// Reset frame tree state associated with this process. This must happen // Reset frame tree state associated with this process. This must happen
// before RenderViewTerminated because observers expect the subframes of any // before RenderViewTerminated because observers expect the subframes of any
// affected frames to be cleared first. Only do this if this is the current // affected frames to be cleared first. Only do this if this is the current
...@@ -5649,4 +5691,52 @@ RenderFrameHostImpl::CommitAsTracedValue( ...@@ -5649,4 +5691,52 @@ RenderFrameHostImpl::CommitAsTracedValue(
return value; return value;
} }
void RenderFrameHostImpl::MaybeGenerateCrashReport(int status) {
if (!last_committed_url_.SchemeIsHTTPOrHTTPS())
return;
// Only generate reports for local root frames.
if (!frame_tree_node_->IsMainFrame() && !IsCrossProcessSubframe())
return;
// Check the termination status to see if a crash occurred (and potentially
// determine the |reason| for the crash).
std::string reason;
switch (status) {
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
case base::TERMINATION_STATUS_PROCESS_CRASHED:
break;
case base::TERMINATION_STATUS_OOM:
#if defined(OS_CHROMEOS)
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
#if defined(OS_ANDROID)
case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
reason = "oom";
break;
default:
// Other termination statuses do not indicate a crash.
return;
}
// Construct the crash report.
std::string crash_id = base::GenerateGUID();
const GURL& url = last_committed_url_;
auto body = std::make_unique<base::DictionaryValue>();
body->SetString("crashId", crash_id);
if (!reason.empty())
body->SetString("reason", "oom");
// Send the crash report to the Reporting API.
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(&SendCrashReport, GetProcess(),
crash_id, url, std::move(body)));
// Set a crash key with the reporting crash ID.
static auto* reporting_crash_id_key = base::debug::AllocateCrashKeyString(
"reporting_crash_id", base::debug::CrashKeySize::Size32);
base::debug::SetCrashKeyString(reporting_crash_id_key, crash_id);
}
} // namespace content } // namespace content
...@@ -23,10 +23,10 @@ ...@@ -23,10 +23,10 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/rand_util.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/supports_user_data.h" #include "base/supports_user_data.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/unguessable_token.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/bad_message.h" #include "content/browser/bad_message.h"
...@@ -1294,6 +1294,10 @@ class CONTENT_EXPORT RenderFrameHostImpl ...@@ -1294,6 +1294,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
std::unique_ptr<base::trace_event::TracedValue> CommitAsTracedValue( std::unique_ptr<base::trace_event::TracedValue> CommitAsTracedValue(
FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params) const; FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params) const;
// Based on the termination |status|, may generate a crash report to be routed
// to the Reporting API.
void MaybeGenerateCrashReport(int status);
// For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a
// refcount that calls Shutdown when it reaches zero. This allows each // refcount that calls Shutdown when it reaches zero. This allows each
// RenderFrameHostManager to just care about RenderFrameHosts, while ensuring // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/feature_list.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/common/console_message_level.h" #include "content/public/common/console_message_level.h"
...@@ -37,6 +38,10 @@ class UnguessableToken; ...@@ -37,6 +38,10 @@ class UnguessableToken;
class Value; class Value;
} }
namespace features {
CONTENT_EXPORT extern const base::Feature kCrashReporting;
} // namespace features
namespace resource_coordinator { namespace resource_coordinator {
class FrameResourceCoordinator; class FrameResourceCoordinator;
} }
......
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