summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc')
-rw-r--r--chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc539
1 files changed, 539 insertions, 0 deletions
diff --git a/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc b/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
new file mode 100644
index 00000000000..c0126266d57
--- /dev/null
+++ b/chromium/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
@@ -0,0 +1,539 @@
+// Copyright 2016 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 "chrome/browser/media/webrtc/webrtc_text_log_handler.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/cpu.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/media/webrtc_logging.mojom.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/webrtc_log.h"
+#include "content/public/common/content_features.h"
+#include "gpu/config/gpu_info.h"
+#include "media/audio/audio_manager.h"
+#include "media/webrtc/webrtc_switches.h"
+#include "net/base/ip_address.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/network_interfaces.h"
+#include "services/network/public/mojom/network_service.mojom.h"
+#include "services/service_manager/sandbox/features.h"
+#include "services/service_manager/sandbox/sandbox_type.h"
+
+#if defined(OS_LINUX)
+#include "base/linux_util.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/system/statistics_provider.h"
+#endif
+
+using base::NumberToString;
+
+namespace {
+
+void ForwardMessageViaTaskRunner(
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ base::Callback<void(const std::string&)> callback,
+ const std::string& message) {
+ task_runner->PostTask(FROM_HERE,
+ base::BindOnce(std::move(callback), message));
+}
+
+std::string Format(const std::string& message,
+ base::Time timestamp,
+ base::Time start_time) {
+ int32_t interval_ms =
+ static_cast<int32_t>((timestamp - start_time).InMilliseconds());
+ return base::StringPrintf("[%03d:%03d] %s", interval_ms / 1000,
+ interval_ms % 1000, message.c_str());
+}
+
+std::string FormatMetaDataAsLogMessage(const WebRtcLogMetaDataMap& meta_data) {
+ std::string message;
+ for (auto& kv : meta_data) {
+ message += kv.first + ": " + kv.second + '\n';
+ }
+ // Remove last '\n'.
+ if (!message.empty())
+ message.erase(message.size() - 1, 1); // TODO(terelius): Use pop_back()
+ return message;
+}
+
+// For privacy reasons when logging IP addresses. The returned "sensitive
+// string" is for release builds a string with the end stripped away. Last
+// octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
+// "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
+// not stripped.
+std::string IPAddressToSensitiveString(const net::IPAddress& address) {
+#if defined(NDEBUG)
+ std::string sensitive_address;
+ switch (address.size()) {
+ case net::IPAddress::kIPv4AddressSize: {
+ sensitive_address = address.ToString();
+ size_t find_pos = sensitive_address.rfind('.');
+ if (find_pos == std::string::npos)
+ return std::string();
+ sensitive_address.resize(find_pos);
+ sensitive_address += ".x";
+ break;
+ }
+ case net::IPAddress::kIPv6AddressSize: {
+ // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
+ // that the end has been stripped out.
+ net::IPAddressBytes stripped = address.bytes();
+ std::fill(stripped.begin() + 6, stripped.end(), 0);
+ sensitive_address = net::IPAddress(stripped).ToString();
+ break;
+ }
+ default: { break; }
+ }
+ return sensitive_address;
+#else
+ return address.ToString();
+#endif
+}
+
+} // namespace
+
+WebRtcTextLogHandler::WebRtcTextLogHandler(int render_process_id)
+ : render_process_id_(render_process_id), logging_state_(CLOSED) {}
+
+WebRtcTextLogHandler::~WebRtcTextLogHandler() {
+ // If the log isn't closed that means we haven't decremented the log count
+ // in the LogUploader.
+ DCHECK(logging_state_ == CLOSED || channel_is_closing_);
+ DCHECK(!log_buffer_);
+}
+
+WebRtcTextLogHandler::LoggingState WebRtcTextLogHandler::GetState() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return logging_state_;
+}
+
+bool WebRtcTextLogHandler::GetChannelIsClosing() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return channel_is_closing_;
+}
+
+void WebRtcTextLogHandler::SetMetaData(
+ std::unique_ptr<WebRtcLogMetaDataMap> meta_data,
+ const GenericDoneCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+
+ if (channel_is_closing_) {
+ FireGenericDoneCallback(callback, false, "The renderer is closing.");
+ return;
+ }
+
+ if (logging_state_ != CLOSED && logging_state_ != STARTED) {
+ FireGenericDoneCallback(callback, false,
+ "Meta data must be set before stop or upload.");
+ return;
+ }
+
+ if (logging_state_ == LoggingState::STARTED) {
+ std::string meta_data_message = FormatMetaDataAsLogMessage(*meta_data);
+ LogToCircularBuffer(meta_data_message);
+ }
+
+ if (!meta_data_) {
+ // If no meta data has been set previously, set it now.
+ meta_data_ = std::move(meta_data);
+ } else if (meta_data) {
+ // If there is existing meta data, update it and any new fields. The meta
+ // data is kept around to be uploaded separately from the log.
+ for (const auto& it : *meta_data)
+ (*meta_data_)[it.first] = it.second;
+ }
+
+ FireGenericDoneCallback(callback, true, "");
+}
+
+bool WebRtcTextLogHandler::StartLogging(WebRtcLogUploader* log_uploader,
+ const GenericDoneCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+
+ if (channel_is_closing_) {
+ FireGenericDoneCallback(callback, false, "The renderer is closing.");
+ return false;
+ }
+
+ if (logging_state_ != CLOSED) {
+ FireGenericDoneCallback(callback, false, "A log is already open.");
+ return false;
+ }
+
+ if (!log_uploader->ApplyForStartLogging()) {
+ FireGenericDoneCallback(callback, false,
+ "Cannot start, maybe the maximum number of "
+ "simultaneuos logs has been reached.");
+ return false;
+ }
+
+ logging_state_ = STARTING;
+
+ DCHECK(!log_buffer_);
+ log_buffer_.reset(new WebRtcLogBuffer());
+ if (!meta_data_)
+ meta_data_.reset(new WebRtcLogMetaDataMap());
+
+ content::GetNetworkService()->GetNetworkList(
+ net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ base::BindOnce(&WebRtcTextLogHandler::OnGetNetworkInterfaceList,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+ return true;
+}
+
+void WebRtcTextLogHandler::StartDone(const GenericDoneCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+
+ if (channel_is_closing_) {
+ FireGenericDoneCallback(callback, false,
+ "Failed to start log. Renderer is closing.");
+ return;
+ }
+
+ DCHECK_EQ(STARTING, logging_state_);
+
+ base::UmaHistogramSparse("WebRtcTextLogging.Start", web_app_id_);
+
+ logging_started_time_ = base::Time::Now();
+ logging_state_ = STARTED;
+ FireGenericDoneCallback(callback, true, "");
+}
+
+bool WebRtcTextLogHandler::StopLogging(const GenericDoneCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+
+ if (channel_is_closing_) {
+ FireGenericDoneCallback(callback, false,
+ "Can't stop log. Renderer is closing.");
+ return false;
+ }
+
+ if (logging_state_ != STARTED) {
+ FireGenericDoneCallback(callback, false, "Logging not started.");
+ return false;
+ }
+
+ stop_callback_ = callback;
+ logging_state_ = STOPPING;
+
+ base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&content::WebRtcLog::ClearLogMessageCallback,
+ render_process_id_));
+ return true;
+}
+
+void WebRtcTextLogHandler::StopDone() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(stop_callback_);
+
+ if (channel_is_closing_) {
+ FireGenericDoneCallback(stop_callback_, false,
+ "Failed to stop log. Renderer is closing.");
+ return;
+ }
+
+ // If we aren't in STOPPING state, then there is a bug in the caller, since
+ // it is responsible for checking the state before making the call. If we do
+ // enter here in a bad state, then we can't use the stop_callback_ or we
+ // might fire the same callback multiple times.
+ DCHECK_EQ(STOPPING, logging_state_);
+ if (logging_state_ == STOPPING) {
+ logging_started_time_ = base::Time();
+ logging_state_ = STOPPED;
+ FireGenericDoneCallback(stop_callback_, true, "");
+ stop_callback_.Reset();
+ }
+}
+
+void WebRtcTextLogHandler::ChannelClosing() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (logging_state_ == STARTING || logging_state_ == STARTED) {
+ base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&content::WebRtcLog::ClearLogMessageCallback,
+ render_process_id_));
+ }
+ channel_is_closing_ = true;
+}
+
+void WebRtcTextLogHandler::DiscardLog() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(logging_state_ == STOPPED ||
+ (channel_is_closing_ && logging_state_ != CLOSED));
+
+ base::UmaHistogramSparse("WebRtcTextLogging.Discard", web_app_id_);
+
+ log_buffer_.reset();
+ meta_data_.reset();
+ logging_state_ = LoggingState::CLOSED;
+}
+
+void WebRtcTextLogHandler::ReleaseLog(
+ std::unique_ptr<WebRtcLogBuffer>* log_buffer,
+ std::unique_ptr<WebRtcLogMetaDataMap>* meta_data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(logging_state_ == STOPPED ||
+ (channel_is_closing_ && logging_state_ != CLOSED));
+ DCHECK(log_buffer_);
+ DCHECK(meta_data_);
+
+ // Checking log_buffer_ here due to seeing some crashes out in the wild.
+ // See crbug/699960 for more details.
+ // TODO(crbug/807547): Remove if condition.
+ if (log_buffer_) {
+ log_buffer_->SetComplete();
+ *log_buffer = std::move(log_buffer_);
+ }
+
+ if (meta_data_)
+ *meta_data = std::move(meta_data_);
+
+ logging_state_ = LoggingState::CLOSED;
+}
+
+void WebRtcTextLogHandler::LogToCircularBuffer(const std::string& message) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_NE(logging_state_, CLOSED);
+ if (log_buffer_) {
+ log_buffer_->Log(message);
+ }
+}
+
+void WebRtcTextLogHandler::LogMessage(const std::string& message) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (logging_state_ == STARTED && !channel_is_closing_) {
+ LogToCircularBuffer(
+ Format(message, base::Time::Now(), logging_started_time_));
+ }
+}
+
+void WebRtcTextLogHandler::LogWebRtcLoggingMessage(
+ const chrome::mojom::WebRtcLoggingMessage* message) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ LogToCircularBuffer(
+ Format(message->data, message->timestamp, logging_started_time_));
+}
+
+bool WebRtcTextLogHandler::ExpectLoggingStateStopped(
+ const GenericDoneCallback& callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (logging_state_ != STOPPED) {
+ FireGenericDoneCallback(callback, false,
+ "Logging not stopped or no log open.");
+ return false;
+ }
+ return true;
+}
+
+void WebRtcTextLogHandler::FireGenericDoneCallback(
+ const GenericDoneCallback& callback,
+ bool success,
+ const std::string& error_message) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+
+ if (error_message.empty()) {
+ DCHECK(success);
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, success, error_message));
+ return;
+ }
+
+ DCHECK(!success);
+
+ // Add current logging state to error message.
+ auto state_string = [&] {
+ switch (logging_state_) {
+ case LoggingState::CLOSED:
+ return "closed";
+ case LoggingState::STARTING:
+ return "starting";
+ case LoggingState::STARTED:
+ return "started";
+ case LoggingState::STOPPING:
+ return "stopping";
+ case LoggingState::STOPPED:
+ return "stopped";
+ }
+ NOTREACHED();
+ return "";
+ };
+
+ std::string error_message_with_state =
+ base::StrCat({error_message, ". State=", state_string(), ". Channel is ",
+ channel_is_closing_ ? "" : "not ", "closing."});
+
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, success, error_message_with_state));
+}
+
+void WebRtcTextLogHandler::SetWebAppId(int web_app_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ web_app_id_ = web_app_id;
+}
+
+void WebRtcTextLogHandler::OnGetNetworkInterfaceList(
+ const GenericDoneCallback& callback,
+ const base::Optional<net::NetworkInterfaceList>& networks) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (logging_state_ != STARTING || channel_is_closing_) {
+ FireGenericDoneCallback(callback, false, "Logging cancelled.");
+ return;
+ }
+
+ // Log start time (current time). We don't use base/i18n/time_formatting.h
+ // here because we don't want the format of the current locale.
+ base::Time::Exploded now = {0};
+ base::Time::Now().LocalExplode(&now);
+ LogToCircularBuffer(base::StringPrintf("Start %d-%02d-%02d %02d:%02d:%02d",
+ now.year, now.month, now.day_of_month,
+ now.hour, now.minute, now.second));
+
+ // Write metadata if received before logging started.
+ if (meta_data_ && !meta_data_->empty()) {
+ std::string info = FormatMetaDataAsLogMessage(*meta_data_);
+ LogToCircularBuffer(info);
+ }
+
+ // Chrome version
+ LogToCircularBuffer("Chrome version: " + version_info::GetVersionNumber() +
+ " " + chrome::GetChannelName());
+
+ // OS
+ LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
+ base::SysInfo::OperatingSystemVersion() + " " +
+ base::SysInfo::OperatingSystemArchitecture());
+#if defined(OS_LINUX)
+ LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
+#endif
+
+ // CPU
+ base::CPU cpu;
+ LogToCircularBuffer(
+ "Cpu: " + NumberToString(cpu.family()) + "." +
+ NumberToString(cpu.model()) + "." + NumberToString(cpu.stepping()) +
+ ", x" + NumberToString(base::SysInfo::NumberOfProcessors()) + ", " +
+ NumberToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
+ LogToCircularBuffer("Cpu brand: " + cpu.cpu_brand());
+
+ // Computer model
+ std::string computer_model = "Not available";
+#if defined(OS_MACOSX)
+ computer_model = base::mac::GetModelIdentifier();
+#elif defined(OS_CHROMEOS)
+ chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
+ chromeos::system::kHardwareClassKey, &computer_model);
+#endif
+ LogToCircularBuffer("Computer model: " + computer_model);
+
+ // GPU
+ gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
+ const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu();
+ LogToCircularBuffer(
+ "Gpu: machine-model-name=" + gpu_info.machine_model_name +
+ ", machine-model-version=" + gpu_info.machine_model_version +
+ ", vendor-id=" + base::NumberToString(active_gpu.vendor_id) +
+ ", device-id=" + base::NumberToString(active_gpu.device_id) +
+ ", driver-vendor=" + active_gpu.driver_vendor +
+ ", driver-version=" + active_gpu.driver_version);
+ LogToCircularBuffer("OpenGL: gl-vendor=" + gpu_info.gl_vendor +
+ ", gl-renderer=" + gpu_info.gl_renderer +
+ ", gl-version=" + gpu_info.gl_version);
+
+ // AudioService features
+ auto enabled_or_disabled_feature_string = [](auto& feature) {
+ return base::FeatureList::IsEnabled(feature) ? "enabled" : "disabled";
+ };
+ auto enabled_or_disabled_bool_string = [](bool value) {
+ return value ? "enabled" : "disabled";
+ };
+ LogToCircularBuffer(base::StrCat(
+ {"AudioService: AudioStreams=",
+ enabled_or_disabled_feature_string(features::kAudioServiceAudioStreams),
+ ", OutOfProcess=",
+ enabled_or_disabled_feature_string(features::kAudioServiceOutOfProcess),
+ ", LaunchOnStartup=",
+ enabled_or_disabled_feature_string(
+ features::kAudioServiceLaunchOnStartup),
+ ", Sandbox=",
+ enabled_or_disabled_bool_string(
+ service_manager::IsAudioSandboxEnabled()),
+ ", ApmInAudioService=",
+ enabled_or_disabled_bool_string(
+ media::IsWebRtcApmInAudioServiceEnabled())}));
+
+ // Audio manager
+ // On some platforms, this can vary depending on build flags and failure
+ // fallbacks. On Linux for example, we fallback on ALSA if PulseAudio fails to
+ // initialize. TODO(http://crbug/843202): access AudioManager name via Audio
+ // service interface.
+ media::AudioManager* audio_manager = media::AudioManager::Get();
+ LogToCircularBuffer(base::StringPrintf(
+ "Audio manager: %s",
+ audio_manager ? audio_manager->GetName() : "Out of process"));
+
+ // Network interfaces
+ const net::NetworkInterfaceList empty_network_list;
+ const net::NetworkInterfaceList& network_list =
+ networks.has_value() ? *networks : empty_network_list;
+ LogToCircularBuffer("Discovered " +
+ base::NumberToString(network_list.size()) +
+ " network interfaces:");
+ for (const auto& network : network_list) {
+ LogToCircularBuffer(
+ "Name: " + network.friendly_name + ", Address: " +
+ IPAddressToSensitiveString(network.address) + ", Type: " +
+ net::NetworkChangeNotifier::ConnectionTypeToString(network.type));
+ }
+
+ StartDone(callback);
+
+ // After the above data has been written, tell the browser to enable logging.
+ // TODO(terelius): Once we have moved over to Mojo, we could tell the
+ // renderer to start logging here, but for the time being
+ // WebRtcLoggingHandlerHost::StartLogging will be responsible for sending
+ // that IPC message.
+
+ // TODO(darin): Change SetLogMessageCallback to run on the UI thread.
+
+ auto log_message_callback = base::Bind(
+ &ForwardMessageViaTaskRunner, base::SequencedTaskRunnerHandle::Get(),
+ base::Bind(&WebRtcTextLogHandler::LogMessage,
+ weak_factory_.GetWeakPtr()));
+ base::PostTask(
+ FROM_HERE, {content::BrowserThread::IO},
+ base::BindOnce(&content::WebRtcLog::SetLogMessageCallback,
+ render_process_id_, std::move(log_message_callback)));
+}