blob: 08d714fbc7b1303d9fe195bb7a841cc30eff7b3d [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/webrtc/webrtc_event_log_manager_local.h"
#include "base/files/file_util.h"
#include "base/i18n/time_formatting.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#if BUILDFLAG(IS_WIN)
#define NumberToStringType base::NumberToString16
#else
#define NumberToStringType base::NumberToString
#endif
namespace webrtc_event_logging {
#if BUILDFLAG(IS_ANDROID)
const size_t kDefaultMaxLocalEventLogFileSizeBytes = 10'000'000;
const size_t kMaxNumberLocalWebRtcEventLogFiles = 3;
const size_t kDefaultMaxLocalDataChannelFileSizeBytes = 10'000'000;
const size_t kMaxNumberLocalWebRtcDataChannelLogFiles = 3;
#else
const size_t kDefaultMaxLocalEventLogFileSizeBytes = 60'000'000;
const size_t kMaxNumberLocalWebRtcEventLogFiles = 5;
const size_t kDefaultMaxLocalDataChannelFileSizeBytes = 100'000'000;
const size_t kMaxNumberLocalWebRtcDataChannelLogFiles = 5;
#endif
struct WebRtcLocalEventLogManager::LogFiles {
std::unique_ptr<LogFileWriter> event_log;
std::unique_ptr<LogFileWriter> data_channel_log;
};
WebRtcLocalEventLogManager::WebRtcLocalEventLogManager(
WebRtcLocalEventLogsObserver* observer)
: observer_(observer), clock_for_testing_(nullptr) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DETACH_FROM_SEQUENCE(io_task_sequence_checker_);
}
WebRtcLocalEventLogManager::~WebRtcLocalEventLogManager() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
bool WebRtcLocalEventLogManager::OnPeerConnectionAdded(
const PeerConnectionKey& key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
const auto insertion_result = log_files_.try_emplace(key);
if (!insertion_result.second) {
return false; // Attempt to re-add the PeerConnection.
}
MaybeStartEventLogFile(insertion_result.first);
MaybeStartDataChannelLogFile(insertion_result.first);
return true;
}
bool WebRtcLocalEventLogManager::OnPeerConnectionRemoved(
const PeerConnectionKey& key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
auto it = log_files_.find(key);
if (it == log_files_.end()) {
return false;
}
StopEventLogFileIfStarted(it);
StopDataChannelLogFileIfStarted(it);
log_files_.erase(it);
return true;
}
bool WebRtcLocalEventLogManager::EnableEventLogging(
const base::FilePath& base_path,
size_t max_file_size_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (event_log_state_.has_value()) {
return false;
}
std::optional<size_t> max_size_bytes =
(max_file_size_bytes == kWebRtcEventLogManagerUnlimitedFileSize)
? std::optional<size_t>()
: std::optional<size_t>(max_file_size_bytes);
event_log_state_.emplace(
base_path, max_size_bytes,
/*max_logs_active=*/kMaxNumberLocalWebRtcEventLogFiles,
/*max_logs_created=*/std::nullopt);
for (auto it = log_files_.begin(); it != log_files_.end(); ++it) {
MaybeStartEventLogFile(it);
}
return true;
}
bool WebRtcLocalEventLogManager::DisableEventLogging() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (!event_log_state_.has_value()) {
return false;
}
for (auto it = log_files_.begin(); it != log_files_.end(); ++it) {
StopEventLogFileIfStarted(it);
}
CHECK_EQ(event_log_state_->logs_active, 0U);
event_log_state_.reset();
return true;
}
bool WebRtcLocalEventLogManager::EventLogWrite(const PeerConnectionKey& key,
const std::string& message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
auto it = log_files_.find(key);
if (it == log_files_.end() || it->second.event_log == nullptr) {
return false;
}
const bool write_successful = it->second.event_log->Write(message);
if (!write_successful || it->second.event_log->MaxSizeReached()) {
StopEventLogFileIfStarted(it);
}
return write_successful;
}
bool WebRtcLocalEventLogManager::EnableDataChannelLogging(
const base::FilePath& base_path,
size_t max_file_size_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (data_channel_log_state_.has_value()) {
return false;
}
std::optional<size_t> max_size_bytes =
(max_file_size_bytes == kWebRtcEventLogManagerUnlimitedFileSize)
? std::optional<size_t>()
: std::optional<size_t>(max_file_size_bytes);
data_channel_log_state_.emplace(
base_path, max_size_bytes,
/*max_logs_active=*/std::nullopt,
/*max_logs_created=*/kMaxNumberLocalWebRtcDataChannelLogFiles);
for (auto it = log_files_.begin(); it != log_files_.end(); ++it) {
MaybeStartDataChannelLogFile(it);
}
return true;
}
bool WebRtcLocalEventLogManager::DisableDataChannelLogging() {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
if (!data_channel_log_state_.has_value()) {
return false;
}
for (auto it = log_files_.begin(); it != log_files_.end(); ++it) {
StopDataChannelLogFileIfStarted(it);
}
CHECK_EQ(data_channel_log_state_->logs_active, 0U);
data_channel_log_state_.reset();
return true;
}
bool WebRtcLocalEventLogManager::DataChannelLogWrite(
const PeerConnectionKey& key,
const std::string& message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
auto it = log_files_.find(key);
if (it == log_files_.end() || it->second.data_channel_log == nullptr) {
return false;
}
const bool write_successful = it->second.data_channel_log->Write(message);
if (!write_successful || it->second.data_channel_log->MaxSizeReached()) {
StopDataChannelLogFileIfStarted(it);
}
return write_successful;
}
void WebRtcLocalEventLogManager::RenderProcessHostExitedDestroyed(
int render_process_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// Remove all of the peer connections associated with this render process.
for (auto it = log_files_.begin(); it != log_files_.end();) {
if (it->first.render_process_id == render_process_id) {
StopEventLogFileIfStarted(it);
StopDataChannelLogFileIfStarted(it);
it = log_files_.erase(it);
} else {
++it;
}
}
}
void WebRtcLocalEventLogManager::SetClockForTesting(base::Clock* clock) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
clock_for_testing_ = clock;
}
void WebRtcLocalEventLogManager::MaybeStartEventLogFile(
LogFilesMap::iterator log_it) {
if (!event_log_state_.has_value()) {
return;
}
LogTypeState& state = *event_log_state_;
if (state.logs_active >= state.max_logs_active ||
log_it->second.event_log != nullptr) {
return;
}
std::unique_ptr<LogFileWriter> log_file =
CreateLogFile(log_it->first, state.base_path, state.max_size_bytes);
if (!log_file) {
return;
}
log_it->second.event_log = std::move(log_file);
++state.logs_active;
if (observer_) {
observer_->OnLocalEventLogStarted(log_it->first,
log_it->second.event_log->path());
}
}
void WebRtcLocalEventLogManager::StopEventLogFileIfStarted(
LogFilesMap::iterator log_it) {
if (!log_it->second.event_log) {
return;
}
log_it->second.event_log.reset();
--event_log_state_->logs_active;
if (observer_) {
observer_->OnLocalEventLogStopped(log_it->first);
}
}
void WebRtcLocalEventLogManager::MaybeStartDataChannelLogFile(
LogFilesMap::iterator log_it) {
if (!data_channel_log_state_.has_value()) {
return;
}
LogTypeState& state = *data_channel_log_state_;
if (state.logs_created >= state.max_logs_created ||
log_it->second.data_channel_log != nullptr) {
return;
}
std::unique_ptr<LogFileWriter> log_file =
CreateLogFile(log_it->first, state.base_path, state.max_size_bytes);
if (!log_file) {
return;
}
log_it->second.data_channel_log = std::move(log_file);
++state.logs_created;
if (observer_) {
observer_->OnLocalDataChannelLogStarted(
log_it->first, log_it->second.data_channel_log->path());
}
}
void WebRtcLocalEventLogManager::StopDataChannelLogFileIfStarted(
LogFilesMap::iterator log_it) {
if (!log_it->second.data_channel_log) {
return;
}
log_it->second.data_channel_log.reset();
if (observer_) {
observer_->OnLocalDataChannelLogStopped(log_it->first);
}
}
std::unique_ptr<LogFileWriter> WebRtcLocalEventLogManager::CreateLogFile(
const PeerConnectionKey& key,
const base::FilePath& base_path,
std::optional<size_t> max_log_size_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// Add some information to the name given by the caller.
base::FilePath file_path = GetFilePath(base_path, key);
CHECK(!file_path.empty()) << "Couldn't set path for local log file.";
// In the unlikely case that this filename is already taken, find a unique
// number to append to the filename, if possible.
file_path = base::GetUniquePath(file_path);
if (file_path.empty()) {
return nullptr; // No available file path was found.
}
auto log_file =
log_file_writer_factory_.Create(file_path, max_log_size_bytes);
if (!log_file) {
LOG(WARNING) << "Couldn't open " << file_path << " for logging.";
return nullptr;
}
return log_file;
}
base::FilePath WebRtcLocalEventLogManager::GetFilePath(
const base::FilePath& base_path,
const PeerConnectionKey& key) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_task_sequence_checker_);
// [user_defined]_[date]_[time]_[render_process_id]_[lid].[extension]
const base::Time now =
clock_for_testing_ ? clock_for_testing_->Now() : base::Time::Now();
base::Time::Exploded exploded;
now.LocalExplode(&exploded);
const std::string timestamp =
base::UnlocalizedTimeFormatWithPattern(now, "yyyyMMdd_HHmm");
return base_path.InsertBeforeExtension(FILE_PATH_LITERAL("_"))
.AddExtension(log_file_writer_factory_.Extension())
.InsertBeforeExtensionASCII(base::StringPrintf(
"%s_%d_%d", timestamp.c_str(), key.render_process_id, key.lid));
}
} // namespace webrtc_event_logging