Commit 9d19a126 authored by Lambros Lambrou's avatar Lambros Lambrou Committed by Commit Bot

[remoting host] Add RTC event log recording.

This records RTC event logs for the duration of each session. This data
is kept in a memory buffer, and limited in size to avoid the buffer
growing indefinitely.

Followup CLs will add the ability to send the recorded log to the
client.

Bug: 1122798
Change-Id: If32a1ea65d93a53ee5cf6e74cd7dbea151fb1214
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380961
Commit-Queue: Lambros Lambrou <lambroslambrou@chromium.org>
Reviewed-by: default avatarJamie Walch <jamiewalch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804397}
parent c6935ead
...@@ -203,6 +203,8 @@ static_library("protocol") { ...@@ -203,6 +203,8 @@ static_library("protocol") {
"webrtc_data_stream_adapter.h", "webrtc_data_stream_adapter.h",
"webrtc_dummy_video_encoder.cc", "webrtc_dummy_video_encoder.cc",
"webrtc_dummy_video_encoder.h", "webrtc_dummy_video_encoder.h",
"webrtc_event_log_data.cc",
"webrtc_event_log_data.h",
"webrtc_transport.cc", "webrtc_transport.cc",
"webrtc_transport.h", "webrtc_transport.h",
"webrtc_video_renderer_adapter.cc", "webrtc_video_renderer_adapter.cc",
...@@ -356,6 +358,7 @@ source_set("unit_tests") { ...@@ -356,6 +358,7 @@ source_set("unit_tests") {
"third_party_authenticator_unittest.cc", "third_party_authenticator_unittest.cc",
"v2_authenticator_unittest.cc", "v2_authenticator_unittest.cc",
"validating_authenticator_unittest.cc", "validating_authenticator_unittest.cc",
"webrtc_event_log_data_unittest.cc",
"webrtc_transport_unittest.cc", "webrtc_transport_unittest.cc",
] ]
......
// Copyright 2020 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 "remoting/protocol/webrtc_event_log_data.h"
#include <utility>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace remoting {
namespace protocol {
WebrtcEventLogData::WebrtcEventLogData() {
// See the caveat for base::circular_deque::reserve(). Calling reserve() is
// OK here, since items are never removed until the list reaches its maximum
// allowed size.
sections_.reserve(max_sections_);
}
WebrtcEventLogData::~WebrtcEventLogData() = default;
void WebrtcEventLogData::SetMaxSectionSizeForTest(int max_section_size) {
max_section_size_ = max_section_size;
}
void WebrtcEventLogData::SetMaxSectionsForTest(int max_sections) {
max_sections_ = max_sections;
sections_.reserve(max_sections_);
}
base::circular_deque<WebrtcEventLogData::LogSection>
WebrtcEventLogData::TakeLogData() {
auto result = std::move(sections_);
// The |sections_| container is still valid but unspecified. Call Clear() to
// be certain it is empty (and the correct capacity is reserved).
Clear();
return result;
}
void WebrtcEventLogData::Write(const std::string& log_event) {
if (base::checked_cast<int>(log_event.size()) > max_section_size_) {
LOG(WARNING) << "Oversized RTC log event: size = " << log_event.size();
}
if (NeedNewSection(log_event)) {
CreateNewSection();
}
// Append the log_event to the end of the latest section.
auto& section = sections_.back();
section.insert(section.end(), log_event.begin(), log_event.end());
}
void WebrtcEventLogData::Clear() {
sections_.clear();
sections_.reserve(max_sections_);
}
bool WebrtcEventLogData::NeedNewSection(const std::string& log_event) const {
if (sections_.empty())
return true;
// The event log entries are packet headers generated by WebRTC, and it is
// assumed that the sizes are small enough to prevent integer overflow.
return base::checked_cast<int>(sections_.back().size() + log_event.size()) >
max_section_size_;
}
void WebrtcEventLogData::CreateNewSection() {
if (static_cast<int>(sections_.size()) >= max_sections_) {
// Discard oldest section to make room.
sections_.pop_front();
}
sections_.emplace_back();
sections_.back().reserve(max_section_size_);
}
} // namespace protocol
} // namespace remoting
// Copyright 2020 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 REMOTING_PROTOCOL_WEBRTC_EVENT_LOG_DATA_H_
#define REMOTING_PROTOCOL_WEBRTC_EVENT_LOG_DATA_H_
#include <cstdint>
#include <vector>
#include "base/containers/circular_deque.h"
namespace remoting {
namespace protocol {
// A data store which records the most recent RTC event log data. This is
// written to by an RTCEventLogOutput instance, which is owned by the
// PeerConnection (but the data itself is owned by the caller). In order to
// keep the most recent events (but keep the amount of memory-allocations
// reasonable), the data is maintained as a circular list of sections.
class WebrtcEventLogData {
public:
using LogSection = std::vector<std::uint8_t>;
WebrtcEventLogData();
~WebrtcEventLogData();
WebrtcEventLogData(const WebrtcEventLogData&) = delete;
WebrtcEventLogData& operator=(const WebrtcEventLogData&) = delete;
// Allows tests to lower the storage limits.
void SetMaxSectionSizeForTest(int max_section_size);
void SetMaxSectionsForTest(int max_sections);
// Transfers the current log data to the caller and clears the data. This can
// safely be used without stopping the event logging.
base::circular_deque<LogSection> TakeLogData();
// Adds a new log event. This may discard old events, and may invalidate
// iterators even if nothing is discarded. Note that the log_event may
// theoretically exceed the maximum size of a section (though this should
// never occur because these are packet-headers which are small, no bigger
// than RTCP packets). If that ever happens, the log_event will be stored in
// a new section anyway - the buffer's reserved capacity may be exceeded and
// re-allocation may occur.
void Write(const std::string& log_event);
// Removes all event data, so the instance can be reused.
void Clear();
private:
// Returns true if a new section must be created to store the event.
bool NeedNewSection(const std::string& log_event) const;
// Appends a new section of zero size to the end of the list, removing the
// oldest one if necessary. On return, the section at the end (the list's
// "back") will be empty, ready to accept the new data.
void CreateNewSection();
base::circular_deque<LogSection> sections_;
// Value chosen to keep the memory-usage within reasonable limits, but also
// allow for recording "most" sessions entirely.
const int kMaxSections = 1000;
int max_sections_ = kMaxSections;
// A larger value will reduce memory-allocations at the cost of discarding a
// larger chunk of the event log.
const int kMaxSectionSize = 102400; // 100K
int max_section_size_ = kMaxSectionSize;
};
} // namespace protocol
} // namespace remoting
#endif // REMOTING_PROTOCOL_WEBRTC_EVENT_LOG_DATA_H_
// Copyright 2020 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 "remoting/protocol/webrtc_event_log_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
namespace protocol {
namespace {
// Converts a log section to a std::string for convenient testing.
std::string ToString(const WebrtcEventLogData::LogSection& section) {
return std::string(section.begin(), section.end());
}
} // namespace
TEST(WebrtcEventLogDataTest, WriteSingleEntry_IsStored) {
WebrtcEventLogData event_log;
event_log.Write("abc");
auto data = event_log.TakeLogData();
ASSERT_EQ(data.size(), 1U);
EXPECT_EQ(ToString(data[0]), "abc");
}
TEST(WebrtcEventLogDataTest, WriteBeyondSection_OverflowsToNewSection) {
WebrtcEventLogData event_log;
event_log.SetMaxSectionSizeForTest(5);
event_log.Write("aa");
event_log.Write("bb");
event_log.Write("cc");
event_log.Write("dd");
event_log.Write("ee");
auto data = event_log.TakeLogData();
ASSERT_EQ(data.size(), 3U);
EXPECT_EQ(ToString(data[0]), "aabb");
EXPECT_EQ(ToString(data[1]), "ccdd");
EXPECT_EQ(ToString(data[2]), "ee");
}
TEST(WebrtcEventLogDataTest, WriteTooMuchData_OldestSectionDeleted) {
WebrtcEventLogData event_log;
event_log.SetMaxSectionSizeForTest(5);
event_log.SetMaxSectionsForTest(2);
event_log.Write("aa");
event_log.Write("bb");
event_log.Write("cc");
event_log.Write("dd");
event_log.Write("ee");
auto data = event_log.TakeLogData();
ASSERT_EQ(data.size(), 2U);
EXPECT_EQ(ToString(data[0]), "ccdd");
EXPECT_EQ(ToString(data[1]), "ee");
}
TEST(WebrtcEventLogDataTest, StoreThenClear_IsEmpty) {
WebrtcEventLogData event_log;
event_log.Write("aaa");
event_log.Clear();
auto data = event_log.TakeLogData();
EXPECT_TRUE(data.empty());
}
} // namespace protocol
} // namespace remoting
...@@ -303,6 +303,32 @@ class RTCStatsCollectorCallback : public webrtc::RTCStatsCollectorCallback { ...@@ -303,6 +303,32 @@ class RTCStatsCollectorCallback : public webrtc::RTCStatsCollectorCallback {
DISALLOW_COPY_AND_ASSIGN(RTCStatsCollectorCallback); DISALLOW_COPY_AND_ASSIGN(RTCStatsCollectorCallback);
}; };
class RtcEventLogOutput : public webrtc::RtcEventLogOutput {
public:
// |event_log_data| will be populated with the RTC event data during logging.
// The caller owns |event_log_data| and must keep it alive as long as
// WebRTC provides event logging to this instance (that is, until
// PeerConnection::StopEventLog() is called, or the PeerConnection is
// destroyed).
explicit RtcEventLogOutput(WebrtcEventLogData& event_log_data)
: event_log_data_(event_log_data) {}
~RtcEventLogOutput() override = default;
RtcEventLogOutput(const RtcEventLogOutput&) = delete;
RtcEventLogOutput& operator=(const RtcEventLogOutput&) = delete;
// webrtc::RtcEventLogOutput interface
bool IsActive() const override { return true; }
bool Write(const std::string& output) override {
event_log_data_.Write(output);
return true;
}
private:
// Holds the recorded event log data. This buffer is owned by the caller.
WebrtcEventLogData& event_log_data_;
};
} // namespace } // namespace
class WebrtcTransport::PeerConnectionWrapper class WebrtcTransport::PeerConnectionWrapper
...@@ -453,6 +479,8 @@ WebrtcTransport::WebrtcTransport( ...@@ -453,6 +479,8 @@ WebrtcTransport::WebrtcTransport(
peer_connection_wrapper_.reset(new PeerConnectionWrapper( peer_connection_wrapper_.reset(new PeerConnectionWrapper(
worker_thread, base::WrapUnique(video_encoder_factory_), worker_thread, base::WrapUnique(video_encoder_factory_),
std::move(port_allocator), weak_factory_.GetWeakPtr())); std::move(port_allocator), weak_factory_.GetWeakPtr()));
StartRtcEventLogging();
} }
WebrtcTransport::~WebrtcTransport() { WebrtcTransport::~WebrtcTransport() {
...@@ -744,6 +772,10 @@ void WebrtcTransport::Close(ErrorCode error) { ...@@ -744,6 +772,10 @@ void WebrtcTransport::Close(ErrorCode error) {
weak_factory_.InvalidateWeakPtrs(); weak_factory_.InvalidateWeakPtrs();
// Stop recording into the buffer, otherwise WebRTC might try to record
// events into the buffer while closing the connection, after |this| has been
// destroyed.
StopRtcEventLogging();
ClosePeerConnection(std::move(control_data_channel_), ClosePeerConnection(std::move(control_data_channel_),
std::move(event_data_channel_), std::move(event_data_channel_),
std::move(peer_connection_wrapper_)); std::move(peer_connection_wrapper_));
...@@ -1223,5 +1255,24 @@ WebrtcTransport::GetVideoSender() { ...@@ -1223,5 +1255,24 @@ WebrtcTransport::GetVideoSender() {
return video_transceiver_ ? video_transceiver_->sender() : nullptr; return video_transceiver_ ? video_transceiver_->sender() : nullptr;
} }
void WebrtcTransport::StartRtcEventLogging() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!peer_connection())
return;
// Start recording into |rtc_event_log_|. This is safe because, when |this| is
// destroyed, it calls Close() which stops recording the RTC event log.
rtc_event_log_.Clear();
peer_connection()->StartRtcEventLog(
std::make_unique<RtcEventLogOutput>(rtc_event_log_));
}
void WebrtcTransport::StopRtcEventLogging() {
DCHECK(thread_checker_.CalledOnValidThread());
if (peer_connection()) {
peer_connection()->StopRtcEventLog();
}
}
} // namespace protocol } // namespace protocol
} // namespace remoting } // namespace remoting
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "remoting/protocol/transport.h" #include "remoting/protocol/transport.h"
#include "remoting/protocol/webrtc_data_stream_adapter.h" #include "remoting/protocol/webrtc_data_stream_adapter.h"
#include "remoting/protocol/webrtc_dummy_video_encoder.h" #include "remoting/protocol/webrtc_dummy_video_encoder.h"
#include "remoting/protocol/webrtc_event_log_data.h"
#include "remoting/signaling/signal_strategy.h" #include "remoting/signaling/signal_strategy.h"
#include "third_party/webrtc/api/peer_connection_interface.h" #include "third_party/webrtc/api/peer_connection_interface.h"
...@@ -203,6 +204,9 @@ class WebrtcTransport : public Transport, ...@@ -203,6 +204,9 @@ class WebrtcTransport : public Transport,
// been created yet. // been created yet.
rtc::scoped_refptr<webrtc::RtpSenderInterface> GetVideoSender(); rtc::scoped_refptr<webrtc::RtpSenderInterface> GetVideoSender();
void StartRtcEventLogging();
void StopRtcEventLogging();
base::ThreadChecker thread_checker_; base::ThreadChecker thread_checker_;
scoped_refptr<TransportContext> transport_context_; scoped_refptr<TransportContext> transport_context_;
...@@ -248,6 +252,9 @@ class WebrtcTransport : public Transport, ...@@ -248,6 +252,9 @@ class WebrtcTransport : public Transport,
base::Optional<int> preferred_min_bitrate_bps_; base::Optional<int> preferred_min_bitrate_bps_;
base::Optional<int> preferred_max_bitrate_bps_; base::Optional<int> preferred_max_bitrate_bps_;
// Stores event log data generated by WebRTC for the PeerConnection.
WebrtcEventLogData rtc_event_log_;
base::WeakPtrFactory<WebrtcTransport> weak_factory_{this}; base::WeakPtrFactory<WebrtcTransport> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WebrtcTransport); DISALLOW_COPY_AND_ASSIGN(WebrtcTransport);
......
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