blob: ffbdcdbcb4a20aae57b5a330c9f36a9b63680f60 [file] [log] [blame]
// Copyright 2020 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 CHROME_BROWSER_ERROR_REPORTING_CHROME_JS_ERROR_REPORT_PROCESSOR_H_
#define CHROME_BROWSER_ERROR_REPORTING_CHROME_JS_ERROR_REPORT_PROCESSOR_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/containers/flat_map.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "build/chromeos_buildflags.h"
#include "components/crash/content/browser/error_reporting/js_error_report_processor.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
class Process;
}
namespace content {
class BrowserContext;
}
namespace network {
class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
class GURL;
struct JavaScriptErrorReport;
namespace variations {
struct ExperimentListInfo;
}
// Chrome's implementation of the JavaScript error reporter.
class ChromeJsErrorReportProcessor : public JsErrorReportProcessor {
public:
// Creates a ChromeJsErrorReportProcessor and sets it as the processor that
// will be returned from JsErrorReportProcessor::Get(). This will only create
// the processor if appropriate.
static void Create();
// JsErrorReportProcessor:
void SendErrorReport(JavaScriptErrorReport error_report,
base::OnceClosure completion_callback,
content::BrowserContext* browser_context) override;
void set_clock_for_testing(base::Clock* clock) { clock_ = clock; }
// Access to the recent_error_reports map allows tests to confirm we are not
// growing this map without bound.
const base::flat_map<std::string, base::Time>&
get_recent_error_reports_for_testing() const {
return recent_error_reports_;
}
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
// Force the error report processor to use the less-commonly-used temp file
// solution for communicating with crash_reporter. This is normally only used
// on old kernels without memfd_create, so we don't get good unit test
// coverage unless we force it.
void set_force_non_memfd_for_test() { force_non_memfd_for_test_ = true; }
// Set the length of time we want for the crash_reporter (or the
// mock_crash_reporter) to finish.
void set_maximium_wait_for_crash_reporter_for_test(base::TimeDelta max_wait) {
maximium_wait_for_crash_reporter_ = max_wait;
}
#endif
protected:
// Non-tests should call ChromeJsErrorReportProcessor::Create() instead.
ChromeJsErrorReportProcessor();
~ChromeJsErrorReportProcessor() override;
// Wrapper around variations::GetExperimentListInfo(). Separate virtual
// wrapper to allow dependency injection.
virtual variations::ExperimentListInfo GetExperimentListInfo() const;
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
// Returns the first element(s) of the crash_reporter argv. By default, this
// is just the command name (so {"/sbin/crash_reporter"}). Virtual so that
// tests can override and can provide additional arguments to the test binary
// if needed.
virtual std::vector<std::string> GetCrashReporterArgvStart();
#else
// Determines the version of the OS we are on. Virtual so that tests can
// override. On Chrome OS, this information is added by the crash_reporter.
virtual std::string GetOsVersion();
// Testing hook -- returns the URL we will send the error reports to. By
// default, returns the real endpoint.
virtual std::string GetCrashEndpoint();
// Testing hook -- returns the URL we will send the error reports to if
// JavaScriptErrorReport::send_to_production_servers is false. By
// default, returns the real staging endpoint.
virtual std::string GetCrashEndpointStaging();
// Update the uploads.log file with a record of this error report. This
// ensures that the error appears on chrome://crashes and is listed in the
// feedback reports.
virtual void UpdateReportDatabase(std::string remote_report_id,
base::Time report_time);
#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
private:
struct PlatformInfo;
using ParameterMap = std::map<std::string, std::string>;
absl::optional<JavaScriptErrorReport> CheckConsentAndRedact(
JavaScriptErrorReport error_report);
PlatformInfo GetPlatformInfo();
void SendReport(const GURL& url,
const std::string& body,
base::ScopedClosureRunner callback_runner,
base::Time report_time,
network::SharedURLLoaderFactory* loader_factory);
void OnConsentCheckCompleted(
base::ScopedClosureRunner callback_runner,
scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
base::TimeDelta browser_process_uptime,
base::Time report_time,
absl::optional<JavaScriptErrorReport> error_report);
// To avoid spamming the error collection system, do not send duplicate
// error reports more than once per hour. Otherwise, if we get an error
// each time the user types another character in a search box (for
// instance), we would get flooded.
// This function both determines if we should send the error report and also
// updates the map to indicate that we did send it. It assumes we will send
// the report if it sets |should_send| to true.
void CheckAndUpdateRecentErrorReports(
const JavaScriptErrorReport& error_report,
bool* should_send);
void SendReport(
ParameterMap params,
absl::optional<std::string> stack_trace,
bool send_to_production_servers,
base::ScopedClosureRunner callback_runner,
base::Time report_time,
scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
// Add parameters indicating the current field trial experiments.
void AddExperimentIds(ParameterMap& params);
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
// Write the parameters (and the stack_trace, if present) into a string
// suitable for passing the crash_reporter. Returns the string.
//
// Format is the same key:length:value format used by Crashpad and Breakpad
// when talking to crash_reporter. Example:
// value1:5:abcdevalue2:10:hellothere
static std::string ParamsToCrashReporterString(
const ParameterMap& params,
const absl::optional<std::string>& stack_trace);
void SendReportViaCrashReporter(ParameterMap params,
absl::optional<std::string> stack_trace,
base::ScopedClosureRunner callback_runner);
void WaitForCrashReporter(base::Process process,
base::Time process_creation_time,
base::ScopedClosureRunner file_cleanup,
base::ScopedClosureRunner external_callback_runner);
bool force_non_memfd_for_test_ = false;
// If crash_reporter isn't finished after this long, kill it and clean up
// anyways.
base::TimeDelta maximium_wait_for_crash_reporter_;
#else
// Turn the parameter key/value pairs into a list of parameters suitable for
// being the query part of a URL. Does URL escaping and such.
static std::string BuildPostRequestQueryString(const ParameterMap& params);
void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader,
base::ScopedClosureRunner callback_runner,
base::Time report_time,
std::unique_ptr<std::string> response_body);
#endif
// For JavaScript error reports, a mapping of message+product+line+column to
// the last time we sent an error message for that
// message+product+line+column.
base::flat_map<std::string, base::Time> recent_error_reports_;
// To avoid recent_error_reports_ growing without bound, we clean it out every
// once in while. This is the last time we cleaned it out.
base::Time last_recent_error_reports_cleaning_;
// Clock for dependency injection. Not owned.
base::Clock* clock_;
};
#endif // CHROME_BROWSER_ERROR_REPORTING_CHROME_JS_ERROR_REPORT_PROCESSOR_H_