Commit 036ea24b authored by Elad Alon's avatar Elad Alon Committed by Commit Bot

Introduce WebRtcRemoteEventLogManager

WebRtcRemoteEventLogManager is a member of WebRtcEventLogManager, in
charge of managing remote-bound WebRTC event logs:
* Store remote-bound log files locally, persisting them between session
  of Chrome.
* Uploading them when permissible (when no active peer connections are
  present, with which the upload's increased bandwidth utilization might
  interfere).
* Purge remote-bound log files from local disk once an upload attempt
  was made, regardless of whether it was successful. (Otherwise, we might
  end up attempting multiple times, wasting bandwidth on an upload that
  always fails after a non-trivial amount of data was pushed upstream.
  More refined retry behavior is to be added at a later stage.)

Upcoming related work:
* Actual implementation of the uploader (this CL deals with managing the
  files to be uploaded, and the timing of the upload).
* Implementation of the JS hooks.

Bug: 775415
Change-Id: Ib5ee6081ca13d85cb8a3f38d6462bbd61a99dcae
Reviewed-on: https://chromium-review.googlesource.com/891219
Commit-Queue: Elad Alon <eladalon@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534826}
parent 5b1c5771
......@@ -1800,7 +1800,10 @@ jumbo_source_set("browser") {
"renderer_host/p2p/socket_host_udp.h",
"webrtc/webrtc_event_log_manager.cc",
"webrtc/webrtc_event_log_manager.h",
"webrtc/webrtc_event_log_manager_common.cc",
"webrtc/webrtc_event_log_manager_common.h",
"webrtc/webrtc_event_log_uploader.cc",
"webrtc/webrtc_event_log_uploader.h",
"webrtc/webrtc_internals.cc",
"webrtc/webrtc_internals.h",
"webrtc/webrtc_internals_message_handler.cc",
......@@ -1810,6 +1813,8 @@ jumbo_source_set("browser") {
"webrtc/webrtc_internals_ui_observer.h",
"webrtc/webrtc_local_event_log_manager.cc",
"webrtc/webrtc_local_event_log_manager.h",
"webrtc/webrtc_remote_event_log_manager.cc",
"webrtc/webrtc_remote_event_log_manager.h",
]
deps += [
......
......@@ -33,6 +33,7 @@
#include "content/browser/push_messaging/push_messaging_router.h"
#include "content/browser/service_manager/common_browser_interfaces.h"
#include "content/browser/storage_partition_impl_map.h"
#include "content/browser/webrtc/webrtc_event_log_manager.h"
#include "content/common/child_process_host_impl.h"
#include "content/public/browser/blob_handle.h"
#include "content/public/browser/browser_thread.h"
......@@ -445,7 +446,6 @@ void BrowserContext::SetDownloadManagerForTesting(
void BrowserContext::Initialize(
BrowserContext* browser_context,
const base::FilePath& path) {
std::string new_id;
if (GetContentClient() && GetContentClient()->browser()) {
new_id = GetContentClient()->browser()->GetServiceUserIdForBrowserContext(
......@@ -511,6 +511,13 @@ void BrowserContext::Initialize(
RegisterCommonBrowserInterfaces(connection);
connection->Start();
}
if (!browser_context->IsOffTheRecord()) {
auto* webrtc_event_log_manager = WebRtcEventLogManager::GetInstance();
if (webrtc_event_log_manager) {
webrtc_event_log_manager->EnableForBrowserContext(browser_context);
}
}
}
// static
......@@ -561,6 +568,12 @@ BrowserContext::~BrowserContext() {
DCHECK(!GetUserData(kStoragePartitionMapKeyName))
<< "StoragePartitionMap is not shut down properly";
auto* webrtc_event_log_manager = WebRtcEventLogManager::GetInstance();
if (webrtc_event_log_manager) {
const auto id = WebRtcEventLogManager::GetBrowserContextId(this);
webrtc_event_log_manager->DisableForBrowserContext(id);
}
RemoveBrowserContextFromUserIdMap(this);
if (GetUserData(kDownloadManagerKeyName))
......
......@@ -6,33 +6,57 @@
#include "base/task_scheduler/post_task.h"
#include "content/common/media/peer_connection_tracker_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
namespace content {
namespace {
using BrowserContextId = WebRtcEventLogManager::BrowserContextId;
class PeerConnectionTrackerProxyImpl
: public WebRtcEventLogManager::PeerConnectionTrackerProxy {
public:
~PeerConnectionTrackerProxyImpl() override = default;
void StartEventLogOutput(WebRtcEventLogPeerConnectionKey key) override {
RenderProcessHost* host = RenderProcessHost::FromID(key.render_process_id);
if (!host) {
return; // The host has been asynchronously removed; not a problem.
}
host->Send(new PeerConnectionTracker_StartEventLogOutput(key.lid));
void SetWebRtcEventLoggingState(WebRtcEventLogPeerConnectionKey key,
bool event_logging_enabled) override {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
&PeerConnectionTrackerProxyImpl::SetWebRtcEventLoggingStateInternal,
key, event_logging_enabled));
}
void StopEventLogOutput(WebRtcEventLogPeerConnectionKey key) override {
private:
static void SetWebRtcEventLoggingStateInternal(
WebRtcEventLogPeerConnectionKey key,
bool event_logging_enabled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(key.render_process_id);
if (!host) {
return; // The host has been asynchronously removed; not a problem.
}
host->Send(new PeerConnectionTracker_StopEventLog(key.lid));
if (event_logging_enabled) {
host->Send(new PeerConnectionTracker_StartEventLogOutput(key.lid));
} else {
host->Send(new PeerConnectionTracker_StopEventLog(key.lid));
}
}
};
const BrowserContext* GetBrowserContext(int render_process_id) {
// Since this function is only allowed to be called from the UI thread,
// it is also reasonable to demand that it only be called with a valid
// render_process_id (since those only become invalidated on the UI thread).
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
CHECK(host);
return host->GetBrowserContext();
}
} // namespace
const size_t kWebRtcEventLogManagerUnlimitedFileSize = 0;
......@@ -40,6 +64,12 @@ const size_t kWebRtcEventLogManagerUnlimitedFileSize = 0;
WebRtcEventLogManager* WebRtcEventLogManager::g_webrtc_event_log_manager =
nullptr;
BrowserContextId WebRtcEventLogManager::GetBrowserContextId(
const BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return reinterpret_cast<BrowserContextId>(browser_context);
}
WebRtcEventLogManager* WebRtcEventLogManager::CreateSingletonInstance() {
DCHECK(!g_webrtc_event_log_manager);
g_webrtc_event_log_manager = new WebRtcEventLogManager;
......@@ -52,7 +82,9 @@ WebRtcEventLogManager* WebRtcEventLogManager::GetInstance() {
WebRtcEventLogManager::WebRtcEventLogManager()
: local_logs_observer_(nullptr),
remote_logs_observer_(nullptr),
local_logs_manager_(this),
remote_logs_manager_(this),
pc_tracker_proxy_(new PeerConnectionTrackerProxyImpl),
task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BACKGROUND,
......@@ -63,10 +95,35 @@ WebRtcEventLogManager::WebRtcEventLogManager()
}
WebRtcEventLogManager::~WebRtcEventLogManager() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(g_webrtc_event_log_manager);
g_webrtc_event_log_manager = nullptr;
}
void WebRtcEventLogManager::EnableForBrowserContext(
const BrowserContext* browser_context,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(!browser_context->IsOffTheRecord());
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::EnableForBrowserContextInternal,
base::Unretained(this),
GetBrowserContextId(browser_context),
browser_context->GetPath(), std::move(reply)));
}
void WebRtcEventLogManager::DisableForBrowserContext(
BrowserContextId browser_context_id,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::DisableForBrowserContextInternal,
base::Unretained(this), browser_context_id,
std::move(reply)));
}
void WebRtcEventLogManager::PeerConnectionAdded(
int render_process_id,
int lid,
......@@ -84,15 +141,30 @@ void WebRtcEventLogManager::PeerConnectionRemoved(
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
if (!host) {
// TODO(eladalon): This should not be possible, but it happens in
// WebRtcMediaRecorderTest.PeerConnection. We should fix that and remove
// this check, using GetBrowserContext() instead.
// https://crbug.com/775415
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), false));
}
return;
}
const BrowserContext* browser_context = host->GetBrowserContext();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::PeerConnectionRemovedInternal,
base::Unretained(this), render_process_id, lid,
std::move(reply)));
GetBrowserContextId(browser_context), std::move(reply)));
}
void WebRtcEventLogManager::EnableLocalLogging(
base::FilePath base_path,
const base::FilePath& base_path,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
......@@ -113,16 +185,39 @@ void WebRtcEventLogManager::DisableLocalLogging(
base::Unretained(this), std::move(reply)));
}
void WebRtcEventLogManager::OnWebRtcEventLogWrite(
void WebRtcEventLogManager::StartRemoteLogging(
int render_process_id,
int lid,
const std::string& output,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const BrowserContext* browser_context = GetBrowserContext(render_process_id);
base::Optional<BrowserContextId> browser_context_id;
if (!browser_context->IsOffTheRecord()) {
browser_context_id = GetBrowserContextId(browser_context);
}
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::StartRemoteLoggingInternal,
base::Unretained(this), render_process_id, lid,
browser_context_id, browser_context->GetPath(),
max_file_size_bytes, std::move(reply)));
}
void WebRtcEventLogManager::OnWebRtcEventLogWrite(
int render_process_id,
int lid,
const std::string& message,
base::OnceCallback<void(std::pair<bool, bool>)> reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const BrowserContext* browser_context = GetBrowserContext(render_process_id);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::OnWebRtcEventLogWriteInternal,
base::Unretained(this), render_process_id, lid, output,
base::Unretained(this), render_process_id, lid,
!browser_context->IsOffTheRecord(), message,
std::move(reply)));
}
......@@ -136,33 +231,33 @@ void WebRtcEventLogManager::SetLocalLogsObserver(
base::Unretained(this), observer, std::move(reply)));
}
void WebRtcEventLogManager::SetRemoteLogsObserver(
WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::SetRemoteLogsObserverInternal,
base::Unretained(this), observer, std::move(reply)));
}
void WebRtcEventLogManager::SetTaskRunnerForTesting(
const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
// Testing function only - threading left for the caller's discretion.
task_runner_ = task_runner;
}
scoped_refptr<base::SequencedTaskRunner>&
WebRtcEventLogManager::GetTaskRunnerForTesting() {
// Testing function only - threading left for the caller's discretion.
return task_runner_;
}
void WebRtcEventLogManager::OnLocalLogStarted(PeerConnectionKey peer_connection,
base::FilePath file_path) {
const base::FilePath& file_path) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
auto it = peer_connections_with_event_logging_enabled_.find(peer_connection);
if (it != peer_connections_with_event_logging_enabled_.end()) {
// We're already receiving WebRTC event logs for this peer connection.
// Keep track that we also need it for local logging, so that if all
// other reasons (remote logging) stop holding, we still keep it on for
// local logging.
DCHECK_EQ((it->second & LoggingTarget::kLocalLogging), 0u);
it->second |= LoggingTarget::kLocalLogging;
} else {
// This is the first client for WebRTC event logging - let WebRTC know
// that it should start informing us of events.
peer_connections_with_event_logging_enabled_.emplace(
peer_connection, LoggingTarget::kLocalLogging);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::UpdateWebRtcEventLoggingState,
base::Unretained(this), peer_connection, true));
}
OnLoggingTargetStarted(LoggingTarget::kLocalLogging, peer_connection);
if (local_logs_observer_) {
local_logs_observer_->OnLocalLogStarted(peer_connection, file_path);
......@@ -173,24 +268,84 @@ void WebRtcEventLogManager::OnLocalLogStopped(
PeerConnectionKey peer_connection) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Record that we're no longer performing local-logging for this PC.
auto it = peer_connections_with_event_logging_enabled_.find(peer_connection);
OnLoggingTargetStopped(LoggingTarget::kLocalLogging, peer_connection);
if (local_logs_observer_) {
local_logs_observer_->OnLocalLogStopped(peer_connection);
}
}
void WebRtcEventLogManager::OnRemoteLogStarted(
PeerConnectionKey key,
const base::FilePath& file_path) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStarted(LoggingTarget::kRemoteLogging, key);
if (remote_logs_observer_) {
remote_logs_observer_->OnRemoteLogStarted(key, file_path);
}
}
void WebRtcEventLogManager::OnRemoteLogStopped(
WebRtcEventLogPeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
OnLoggingTargetStopped(LoggingTarget::kRemoteLogging, key);
if (remote_logs_observer_) {
remote_logs_observer_->OnRemoteLogStopped(key);
}
}
void WebRtcEventLogManager::OnLoggingTargetStarted(LoggingTarget target,
PeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
auto it = peer_connections_with_event_logging_enabled_.find(key);
if (it != peer_connections_with_event_logging_enabled_.end()) {
DCHECK_EQ((it->second & target), 0u);
it->second |= target;
} else {
// This is the first client for WebRTC event logging - let WebRTC know
// that it should start informing us of events.
peer_connections_with_event_logging_enabled_.emplace(key, target);
pc_tracker_proxy_->SetWebRtcEventLoggingState(key, true);
}
}
void WebRtcEventLogManager::OnLoggingTargetStopped(LoggingTarget target,
PeerConnectionKey key) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Record that we're no longer performing this type of logging for this PC.
auto it = peer_connections_with_event_logging_enabled_.find(key);
CHECK(it != peer_connections_with_event_logging_enabled_.end());
DCHECK_NE((it->second & LoggingTarget::kLocalLogging), 0u);
it->second &= ~LoggingTarget::kLocalLogging;
DCHECK_NE(it->second, 0u);
it->second &= ~target;
// If we're not doing any other type of logging for this peer connection,
// it's time to stop receiving notifications for it from WebRTC.
if (it->second == 0u) {
peer_connections_with_event_logging_enabled_.erase(it);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&WebRtcEventLogManager::UpdateWebRtcEventLoggingState,
base::Unretained(this), peer_connection, false));
pc_tracker_proxy_->SetWebRtcEventLoggingState(key, false);
}
}
if (local_logs_observer_) {
local_logs_observer_->OnLocalLogStopped(peer_connection);
void WebRtcEventLogManager::EnableForBrowserContextInternal(
BrowserContextId browser_context_id,
const base::FilePath& browser_context_dir,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_manager_.EnableForBrowserContext(browser_context_id,
browser_context_dir);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::DisableForBrowserContextInternal(
BrowserContextId browser_context_id,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_manager_.DisableForBrowserContext(browser_context_id);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
......@@ -199,29 +354,36 @@ void WebRtcEventLogManager::PeerConnectionAddedInternal(
int lid,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result =
const bool local_result =
local_logs_manager_.PeerConnectionAdded(render_process_id, lid);
const bool remote_result =
remote_logs_manager_.PeerConnectionAdded(render_process_id, lid);
DCHECK_EQ(local_result, remote_result);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
base::BindOnce(std::move(reply), local_result));
}
}
void WebRtcEventLogManager::PeerConnectionRemovedInternal(
int render_process_id,
int lid,
BrowserContextId browser_context_id,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result =
const bool local_result =
local_logs_manager_.PeerConnectionRemoved(render_process_id, lid);
const bool remote_result = remote_logs_manager_.PeerConnectionRemoved(
render_process_id, lid, browser_context_id);
DCHECK_EQ(local_result, remote_result);
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
base::BindOnce(std::move(reply), local_result));
}
}
void WebRtcEventLogManager::EnableLocalLoggingInternal(
base::FilePath base_path,
const base::FilePath& base_path,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
......@@ -243,20 +405,51 @@ void WebRtcEventLogManager::DisableLocalLoggingInternal(
}
}
void WebRtcEventLogManager::OnWebRtcEventLogWriteInternal(
void WebRtcEventLogManager::StartRemoteLoggingInternal(
int render_process_id,
int lid, // Renderer-local PeerConnection ID.
const std::string& output,
int lid,
base::Optional<BrowserContextId> browser_context_id,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool result =
local_logs_manager_.EventLogWrite(render_process_id, lid, output);
bool result;
if (browser_context_id) { // Not off the records.
result = remote_logs_manager_.StartRemoteLogging(
render_process_id, lid, *browser_context_id, browser_context_dir,
max_file_size_bytes);
} else {
result = false;
}
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply), result));
}
}
void WebRtcEventLogManager::OnWebRtcEventLogWriteInternal(
int render_process_id,
int lid,
bool remote_logging_allowed,
const std::string& message,
base::OnceCallback<void(std::pair<bool, bool>)> reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
const bool local_result =
local_logs_manager_.EventLogWrite(render_process_id, lid, message);
const bool remote_result =
remote_logging_allowed
? remote_logs_manager_.EventLogWrite(render_process_id, lid, message)
: false;
if (reply) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(reply),
std::make_pair(local_result, remote_result)));
}
}
void WebRtcEventLogManager::SetLocalLogsObserverInternal(
WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply) {
......@@ -267,28 +460,32 @@ void WebRtcEventLogManager::SetLocalLogsObserverInternal(
}
}
void WebRtcEventLogManager::UpdateWebRtcEventLoggingState(
PeerConnectionKey peer_connection,
bool enabled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (enabled) {
pc_tracker_proxy_->StartEventLogOutput(peer_connection);
} else {
pc_tracker_proxy_->StopEventLogOutput(peer_connection);
void WebRtcEventLogManager::SetRemoteLogsObserverInternal(
WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
remote_logs_observer_ = observer;
if (reply) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(reply));
}
}
void WebRtcEventLogManager::SetClockForTesting(base::Clock* clock) {
// Testing only; no need for threading guarantees (called before anything
// could be put on the TQ).
// Testing function only - threading left for the caller's discretion.
local_logs_manager_.SetClockForTesting(clock);
}
void WebRtcEventLogManager::SetPeerConnectionTrackerProxyForTesting(
std::unique_ptr<PeerConnectionTrackerProxy> pc_tracker_proxy) {
// Testing only; no need for threading guarantees (called before anything
// could be put on the TQ).
// Testing function only - threading left for the caller's discretion.
pc_tracker_proxy_ = std::move(pc_tracker_proxy);
}
void WebRtcEventLogManager::SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory) {
// Testing function only - threading left for the caller's discretion.
remote_logs_manager_.SetWebRtcEventLogUploaderFactoryForTesting(
std::move(uploader_factory));
}
} // namespace content
......@@ -8,29 +8,35 @@
#include <map>
#include <memory>
#include <type_traits>
#include <utility>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/sequenced_task_runner.h"
#include "base/time/clock.h"
#include "content/browser/webrtc/webrtc_event_log_manager_common.h"
#include "content/browser/webrtc/webrtc_local_event_log_manager.h"
#include "content/browser/webrtc/webrtc_remote_event_log_manager.h"
#include "content/common/content_export.h"
namespace content {
// This is a singleton class running in the browser UI thread.
// It is in charge of writing RTC event logs to temporary files, then uploading
// those files to remote servers, as well as of writing the logs to files which
// were manually indicated by the user from the WebRTCIntenals. (A log may
// simulatenously be written to both, either, or none.)
// TODO(eladalon): This currently only supports the old use-case - locally
// stored log files. An upcoming CL will add remote-support.
// https://crbug.com/775415
class BrowserContext;
// This is a singleton class running in the browser UI thread (ownership of
// the only instance lies in BrowserContext). It is in charge of writing WebRTC
// event logs to temporary files, then uploading those files to remote servers,
// as well as of writing the logs to files which were manually indicated by the
// user from the WebRTCIntenals. (A log may simulatenously be written to both,
// either, or none.)
class CONTENT_EXPORT WebRtcEventLogManager
: protected WebRtcLocalEventLogsObserver {
: public WebRtcLocalEventLogsObserver,
public WebRtcRemoteEventLogsObserver {
public:
using BrowserContextId = WebRtcRemoteEventLogManager::BrowserContextId;
// To turn WebRTC on and off, we go through PeerConnectionTrackerProxy. In
// order to make this toggling easily testable, PeerConnectionTrackerProxyImpl
// will send real messages to PeerConnectionTracker, whereas
......@@ -39,10 +45,16 @@ class CONTENT_EXPORT WebRtcEventLogManager
class PeerConnectionTrackerProxy {
public:
virtual ~PeerConnectionTrackerProxy() = default;
virtual void StartEventLogOutput(WebRtcEventLogPeerConnectionKey key) = 0;
virtual void StopEventLogOutput(WebRtcEventLogPeerConnectionKey key) = 0;
virtual void SetWebRtcEventLoggingState(WebRtcEventLogPeerConnectionKey key,
bool event_logging_enabled) = 0;
};
// Translate a BrowserContext into an ID, allowing associating PeerConnections
// with it while making sure that its methods would never be called outside
// of the UI thread.
static BrowserContextId GetBrowserContextId(
const BrowserContext* browser_context);
// Ensures that no previous instantiation of the class was performed, then
// instantiates the class and returns the object. Subsequent calls to
// GetInstance() will return this object.
......@@ -54,10 +66,25 @@ class CONTENT_EXPORT WebRtcEventLogManager
~WebRtcEventLogManager() override;
// Currently, we only support manual logs initiated by the user
// through WebRTCInternals, which are stored locally.
// TODO(eladalon): Allow starting/stopping an RTC event log
// that will be uploaded to the server. https://crbug.com/775415
// Enables WebRTC event logging for a given BrowserContext:
// * Pending logs from previous sessions become eligible to be uploaded.
// * New logs for active peer connections *may* be recorded. (This does *not*
// start logging; it just makes it possible.)
// This function would typically be called during a BrowserContext's
// initialization.
// This function must not be called for an off-the-records BrowserContext.
// Local-logging is not associated with BrowserContexts, and is allowed even
// if EnableForBrowserContext is not called. That is, even for incognito mode.
void EnableForBrowserContext(const BrowserContext* browser_context,
base::OnceClosure reply = base::OnceClosure());
// Disables WebRTC event logging for a given BrowserContext. New remote-bound
// WebRTC event logs will no longer be created for this BrowserContext.
// This would typically be called when a BrowserContext is destroyed, so it
// receives the ID instead of a pointer to the BrowserContext itself, to
// ensure that it would not call any virtual functions during destruction.
void DisableForBrowserContext(BrowserContextId browser_context_id,
base::OnceClosure reply = base::OnceClosure());
// Call this to let the manager know when a PeerConnection was created.
// If a reply callback is given, it will be posted back to BrowserThread::UI,
......@@ -97,7 +124,7 @@ class CONTENT_EXPORT WebRtcEventLogManager
// will get a local log file associated (specifically, we do *not* guarantee
// it would be either the oldest or the newest).
void EnableLocalLogging(
base::FilePath base_path,
const base::FilePath& base_path,
size_t max_file_size_bytes = kDefaultMaxLocalLogFileSizeBytes,
base::OnceCallback<void(bool)> reply = base::OnceCallback<void(bool)>());
......@@ -107,18 +134,33 @@ class CONTENT_EXPORT WebRtcEventLogManager
void DisableLocalLogging(
base::OnceCallback<void(bool)> reply = base::OnceCallback<void(bool)>());
// Start logging the peer connection's WebRTC events to a file, which will
// later be uploaded to a remote server. If a reply is provided, it will be
// posted back to BrowserThread::UI with the return value provided by
// WebRtcRemoteEventLogManager::StartRemoteLogging - see the comment there
// for more details.
// TODO(eladalon): Add support for injecting metadata through this call.
// https://crbug.com/775415
void StartRemoteLogging(
int render_process_id,
int lid, // Renderer-local PeerConnection ID.
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply = base::OnceCallback<void(bool)>());
// Called when a new log fragment is sent from the renderer. This will
// potentially be written to a local WebRTC event log, a log destined for
// upload, or both.
// If a reply callback is given, it will be posted back to BrowserThread::UI,
// with true if and only if |output| was written in its entirety to both the
// local log (if any) as well as the remote log (if any). In the edge case
// that neither log file exists, false will be returned.
// If a reply callback is given, it will be posted back to BrowserThread::UI
// with a pair of bools, the first bool associated with local logging and the
// second bool associated with remote-bound logging. Each bool assumes the
// value true if and only if the message was written in its entirety into
// a local/remote-bound log file.
void OnWebRtcEventLogWrite(
int render_process_id,
int lid, // Renderer-local PeerConnection ID.
const std::string& output,
base::OnceCallback<void(bool)> reply = base::OnceCallback<void(bool)>());
const std::string& message,
base::OnceCallback<void(std::pair<bool, bool>)> reply =
base::OnceCallback<void(std::pair<bool, bool>)>());
// Set (or unset) an observer that will be informed whenever a local log file
// is started/stopped. The observer needs to be able to either run from
......@@ -129,14 +171,30 @@ class CONTENT_EXPORT WebRtcEventLogManager
void SetLocalLogsObserver(WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply = base::OnceClosure());
// Set (or unset) an observer that will be informed whenever a remote log file
// is started/stopped. Note that this refers to writing these files to disk,
// not for uploading them to the server.
// The observer needs to be able to either run from anywhere. If you need the
// code to run on specific runners or queues, have the observer post
// them there.
// If a reply callback is given, it will be posted back to BrowserThread::UI
// after the observer has been set.
void SetRemoteLogsObserver(WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply = base::OnceClosure());
protected:
friend class WebRtcEventLogManagerTest; // Unit tests inject a frozen clock.
WebRtcEventLogManager();
// This can be used by unit tests to ensure that they would run synchronously.
void SetTaskRunnerForTesting(
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
// This allows unit tests that do not wish to change the task runner to still
// check when certain operations are finished.
scoped_refptr<base::SequencedTaskRunner>& GetTaskRunnerForTesting();
private:
using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
......@@ -144,8 +202,8 @@ class CONTENT_EXPORT WebRtcEventLogManager
// we have turned WebRTC event logging on for a given peer connection, so that
// we may turn it off only when the last client no longer needs it.
enum LoggingTarget : unsigned int {
kLocalLogging = 0x01
// TODO(eladalon): Add kRemoteLogging as 0x02. https://crbug.com/775415
kLocalLogging = 1 << 0,
kRemoteLogging = 1 << 1
};
using LoggingTargetBitmap = std::underlying_type<LoggingTarget>::type;
......@@ -153,49 +211,84 @@ class CONTENT_EXPORT WebRtcEventLogManager
// WebRtcLocalEventLogsObserver implementation:
void OnLocalLogStarted(PeerConnectionKey peer_connection,
base::FilePath file_path) override;
const base::FilePath& file_path) override;
void OnLocalLogStopped(PeerConnectionKey peer_connection) override;
// WebRtcRemoteEventLogsObserver implementation:
void OnRemoteLogStarted(PeerConnectionKey key,
const base::FilePath& file_path) override;
void OnRemoteLogStopped(PeerConnectionKey key) override;
void OnLoggingTargetStarted(LoggingTarget target, PeerConnectionKey key);
void OnLoggingTargetStopped(LoggingTarget target, PeerConnectionKey key);
void EnableForBrowserContextInternal(
BrowserContextId browser_context_id,
const base::FilePath& browser_context_dir,
base::OnceClosure reply);
void DisableForBrowserContextInternal(BrowserContextId browser_context_id,
base::OnceClosure reply);
void PeerConnectionAddedInternal(int render_process_id,
int lid,
base::OnceCallback<void(bool)> reply);
void PeerConnectionRemovedInternal(int render_process_id,
int lid,
BrowserContextId browser_context_id,
base::OnceCallback<void(bool)> reply);
void EnableLocalLoggingInternal(base::FilePath base_path,
void EnableLocalLoggingInternal(const base::FilePath& base_path,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply);
void DisableLocalLoggingInternal(base::OnceCallback<void(bool)> reply);
void OnWebRtcEventLogWriteInternal(
void StartRemoteLoggingInternal(
int render_process_id,
int lid, // Renderer-local PeerConnection ID.
const std::string& output,
base::Optional<BrowserContextId> browser_context_id,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes,
base::OnceCallback<void(bool)> reply);
void OnWebRtcEventLogWriteInternal(
int render_process_id,
int lid, // Renderer-local PeerConnection ID.
bool remote_logging_allowed,
const std::string& message,
base::OnceCallback<void(std::pair<bool, bool>)> reply);
void SetLocalLogsObserverInternal(WebRtcLocalEventLogsObserver* observer,
base::OnceClosure reply);
// Send a message to WebRTC telling it to start/stop sending event-log
// notifications for a given peer connection.
void UpdateWebRtcEventLoggingState(PeerConnectionKey peer_connection,
bool enabled);
void SetRemoteLogsObserverInternal(WebRtcRemoteEventLogsObserver* observer,
base::OnceClosure reply);
// Methods for injecting testing utilities in place of actual implementations.
// Because these are only intended for testing, we perform these changes
// asynchronously, trusting the unit tests to do so carefully enough.
void SetClockForTesting(base::Clock* clock);
void SetPeerConnectionTrackerProxyForTesting(
std::unique_ptr<PeerConnectionTrackerProxy> pc_tracker_proxy);
void SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory);
// Observer which will be informed whenever a local log file is started or
// stopped. Its callbacks are called synchronously from |task_runner_|,
// so the observer needs to be able to either run from any (sequenced) runner.
WebRtcLocalEventLogsObserver* local_logs_observer_;
// Observer which will be informed whenever a remote log file is started or
// stopped. Its callbacks are called synchronously from |task_runner_|,
// so the observer needs to be able to either run from any (sequenced) runner.
WebRtcRemoteEventLogsObserver* remote_logs_observer_;
// Manages local-bound logs - logs stored on the local filesystem when
// logging has been explicitly enabled by the user.
WebRtcLocalEventLogManager local_logs_manager_;
// Manages remote-bound logs - logs which will be sent to a remote server.
WebRtcRemoteEventLogManager remote_logs_manager_;
// This keeps track of which peer connections have event logging turned on
// in WebRTC, and for which client(s).
std::map<PeerConnectionKey, LoggingTargetBitmap>
......
// Copyright (c) 2018 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 "content/browser/webrtc/webrtc_event_log_manager_common.h"
#include <limits>
namespace content {
bool LogFileWriter::WriteToLogFile(LogFilesMap::iterator it,
const std::string& message) {
DCHECK_LE(message.length(),
static_cast<size_t>(std::numeric_limits<int>::max()));
// Observe the file size limit, if any. Note that base::File's interface does
// not allow writing more than numeric_limits<int>::max() bytes at a time.
int message_len = static_cast<int>(message.length()); // DCHECKed above.
LogFile& log_file = it->second;
if (log_file.max_file_size_bytes != kWebRtcEventLogManagerUnlimitedFileSize) {
DCHECK_LT(log_file.file_size_bytes, log_file.max_file_size_bytes);
const bool size_will_wrap_around =
log_file.file_size_bytes + message.length() < log_file.file_size_bytes;
const bool size_limit_will_be_exceeded =
log_file.file_size_bytes + message.length() >
log_file.max_file_size_bytes;
if (size_will_wrap_around || size_limit_will_be_exceeded) {
message_len = log_file.max_file_size_bytes - log_file.file_size_bytes;
}
}
int written = log_file.file.WriteAtCurrentPos(message.c_str(), message_len);
if (written != message_len) {
LOG(WARNING) << "WebRTC event log message couldn't be written to the "
"locally stored file in its entirety.";
CloseLogFile(it);
return false;
}
log_file.file_size_bytes += static_cast<size_t>(written);
if (log_file.max_file_size_bytes != kWebRtcEventLogManagerUnlimitedFileSize) {
DCHECK_LE(log_file.file_size_bytes, log_file.max_file_size_bytes);
if (log_file.file_size_bytes >= log_file.max_file_size_bytes) {
CloseLogFile(it);
}
}
// Truncated message due to exceeding the maximum is reported as an error -
// the caller is interested to know that not all of its message was written,
// regardless of the reason.
return (static_cast<size_t>(written) == message.length());
}
} // namespace content
......@@ -5,12 +5,22 @@
#ifndef CONTENT_BROWSER_WEBRTC_WEBRTC_RTC_EVENT_LOG_MANAGER_COMMON_H_
#define CONTENT_BROWSER_WEBRTC_WEBRTC_RTC_EVENT_LOG_MANAGER_COMMON_H_
#include <map>
#include <string>
#include <tuple>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
// This file is intended for:
// 1. Code shared between WebRtcEventLogManager, WebRtcLocalEventLogManager
// and WebRtcRemoteEventLogManager.
// 2. Code specific to either of the above classes, but which also needs
// to be seen by unit tests (such as constants).
namespace content {
CONTENT_EXPORT extern const size_t kWebRtcEventLogManagerUnlimitedFileSize;
......@@ -18,6 +28,29 @@ CONTENT_EXPORT extern const size_t kWebRtcEventLogManagerUnlimitedFileSize;
CONTENT_EXPORT extern const size_t kDefaultMaxLocalLogFileSizeBytes;
CONTENT_EXPORT extern const size_t kMaxNumberLocalWebRtcEventLogFiles;
// Limit over the number of concurrently active (currently being written to
// disk) remote-bound log files. This limits IO operations, and so it is
// applied globally (all browser contexts are limited together).
CONTENT_EXPORT extern const size_t kMaxActiveRemoteBoundWebRtcEventLogs;
// Limit over the number of pending logs (logs stored on disk and awaiting to
// be uploaded to a remote server). This limit avoids excessive storage. If a
// user chooses to have multiple profiles (and hence browser contexts) on a
// system, it is assumed that the user has enough storage to accommodate
// the increased storage consumption that comes with it. Therefore, this
// limit is applied per browser context.
CONTENT_EXPORT extern const size_t kMaxPendingRemoteBoundWebRtcEventLogs;
// The file extension to be associated with remote-bound logs while they are
// kept on local disk.
CONTENT_EXPORT extern const base::FilePath::CharType kRemoteBoundLogExtension[];
// Remote-bound event logs will not be uploaded if the time since their last
// modification (meaning the time when they were completed) exceeds this value.
// Such expired files will be purged from disk when examined.
CONTENT_EXPORT extern const base::TimeDelta
kRemoteBoundWebRtcEventLogsMaxRetention;
// For a given Chrome session, this is a unique key for PeerConnections.
// It's not, however, unique between sessions (after Chrome is restarted).
struct WebRtcEventLogPeerConnectionKey {
......@@ -42,10 +75,70 @@ struct WebRtcEventLogPeerConnectionKey {
// the paths which will be used for these logs.
class WebRtcLocalEventLogsObserver {
public:
virtual ~WebRtcLocalEventLogsObserver() = default;
virtual void OnLocalLogStarted(WebRtcEventLogPeerConnectionKey key,
base::FilePath file_path) = 0;
const base::FilePath& file_path) = 0;
virtual void OnLocalLogStopped(WebRtcEventLogPeerConnectionKey key) = 0;
protected:
virtual ~WebRtcLocalEventLogsObserver() = default;
};
// An observer for notifications of remote-bound log files being
// started/stopped. The start event would likely only interest unit tests
// (because it exposes the randomized filename to them). The stop event is of
// general interest, because it would often mean that WebRTC can stop sending
// us event logs for this peer connection.
// Some cases where OnRemoteLogStopped would be called include:
// 1. The PeerConnection has become inactive.
// 2. The file's maximum size has been reached.
// 3. Any type of error while writing to the file.
class WebRtcRemoteEventLogsObserver {
public:
virtual void OnRemoteLogStarted(WebRtcEventLogPeerConnectionKey key,
const base::FilePath& file_path) = 0;
virtual void OnRemoteLogStopped(WebRtcEventLogPeerConnectionKey key) = 0;
protected:
virtual ~WebRtcRemoteEventLogsObserver() = default;
};
struct LogFile {
LogFile(const base::FilePath& path,
base::File file,
size_t max_file_size_bytes)
: path(path),
file(std::move(file)),
max_file_size_bytes(max_file_size_bytes),
file_size_bytes(0) {}
const base::FilePath path;
base::File file;
const size_t max_file_size_bytes;
size_t file_size_bytes;
};
// WebRtcLocalEventLogManager and WebRtcRemoteEventLogManager share some logic
// when it comes to handling of files on disk.
class LogFileWriter {
protected:
using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
using LogFilesMap = std::map<PeerConnectionKey, LogFile>;
virtual ~LogFileWriter() = default;
// Given a peer connection and its associated log file, and given a log
// fragment that should be written to the log file, attempt to write to
// the log file (return value indicates success/failure).
// If an error occurs, or if the file reaches its capacity, CloseLogFile()
// will be called, closing the file.
bool WriteToLogFile(LogFilesMap::iterator it, const std::string& message);
// Called when WriteToLogFile() either encounters an error, or if the file's
// intended capacity is reached. It indicates to the inheriting class that
// the file should also be purged from its set of active log files.
// The function should return an iterator to the next element in the set
// of active logs. This makes the function more useful, allowing it to be
// used when iterating and closing several log files.
virtual LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it) = 0;
};
} // namespace content
......
This source diff could not be displayed because it is too large. You can view the blob instead.
// Copyright (c) 2018 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 "content/browser/webrtc/webrtc_event_log_uploader.h"
#include "base/files/file_util.h"
#include "base/logging.h"
namespace content {
WebRtcEventLogUploaderImpl::WebRtcEventLogUploaderImpl(
const base::FilePath& path,
WebRtcEventLogUploaderObserver* observer) {
DCHECK(observer);
// TODO(eladalon): Provide an actual implementation; really upload the file.
// https://crbug.com/775415
// If the upload was successful, the file is no longer needed.
// If the upload failed, we don't want to retry, because we run the risk of
// uploading significant amounts of data once again, only for the upload to
// fail again after (as an example) wasting 50MBs of upload bandwidth.
const bool deletion_successful = base::DeleteFile(path, /*recursive=*/false);
if (!deletion_successful) {
// This is a somewhat serious (though unlikely) error, because now we'll try
// to upload this file again next time Chrome launches.
LOG(ERROR) << "Could not delete pending log file.";
}
// TODO(eladalon): Provide actual success/failure of upload.
// https://crbug.com/775415
observer->OnWebRtcEventLogUploadComplete(path, true);
}
std::unique_ptr<WebRtcEventLogUploader>
WebRtcEventLogUploaderImpl::Factory::Create(
const base::FilePath& log_file,
WebRtcEventLogUploaderObserver* observer) {
return std::make_unique<WebRtcEventLogUploaderImpl>(log_file, observer);
}
} // namespace content
// Copyright (c) 2018 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 CONTENT_BROWSER_WEBRTC_WEBRTC_EVENT_LOG_UPLOADER_H_
#define CONTENT_BROWSER_WEBRTC_WEBRTC_EVENT_LOG_UPLOADER_H_
#include <memory>
#include "base/files/file_path.h"
namespace content {
// A class implementing this interace can register for notification of an
// upload's eventual result (success/failure).
class WebRtcEventLogUploaderObserver {
public:
virtual void OnWebRtcEventLogUploadComplete(const base::FilePath& file_path,
bool upload_successful) = 0;
protected:
virtual ~WebRtcEventLogUploaderObserver() = default;
};
// A sublcass of this interface would take ownership of a file, and either
// upload it to a remote server (actual implementation), or pretend to do
// so (in unit tests). It will typically take on an observer of type
// WebRtcEventLogUploaderObserver, and inform it of the success or failure
// of the upload.
class WebRtcEventLogUploader {
public:
virtual ~WebRtcEventLogUploader() = default;
// Since we'll need more than one instance of the abstract
// WebRtcEventLogUploader, we'll need an abstract factory for it.
class Factory {
public:
virtual ~Factory() = default;
// Creates uploaders. The observer is passed to each call of Create,
// rather than be memorized by the factory's constructor, because factories
// created by unit tests have no visibility into the real implementation's
// observer (WebRtcRemoteEventLogManager).
virtual std::unique_ptr<WebRtcEventLogUploader> Create(
const base::FilePath& log_file,
WebRtcEventLogUploaderObserver* observer) = 0;
};
};
class WebRtcEventLogUploaderImpl : public WebRtcEventLogUploader {
public:
WebRtcEventLogUploaderImpl(const base::FilePath& path,
WebRtcEventLogUploaderObserver* observer);
~WebRtcEventLogUploaderImpl() override = default;
class Factory : public WebRtcEventLogUploader::Factory {
public:
~Factory() override = default;
std::unique_ptr<WebRtcEventLogUploader> Create(
const base::FilePath& log_file,
WebRtcEventLogUploaderObserver* observer) override;
};
};
} // namespace content
#endif // CONTENT_BROWSER_WEBRTC_WEBRTC_EVENT_LOG_UPLOADER_H_
......@@ -4,8 +4,6 @@
#include "content/browser/webrtc/webrtc_local_event_log_manager.h"
#include <limits>
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
......@@ -33,7 +31,9 @@ WebRtcLocalEventLogManager::WebRtcLocalEventLogManager(
clock_for_testing_(nullptr),
max_log_file_size_bytes_(kDefaultMaxLocalLogFileSizeBytes) {}
WebRtcLocalEventLogManager::~WebRtcLocalEventLogManager() {}
WebRtcLocalEventLogManager::~WebRtcLocalEventLogManager() {
// This should never actually run, except in unit tests.
}
bool WebRtcLocalEventLogManager::PeerConnectionAdded(int render_process_id,
int lid) {
......@@ -67,7 +67,7 @@ bool WebRtcLocalEventLogManager::PeerConnectionRemoved(int render_process_id,
if (local_log != log_files_.end()) {
// Note that success/failure of stopping the local log file is unrelated
// to the success/failure of PeerConnectionRemoved().
StopLogFile(render_process_id, lid);
CloseLogFile(local_log);
}
active_peer_connections_.erase(peer_connection);
......@@ -75,7 +75,7 @@ bool WebRtcLocalEventLogManager::PeerConnectionRemoved(int render_process_id,
return true;
}
bool WebRtcLocalEventLogManager::EnableLogging(base::FilePath base_path,
bool WebRtcLocalEventLogManager::EnableLogging(const base::FilePath& base_path,
size_t max_file_size_bytes) {
if (!base_path_.empty()) {
return false;
......@@ -113,48 +113,12 @@ bool WebRtcLocalEventLogManager::DisableLogging() {
bool WebRtcLocalEventLogManager::EventLogWrite(int render_process_id,
int lid,
const std::string& output) {
DCHECK_LE(output.length(),
static_cast<size_t>(std::numeric_limits<int>::max()));
const std::string& message) {
auto it = log_files_.find(PeerConnectionKey(render_process_id, lid));
if (it == log_files_.end()) {
return false;
}
// Observe the file size limit, if any. Note that base::File's interface does
// not allow writing more than numeric_limits<int>::max() bytes at a time.
int output_len = static_cast<int>(output.length()); // DCHECKed above.
LogFile& log_file = it->second;
if (log_file.max_file_size_bytes != kWebRtcEventLogManagerUnlimitedFileSize) {
DCHECK_LT(log_file.file_size_bytes, log_file.max_file_size_bytes);
if (log_file.file_size_bytes + output.length() < log_file.file_size_bytes ||
log_file.file_size_bytes + output.length() >
log_file.max_file_size_bytes) {
output_len = log_file.max_file_size_bytes - log_file.file_size_bytes;
}
}
int written = log_file.file.WriteAtCurrentPos(output.c_str(), output_len);
if (written < 0 || written != output_len) { // Error
LOG(WARNING) << "WebRTC event log output couldn't be written to local "
"file in its entirety.";
CloseLogFile(it);
return false;
}
log_file.file_size_bytes += static_cast<size_t>(written);
if (log_file.max_file_size_bytes != kWebRtcEventLogManagerUnlimitedFileSize) {
DCHECK_LE(log_file.file_size_bytes, log_file.max_file_size_bytes);
if (log_file.file_size_bytes >= log_file.max_file_size_bytes) {
CloseLogFile(it);
}
}
// Truncated output due to exceeding the maximum is reported as an error - the
// caller is interested to know that not all of its output was written,
// regardless of the reason.
return (static_cast<size_t>(written) == output.length());
return WriteToLogFile(it, message);
}
void WebRtcLocalEventLogManager::SetClockForTesting(base::Clock* clock) {
......@@ -194,7 +158,8 @@ void WebRtcLocalEventLogManager::StartLogFile(int render_process_id, int lid) {
// If the file was successfully created, it's now ready to be written to.
DCHECK(log_files_.find({render_process_id, lid}) == log_files_.end());
log_files_.emplace(key, LogFile(std::move(file), max_log_file_size_bytes_));
log_files_.emplace(
key, LogFile(file_path, std::move(file), max_log_file_size_bytes_));
// The observer needs to be able to run on any TaskQueue.
if (observer_) {
......@@ -202,14 +167,6 @@ void WebRtcLocalEventLogManager::StartLogFile(int render_process_id, int lid) {
}
}
void WebRtcLocalEventLogManager::StopLogFile(int render_process_id, int lid) {
auto it = log_files_.find(PeerConnectionKey(render_process_id, lid));
if (it == log_files_.end()) {
return;
}
CloseLogFile(it);
}
WebRtcLocalEventLogManager::LogFilesMap::iterator
WebRtcLocalEventLogManager::CloseLogFile(LogFilesMap::iterator it) {
const PeerConnectionKey peer_connection = it->first;
......
......@@ -16,18 +16,21 @@
namespace content {
class WebRtcLocalEventLogManager {
class WebRtcLocalEventLogManager final : public LogFileWriter {
public:
explicit WebRtcLocalEventLogManager(WebRtcLocalEventLogsObserver* observer);
~WebRtcLocalEventLogManager();
~WebRtcLocalEventLogManager() override;
bool PeerConnectionAdded(int render_process_id, int lid);
bool PeerConnectionRemoved(int render_process_id, int lid);
bool EnableLogging(base::FilePath base_path, size_t max_file_size_bytes);
bool EnableLogging(const base::FilePath& base_path,
size_t max_file_size_bytes);
bool DisableLogging();
bool EventLogWrite(int render_process_id, int lid, const std::string& output);
bool EventLogWrite(int render_process_id,
int lid,
const std::string& message);
// This function is public, but this entire class is a protected
// implementation detail of WebRtcEventLogManager, which hides this
......@@ -35,24 +38,12 @@ class WebRtcLocalEventLogManager {
void SetClockForTesting(base::Clock* clock);
private:
using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
struct LogFile {
LogFile(base::File file, size_t max_file_size_bytes)
: file(std::move(file)),
max_file_size_bytes(max_file_size_bytes),
file_size_bytes(0) {}
base::File file;
const size_t max_file_size_bytes;
size_t file_size_bytes;
};
typedef std::map<PeerConnectionKey, LogFile> LogFilesMap;
// File handling.
// Create a local log file.
void StartLogFile(int render_process_id, int lid);
void StopLogFile(int render_process_id, int lid);
LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it);
// LogFileWriter implementation. Closes a log file, flushing it to disk
// and relinquishing its handle.
LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it) override;
// Derives the name of a local log file. The format is:
// [user_defined]_[date]_[time]_[pid]_[lid].log
......
// Copyright (c) 2018 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 "content/browser/webrtc/webrtc_remote_event_log_manager.h"
#include "base/bind.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace content {
// TODO(eladalon): Block remote-bound logging on mobile devices.
// https://crbug.com/775415
namespace {
const base::FilePath::CharType kRemoteBoundLogSubDirectory[] =
FILE_PATH_LITERAL("webrtc_event_logs");
} // namespace
const size_t kMaxActiveRemoteBoundWebRtcEventLogs = 3;
const size_t kMaxPendingRemoteBoundWebRtcEventLogs = 5;
static_assert(kMaxActiveRemoteBoundWebRtcEventLogs <=
kMaxPendingRemoteBoundWebRtcEventLogs,
"This assumption affects unit test coverage.");
const base::TimeDelta kRemoteBoundWebRtcEventLogsMaxRetention =
base::TimeDelta::FromDays(3);
const base::FilePath::CharType kRemoteBoundLogExtension[] =
FILE_PATH_LITERAL("log");
WebRtcRemoteEventLogManager::WebRtcRemoteEventLogManager(
WebRtcRemoteEventLogsObserver* observer)
: observer_(observer),
uploader_factory_(new WebRtcEventLogUploaderImpl::Factory) {}
WebRtcRemoteEventLogManager::~WebRtcRemoteEventLogManager() {
// TODO(eladalon): Purge from disk files which were being uploaded while
// destruction took place, thereby avoiding endless attempts to upload
// the same file. https://crbug.com/775415
}
void WebRtcRemoteEventLogManager::EnableForBrowserContext(
BrowserContextId browser_context,
const base::FilePath& browser_context_dir) {
const base::FilePath remote_bound_logs_dir =
GetLogsDirectoryPath(browser_context_dir);
if (!MaybeCreateLogsDirectory(remote_bound_logs_dir)) {
LOG(WARNING)
<< "WebRtcRemoteEventLogManager couldn't create logs directory.";
return;
}
AddPendingLogs(browser_context, remote_bound_logs_dir);
}
void WebRtcRemoteEventLogManager::DisableForBrowserContext(
BrowserContextId browser_context) {
auto active_it = active_logs_counts_.find(browser_context);
if (active_it != active_logs_counts_.end()) {
active_logs_counts_.erase(active_it);
}
auto pending_it = pending_logs_counts_.find(browser_context);
if (pending_it != pending_logs_counts_.end()) {
pending_logs_counts_.erase(pending_it);
}
}
bool WebRtcRemoteEventLogManager::PeerConnectionAdded(int render_process_id,
int lid) {
PrunePendingLogs(); // Infrequent event - good opportunity to prune.
const auto result = active_peer_connections_.emplace(render_process_id, lid);
return result.second;
}
bool WebRtcRemoteEventLogManager::PeerConnectionRemoved(
int render_process_id,
int lid,
BrowserContextId browser_context) {
PrunePendingLogs(); // Infrequent event - good opportunity to prune.
const PeerConnectionKey key = PeerConnectionKey(render_process_id, lid);
auto peer_connection = active_peer_connections_.find(key);
if (peer_connection == active_peer_connections_.end()) {
return false;
}
MaybeStopRemoteLogging(render_process_id, lid, browser_context);
active_peer_connections_.erase(peer_connection);
MaybeStartUploading();
return true;
}
bool WebRtcRemoteEventLogManager::StartRemoteLogging(
int render_process_id,
int lid,
BrowserContextId browser_context,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes) {
// TODO(eladalon): Set a tighter limit (following discussion with rschriebman
// and manj). https://crbug.com/775415
if (max_file_size_bytes == kWebRtcEventLogManagerUnlimitedFileSize) {
return false;
}
const PeerConnectionKey key(render_process_id, lid);
// May not start logging inactive peer connections.
if (active_peer_connections_.find(key) == active_peer_connections_.end()) {
return false;
}
// May not restart active remote logs.
auto it = active_logs_.find(key);
if (it != active_logs_.end()) {
LOG(ERROR) << "Remote logging already underway for (" << render_process_id
<< ", " << lid << ").";
return false;
}
// This is a good opportunity to prune the list of pending logs, potentially
// making room for this file.
PrunePendingLogs();
// Limit over concurrently active logs (across BrowserContext-s).
if (active_logs_.size() >= kMaxActiveRemoteBoundWebRtcEventLogs) {
return false;
}
// Limit over the number of pending logs (per BrowserContext). We count active
// logs too, since they become pending logs once completed.
if (active_logs_counts_[browser_context] +
pending_logs_counts_[browser_context] >=
kMaxPendingRemoteBoundWebRtcEventLogs) {
return false;
}
return StartWritingLog(render_process_id, lid, browser_context,
browser_context_dir, max_file_size_bytes);
}
bool WebRtcRemoteEventLogManager::EventLogWrite(int render_process_id,
int lid,
const std::string& message) {
auto it = active_logs_.find(PeerConnectionKey(render_process_id, lid));
if (it == active_logs_.end()) {
return false;
}
return WriteToLogFile(it, message);
}
void WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadComplete(
const base::FilePath& file_path,
bool upload_successful) {
// Post a task to deallocate the uploader (can't do this directly,
// because this function is a callback from the uploader), potentially
// starting a new upload for the next file.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadCompleteInternal,
base::Unretained(this)));
// TODO(eladalon): Send indication of success/failure back to JS.
// https://crbug.com/775415
}
void WebRtcRemoteEventLogManager::SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory) {
uploader_factory_ = std::move(uploader_factory);
// Unit tests would initially set a null uploader factory, so that files would
// be kept around. Some tests would later change to a different factory
// (e.g. one that always simulates upload failure); inthat case, we should
// get rid of the null uploader, since it never terminates.
uploader_.reset();
MaybeStartUploading();
}
base::FilePath WebRtcRemoteEventLogManager::GetLogsDirectoryPath(
const base::FilePath& browser_context_dir) {
return browser_context_dir.Append(kRemoteBoundLogSubDirectory);
}
WebRtcRemoteEventLogManager::LogFilesMap::iterator
WebRtcRemoteEventLogManager::CloseLogFile(LogFilesMap::iterator it) {
const PeerConnectionKey peer_connection = it->first;
it->second.file.Flush();
it = active_logs_.erase(it); // file.Close() called by destructor.
if (observer_) {
observer_->OnRemoteLogStopped(peer_connection);
}
MaybeStartUploading();
return it;
}
bool WebRtcRemoteEventLogManager::MaybeCreateLogsDirectory(
const base::FilePath& remote_bound_logs_dir) {
if (base::PathExists(remote_bound_logs_dir)) {
if (!base::DirectoryExists(remote_bound_logs_dir)) {
LOG(ERROR) << "Path for remote-bound logs is taken by a non-directory.";
return false;
}
} else if (!base::CreateDirectory(remote_bound_logs_dir)) {
LOG(ERROR) << "Failed to create the local directory for remote-bound logs.";
return false;
}
// TODO(eladalon): Test for appropriate permissions. https://crbug.com/775415
return true;
}
void WebRtcRemoteEventLogManager::AddPendingLogs(
BrowserContextId browser_context,
const base::FilePath& remote_bound_logs_dir) {
DCHECK(active_logs_counts_.find(browser_context) ==
active_logs_counts_.end());
DCHECK(pending_logs_counts_.find(browser_context) ==
pending_logs_counts_.end());
active_logs_counts_.emplace(browser_context, 0);
pending_logs_counts_.emplace(browser_context, 0);
base::FilePath::StringType pattern =
base::FilePath::StringType(FILE_PATH_LITERAL("*")) +
kRemoteBoundLogExtension;
base::FileEnumerator enumerator(remote_bound_logs_dir,
/*recursive=*/false,
base::FileEnumerator::FILES, pattern);
for (auto path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
const auto last_modified = enumerator.GetInfo().GetLastModifiedTime();
pending_logs_.emplace(browser_context, path, last_modified);
pending_logs_counts_[browser_context] += 1;
}
MaybeStartUploading();
}
bool WebRtcRemoteEventLogManager::StartWritingLog(
int render_process_id,
int lid,
BrowserContextId browser_context,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes) {
// Randomize a new filename. In the highly unlikely event that this filename
// is already taken, it will be treated the same way as any other failure
// to start the log file.
// TODO(eladalon): Add a unit test for above comment. https://crbug.com/775415
const std::string unique_filename =
"event_log_" + std::to_string(base::RandUint64());
const base::FilePath base_path = GetLogsDirectoryPath(browser_context_dir);
const base::FilePath file_path = base_path.AppendASCII(unique_filename)
.AddExtension(kRemoteBoundLogExtension);
// Attempt to create the file.
constexpr int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
base::File file(file_path, file_flags);
if (!file.IsValid() || !file.created()) {
LOG(WARNING) << "Couldn't create and/or open remote WebRTC event log file.";
return false;
}
// Record that we're now writing this remote-bound log to this file.
const PeerConnectionKey key(render_process_id, lid);
const auto it = active_logs_.emplace(
key, LogFile(file_path, std::move(file), max_file_size_bytes));
DCHECK(it.second);
active_logs_counts_[browser_context] += 1;
observer_->OnRemoteLogStarted(PeerConnectionKey(render_process_id, lid),
file_path);
return true;
}
void WebRtcRemoteEventLogManager::MaybeStopRemoteLogging(
int render_process_id,
int lid,
BrowserContextId browser_context) {
const PeerConnectionKey key(render_process_id, lid);
const auto it = active_logs_.find(key);
if (it == active_logs_.end()) {
return;
}
it->second.file.Flush();
it->second.file.Close();
// The current time is a good enough approximation of the file's last
// modification time.
base::Time last_modified = base::Time::Now();
pending_logs_.emplace(browser_context, it->second.path, last_modified);
pending_logs_counts_[browser_context] += 1;
active_logs_.erase(it);
active_logs_counts_[browser_context] -= 1;
observer_->OnRemoteLogStopped(PeerConnectionKey(render_process_id, lid));
MaybeStartUploading();
}
void WebRtcRemoteEventLogManager::PrunePendingLogs() {
const base::Time oldest_non_expired_timestamp =
base::Time::Now() - kRemoteBoundWebRtcEventLogsMaxRetention;
for (auto it = pending_logs_.begin(); it != pending_logs_.end();) {
if (it->last_modified < oldest_non_expired_timestamp) {
if (!base::DeleteFile(it->path, /*recursive=*/false)) {
LOG(ERROR) << "Failed to delete " << it->path << ".";
}
pending_logs_counts_[it->browser_context] -= 1;
it = pending_logs_.erase(it);
} else {
++it;
}
}
}
bool WebRtcRemoteEventLogManager::UploadingAllowed() const {
return active_peer_connections_.empty();
}
void WebRtcRemoteEventLogManager::MaybeStartUploading() {
PrunePendingLogs(); // Avoid uploading freshly expired files.
if (uploader_) {
return; // Upload already underway.
}
if (pending_logs_.empty()) {
return; // Nothing to upload.
}
if (!UploadingAllowed()) {
return;
}
// The uploader takes ownership of the file; it's no longer considered to be
// pending. (If the upload fails, the log will be deleted.)
// TODO(eladalon): Add more refined retry behavior, so that we would not
// delete the log permanently if the network is just down, on the one hand,
// but also would not be uploading unlimited data on endless retries on the
// other hand. https://crbug.com/775415
uploader_ = uploader_factory_->Create(pending_logs_.begin()->path, this);
pending_logs_.erase(pending_logs_.begin());
}
void WebRtcRemoteEventLogManager::OnWebRtcEventLogUploadCompleteInternal() {
uploader_.reset();
MaybeStartUploading();
}
} // namespace content
// Copyright (c) 2018 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 CONTENT_BROWSER_WEBRTC_WEBRTC_REMOTE_EVENT_LOG_MANAGER_H_
#define CONTENT_BROWSER_WEBRTC_WEBRTC_REMOTE_EVENT_LOG_MANAGER_H_
#include <map>
#include <set>
#include <vector>
#include "base/time/time.h"
#include "content/browser/webrtc/webrtc_event_log_manager_common.h"
#include "content/browser/webrtc/webrtc_event_log_uploader.h"
#include "content/common/content_export.h"
namespace content {
// TODO(eladalon): Prevent uploading of logs when Chrome shutdown imminent.
// https://crbug.com/775415
class CONTENT_EXPORT WebRtcRemoteEventLogManager final
: public LogFileWriter,
public WebRtcEventLogUploaderObserver {
public:
using BrowserContextId = uintptr_t;
explicit WebRtcRemoteEventLogManager(WebRtcRemoteEventLogsObserver* observer);
~WebRtcRemoteEventLogManager() override;
// Enables remote-bound logging for a given BrowserContext. Logs stored during
// previous sessions become eligible for upload, and recording of new logs for
// peer connections associated with this BrowserContext, in the
// BrowserContext's user-data directory, becomes possible.
// This method would typically be called when a BrowserContext is initialized.
void EnableForBrowserContext(BrowserContextId browser_context,
const base::FilePath& browser_context_dir);
// Enables remote-bound logging for a given BrowserContext. Pending logs from
// earlier (while it was enabled) may still be uploaded, but no additional
// logs will be created.
void DisableForBrowserContext(BrowserContextId browser_context);
// Called to inform |this| of peer connections being added/removed.
// This information is used to:
// 1. Make decisions about when to upload previously finished logs.
// 2. When a peer connection is removed, if it was being logged, its log
// changes from ACTIVE to PENDING.
// The return value of both methods indicates only the consistency of the
// information with previously received information (e.g. can't remove a
// peer connection that was never added, etc.).
bool PeerConnectionAdded(int render_process_id, int lid);
bool PeerConnectionRemoved(int render_process_id,
int lid,
BrowserContextId browser_context);
// Attempt to start logging the WebRTC events of an active peer connection.
// Logging is subject to several restrictions:
// 1. May not log more than kMaxNumberActiveRemoteWebRtcEventLogFiles logs
// at the same time.
// 2. Each browser context may have only kMaxPendingLogFilesPerBrowserContext
// pending logs. Since active logs later become pending logs, it is also
// forbidden to start a remote-bound log that would, once completed, become
// a pending log that would exceed that limit.
// 3. The maximum file size must be sensible.
// The return value is true if all of the restrictions were observed, and if
// a file was successfully created for this log.
bool StartRemoteLogging(int render_process_id,
int lid,
BrowserContextId browser_context,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes);
// If an active remote-bound log exists for the given peer connection, this
// will append |message| to that log. If writing |message| to the log would
// exceed its maximum allowed size, |message| is first truncated.
// If the log file's capacity is exhausted as a result of this function call,
// or if a write error occurs, the file is closed, and the remote-bound log
// changes from ACTIVE to PENDING.
// True is returned if and only if |message| was written in its entirety to
// an active log.
bool EventLogWrite(int render_process_id,
int lid,
const std::string& message);
// WebRtcEventLogUploaderObserver implementation.
void OnWebRtcEventLogUploadComplete(const base::FilePath& file_path,
bool upload_successful) override;
// Unit tests may use this to inject null uploaders, or ones which are
// directly controlled by the unit test (succeed or fail according to the
// test's needs).
// Note that for simplicity's sake, this may be called from outside the
// task queue on which this object lives (WebRtcEventLogManager::task_queue_).
// Therefore, if a test calls this, it should call it before it initializes
// any BrowserContext with pending log files in its directory.
void SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory);
protected:
friend class WebRtcEventLogManagerTest;
// Given a BrowserContext's directory, return the path to the directory where
// we store the pending remote-bound logs associated with this BrowserContext.
static base::FilePath GetLogsDirectoryPath(
const base::FilePath& browser_context_dir);
private:
using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
struct PendingLog {
PendingLog(BrowserContextId browser_context,
const base::FilePath& path,
base::Time last_modified)
: browser_context(browser_context),
path(path),
last_modified(last_modified) {}
bool operator<(const PendingLog& other) const {
if (last_modified != other.last_modified) {
return last_modified < other.last_modified;
}
return path < other.path; // Break ties arbitrarily, but consistently.
}
const BrowserContextId browser_context; // This file's owner.
const base::FilePath path;
// |last_modified| recorded at BrowserContext initialization. Chrome will
// not modify it afterwards, and neither should the user.
const base::Time last_modified;
};
// LogFileWriter implementation. Closes an active log file, changing its
// state from ACTIVE to PENDING.
LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it) override;
// Attempts to create the directory where we'll write the logs, if it does
// not already exist. Returns true if the directory exists (either it already
// existed, or it was successfully created).
bool MaybeCreateLogsDirectory(const base::FilePath& remote_bound_logs_dir);
// Scans the user data directory associated with this BrowserContext for
// remote-bound logs that were created during previous Chrome sessions.
// This function does *not* protect against manipulation by the user,
// who might seed the directory with more files than were permissible.
void AddPendingLogs(BrowserContextId browser_context,
const base::FilePath& remote_bound_logs_dir);
// Attempts the creation of a locally stored file into which a remote-bound
// log may be written. True is returned if and only if such a file was
// successfully created.
bool StartWritingLog(int render_process_id,
int lid,
BrowserContextId browser_context,
const base::FilePath& browser_context_dir,
size_t max_file_size_bytes);
// Checks if the referenced peer connection has an associated active
// remote-bound log. If it does, the log is changed from ACTIVE to PENDING.
void MaybeStopRemoteLogging(int render_process_id,
int lid,
BrowserContextId browser_context);
// Get rid of pending logs whose age exceeds our retention policy.
// On the one hand, we want to remove expired files as soon as possible, but
// on the other hand, we don't want to waste CPU by checking this too often.
// Therefore, we prune pending files:
// 1. When a new BrowserContext is initalized, thereby also pruning the
// pending logs contributed by that BrowserContext.
// 2. Before initiating a new upload, thereby avoiding uploading a file that
// has just now expired.
// 3. On infrequent events - peer connection addition/removal, but NOT
// on something that could potentially be frequent, such as EventLogWrite.
// Note that the last modification date of a file, which is the value measured
// against for retention, is only read from disk once per file, meaning
// this check is not too expensive.
void PrunePendingLogs();
// Initiating a new upload is only allowed when there are no active peer
// connection which might be adversely affected by the bandwidth consumption
// of the upload.
// TODO(eladalon): Add support for pausing/resuming an upload when peer
// connections are added/removed after an upload was already initiated.
// https://crbug.com/775415
bool UploadingAllowed() const;
// If no upload is in progress, and if uploading is currently permissible,
// start a new upload.
void MaybeStartUploading();
// When an upload is complete, it might be time to upload the next file.
void OnWebRtcEventLogUploadCompleteInternal();
// This is used to inform WebRtcEventLogManager when remote-bound logging
// of a peer connection starts/stops, which allows WebRtcEventLogManager to
// decide when to ask WebRTC to start/stop sending event logs.
WebRtcRemoteEventLogsObserver* const observer_;
// Currently active peer connections. PeerConnections which have been closed
// are not considered active, regardless of whether they have been torn down.
std::set<PeerConnectionKey> active_peer_connections_;
// Remote-bound logs which we're currently in the process of writing to disk.
std::map<PeerConnectionKey, LogFile> active_logs_;
// Remote-bound logs which have been written to disk before (either during
// this Chrome session or during an earlier one), and which are no waiting to
// be uploaded.
std::set<PendingLog> pending_logs_;
// Null if no ongoing upload, or an uploader which owns a file, and is
// currently busy uploading it to a remote server.
std::unique_ptr<WebRtcEventLogUploader> uploader_;
// Producer of uploader objects. (In unit tests, this would create
// null-implementation uploaders, or uploaders whose behavior is controlled
// by the unit test.)
std::unique_ptr<WebRtcEventLogUploader::Factory> uploader_factory_;
// Active logs are subject to a global limit (no more than X active logs,
// regardless of which context they belong to). This makes sense, because
// performance is the limiting factor. However, pending logs are subject
// to a per-browser-context limit, because a user that chooses to run multiple
// profiles is resigning himself to increased disk utilization. We only keep
// track of active_logs_counts_ - the per-browser-context active log count -
// because active logs become pending logs once completed.
std::map<BrowserContextId, size_t> active_logs_counts_;
std::map<BrowserContextId, size_t> pending_logs_counts_;
DISALLOW_COPY_AND_ASSIGN(WebRtcRemoteEventLogManager);
};
} // namespace content
#endif // CONTENT_BROWSER_WEBRTC_WEBRTC_REMOTE_EVENT_LOG_MANAGER_H_
......@@ -7,6 +7,7 @@
#include <utility>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/test/null_task_runner.h"
#include "content/public/browser/permission_manager.h"
......@@ -45,8 +46,13 @@ class TestContextURLRequestContextGetter : public net::URLRequestContextGetter {
namespace content {
TestBrowserContext::TestBrowserContext() {
EXPECT_TRUE(browser_context_dir_.CreateUniqueTempDir());
TestBrowserContext::TestBrowserContext(
base::FilePath browser_context_dir_path) {
if (browser_context_dir_path.empty()) {
EXPECT_TRUE(browser_context_dir_.CreateUniqueTempDir());
} else {
EXPECT_TRUE(browser_context_dir_.Set(browser_context_dir_path));
}
BrowserContext::Initialize(this, browser_context_dir_.GetPath());
}
......
......@@ -25,7 +25,8 @@ class ZoomLevelDelegate;
class TestBrowserContext : public BrowserContext {
public:
TestBrowserContext();
explicit TestBrowserContext(
base::FilePath browser_context_dir_path = base::FilePath());
~TestBrowserContext() override;
// Takes ownership of the temporary directory so that it's not deleted when
......
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