Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 1 | /* |
| 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 Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "logging/rtc_event_log/rtc_event_log.h" |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 12 | |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 13 | #include <atomic> |
| 14 | #include <deque> |
| 15 | #include <functional> |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 16 | #include <limits> |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 17 | #include <memory> |
eladalon | 333264f | 2017-07-16 16:44:08 -0700 | [diff] [blame] | 18 | #include <utility> |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 19 | #include <vector> |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 20 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 21 | #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 22 | #include "logging/rtc_event_log/events/rtc_event_logging_started.h" |
| 23 | #include "logging/rtc_event_log/events/rtc_event_logging_stopped.h" |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 24 | #include "logging/rtc_event_log/output/rtc_event_log_output_file.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 25 | #include "rtc_base/checks.h" |
| 26 | #include "rtc_base/constructormagic.h" |
| 27 | #include "rtc_base/event.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 28 | #include "rtc_base/logging.h" |
Karl Wiberg | e40468b | 2017-11-22 10:42:26 +0100 | [diff] [blame] | 29 | #include "rtc_base/numerics/safe_conversions.h" |
| 30 | #include "rtc_base/numerics/safe_minmax.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 31 | #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 Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 35 | |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 36 | namespace webrtc { |
| 37 | |
ivoc | 14d5dbe | 2016-07-04 07:06:55 -0700 | [diff] [blame] | 38 | #ifdef ENABLE_RTC_EVENT_LOG |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 39 | |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 40 | namespace { |
Elad Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 41 | constexpr 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. |
| 44 | constexpr size_t kMaxEventsInConfigHistory = 1000; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 45 | |
Elad Alon | f491c52 | 2017-09-15 14:49:40 +0200 | [diff] [blame] | 46 | // 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|. |
| 49 | std::atomic<int> rtc_event_log_count(0); |
| 50 | |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 51 | // 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. |
| 54 | template <typename T> |
| 55 | class 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 | }; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 70 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 71 | std::unique_ptr<RtcEventLogEncoder> CreateEncoder( |
| 72 | RtcEventLog::EncodingType type) { |
| 73 | switch (type) { |
| 74 | case RtcEventLog::EncodingType::Legacy: |
| 75 | return rtc::MakeUnique<RtcEventLogEncoderLegacy>(); |
| 76 | default: |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 77 | RTC_LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type) |
| 78 | << ")"; |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 79 | RTC_NOTREACHED(); |
| 80 | return std::unique_ptr<RtcEventLogEncoder>(nullptr); |
| 81 | } |
| 82 | } |
| 83 | |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 84 | class RtcEventLogImpl final : public RtcEventLog { |
| 85 | public: |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 86 | explicit RtcEventLogImpl(std::unique_ptr<RtcEventLogEncoder> event_encoder); |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 87 | ~RtcEventLogImpl() override; |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 88 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 89 | // 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 Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 91 | bool StartLogging(std::unique_ptr<RtcEventLogOutput> output, |
| 92 | int64_t output_period_ms) override; |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 93 | void StopLogging() override; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 94 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 95 | void Log(std::unique_ptr<RtcEvent> event) override; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 96 | |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 97 | private: |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 98 | // 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 Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 101 | // The event is encoded before being appended. |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 102 | // 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 Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 105 | bool AppendEventToString(const RtcEvent& event, |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 106 | std::string* output_string) RTC_WARN_UNUSED_RESULT; |
terelius | c8e0552 | 2017-06-28 06:40:51 -0700 | [diff] [blame] | 107 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 108 | void LogToMemory(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_); |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 109 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 110 | 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_); |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 117 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 118 | void ScheduleOutput() RTC_RUN_ON(&task_queue_); |
| 119 | |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 120 | // 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 Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 125 | std::deque<std::unique_ptr<RtcEvent>> config_history_ |
danilchap | a37de39 | 2017-09-09 04:17:22 -0700 | [diff] [blame] | 126 | RTC_ACCESS_ON(task_queue_); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 127 | |
| 128 | // History containing the most recent (non-configuration) events (~10s). |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 129 | std::deque<std::unique_ptr<RtcEvent>> history_ RTC_ACCESS_ON(task_queue_); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 130 | |
danilchap | a37de39 | 2017-09-09 04:17:22 -0700 | [diff] [blame] | 131 | size_t max_size_bytes_ RTC_ACCESS_ON(task_queue_); |
| 132 | size_t written_bytes_ RTC_ACCESS_ON(task_queue_); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 133 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 134 | std::unique_ptr<RtcEventLogEncoder> event_encoder_ RTC_ACCESS_ON(task_queue_); |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 135 | std::unique_ptr<RtcEventLogOutput> event_output_ RTC_ACCESS_ON(task_queue_); |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 136 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 137 | 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. |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 146 | rtc::TaskQueue task_queue_; |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 147 | |
nisse | 3061276 | 2016-12-20 05:03:58 -0800 | [diff] [blame] | 148 | RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl); |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 149 | }; |
| 150 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 151 | RtcEventLogImpl::RtcEventLogImpl( |
| 152 | std::unique_ptr<RtcEventLogEncoder> event_encoder) |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 153 | : max_size_bytes_(std::numeric_limits<decltype(max_size_bytes_)>::max()), |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 154 | written_bytes_(0), |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 155 | event_encoder_(std::move(event_encoder)), |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 156 | num_config_events_written_(0), |
| 157 | output_period_ms_(kImmediateOutput), |
| 158 | last_output_ms_(rtc::TimeMillis()), |
| 159 | output_scheduled_(false), |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 160 | task_queue_("rtc_event_log") {} |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 161 | |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 162 | RtcEventLogImpl::~RtcEventLogImpl() { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 163 | RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); |
| 164 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 165 | // If we're logging to the output, this will stop that. Blocking function. |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 166 | StopLogging(); |
| 167 | |
Elad Alon | f491c52 | 2017-09-15 14:49:40 +0200 | [diff] [blame] | 168 | int count = std::atomic_fetch_sub(&rtc_event_log_count, 1) - 1; |
terelius | c8e0552 | 2017-06-28 06:40:51 -0700 | [diff] [blame] | 169 | RTC_DCHECK_GE(count, 0); |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 170 | } |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 171 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 172 | bool RtcEventLogImpl::StartLogging(std::unique_ptr<RtcEventLogOutput> output, |
| 173 | int64_t output_period_ms) { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 174 | RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); |
| 175 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 176 | RTC_DCHECK(output_period_ms == kImmediateOutput || output_period_ms > 0); |
| 177 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 178 | if (!output->IsActive()) { |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 179 | // TODO(eladalon): We may want to remove the IsActive method. Otherwise |
| 180 | // we probably want to be consistent and terminate any existing output. |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 181 | return false; |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 182 | } |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 183 | |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 184 | RTC_LOG(LS_INFO) << "Starting WebRTC event log."; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 185 | |
| 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 Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 193 | // Binding to |this| is safe because |this| outlives the |task_queue_|. |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 194 | 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 Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 198 | num_config_events_written_ = 0; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 199 | LogToOutput(rtc::MakeUnique<RtcEventLoggingStarted>(start_event)); |
| 200 | LogEventsFromMemoryToOutput(); |
| 201 | }; |
| 202 | |
| 203 | task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEventLogOutput>>( |
| 204 | std::move(output), start)); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 205 | |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 206 | return true; |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | void RtcEventLogImpl::StopLogging() { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 210 | RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_); |
| 211 | |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 212 | RTC_LOG(LS_INFO) << "Stopping WebRTC event log."; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 213 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 214 | rtc::Event output_stopped(true, false); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 215 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 216 | // Binding to |this| is safe because |this| outlives the |task_queue_|. |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 217 | task_queue_.PostTask([this, &output_stopped]() { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 218 | RTC_DCHECK_RUN_ON(&task_queue_); |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 219 | if (event_output_) { |
| 220 | RTC_DCHECK(event_output_->IsActive()); |
| 221 | LogEventsFromMemoryToOutput(); |
| 222 | } |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 223 | StopLoggingInternal(); |
| 224 | output_stopped.Set(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 225 | }); |
| 226 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 227 | output_stopped.Wait(rtc::Event::kForever); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 228 | |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 229 | RTC_LOG(LS_INFO) << "WebRTC event log successfully stopped."; |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 230 | } |
| 231 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 232 | void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) { |
| 233 | RTC_DCHECK(event); |
| 234 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 235 | // Binding to |this| is safe because |this| outlives the |task_queue_|. |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 236 | auto event_handler = [this](std::unique_ptr<RtcEvent> unencoded_event) { |
| 237 | RTC_DCHECK_RUN_ON(&task_queue_); |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 238 | LogToMemory(std::move(unencoded_event)); |
| 239 | if (event_output_) |
| 240 | ScheduleOutput(); |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 241 | }; |
| 242 | |
| 243 | task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEvent>>( |
| 244 | std::move(event), event_handler)); |
| 245 | } |
| 246 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 247 | void 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 Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 282 | bool RtcEventLogImpl::AppendEventToString(const RtcEvent& event, |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 283 | std::string* output_string) { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 284 | RTC_DCHECK_RUN_ON(&task_queue_); |
| 285 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 286 | std::string encoded_event = event_encoder_->Encode(event); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 287 | |
| 288 | bool appended; |
| 289 | size_t potential_new_size = |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 290 | written_bytes_ + output_string->size() + encoded_event.length(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 291 | if (potential_new_size <= max_size_bytes_) { |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 292 | // TODO(eladalon): This is inefficient; fix this in a separate CL. |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 293 | *output_string += encoded_event; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 294 | appended = true; |
| 295 | } else { |
| 296 | appended = false; |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 297 | } |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 298 | |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 299 | return appended; |
| 300 | } |
| 301 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 302 | void RtcEventLogImpl::LogToMemory(std::unique_ptr<RtcEvent> event) { |
Elad Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 303 | std::deque<std::unique_ptr<RtcEvent>>& container = |
| 304 | event->IsConfigEvent() ? config_history_ : history_; |
| 305 | const size_t container_max_size = |
Bjorn Terelius | 3d55ed6 | 2017-11-08 18:07:36 +0100 | [diff] [blame] | 306 | event->IsConfigEvent() ? kMaxEventsInConfigHistory : kMaxEventsInHistory; |
Elad Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 307 | |
| 308 | if (container.size() >= container_max_size) { |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 309 | RTC_DCHECK(!event_output_); // Shouldn't lose events if we have an output. |
Elad Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 310 | container.pop_front(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 311 | } |
Elad Alon | 92a773d | 2017-10-13 11:54:51 +0200 | [diff] [blame] | 312 | container.push_back(std::move(event)); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 313 | } |
| 314 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 315 | void RtcEventLogImpl::LogEventsFromMemoryToOutput() { |
| 316 | RTC_DCHECK(event_output_ && event_output_->IsActive()); |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 317 | last_output_ms_ = rtc::TimeMillis(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 318 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 319 | std::string output_string; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 320 | |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 321 | // 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 Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 325 | bool appended = true; |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 326 | while (num_config_events_written_ < config_history_.size()) { |
| 327 | appended = AppendEventToString(*config_history_[num_config_events_written_], |
| 328 | &output_string); |
| 329 | if (!appended) |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 330 | break; |
Bjorn Terelius | de93943 | 2017-11-20 17:38:14 +0100 | [diff] [blame] | 331 | ++num_config_events_written_; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | // Serialize the events in the event queue. |
| 335 | while (appended && !history_.empty()) { |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 336 | appended = AppendEventToString(*history_.front(), &output_string); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 337 | if (appended) { |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 338 | // 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 |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 340 | // from it. |
| 341 | history_.pop_front(); |
| 342 | } |
| 343 | } |
| 344 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 345 | WriteToOutput(output_string); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 346 | |
| 347 | if (!appended) { |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 348 | // Successful partial write to the output. Some events could not be written; |
| 349 | // the output should be closed, to avoid gaps. |
| 350 | StopOutput(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 351 | } |
| 352 | } |
| 353 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 354 | void RtcEventLogImpl::LogToOutput(std::unique_ptr<RtcEvent> event) { |
| 355 | RTC_DCHECK(event_output_ && event_output_->IsActive()); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 356 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 357 | std::string output_string; |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 358 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 359 | bool appended = AppendEventToString(*event, &output_string); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 360 | |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 361 | if (event->IsConfigEvent()) { |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 362 | // Config events need to be kept in memory too, so that they may be |
| 363 | // rewritten into future outputs, too. |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 364 | config_history_.push_back(std::move(event)); |
| 365 | } |
| 366 | |
| 367 | if (!appended) { |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 368 | 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(); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 374 | return; |
| 375 | } |
| 376 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 377 | WriteToOutput(output_string); |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 378 | } |
| 379 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 380 | void RtcEventLogImpl::StopOutput() { |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 381 | max_size_bytes_ = std::numeric_limits<decltype(max_size_bytes_)>::max(); |
| 382 | written_bytes_ = 0; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 383 | event_output_.reset(); |
| 384 | } |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 385 | |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 386 | void 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 | |
| 395 | void RtcEventLogImpl::WriteToOutput(const std::string& output_string) { |
| 396 | RTC_DCHECK(event_output_ && event_output_->IsActive()); |
| 397 | if (!event_output_->Write(output_string)) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 398 | RTC_LOG(LS_ERROR) << "Failed to write RTC event to output."; |
Elad Alon | 83ccca1 | 2017-10-04 13:18:26 +0200 | [diff] [blame] | 399 | // 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 Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 405 | } |
| 406 | |
Elad Alon | 2782904 | 2017-09-15 14:49:55 +0200 | [diff] [blame] | 407 | } // namespace |
| 408 | |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 409 | #endif // ENABLE_RTC_EVENT_LOG |
| 410 | |
| 411 | // RtcEventLog member functions. |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 412 | std::unique_ptr<RtcEventLog> RtcEventLog::Create(EncodingType encoding_type) { |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 413 | #ifdef ENABLE_RTC_EVENT_LOG |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 414 | // TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|. |
terelius | c8e0552 | 2017-06-28 06:40:51 -0700 | [diff] [blame] | 415 | constexpr int kMaxLogCount = 5; |
Elad Alon | f491c52 | 2017-09-15 14:49:40 +0200 | [diff] [blame] | 416 | int count = 1 + std::atomic_fetch_add(&rtc_event_log_count, 1); |
terelius | c8e0552 | 2017-06-28 06:40:51 -0700 | [diff] [blame] | 417 | if (count > kMaxLogCount) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 418 | RTC_LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. " |
| 419 | << count - 1 << " logs open already."; |
Elad Alon | f491c52 | 2017-09-15 14:49:40 +0200 | [diff] [blame] | 420 | std::atomic_fetch_sub(&rtc_event_log_count, 1); |
Elad Alon | 2782904 | 2017-09-15 14:49:55 +0200 | [diff] [blame] | 421 | return CreateNull(); |
terelius | c8e0552 | 2017-06-28 06:40:51 -0700 | [diff] [blame] | 422 | } |
Elad Alon | 4a87e1c | 2017-10-03 16:11:34 +0200 | [diff] [blame] | 423 | auto encoder = CreateEncoder(encoding_type); |
| 424 | return rtc::MakeUnique<RtcEventLogImpl>(std::move(encoder)); |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 425 | #else |
eladalon | 248fd4f | 2017-09-06 05:18:15 -0700 | [diff] [blame] | 426 | return CreateNull(); |
terelius | 4311ba5 | 2016-04-22 12:40:37 -0700 | [diff] [blame] | 427 | #endif // ENABLE_RTC_EVENT_LOG |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 428 | } |
terelius | 1adce14 | 2015-10-16 08:51:08 -0700 | [diff] [blame] | 429 | |
ivoc | 14d5dbe | 2016-07-04 07:06:55 -0700 | [diff] [blame] | 430 | std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() { |
| 431 | return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl()); |
| 432 | } |
| 433 | |
Bjorn Terelius | 3641185 | 2015-07-30 12:45:18 +0200 | [diff] [blame] | 434 | } // namespace webrtc |