blob: 9bbccafdc0d3b7cdbdd6776abf517f2ec48cf9ff [file] [log] [blame]
Bjorn Terelius36411852015-07-30 12:45:18 +02001/*
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "logging/rtc_event_log/rtc_event_log.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020012
eladalon248fd4f2017-09-06 05:18:15 -070013#include <atomic>
14#include <deque>
15#include <functional>
terelius4311ba52016-04-22 12:40:37 -070016#include <limits>
eladalon248fd4f2017-09-06 05:18:15 -070017#include <memory>
eladalon333264f2017-07-16 16:44:08 -070018#include <utility>
terelius1adce142015-10-16 08:51:08 -070019#include <vector>
Bjorn Terelius36411852015-07-30 12:45:18 +020020
Elad Alon4a87e1c2017-10-03 16:11:34 +020021#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h"
Elad Alon4a87e1c2017-10-03 16:11:34 +020022#include "logging/rtc_event_log/events/rtc_event_logging_started.h"
23#include "logging/rtc_event_log/events/rtc_event_logging_stopped.h"
Elad Alon83ccca12017-10-04 13:18:26 +020024#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/checks.h"
26#include "rtc_base/constructormagic.h"
27#include "rtc_base/event.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020028#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010029#include "rtc_base/numerics/safe_conversions.h"
30#include "rtc_base/numerics/safe_minmax.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020031#include "rtc_base/ptr_util.h"
32#include "rtc_base/sequenced_task_checker.h"
33#include "rtc_base/task_queue.h"
34#include "rtc_base/thread_annotations.h"
Bjorn Terelius36411852015-07-30 12:45:18 +020035
Bjorn Terelius36411852015-07-30 12:45:18 +020036namespace webrtc {
37
ivoc14d5dbe2016-07-04 07:06:55 -070038#ifdef ENABLE_RTC_EVENT_LOG
Bjorn Terelius36411852015-07-30 12:45:18 +020039
eladalon248fd4f2017-09-06 05:18:15 -070040namespace {
Elad Alon92a773d2017-10-13 11:54:51 +020041constexpr size_t kMaxEventsInHistory = 10000;
42// The config-history is supposed to be unbounded, but needs to have some bound
43// to prevent an attack via unreasonable memory use.
44constexpr size_t kMaxEventsInConfigHistory = 1000;
eladalon248fd4f2017-09-06 05:18:15 -070045
Elad Alonf491c522017-09-15 14:49:40 +020046// Observe a limit on the number of concurrent logs, so as not to run into
47// OS-imposed limits on open files and/or threads/task-queues.
48// TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
49std::atomic<int> rtc_event_log_count(0);
50
eladalon248fd4f2017-09-06 05:18:15 -070051// TODO(eladalon): This class exists because C++11 doesn't allow transferring a
52// unique_ptr to a lambda (a copy constructor is required). We should get
53// rid of this when we move to C++14.
54template <typename T>
55class ResourceOwningTask final : public rtc::QueuedTask {
56 public:
57 ResourceOwningTask(std::unique_ptr<T> resource,
58 std::function<void(std::unique_ptr<T>)> handler)
59 : resource_(std::move(resource)), handler_(handler) {}
60
61 bool Run() override {
62 handler_(std::move(resource_));
63 return true;
64 }
65
66 private:
67 std::unique_ptr<T> resource_;
68 std::function<void(std::unique_ptr<T>)> handler_;
69};
eladalon248fd4f2017-09-06 05:18:15 -070070
Elad Alon4a87e1c2017-10-03 16:11:34 +020071std::unique_ptr<RtcEventLogEncoder> CreateEncoder(
72 RtcEventLog::EncodingType type) {
73 switch (type) {
74 case RtcEventLog::EncodingType::Legacy:
75 return rtc::MakeUnique<RtcEventLogEncoderLegacy>();
76 default:
Mirko Bonadei675513b2017-11-09 11:09:25 +010077 RTC_LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type)
78 << ")";
Elad Alon4a87e1c2017-10-03 16:11:34 +020079 RTC_NOTREACHED();
80 return std::unique_ptr<RtcEventLogEncoder>(nullptr);
81 }
82}
83
Bjorn Terelius36411852015-07-30 12:45:18 +020084class RtcEventLogImpl final : public RtcEventLog {
85 public:
Elad Alon4a87e1c2017-10-03 16:11:34 +020086 explicit RtcEventLogImpl(std::unique_ptr<RtcEventLogEncoder> event_encoder);
terelius4311ba52016-04-22 12:40:37 -070087 ~RtcEventLogImpl() override;
terelius1adce142015-10-16 08:51:08 -070088
Elad Alon83ccca12017-10-04 13:18:26 +020089 // TODO(eladalon): We should change these name to reflect that what we're
90 // actually starting/stopping is the output of the log, not the log itself.
Bjorn Tereliusde939432017-11-20 17:38:14 +010091 bool StartLogging(std::unique_ptr<RtcEventLogOutput> output,
92 int64_t output_period_ms) override;
Bjorn Terelius36411852015-07-30 12:45:18 +020093 void StopLogging() override;
Elad Alon83ccca12017-10-04 13:18:26 +020094
Elad Alon4a87e1c2017-10-03 16:11:34 +020095 void Log(std::unique_ptr<RtcEvent> event) override;
Elad Alon83ccca12017-10-04 13:18:26 +020096
Bjorn Terelius36411852015-07-30 12:45:18 +020097 private:
eladalon248fd4f2017-09-06 05:18:15 -070098 // Appends an event to the output protobuf string, returning true on success.
99 // Fails and returns false in case the limit on output size prevents the
100 // event from being added; in this case, the output string is left unchanged.
Elad Alon4a87e1c2017-10-03 16:11:34 +0200101 // The event is encoded before being appended.
Elad Alon83ccca12017-10-04 13:18:26 +0200102 // We could have avoided this, because the output repeats the check, but this
103 // way, we minimize the number of lock acquisitions, task switches, etc.,
104 // that might be associated with each call to RtcEventLogOutput::Write().
Elad Alon4a87e1c2017-10-03 16:11:34 +0200105 bool AppendEventToString(const RtcEvent& event,
Elad Alon83ccca12017-10-04 13:18:26 +0200106 std::string* output_string) RTC_WARN_UNUSED_RESULT;
tereliusc8e05522017-06-28 06:40:51 -0700107
Elad Alon83ccca12017-10-04 13:18:26 +0200108 void LogToMemory(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);
Bjorn Terelius36411852015-07-30 12:45:18 +0200109
Elad Alon83ccca12017-10-04 13:18:26 +0200110 void LogEventsFromMemoryToOutput() RTC_RUN_ON(&task_queue_);
111 void LogToOutput(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);
112 void StopOutput() RTC_RUN_ON(&task_queue_);
113
114 void WriteToOutput(const std::string& output_string) RTC_RUN_ON(&task_queue_);
115
116 void StopLoggingInternal() RTC_RUN_ON(&task_queue_);
terelius1adce142015-10-16 08:51:08 -0700117
Bjorn Tereliusde939432017-11-20 17:38:14 +0100118 void ScheduleOutput() RTC_RUN_ON(&task_queue_);
119
eladalon248fd4f2017-09-06 05:18:15 -0700120 // Make sure that the event log is "managed" - created/destroyed, as well
121 // as started/stopped - from the same thread/task-queue.
122 rtc::SequencedTaskChecker owner_sequence_checker_;
123
124 // History containing all past configuration events.
Elad Alon92a773d2017-10-13 11:54:51 +0200125 std::deque<std::unique_ptr<RtcEvent>> config_history_
danilchapa37de392017-09-09 04:17:22 -0700126 RTC_ACCESS_ON(task_queue_);
eladalon248fd4f2017-09-06 05:18:15 -0700127
128 // History containing the most recent (non-configuration) events (~10s).
Elad Alon4a87e1c2017-10-03 16:11:34 +0200129 std::deque<std::unique_ptr<RtcEvent>> history_ RTC_ACCESS_ON(task_queue_);
eladalon248fd4f2017-09-06 05:18:15 -0700130
danilchapa37de392017-09-09 04:17:22 -0700131 size_t max_size_bytes_ RTC_ACCESS_ON(task_queue_);
132 size_t written_bytes_ RTC_ACCESS_ON(task_queue_);
eladalon248fd4f2017-09-06 05:18:15 -0700133
Elad Alon4a87e1c2017-10-03 16:11:34 +0200134 std::unique_ptr<RtcEventLogEncoder> event_encoder_ RTC_ACCESS_ON(task_queue_);
Elad Alon83ccca12017-10-04 13:18:26 +0200135 std::unique_ptr<RtcEventLogOutput> event_output_ RTC_ACCESS_ON(task_queue_);
Elad Alon4a87e1c2017-10-03 16:11:34 +0200136
Bjorn Tereliusde939432017-11-20 17:38:14 +0100137 size_t num_config_events_written_ RTC_ACCESS_ON(task_queue_);
138 int64_t output_period_ms_ RTC_ACCESS_ON(task_queue_);
139 int64_t last_output_ms_ RTC_ACCESS_ON(task_queue_);
140 bool output_scheduled_ RTC_ACCESS_ON(task_queue_);
141
142 // Since we are posting tasks bound to |this|, it is critical that the event
143 // log and it's members outlive the |task_queue_|. Keep the "task_queue_|
144 // last to ensure it destructs first, or else tasks living on the queue might
145 // access other members after they've been torn down.
eladalon248fd4f2017-09-06 05:18:15 -0700146 rtc::TaskQueue task_queue_;
terelius4311ba52016-04-22 12:40:37 -0700147
nisse30612762016-12-20 05:03:58 -0800148 RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl);
Bjorn Terelius36411852015-07-30 12:45:18 +0200149};
150
Elad Alon4a87e1c2017-10-03 16:11:34 +0200151RtcEventLogImpl::RtcEventLogImpl(
152 std::unique_ptr<RtcEventLogEncoder> event_encoder)
Elad Alon83ccca12017-10-04 13:18:26 +0200153 : max_size_bytes_(std::numeric_limits<decltype(max_size_bytes_)>::max()),
eladalon248fd4f2017-09-06 05:18:15 -0700154 written_bytes_(0),
Elad Alon4a87e1c2017-10-03 16:11:34 +0200155 event_encoder_(std::move(event_encoder)),
Bjorn Tereliusde939432017-11-20 17:38:14 +0100156 num_config_events_written_(0),
157 output_period_ms_(kImmediateOutput),
158 last_output_ms_(rtc::TimeMillis()),
159 output_scheduled_(false),
eladalon248fd4f2017-09-06 05:18:15 -0700160 task_queue_("rtc_event_log") {}
terelius1adce142015-10-16 08:51:08 -0700161
terelius4311ba52016-04-22 12:40:37 -0700162RtcEventLogImpl::~RtcEventLogImpl() {
eladalon248fd4f2017-09-06 05:18:15 -0700163 RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
164
Elad Alon83ccca12017-10-04 13:18:26 +0200165 // If we're logging to the output, this will stop that. Blocking function.
eladalon248fd4f2017-09-06 05:18:15 -0700166 StopLogging();
167
Elad Alonf491c522017-09-15 14:49:40 +0200168 int count = std::atomic_fetch_sub(&rtc_event_log_count, 1) - 1;
tereliusc8e05522017-06-28 06:40:51 -0700169 RTC_DCHECK_GE(count, 0);
terelius1adce142015-10-16 08:51:08 -0700170}
Bjorn Terelius36411852015-07-30 12:45:18 +0200171
Bjorn Tereliusde939432017-11-20 17:38:14 +0100172bool RtcEventLogImpl::StartLogging(std::unique_ptr<RtcEventLogOutput> output,
173 int64_t output_period_ms) {
eladalon248fd4f2017-09-06 05:18:15 -0700174 RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
175
Bjorn Tereliusde939432017-11-20 17:38:14 +0100176 RTC_DCHECK(output_period_ms == kImmediateOutput || output_period_ms > 0);
177
Elad Alon83ccca12017-10-04 13:18:26 +0200178 if (!output->IsActive()) {
Bjorn Tereliusde939432017-11-20 17:38:14 +0100179 // TODO(eladalon): We may want to remove the IsActive method. Otherwise
180 // we probably want to be consistent and terminate any existing output.
terelius4311ba52016-04-22 12:40:37 -0700181 return false;
Bjorn Terelius36411852015-07-30 12:45:18 +0200182 }
eladalon248fd4f2017-09-06 05:18:15 -0700183
Mirko Bonadei675513b2017-11-09 11:09:25 +0100184 RTC_LOG(LS_INFO) << "Starting WebRTC event log.";
Elad Alon83ccca12017-10-04 13:18:26 +0200185
186 // |start_event| captured by value. This is done here because we want the
187 // timestamp to reflect when StartLogging() was called; not the queueing
188 // delay of the TaskQueue.
189 // This is a bit inefficient - especially since we copy again to get it
190 // to comply with LogToOutput()'s signature - but it's a small problem.
191 RtcEventLoggingStarted start_event;
192
Bjorn Tereliusde939432017-11-20 17:38:14 +0100193 // Binding to |this| is safe because |this| outlives the |task_queue_|.
Elad Alon83ccca12017-10-04 13:18:26 +0200194 auto start = [this, start_event](std::unique_ptr<RtcEventLogOutput> output) {
195 RTC_DCHECK_RUN_ON(&task_queue_);
196 RTC_DCHECK(output->IsActive());
197 event_output_ = std::move(output);
Bjorn Tereliusde939432017-11-20 17:38:14 +0100198 num_config_events_written_ = 0;
Elad Alon83ccca12017-10-04 13:18:26 +0200199 LogToOutput(rtc::MakeUnique<RtcEventLoggingStarted>(start_event));
200 LogEventsFromMemoryToOutput();
201 };
202
203 task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEventLogOutput>>(
204 std::move(output), start));
eladalon248fd4f2017-09-06 05:18:15 -0700205
terelius4311ba52016-04-22 12:40:37 -0700206 return true;
Bjorn Terelius36411852015-07-30 12:45:18 +0200207}
208
209void RtcEventLogImpl::StopLogging() {
eladalon248fd4f2017-09-06 05:18:15 -0700210 RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
211
Mirko Bonadei675513b2017-11-09 11:09:25 +0100212 RTC_LOG(LS_INFO) << "Stopping WebRTC event log.";
eladalon248fd4f2017-09-06 05:18:15 -0700213
Elad Alon83ccca12017-10-04 13:18:26 +0200214 rtc::Event output_stopped(true, false);
eladalon248fd4f2017-09-06 05:18:15 -0700215
Bjorn Tereliusde939432017-11-20 17:38:14 +0100216 // Binding to |this| is safe because |this| outlives the |task_queue_|.
Elad Alon83ccca12017-10-04 13:18:26 +0200217 task_queue_.PostTask([this, &output_stopped]() {
eladalon248fd4f2017-09-06 05:18:15 -0700218 RTC_DCHECK_RUN_ON(&task_queue_);
Bjorn Tereliusde939432017-11-20 17:38:14 +0100219 if (event_output_) {
220 RTC_DCHECK(event_output_->IsActive());
221 LogEventsFromMemoryToOutput();
222 }
Elad Alon83ccca12017-10-04 13:18:26 +0200223 StopLoggingInternal();
224 output_stopped.Set();
eladalon248fd4f2017-09-06 05:18:15 -0700225 });
226
Elad Alon83ccca12017-10-04 13:18:26 +0200227 output_stopped.Wait(rtc::Event::kForever);
eladalon248fd4f2017-09-06 05:18:15 -0700228
Mirko Bonadei675513b2017-11-09 11:09:25 +0100229 RTC_LOG(LS_INFO) << "WebRTC event log successfully stopped.";
Bjorn Terelius36411852015-07-30 12:45:18 +0200230}
231
Elad Alon4a87e1c2017-10-03 16:11:34 +0200232void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) {
233 RTC_DCHECK(event);
234
Bjorn Tereliusde939432017-11-20 17:38:14 +0100235 // Binding to |this| is safe because |this| outlives the |task_queue_|.
Elad Alon4a87e1c2017-10-03 16:11:34 +0200236 auto event_handler = [this](std::unique_ptr<RtcEvent> unencoded_event) {
237 RTC_DCHECK_RUN_ON(&task_queue_);
Bjorn Tereliusde939432017-11-20 17:38:14 +0100238 LogToMemory(std::move(unencoded_event));
239 if (event_output_)
240 ScheduleOutput();
Elad Alon4a87e1c2017-10-03 16:11:34 +0200241 };
242
243 task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEvent>>(
244 std::move(event), event_handler));
245}
246
Bjorn Tereliusde939432017-11-20 17:38:14 +0100247void RtcEventLogImpl::ScheduleOutput() {
248 RTC_DCHECK(event_output_ && event_output_->IsActive());
249 if (history_.size() >= kMaxEventsInHistory) {
250 // We have to emergency drain the buffer. We can't wait for the scheduled
251 // output task because there might be other event incoming before that.
252 LogEventsFromMemoryToOutput();
253 return;
254 }
255
256 if (output_period_ms_ == kImmediateOutput) {
257 // We are already on the |task_queue_| so there is no reason to post a task
258 // if we want to output immediately.
259 LogEventsFromMemoryToOutput();
260 return;
261 }
262
263 if (!output_scheduled_) {
264 output_scheduled_ = true;
265 // Binding to |this| is safe because |this| outlives the |task_queue_|.
266 auto output_task = [this]() {
267 RTC_DCHECK_RUN_ON(&task_queue_);
268 if (event_output_) {
269 RTC_DCHECK(event_output_->IsActive());
270 LogEventsFromMemoryToOutput();
271 }
272 output_scheduled_ = false;
273 };
274 int64_t now_ms = rtc::TimeMillis();
275 int64_t time_since_output_ms = now_ms - last_output_ms_;
276 uint32_t delay = rtc::SafeClamp(output_period_ms_ - time_since_output_ms, 0,
277 output_period_ms_);
278 task_queue_.PostDelayedTask(output_task, delay);
279 }
280}
281
Elad Alon4a87e1c2017-10-03 16:11:34 +0200282bool RtcEventLogImpl::AppendEventToString(const RtcEvent& event,
Elad Alon83ccca12017-10-04 13:18:26 +0200283 std::string* output_string) {
eladalon248fd4f2017-09-06 05:18:15 -0700284 RTC_DCHECK_RUN_ON(&task_queue_);
285
Elad Alon4a87e1c2017-10-03 16:11:34 +0200286 std::string encoded_event = event_encoder_->Encode(event);
eladalon248fd4f2017-09-06 05:18:15 -0700287
288 bool appended;
289 size_t potential_new_size =
Elad Alon4a87e1c2017-10-03 16:11:34 +0200290 written_bytes_ + output_string->size() + encoded_event.length();
eladalon248fd4f2017-09-06 05:18:15 -0700291 if (potential_new_size <= max_size_bytes_) {
Elad Alon83ccca12017-10-04 13:18:26 +0200292 // TODO(eladalon): This is inefficient; fix this in a separate CL.
Elad Alon4a87e1c2017-10-03 16:11:34 +0200293 *output_string += encoded_event;
eladalon248fd4f2017-09-06 05:18:15 -0700294 appended = true;
295 } else {
296 appended = false;
Bjorn Terelius36411852015-07-30 12:45:18 +0200297 }
eladalon248fd4f2017-09-06 05:18:15 -0700298
eladalon248fd4f2017-09-06 05:18:15 -0700299 return appended;
300}
301
Elad Alon4a87e1c2017-10-03 16:11:34 +0200302void RtcEventLogImpl::LogToMemory(std::unique_ptr<RtcEvent> event) {
Elad Alon92a773d2017-10-13 11:54:51 +0200303 std::deque<std::unique_ptr<RtcEvent>>& container =
304 event->IsConfigEvent() ? config_history_ : history_;
305 const size_t container_max_size =
Bjorn Terelius3d55ed62017-11-08 18:07:36 +0100306 event->IsConfigEvent() ? kMaxEventsInConfigHistory : kMaxEventsInHistory;
Elad Alon92a773d2017-10-13 11:54:51 +0200307
308 if (container.size() >= container_max_size) {
Bjorn Tereliusde939432017-11-20 17:38:14 +0100309 RTC_DCHECK(!event_output_); // Shouldn't lose events if we have an output.
Elad Alon92a773d2017-10-13 11:54:51 +0200310 container.pop_front();
eladalon248fd4f2017-09-06 05:18:15 -0700311 }
Elad Alon92a773d2017-10-13 11:54:51 +0200312 container.push_back(std::move(event));
eladalon248fd4f2017-09-06 05:18:15 -0700313}
314
Elad Alon83ccca12017-10-04 13:18:26 +0200315void RtcEventLogImpl::LogEventsFromMemoryToOutput() {
316 RTC_DCHECK(event_output_ && event_output_->IsActive());
Bjorn Tereliusde939432017-11-20 17:38:14 +0100317 last_output_ms_ = rtc::TimeMillis();
eladalon248fd4f2017-09-06 05:18:15 -0700318
Elad Alon83ccca12017-10-04 13:18:26 +0200319 std::string output_string;
eladalon248fd4f2017-09-06 05:18:15 -0700320
Bjorn Tereliusde939432017-11-20 17:38:14 +0100321 // Serialize all stream configurations that haven't already been written to
322 // this output. |num_config_events_written_| is used to track which configs we
323 // have already written. (Note that the config may have been written to
324 // previous outputs; configs are not discarded.)
Elad Alon83ccca12017-10-04 13:18:26 +0200325 bool appended = true;
Bjorn Tereliusde939432017-11-20 17:38:14 +0100326 while (num_config_events_written_ < config_history_.size()) {
327 appended = AppendEventToString(*config_history_[num_config_events_written_],
328 &output_string);
329 if (!appended)
eladalon248fd4f2017-09-06 05:18:15 -0700330 break;
Bjorn Tereliusde939432017-11-20 17:38:14 +0100331 ++num_config_events_written_;
eladalon248fd4f2017-09-06 05:18:15 -0700332 }
333
334 // Serialize the events in the event queue.
335 while (appended && !history_.empty()) {
Elad Alon4a87e1c2017-10-03 16:11:34 +0200336 appended = AppendEventToString(*history_.front(), &output_string);
eladalon248fd4f2017-09-06 05:18:15 -0700337 if (appended) {
Elad Alon83ccca12017-10-04 13:18:26 +0200338 // Known issue - if writing to the output fails, these events will have
339 // been lost. If we try to open a new output, these events will be missing
eladalon248fd4f2017-09-06 05:18:15 -0700340 // from it.
341 history_.pop_front();
342 }
343 }
344
Elad Alon83ccca12017-10-04 13:18:26 +0200345 WriteToOutput(output_string);
eladalon248fd4f2017-09-06 05:18:15 -0700346
347 if (!appended) {
Elad Alon83ccca12017-10-04 13:18:26 +0200348 // Successful partial write to the output. Some events could not be written;
349 // the output should be closed, to avoid gaps.
350 StopOutput();
eladalon248fd4f2017-09-06 05:18:15 -0700351 }
352}
353
Elad Alon83ccca12017-10-04 13:18:26 +0200354void RtcEventLogImpl::LogToOutput(std::unique_ptr<RtcEvent> event) {
355 RTC_DCHECK(event_output_ && event_output_->IsActive());
eladalon248fd4f2017-09-06 05:18:15 -0700356
Elad Alon83ccca12017-10-04 13:18:26 +0200357 std::string output_string;
eladalon248fd4f2017-09-06 05:18:15 -0700358
Elad Alon4a87e1c2017-10-03 16:11:34 +0200359 bool appended = AppendEventToString(*event, &output_string);
eladalon248fd4f2017-09-06 05:18:15 -0700360
Elad Alon4a87e1c2017-10-03 16:11:34 +0200361 if (event->IsConfigEvent()) {
Elad Alon83ccca12017-10-04 13:18:26 +0200362 // Config events need to be kept in memory too, so that they may be
363 // rewritten into future outputs, too.
eladalon248fd4f2017-09-06 05:18:15 -0700364 config_history_.push_back(std::move(event));
365 }
366
367 if (!appended) {
Elad Alon83ccca12017-10-04 13:18:26 +0200368 if (!event->IsConfigEvent()) {
369 // This event will not fit into the output; push it into |history_|
370 // instead, so that it might be logged into the next output (if any).
371 history_.push_back(std::move(event));
372 }
373 StopOutput();
eladalon248fd4f2017-09-06 05:18:15 -0700374 return;
375 }
376
Elad Alon83ccca12017-10-04 13:18:26 +0200377 WriteToOutput(output_string);
eladalon248fd4f2017-09-06 05:18:15 -0700378}
379
Elad Alon83ccca12017-10-04 13:18:26 +0200380void RtcEventLogImpl::StopOutput() {
eladalon248fd4f2017-09-06 05:18:15 -0700381 max_size_bytes_ = std::numeric_limits<decltype(max_size_bytes_)>::max();
382 written_bytes_ = 0;
Elad Alon83ccca12017-10-04 13:18:26 +0200383 event_output_.reset();
384}
eladalon248fd4f2017-09-06 05:18:15 -0700385
Elad Alon83ccca12017-10-04 13:18:26 +0200386void RtcEventLogImpl::StopLoggingInternal() {
387 if (event_output_) {
388 RTC_DCHECK(event_output_->IsActive());
389 event_output_->Write(
390 event_encoder_->Encode(*rtc::MakeUnique<RtcEventLoggingStopped>()));
391 }
392 StopOutput();
393}
394
395void RtcEventLogImpl::WriteToOutput(const std::string& output_string) {
396 RTC_DCHECK(event_output_ && event_output_->IsActive());
397 if (!event_output_->Write(output_string)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100398 RTC_LOG(LS_ERROR) << "Failed to write RTC event to output.";
Elad Alon83ccca12017-10-04 13:18:26 +0200399 // The first failure closes the output.
400 RTC_DCHECK(!event_output_->IsActive());
401 StopOutput(); // Clean-up.
402 return;
403 }
404 written_bytes_ += output_string.size();
Bjorn Terelius36411852015-07-30 12:45:18 +0200405}
406
Elad Alon27829042017-09-15 14:49:55 +0200407} // namespace
408
Bjorn Terelius36411852015-07-30 12:45:18 +0200409#endif // ENABLE_RTC_EVENT_LOG
410
411// RtcEventLog member functions.
Elad Alon4a87e1c2017-10-03 16:11:34 +0200412std::unique_ptr<RtcEventLog> RtcEventLog::Create(EncodingType encoding_type) {
terelius4311ba52016-04-22 12:40:37 -0700413#ifdef ENABLE_RTC_EVENT_LOG
Elad Alon4a87e1c2017-10-03 16:11:34 +0200414 // TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
tereliusc8e05522017-06-28 06:40:51 -0700415 constexpr int kMaxLogCount = 5;
Elad Alonf491c522017-09-15 14:49:40 +0200416 int count = 1 + std::atomic_fetch_add(&rtc_event_log_count, 1);
tereliusc8e05522017-06-28 06:40:51 -0700417 if (count > kMaxLogCount) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100418 RTC_LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. "
419 << count - 1 << " logs open already.";
Elad Alonf491c522017-09-15 14:49:40 +0200420 std::atomic_fetch_sub(&rtc_event_log_count, 1);
Elad Alon27829042017-09-15 14:49:55 +0200421 return CreateNull();
tereliusc8e05522017-06-28 06:40:51 -0700422 }
Elad Alon4a87e1c2017-10-03 16:11:34 +0200423 auto encoder = CreateEncoder(encoding_type);
424 return rtc::MakeUnique<RtcEventLogImpl>(std::move(encoder));
terelius4311ba52016-04-22 12:40:37 -0700425#else
eladalon248fd4f2017-09-06 05:18:15 -0700426 return CreateNull();
terelius4311ba52016-04-22 12:40:37 -0700427#endif // ENABLE_RTC_EVENT_LOG
Bjorn Terelius36411852015-07-30 12:45:18 +0200428}
terelius1adce142015-10-16 08:51:08 -0700429
ivoc14d5dbe2016-07-04 07:06:55 -0700430std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() {
431 return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl());
432}
433
Bjorn Terelius36411852015-07-30 12:45:18 +0200434} // namespace webrtc