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))
......
// 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
......
This diff is collapsed.
This diff is collapsed.
......@@ -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