Commit c6cc2c75 authored by Elad Alon's avatar Elad Alon Committed by Commit Bot

Refactor WebRtcEventLogManager

WebRtcEventLogManager can now handle locally-bound WebRTC event logs.
Once we add handling of remote-bound logs, it would be good to have
clear separation of the logic that handles locally-bound and remote-
bound logs. This CL does just that.

Bug: 775415
Change-Id: If7113d2fdbb7f8f240cceb4c309f6032da86341f
Reviewed-on: https://chromium-review.googlesource.com/822938Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Commit-Queue: Elad Alon <eladalon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#524539}
parent 762d8538
...@@ -1788,6 +1788,7 @@ jumbo_source_set("browser") { ...@@ -1788,6 +1788,7 @@ jumbo_source_set("browser") {
"renderer_host/p2p/socket_host_udp.h", "renderer_host/p2p/socket_host_udp.h",
"webrtc/webrtc_event_log_manager.cc", "webrtc/webrtc_event_log_manager.cc",
"webrtc/webrtc_event_log_manager.h", "webrtc/webrtc_event_log_manager.h",
"webrtc/webrtc_event_log_manager_common.h",
"webrtc/webrtc_internals.cc", "webrtc/webrtc_internals.cc",
"webrtc/webrtc_internals.h", "webrtc/webrtc_internals.h",
"webrtc/webrtc_internals_message_handler.cc", "webrtc/webrtc_internals_message_handler.cc",
...@@ -1795,6 +1796,8 @@ jumbo_source_set("browser") { ...@@ -1795,6 +1796,8 @@ jumbo_source_set("browser") {
"webrtc/webrtc_internals_ui.cc", "webrtc/webrtc_internals_ui.cc",
"webrtc/webrtc_internals_ui.h", "webrtc/webrtc_internals_ui.h",
"webrtc/webrtc_internals_ui_observer.h", "webrtc/webrtc_internals_ui_observer.h",
"webrtc/webrtc_local_event_log_manager.cc",
"webrtc/webrtc_local_event_log_manager.h",
] ]
deps += [ deps += [
......
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEBRTC_WEBRTC_RTC_EVENT_LOG_MANAGER_COMMON_H_
#define CONTENT_BROWSER_WEBRTC_WEBRTC_RTC_EVENT_LOG_MANAGER_COMMON_H_
#include <tuple>
#include "base/files/file_path.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
namespace content {
CONTENT_EXPORT extern const size_t kWebRtcEventLogManagerUnlimitedFileSize;
CONTENT_EXPORT extern const size_t kDefaultMaxLocalLogFileSizeBytes;
CONTENT_EXPORT extern const size_t kMaxNumberLocalWebRtcEventLogFiles;
// 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 {
constexpr WebRtcEventLogPeerConnectionKey(int render_process_id, int lid)
: render_process_id(render_process_id), lid(lid) {}
bool operator==(const WebRtcEventLogPeerConnectionKey& other) const {
return std::tie(render_process_id, lid) ==
std::tie(other.render_process_id, other.lid);
}
bool operator<(const WebRtcEventLogPeerConnectionKey& other) const {
return std::tie(render_process_id, lid) <
std::tie(other.render_process_id, other.lid);
}
int render_process_id;
int lid; // Renderer-local PeerConnection ID.
};
// An observer for notifications of local log files being started/stopped, and
// the paths which will be used for these logs.
class WebRtcLocalEventLogsObserver {
public:
virtual ~WebRtcLocalEventLogsObserver() = default;
virtual void OnLocalLogStarted(WebRtcEventLogPeerConnectionKey pc_key,
base::FilePath file_path) = 0;
virtual void OnLocalLogStopped(WebRtcEventLogPeerConnectionKey pc_key) = 0;
};
} // namespace content
#endif // CONTENT_BROWSER_WEBRTC_WEBRTC_RTC_EVENT_LOG_MANAGER_COMMON_H_
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "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"
#include "build/build_config.h"
#if defined(OS_WIN)
#define IntToStringType base::IntToString16
#else
#define IntToStringType base::IntToString
#endif
namespace content {
#if defined(OS_ANDROID)
const size_t kDefaultMaxLocalLogFileSizeBytes = 10000000;
const size_t kMaxNumberLocalWebRtcEventLogFiles = 3;
#else
const size_t kDefaultMaxLocalLogFileSizeBytes = 60000000;
const size_t kMaxNumberLocalWebRtcEventLogFiles = 5;
#endif
WebRtcLocalEventLogManager::WebRtcLocalEventLogManager(
WebRtcLocalEventLogsObserver* observer)
: observer_(observer),
clock_for_testing_(nullptr),
max_log_file_size_bytes_(kDefaultMaxLocalLogFileSizeBytes) {}
WebRtcLocalEventLogManager::~WebRtcLocalEventLogManager() {}
bool WebRtcLocalEventLogManager::PeerConnectionAdded(int render_process_id,
int lid) {
const auto result = active_peer_connections_.emplace(render_process_id, lid);
if (!result.second) { // PeerConnection already registered.
return false;
}
if (!base_path_.empty() &&
log_files_.size() < kMaxNumberLocalWebRtcEventLogFiles) {
// Note that success/failure of starting the local log file is unrelated
// to the success/failure of PeerConnectionAdded().
StartLogFile(render_process_id, lid);
}
return true;
}
bool WebRtcLocalEventLogManager::PeerConnectionRemoved(int render_process_id,
int lid) {
const PeerConnectionKey key = PeerConnectionKey(render_process_id, lid);
auto peer_connection = active_peer_connections_.find(key);
if (peer_connection == active_peer_connections_.end()) {
DCHECK(log_files_.find(key) == log_files_.end());
return false;
}
auto local_log = log_files_.find(key);
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);
}
active_peer_connections_.erase(peer_connection);
return true;
}
bool WebRtcLocalEventLogManager::EnableLogging(base::FilePath base_path,
size_t max_file_size_bytes) {
if (!base_path_.empty()) {
return false;
}
DCHECK_EQ(log_files_.size(), 0u);
base_path_ = base_path;
max_log_file_size_bytes_ = max_file_size_bytes;
for (const PeerConnectionKey& peer_connection : active_peer_connections_) {
if (log_files_.size() >= kMaxNumberLocalWebRtcEventLogFiles) {
break;
}
StartLogFile(peer_connection.render_process_id, peer_connection.lid);
}
return true;
}
bool WebRtcLocalEventLogManager::DisableLogging() {
if (base_path_.empty()) {
return false;
}
for (auto local_log = log_files_.begin(); local_log != log_files_.end();) {
local_log = CloseLogFile(local_log);
}
base_path_.clear(); // Marks local-logging as disabled.
max_log_file_size_bytes_ = kDefaultMaxLocalLogFileSizeBytes;
return true;
}
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()));
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());
}
void WebRtcLocalEventLogManager::InjectClockForTesting(base::Clock* clock) {
clock_for_testing_ = clock;
}
void WebRtcLocalEventLogManager::StartLogFile(int render_process_id, int lid) {
// Add some information to the name given by the caller.
base::FilePath file_path = GetFilePath(base_path_, render_process_id, lid);
CHECK(!file_path.empty()) << "Couldn't set path for local WebRTC log file.";
// In the unlikely case that this filename is already taken, find a unique
// number to append to the filename, if possible.
int unique_number =
base::GetUniquePathNumber(file_path, base::FilePath::StringType());
if (unique_number < 0) {
return; // No available file path was found.
} else if (unique_number != 0) {
// The filename is taken, but a unique number was found.
// TODO(eladalon): Fix the way the unique number is used.
// https://crbug.com/785333
file_path = file_path.InsertBeforeExtension(FILE_PATH_LITERAL(" (") +
IntToStringType(unique_number) +
FILE_PATH_LITERAL(")"));
}
// 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 local WebRTC event log file.";
return;
}
const PeerConnectionKey key(render_process_id, 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_));
// The observer needs to be able to run on any TaskQueue.
if (observer_) {
observer_->OnLocalLogStarted(key, file_path);
}
}
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;
it->second.file.Flush();
it = log_files_.erase(it); // file.Close() called by destructor.
if (observer_) {
observer_->OnLocalLogStopped(peer_connection);
}
return it;
}
base::FilePath WebRtcLocalEventLogManager::GetFilePath(
const base::FilePath& base_path,
int render_process_id,
int lid) {
base::Time::Exploded now;
if (clock_for_testing_) {
clock_for_testing_->Now().LocalExplode(&now);
} else {
base::Time::Now().LocalExplode(&now);
}
// [user_defined]_[date]_[time]_[pid]_[lid].log
char stamp[100];
int written =
base::snprintf(stamp, arraysize(stamp), "%04d%02d%02d_%02d%02d_%d_%d",
now.year, now.month, now.day_of_month, now.hour,
now.minute, render_process_id, lid);
CHECK_GT(written, 0);
CHECK_LT(static_cast<size_t>(written), arraysize(stamp));
return base_path.InsertBeforeExtension(FILE_PATH_LITERAL("_"))
.InsertBeforeExtensionASCII(base::StringPiece(stamp))
.AddExtension(FILE_PATH_LITERAL("log"));
}
} // namespace content
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEBRTC_WEBRTC_LOCAL_EVENT_LOG_MANAGER_H_
#define CONTENT_BROWSER_WEBRTC_WEBRTC_LOCAL_EVENT_LOG_MANAGER_H_
#include <map>
#include <set>
#include <string>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/time/clock.h"
#include "content/browser/webrtc/webrtc_event_log_manager_common.h"
namespace content {
class WebRtcLocalEventLogManager {
public:
explicit WebRtcLocalEventLogManager(WebRtcLocalEventLogsObserver* observer);
~WebRtcLocalEventLogManager();
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 DisableLogging();
bool EventLogWrite(int render_process_id, int lid, const std::string& output);
// This function is public, but this entire class is a protected
// implementation detail of WebRtcEventLogManager, which hides this
// function from everybody except its own unit-tests.
void InjectClockForTesting(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.
void StartLogFile(int render_process_id, int lid);
void StopLogFile(int render_process_id, int lid);
LogFilesMap::iterator CloseLogFile(LogFilesMap::iterator it);
// Derives the name of a local log file. The format is:
// [user_defined]_[date]_[time]_[pid]_[lid].log
base::FilePath GetFilePath(const base::FilePath& base_path,
int render_process_id,
int lid);
// Observer which will be informed whenever a local log file is started or
// stopped. Through this, the owning WebRtcEventLogManager can be informed,
// and decide whether it wants to turn notifications from WebRTC on/off.
WebRtcLocalEventLogsObserver* const observer_;
// For unit tests only, and specifically for unit tests that verify the
// filename format (derived from the current time as well as the renderer PID
// and PeerConnection local ID), we want to make sure that the time and date
// cannot change between the time the clock is read by the unit under test
// (namely WebRtcEventLogManager) and the time it's read by the test.
base::Clock* clock_for_testing_;
// 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_;
// Local log files, stored at the behest of the user (via WebRTCInternals).
LogFilesMap log_files_;
// If |base_path_| is empty, local logging is disabled.
// If nonempty, local logging is enabled, and all local logs will be saved
// to this directory.
base::FilePath base_path_;
// The maximum size for local logs, in bytes. Note that
// kWebRtcEventLogManagerUnlimitedFileSize is a sentinel value (with a
// self-explanatory name).
size_t max_log_file_size_bytes_;
DISALLOW_COPY_AND_ASSIGN(WebRtcLocalEventLogManager);
};
} // namespace content
#endif // CONTENT_BROWSER_WEBRTC_WEBRTC_LOCAL_EVENT_LOG_MANAGER_H_
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