/* * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/logging/rtc_event_log/rtc_event_log.h" #include #include #include #include #include #include #include #include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h" #include "webrtc/rtc_base/checks.h" #include "webrtc/rtc_base/constructormagic.h" #include "webrtc/rtc_base/event.h" #include "webrtc/rtc_base/ignore_wundef.h" #include "webrtc/rtc_base/logging.h" #include "webrtc/rtc_base/protobuf_utils.h" #include "webrtc/rtc_base/ptr_util.h" #include "webrtc/rtc_base/sequenced_task_checker.h" #include "webrtc/rtc_base/task_queue.h" #include "webrtc/rtc_base/thread_annotations.h" #include "webrtc/rtc_base/timeutils.h" #include "webrtc/system_wrappers/include/file_wrapper.h" #include "webrtc/typedefs.h" #ifdef ENABLE_RTC_EVENT_LOG // *.pb.h files are generated at build-time by the protobuf compiler. RTC_PUSH_IGNORING_WUNDEF() #ifdef WEBRTC_ANDROID_PLATFORM_BUILD #include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h" #else #include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h" #endif RTC_POP_IGNORING_WUNDEF() #endif namespace webrtc { #ifdef ENABLE_RTC_EVENT_LOG namespace { const int kEventsInHistory = 10000; bool IsConfigEvent(const rtclog::Event& event) { rtclog::Event_EventType event_type = event.type(); return event_type == rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT || event_type == rtclog::Event::VIDEO_SENDER_CONFIG_EVENT || event_type == rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT || event_type == rtclog::Event::AUDIO_SENDER_CONFIG_EVENT; } // TODO(eladalon): This class exists because C++11 doesn't allow transferring a // unique_ptr to a lambda (a copy constructor is required). We should get // rid of this when we move to C++14. template class ResourceOwningTask final : public rtc::QueuedTask { public: ResourceOwningTask(std::unique_ptr resource, std::function)> handler) : resource_(std::move(resource)), handler_(handler) {} bool Run() override { handler_(std::move(resource_)); return true; } private: std::unique_ptr resource_; std::function)> handler_; }; } // namespace class RtcEventLogImpl final : public RtcEventLog { friend std::unique_ptr RtcEventLog::Create(); public: ~RtcEventLogImpl() override; bool StartLogging(const std::string& file_name, int64_t max_size_bytes) override; bool StartLogging(rtc::PlatformFile platform_file, int64_t max_size_bytes) override; void StopLogging() override; void LogVideoReceiveStreamConfig(const rtclog::StreamConfig& config) override; void LogVideoSendStreamConfig(const rtclog::StreamConfig& config) override; void LogAudioReceiveStreamConfig(const rtclog::StreamConfig& config) override; void LogAudioSendStreamConfig(const rtclog::StreamConfig& config) override; void LogRtpHeader(PacketDirection direction, const uint8_t* header, size_t packet_length) override; void LogRtpHeader(PacketDirection direction, const uint8_t* header, size_t packet_length, int probe_cluster_id) override; void LogRtcpPacket(PacketDirection direction, const uint8_t* packet, size_t length) override; void LogAudioPlayout(uint32_t ssrc) override; void LogLossBasedBweUpdate(int32_t bitrate_bps, uint8_t fraction_loss, int32_t total_packets) override; void LogDelayBasedBweUpdate(int32_t bitrate_bps, BandwidthUsage detector_state) override; void LogAudioNetworkAdaptation( const AudioEncoderRuntimeConfig& config) override; void LogProbeClusterCreated(int id, int bitrate_bps, int min_probes, int min_bytes) override; void LogProbeResultSuccess(int id, int bitrate_bps) override; void LogProbeResultFailure(int id, ProbeFailureReason failure_reason) override; private: void StartLoggingInternal(std::unique_ptr file, int64_t max_size_bytes); RtcEventLogImpl(); // Creation is done by RtcEventLog::Create. void StoreEvent(std::unique_ptr event); void LogProbeResult(int id, rtclog::BweProbeResult::ResultType result, int bitrate_bps); // Appends an event to the output protobuf string, returning true on success. // Fails and returns false in case the limit on output size prevents the // event from being added; in this case, the output string is left unchanged. bool AppendEventToString(rtclog::Event* event, ProtoString* output_string) RTC_WARN_UNUSED_RESULT; void LogToMemory(std::unique_ptr event); void StartLogFile(); void LogToFile(std::unique_ptr event); void StopLogFile(int64_t stop_time); // Observe a limit on the number of concurrent logs, so as not to run into // OS-imposed limits on open files and/or threads/task-queues. // TODO(eladalon): Known issue - there's a race over |log_count_|. static std::atomic log_count_; // Make sure that the event log is "managed" - created/destroyed, as well // as started/stopped - from the same thread/task-queue. rtc::SequencedTaskChecker owner_sequence_checker_; // History containing all past configuration events. std::vector> config_history_ ACCESS_ON(task_queue_); // History containing the most recent (non-configuration) events (~10s). std::deque> history_ ACCESS_ON(task_queue_); std::unique_ptr file_ ACCESS_ON(task_queue_); size_t max_size_bytes_ ACCESS_ON(task_queue_); size_t written_bytes_ ACCESS_ON(task_queue_); // Keep this last to ensure it destructs first, or else tasks living on the // queue might access other members after they've been torn down. rtc::TaskQueue task_queue_; RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl); }; namespace { // The functions in this namespace convert enums from the runtime format // that the rest of the WebRtc project can use, to the corresponding // serialized enum which is defined by the protobuf. rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) { switch (rtcp_mode) { case RtcpMode::kCompound: return rtclog::VideoReceiveConfig::RTCP_COMPOUND; case RtcpMode::kReducedSize: return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE; case RtcpMode::kOff: RTC_NOTREACHED(); return rtclog::VideoReceiveConfig::RTCP_COMPOUND; } RTC_NOTREACHED(); return rtclog::VideoReceiveConfig::RTCP_COMPOUND; } rtclog::DelayBasedBweUpdate::DetectorState ConvertDetectorState( BandwidthUsage state) { switch (state) { case BandwidthUsage::kBwNormal: return rtclog::DelayBasedBweUpdate::BWE_NORMAL; case BandwidthUsage::kBwUnderusing: return rtclog::DelayBasedBweUpdate::BWE_UNDERUSING; case BandwidthUsage::kBwOverusing: return rtclog::DelayBasedBweUpdate::BWE_OVERUSING; } RTC_NOTREACHED(); return rtclog::DelayBasedBweUpdate::BWE_NORMAL; } rtclog::BweProbeResult::ResultType ConvertProbeResultType( ProbeFailureReason failure_reason) { switch (failure_reason) { case kInvalidSendReceiveInterval: return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL; case kInvalidSendReceiveRatio: return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO; case kTimeout: return rtclog::BweProbeResult::TIMEOUT; } RTC_NOTREACHED(); return rtclog::BweProbeResult::SUCCESS; } } // namespace std::atomic RtcEventLogImpl::log_count_(0); RtcEventLogImpl::RtcEventLogImpl() : file_(FileWrapper::Create()), max_size_bytes_(std::numeric_limits::max()), written_bytes_(0), task_queue_("rtc_event_log") {} RtcEventLogImpl::~RtcEventLogImpl() { RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); // If we're logging to the file, this will stop that. Blocking function. StopLogging(); int count = std::atomic_fetch_sub(&RtcEventLogImpl::log_count_, 1) - 1; RTC_DCHECK_GE(count, 0); } bool RtcEventLogImpl::StartLogging(const std::string& file_name, int64_t max_size_bytes) { RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); auto file = rtc::WrapUnique(FileWrapper::Create()); if (!file->OpenFile(file_name.c_str(), false)) { LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; return false; } StartLoggingInternal(std::move(file), max_size_bytes); return true; } bool RtcEventLogImpl::StartLogging(rtc::PlatformFile platform_file, int64_t max_size_bytes) { RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); auto file = rtc::WrapUnique(FileWrapper::Create()); FILE* file_handle = rtc::FdopenPlatformFileForWriting(platform_file); if (!file_handle) { LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; // Even though we failed to open a FILE*, the platform_file is still open // and needs to be closed. if (!rtc::ClosePlatformFile(platform_file)) { LOG(LS_ERROR) << "Can't close file."; } return false; } if (!file->OpenFromFileHandle(file_handle)) { LOG(LS_ERROR) << "Can't open file. WebRTC event log not started."; return false; } StartLoggingInternal(std::move(file), max_size_bytes); return true; } void RtcEventLogImpl::StopLogging() { RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); LOG(LS_INFO) << "Stopping WebRTC event log."; const int64_t stop_time = rtc::TimeMicros(); rtc::Event file_finished(true, false); task_queue_.PostTask([this, stop_time, &file_finished]() { RTC_DCHECK_RUN_ON(&task_queue_); if (file_->is_open()) { StopLogFile(stop_time); } file_finished.Set(); }); file_finished.Wait(rtc::Event::kForever); LOG(LS_INFO) << "WebRTC event log successfully stopped."; } void RtcEventLogImpl::LogVideoReceiveStreamConfig( const rtclog::StreamConfig& config) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); rtclog::VideoReceiveConfig* receiver_config = event->mutable_video_receiver_config(); receiver_config->set_remote_ssrc(config.remote_ssrc); receiver_config->set_local_ssrc(config.local_ssrc); // TODO(perkj): Add field for rsid. receiver_config->set_rtcp_mode(ConvertRtcpMode(config.rtcp_mode)); receiver_config->set_remb(config.remb); for (const auto& e : config.rtp_extensions) { rtclog::RtpHeaderExtension* extension = receiver_config->add_header_extensions(); extension->set_name(e.uri); extension->set_id(e.id); } for (const auto& d : config.codecs) { rtclog::DecoderConfig* decoder = receiver_config->add_decoders(); decoder->set_name(d.payload_name); decoder->set_payload_type(d.payload_type); if (d.rtx_payload_type != 0) { rtclog::RtxMap* rtx = receiver_config->add_rtx_map(); rtx->set_payload_type(d.payload_type); rtx->mutable_config()->set_rtx_ssrc(config.rtx_ssrc); rtx->mutable_config()->set_rtx_payload_type(d.rtx_payload_type); } } StoreEvent(std::move(event)); } void RtcEventLogImpl::LogVideoSendStreamConfig( const rtclog::StreamConfig& config) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); rtclog::VideoSendConfig* sender_config = event->mutable_video_sender_config(); // TODO(perkj): rtclog::VideoSendConfig should only contain one SSRC. sender_config->add_ssrcs(config.local_ssrc); if (config.rtx_ssrc != 0) { sender_config->add_rtx_ssrcs(config.rtx_ssrc); } for (const auto& e : config.rtp_extensions) { rtclog::RtpHeaderExtension* extension = sender_config->add_header_extensions(); extension->set_name(e.uri); extension->set_id(e.id); } // TODO(perkj): rtclog::VideoSendConfig should contain many possible codec // configurations. for (const auto& codec : config.codecs) { sender_config->set_rtx_payload_type(codec.rtx_payload_type); rtclog::EncoderConfig* encoder = sender_config->mutable_encoder(); encoder->set_name(codec.payload_name); encoder->set_payload_type(codec.payload_type); if (config.codecs.size() > 1) { LOG(WARNING) << "LogVideoSendStreamConfig currently only supports one " << "codec. Logging codec :" << codec.payload_name; break; } } StoreEvent(std::move(event)); } void RtcEventLogImpl::LogAudioReceiveStreamConfig( const rtclog::StreamConfig& config) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT); rtclog::AudioReceiveConfig* receiver_config = event->mutable_audio_receiver_config(); receiver_config->set_remote_ssrc(config.remote_ssrc); receiver_config->set_local_ssrc(config.local_ssrc); for (const auto& e : config.rtp_extensions) { rtclog::RtpHeaderExtension* extension = receiver_config->add_header_extensions(); extension->set_name(e.uri); extension->set_id(e.id); } StoreEvent(std::move(event)); } void RtcEventLogImpl::LogAudioSendStreamConfig( const rtclog::StreamConfig& config) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::AUDIO_SENDER_CONFIG_EVENT); rtclog::AudioSendConfig* sender_config = event->mutable_audio_sender_config(); sender_config->set_ssrc(config.local_ssrc); for (const auto& e : config.rtp_extensions) { rtclog::RtpHeaderExtension* extension = sender_config->add_header_extensions(); extension->set_name(e.uri); extension->set_id(e.id); } StoreEvent(std::move(event)); } void RtcEventLogImpl::LogRtpHeader(PacketDirection direction, const uint8_t* header, size_t packet_length) { LogRtpHeader(direction, header, packet_length, PacedPacketInfo::kNotAProbe); } void RtcEventLogImpl::LogRtpHeader(PacketDirection direction, const uint8_t* header, size_t packet_length, int probe_cluster_id) { // Read header length (in bytes) from packet data. if (packet_length < 12u) { return; // Don't read outside the packet. } const bool x = (header[0] & 0x10) != 0; const uint8_t cc = header[0] & 0x0f; size_t header_length = 12u + cc * 4u; if (x) { if (packet_length < 12u + cc * 4u + 4u) { return; // Don't read outside the packet. } size_t x_len = ByteReader::ReadBigEndian(header + 14 + cc * 4); header_length += (x_len + 1) * 4; } std::unique_ptr rtp_event(new rtclog::Event()); rtp_event->set_timestamp_us(rtc::TimeMicros()); rtp_event->set_type(rtclog::Event::RTP_EVENT); rtp_event->mutable_rtp_packet()->set_incoming(direction == kIncomingPacket); rtp_event->mutable_rtp_packet()->set_packet_length(packet_length); rtp_event->mutable_rtp_packet()->set_header(header, header_length); if (probe_cluster_id != PacedPacketInfo::kNotAProbe) rtp_event->mutable_rtp_packet()->set_probe_cluster_id(probe_cluster_id); StoreEvent(std::move(rtp_event)); } void RtcEventLogImpl::LogRtcpPacket(PacketDirection direction, const uint8_t* packet, size_t length) { std::unique_ptr rtcp_event(new rtclog::Event()); rtcp_event->set_timestamp_us(rtc::TimeMicros()); rtcp_event->set_type(rtclog::Event::RTCP_EVENT); rtcp_event->mutable_rtcp_packet()->set_incoming(direction == kIncomingPacket); rtcp::CommonHeader header; const uint8_t* block_begin = packet; const uint8_t* packet_end = packet + length; RTC_DCHECK(length <= IP_PACKET_SIZE); uint8_t buffer[IP_PACKET_SIZE]; uint32_t buffer_length = 0; while (block_begin < packet_end) { if (!header.Parse(block_begin, packet_end - block_begin)) { break; // Incorrect message header. } const uint8_t* next_block = header.NextPacket(); uint32_t block_size = next_block - block_begin; switch (header.type()) { case rtcp::SenderReport::kPacketType: case rtcp::ReceiverReport::kPacketType: case rtcp::Bye::kPacketType: case rtcp::ExtendedJitterReport::kPacketType: case rtcp::Rtpfb::kPacketType: case rtcp::Psfb::kPacketType: case rtcp::ExtendedReports::kPacketType: // We log sender reports, receiver reports, bye messages // inter-arrival jitter, third-party loss reports, payload-specific // feedback and extended reports. memcpy(buffer + buffer_length, block_begin, block_size); buffer_length += block_size; break; case rtcp::Sdes::kPacketType: case rtcp::App::kPacketType: default: // We don't log sender descriptions, application defined messages // or message blocks of unknown type. break; } block_begin += block_size; } rtcp_event->mutable_rtcp_packet()->set_packet_data(buffer, buffer_length); StoreEvent(std::move(rtcp_event)); } void RtcEventLogImpl::LogAudioPlayout(uint32_t ssrc) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT); auto playout_event = event->mutable_audio_playout_event(); playout_event->set_local_ssrc(ssrc); StoreEvent(std::move(event)); } void RtcEventLogImpl::LogLossBasedBweUpdate(int32_t bitrate_bps, uint8_t fraction_loss, int32_t total_packets) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::LOSS_BASED_BWE_UPDATE); auto bwe_event = event->mutable_loss_based_bwe_update(); bwe_event->set_bitrate_bps(bitrate_bps); bwe_event->set_fraction_loss(fraction_loss); bwe_event->set_total_packets(total_packets); StoreEvent(std::move(event)); } void RtcEventLogImpl::LogDelayBasedBweUpdate(int32_t bitrate_bps, BandwidthUsage detector_state) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::DELAY_BASED_BWE_UPDATE); auto bwe_event = event->mutable_delay_based_bwe_update(); bwe_event->set_bitrate_bps(bitrate_bps); bwe_event->set_detector_state(ConvertDetectorState(detector_state)); StoreEvent(std::move(event)); } void RtcEventLogImpl::LogAudioNetworkAdaptation( const AudioEncoderRuntimeConfig& config) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT); auto audio_network_adaptation = event->mutable_audio_network_adaptation(); if (config.bitrate_bps) audio_network_adaptation->set_bitrate_bps(*config.bitrate_bps); if (config.frame_length_ms) audio_network_adaptation->set_frame_length_ms(*config.frame_length_ms); if (config.uplink_packet_loss_fraction) { audio_network_adaptation->set_uplink_packet_loss_fraction( *config.uplink_packet_loss_fraction); } if (config.enable_fec) audio_network_adaptation->set_enable_fec(*config.enable_fec); if (config.enable_dtx) audio_network_adaptation->set_enable_dtx(*config.enable_dtx); if (config.num_channels) audio_network_adaptation->set_num_channels(*config.num_channels); StoreEvent(std::move(event)); } void RtcEventLogImpl::LogProbeClusterCreated(int id, int bitrate_bps, int min_probes, int min_bytes) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT); auto probe_cluster = event->mutable_probe_cluster(); probe_cluster->set_id(id); probe_cluster->set_bitrate_bps(bitrate_bps); probe_cluster->set_min_packets(min_probes); probe_cluster->set_min_bytes(min_bytes); StoreEvent(std::move(event)); } void RtcEventLogImpl::LogProbeResultSuccess(int id, int bitrate_bps) { LogProbeResult(id, rtclog::BweProbeResult::SUCCESS, bitrate_bps); } void RtcEventLogImpl::LogProbeResultFailure(int id, ProbeFailureReason failure_reason) { rtclog::BweProbeResult::ResultType result = ConvertProbeResultType(failure_reason); LogProbeResult(id, result, -1); } void RtcEventLogImpl::LogProbeResult(int id, rtclog::BweProbeResult::ResultType result, int bitrate_bps) { std::unique_ptr event(new rtclog::Event()); event->set_timestamp_us(rtc::TimeMicros()); event->set_type(rtclog::Event::BWE_PROBE_RESULT_EVENT); auto probe_result = event->mutable_probe_result(); probe_result->set_id(id); probe_result->set_result(result); if (result == rtclog::BweProbeResult::SUCCESS) probe_result->set_bitrate_bps(bitrate_bps); StoreEvent(std::move(event)); } void RtcEventLogImpl::StartLoggingInternal(std::unique_ptr file, int64_t max_size_bytes) { LOG(LS_INFO) << "Starting WebRTC event log."; max_size_bytes = (max_size_bytes <= 0) ? std::numeric_limits::max() : max_size_bytes; auto file_handler = [this, max_size_bytes](std::unique_ptr file) { RTC_DCHECK_RUN_ON(&task_queue_); if (!file_->is_open()) { max_size_bytes_ = max_size_bytes; file_ = std::move(file); StartLogFile(); } else { // Already started. Ignore message and close file handle. file->CloseFile(); } }; task_queue_.PostTask(rtc::MakeUnique>( std::move(file), file_handler)); } void RtcEventLogImpl::StoreEvent(std::unique_ptr event) { RTC_DCHECK(event); auto event_handler = [this](std::unique_ptr rtclog_event) { RTC_DCHECK_RUN_ON(&task_queue_); if (file_->is_open()) { LogToFile(std::move(rtclog_event)); } else { LogToMemory(std::move(rtclog_event)); } }; task_queue_.PostTask(rtc::MakeUnique>( std::move(event), event_handler)); } bool RtcEventLogImpl::AppendEventToString(rtclog::Event* event, ProtoString* output_string) { RTC_DCHECK_RUN_ON(&task_queue_); // Even though we're only serializing a single event during this call, what // we intend to get is a list of events, with a tag and length preceding // each actual event. To produce that, we serialize a list of a single event. // If we later serialize additional events, the resulting ProtoString will // be a proper concatenation of all those events. rtclog::EventStream event_stream; event_stream.add_stream(); // As a tweak, we swap the new event into the event-stream, write that to // file, then swap back. This saves on some copying. rtclog::Event* output_event = event_stream.mutable_stream(0); output_event->Swap(event); bool appended; size_t potential_new_size = written_bytes_ + output_string->size() + event_stream.ByteSize(); if (potential_new_size <= max_size_bytes_) { event_stream.AppendToString(output_string); appended = true; } else { appended = false; } // When the function returns, the original Event will be unchanged. output_event->Swap(event); return appended; } void RtcEventLogImpl::LogToMemory(std::unique_ptr event) { RTC_DCHECK_RUN_ON(&task_queue_); RTC_DCHECK(!file_->is_open()); if (IsConfigEvent(*event.get())) { config_history_.push_back(std::move(event)); } else { history_.push_back(std::move(event)); if (history_.size() > kEventsInHistory) { history_.pop_front(); } } } void RtcEventLogImpl::StartLogFile() { RTC_DCHECK_RUN_ON(&task_queue_); RTC_DCHECK(file_->is_open()); ProtoString output_string; // Create and serialize the LOG_START event. // The timestamp used will correspond to when logging has started. The log // may contain events earlier than the LOG_START event. (In general, the // timestamps in the log are not monotonic.) rtclog::Event start_event; start_event.set_timestamp_us(rtc::TimeMicros()); start_event.set_type(rtclog::Event::LOG_START); bool appended = AppendEventToString(&start_event, &output_string); // Serialize the config information for all old streams, including streams // which were already logged to previous files. for (auto& event : config_history_) { if (!appended) { break; } appended = AppendEventToString(event.get(), &output_string); } // Serialize the events in the event queue. while (appended && !history_.empty()) { appended = AppendEventToString(history_.front().get(), &output_string); if (appended) { // Known issue - if writing to the file fails, these events will have // been lost. If we try to open a new file, these events will be missing // from it. history_.pop_front(); } } // Write to file. if (!file_->Write(output_string.data(), output_string.size())) { LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file."; // The current FileWrapper implementation closes the file on error. RTC_DCHECK(!file_->is_open()); return; } written_bytes_ += output_string.size(); if (!appended) { RTC_DCHECK(file_->is_open()); StopLogFile(rtc::TimeMicros()); } } void RtcEventLogImpl::LogToFile(std::unique_ptr event) { RTC_DCHECK_RUN_ON(&task_queue_); RTC_DCHECK(file_->is_open()); ProtoString output_string; bool appended = AppendEventToString(event.get(), &output_string); if (IsConfigEvent(*event.get())) { config_history_.push_back(std::move(event)); } if (!appended) { RTC_DCHECK(file_->is_open()); history_.push_back(std::move(event)); StopLogFile(rtc::TimeMicros()); return; } // Write string to file. if (file_->Write(output_string.data(), output_string.size())) { written_bytes_ += output_string.size(); } else { LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file."; // The current FileWrapper implementation closes the file on error. RTC_DCHECK(!file_->is_open()); } } void RtcEventLogImpl::StopLogFile(int64_t stop_time) { RTC_DCHECK_RUN_ON(&task_queue_); RTC_DCHECK(file_->is_open()); ProtoString output_string; rtclog::Event end_event; end_event.set_timestamp_us(stop_time); end_event.set_type(rtclog::Event::LOG_END); bool appended = AppendEventToString(&end_event, &output_string); if (appended) { if (!file_->Write(output_string.data(), output_string.size())) { LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file."; // The current FileWrapper implementation closes the file on error. RTC_DCHECK(!file_->is_open()); } written_bytes_ += output_string.size(); } max_size_bytes_ = std::numeric_limits::max(); written_bytes_ = 0; file_->CloseFile(); RTC_DCHECK(!file_->is_open()); } bool RtcEventLog::ParseRtcEventLog(const std::string& file_name, rtclog::EventStream* result) { char tmp_buffer[1024]; int bytes_read = 0; std::unique_ptr dump_file(FileWrapper::Create()); if (!dump_file->OpenFile(file_name.c_str(), true)) { return false; } ProtoString dump_buffer; while ((bytes_read = dump_file->Read(tmp_buffer, sizeof(tmp_buffer))) > 0) { dump_buffer.append(tmp_buffer, bytes_read); } dump_file->CloseFile(); return result->ParseFromString(dump_buffer); } #endif // ENABLE_RTC_EVENT_LOG // RtcEventLog member functions. std::unique_ptr RtcEventLog::Create() { #ifdef ENABLE_RTC_EVENT_LOG // TODO(eladalon): Known issue - there's a race over |log_count_| here. constexpr int kMaxLogCount = 5; int count = 1 + std::atomic_fetch_add(&RtcEventLogImpl::log_count_, 1); if (count > kMaxLogCount) { LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. " << count - 1 << " logs open already."; std::atomic_fetch_sub(&RtcEventLogImpl::log_count_, 1); return std::unique_ptr(new RtcEventLogNullImpl()); } return std::unique_ptr(new RtcEventLogImpl()); #else return CreateNull(); #endif // ENABLE_RTC_EVENT_LOG } std::unique_ptr RtcEventLog::CreateNull() { return std::unique_ptr(new RtcEventLogNullImpl()); } } // namespace webrtc