blob: c0f89726311bbc1c983f43291327463289ec0e82 [file] [log] [blame]
terelius4311ba52016-04-22 12:40:37 -07001/*
2 * Copyright (c) 2016 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
11#include "webrtc/call/rtc_event_log_helper_thread.h"
12
13#include <algorithm>
14
15#include "webrtc/base/checks.h"
16#include "webrtc/system_wrappers/include/logging.h"
17
18#ifdef ENABLE_RTC_EVENT_LOG
19
20namespace webrtc {
21
22namespace {
23const int kEventsInHistory = 10000;
24
25bool IsConfigEvent(const rtclog::Event& event) {
26 rtclog::Event_EventType event_type = event.type();
27 return event_type == rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT ||
28 event_type == rtclog::Event::VIDEO_SENDER_CONFIG_EVENT ||
29 event_type == rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT ||
30 event_type == rtclog::Event::AUDIO_SENDER_CONFIG_EVENT;
31}
32} // namespace
33
34// RtcEventLogImpl member functions.
35RtcEventLogHelperThread::RtcEventLogHelperThread(
36 SwapQueue<ControlMessage>* message_queue,
37 SwapQueue<std::unique_ptr<rtclog::Event>>* event_queue,
terelius4311ba52016-04-22 12:40:37 -070038 const Clock* const clock)
39 : message_queue_(message_queue),
40 event_queue_(event_queue),
41 history_(kEventsInHistory),
42 config_history_(),
43 file_(FileWrapper::Create()),
44 thread_(&ThreadOutputFunction, this, "RtcEventLog thread"),
45 max_size_bytes_(std::numeric_limits<int64_t>::max()),
46 written_bytes_(0),
47 start_time_(0),
48 stop_time_(std::numeric_limits<int64_t>::max()),
49 has_recent_event_(false),
50 most_recent_event_(),
51 output_string_(),
tereliusbea89592016-06-08 07:20:29 -070052 wake_periodically_(false, false),
53 wake_from_hibernation_(false, false),
54 file_finished_(false, false),
terelius4311ba52016-04-22 12:40:37 -070055 clock_(clock) {
56 RTC_DCHECK(message_queue_);
57 RTC_DCHECK(event_queue_);
terelius4311ba52016-04-22 12:40:37 -070058 RTC_DCHECK(clock_);
59 thread_.Start();
60}
61
62RtcEventLogHelperThread::~RtcEventLogHelperThread() {
63 ControlMessage message;
64 message.message_type = ControlMessage::TERMINATE_THREAD;
65 message.stop_time = clock_->TimeInMicroseconds();
66 while (!message_queue_->Insert(&message)) {
67 // We can't destroy the event log until we have stopped the thread,
68 // so clear the message queue and try again. Note that if we clear
69 // any STOP_FILE events, then the threads calling StopLogging would likely
70 // wait indefinitely. However, there should not be any such calls as we
71 // are executing the destructor.
72 LOG(LS_WARNING) << "Clearing message queue to terminate thread.";
73 message_queue_->Clear();
74 }
tereliusbea89592016-06-08 07:20:29 -070075 wake_from_hibernation_.Set();
76 wake_periodically_.Set(); // Wake up the output thread.
terelius4311ba52016-04-22 12:40:37 -070077 thread_.Stop(); // Wait for the thread to terminate.
78}
79
tereliusbea89592016-06-08 07:20:29 -070080void RtcEventLogHelperThread::WaitForFileFinished() {
81 wake_from_hibernation_.Set();
82 wake_periodically_.Set();
83 file_finished_.Wait(rtc::Event::kForever);
84}
85
86void RtcEventLogHelperThread::SignalNewEvent() {
87 wake_from_hibernation_.Set();
88}
89
terelius4311ba52016-04-22 12:40:37 -070090bool RtcEventLogHelperThread::AppendEventToString(rtclog::Event* event) {
91 rtclog::EventStream event_stream;
92 event_stream.add_stream();
93 event_stream.mutable_stream(0)->Swap(event);
94 // We create a new event stream per event but because of the way protobufs
95 // are encoded, events can be merged by concatenating them. Therefore,
96 // it will look like a single stream when we read it back from file.
97 bool stop = true;
98 if (written_bytes_ + static_cast<int64_t>(output_string_.size()) +
99 event_stream.ByteSize() <=
100 max_size_bytes_) {
101 event_stream.AppendToString(&output_string_);
102 stop = false;
103 }
104 // Swap the event back so that we don't mix event types in the queues.
105 event_stream.mutable_stream(0)->Swap(event);
106 return stop;
107}
108
tereliusbea89592016-06-08 07:20:29 -0700109bool RtcEventLogHelperThread::LogToMemory() {
tommia6219cc2016-06-15 10:30:14 -0700110 RTC_DCHECK(!file_->is_open());
tereliusbea89592016-06-08 07:20:29 -0700111 bool message_received = false;
terelius4311ba52016-04-22 12:40:37 -0700112
113 // Process each event earlier than the current time and append it to the
114 // appropriate history_.
115 int64_t current_time = clock_->TimeInMicroseconds();
116 if (!has_recent_event_) {
117 has_recent_event_ = event_queue_->Remove(&most_recent_event_);
118 }
119 while (has_recent_event_ &&
120 most_recent_event_->timestamp_us() <= current_time) {
121 if (IsConfigEvent(*most_recent_event_)) {
122 config_history_.push_back(std::move(most_recent_event_));
123 } else {
124 history_.push_back(std::move(most_recent_event_));
125 }
126 has_recent_event_ = event_queue_->Remove(&most_recent_event_);
tereliusbea89592016-06-08 07:20:29 -0700127 message_received = true;
terelius4311ba52016-04-22 12:40:37 -0700128 }
tereliusbea89592016-06-08 07:20:29 -0700129 return message_received;
terelius4311ba52016-04-22 12:40:37 -0700130}
131
132void RtcEventLogHelperThread::StartLogFile() {
tommia6219cc2016-06-15 10:30:14 -0700133 RTC_DCHECK(file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700134 bool stop = false;
135 output_string_.clear();
136
137 // Create and serialize the LOG_START event.
138 rtclog::Event start_event;
139 start_event.set_timestamp_us(start_time_);
140 start_event.set_type(rtclog::Event::LOG_START);
141 AppendEventToString(&start_event);
142
143 // Serialize the config information for all old streams.
144 for (auto& event : config_history_) {
145 AppendEventToString(event.get());
146 }
147
148 // Serialize the events in the event queue.
149 while (!history_.empty() && !stop) {
150 stop = AppendEventToString(history_.front().get());
151 if (!stop) {
152 history_.pop_front();
153 }
154 }
155
156 // Write to file.
tereliusf1a9a542016-05-24 01:03:59 -0700157 if (!file_->Write(output_string_.data(), output_string_.size())) {
158 LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
159 // The current FileWrapper implementation closes the file on error.
tommia6219cc2016-06-15 10:30:14 -0700160 RTC_DCHECK(!file_->is_open());
tereliusf1a9a542016-05-24 01:03:59 -0700161 return;
162 }
terelius4311ba52016-04-22 12:40:37 -0700163 written_bytes_ += output_string_.size();
164
165 // Free the allocated memory since we probably won't need this amount of
166 // space again.
167 output_string_.clear();
168 output_string_.shrink_to_fit();
169
170 if (stop) {
tommia6219cc2016-06-15 10:30:14 -0700171 RTC_DCHECK(file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700172 StopLogFile();
173 }
174}
175
tereliusbea89592016-06-08 07:20:29 -0700176bool RtcEventLogHelperThread::LogToFile() {
tommia6219cc2016-06-15 10:30:14 -0700177 RTC_DCHECK(file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700178 output_string_.clear();
tereliusbea89592016-06-08 07:20:29 -0700179 bool message_received = false;
terelius4311ba52016-04-22 12:40:37 -0700180
181 // Append each event older than both the current time and the stop time
182 // to the output_string_.
183 int64_t current_time = clock_->TimeInMicroseconds();
184 int64_t time_limit = std::min(current_time, stop_time_);
185 if (!has_recent_event_) {
186 has_recent_event_ = event_queue_->Remove(&most_recent_event_);
187 }
188 bool stop = false;
189 while (!stop && has_recent_event_ &&
190 most_recent_event_->timestamp_us() <= time_limit) {
191 stop = AppendEventToString(most_recent_event_.get());
192 if (!stop) {
193 if (IsConfigEvent(*most_recent_event_)) {
194 config_history_.push_back(std::move(most_recent_event_));
195 }
196 has_recent_event_ = event_queue_->Remove(&most_recent_event_);
197 }
tereliusbea89592016-06-08 07:20:29 -0700198 message_received = true;
terelius4311ba52016-04-22 12:40:37 -0700199 }
200
201 // Write string to file.
tereliusf1a9a542016-05-24 01:03:59 -0700202 if (!file_->Write(output_string_.data(), output_string_.size())) {
203 LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
204 // The current FileWrapper implementation closes the file on error.
tommia6219cc2016-06-15 10:30:14 -0700205 RTC_DCHECK(!file_->is_open());
tereliusbea89592016-06-08 07:20:29 -0700206 return message_received;
terelius4311ba52016-04-22 12:40:37 -0700207 }
tereliusf1a9a542016-05-24 01:03:59 -0700208 written_bytes_ += output_string_.size();
terelius4311ba52016-04-22 12:40:37 -0700209
210 // We want to stop logging if we have reached the file size limit. We also
211 // want to stop logging if the remaining events are more recent than the
212 // time limit, or in other words if we have terminated the loop despite
213 // having more events in the queue.
214 if ((has_recent_event_ && most_recent_event_->timestamp_us() > stop_time_) ||
215 stop) {
tommia6219cc2016-06-15 10:30:14 -0700216 RTC_DCHECK(file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700217 StopLogFile();
218 }
tereliusbea89592016-06-08 07:20:29 -0700219 return message_received;
terelius4311ba52016-04-22 12:40:37 -0700220}
221
222void RtcEventLogHelperThread::StopLogFile() {
tommia6219cc2016-06-15 10:30:14 -0700223 RTC_DCHECK(file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700224 output_string_.clear();
225
226 rtclog::Event end_event;
terelius76a44d52016-07-29 12:38:39 -0700227 // This function can be called either because we have reached the stop time,
228 // or because we have reached the log file size limit. Therefore, use the
229 // current time if we have not reached the time limit.
230 end_event.set_timestamp_us(
231 std::min(stop_time_, clock_->TimeInMicroseconds()));
terelius4311ba52016-04-22 12:40:37 -0700232 end_event.set_type(rtclog::Event::LOG_END);
233 AppendEventToString(&end_event);
234
235 if (written_bytes_ + static_cast<int64_t>(output_string_.size()) <=
236 max_size_bytes_) {
tereliusf1a9a542016-05-24 01:03:59 -0700237 if (!file_->Write(output_string_.data(), output_string_.size())) {
238 LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
239 // The current FileWrapper implementation closes the file on error.
tommia6219cc2016-06-15 10:30:14 -0700240 RTC_DCHECK(!file_->is_open());
tereliusf1a9a542016-05-24 01:03:59 -0700241 }
terelius4311ba52016-04-22 12:40:37 -0700242 written_bytes_ += output_string_.size();
243 }
244
245 max_size_bytes_ = std::numeric_limits<int64_t>::max();
246 written_bytes_ = 0;
247 start_time_ = 0;
248 stop_time_ = std::numeric_limits<int64_t>::max();
249 output_string_.clear();
250 file_->CloseFile();
tommia6219cc2016-06-15 10:30:14 -0700251 RTC_DCHECK(!file_->is_open());
terelius4311ba52016-04-22 12:40:37 -0700252}
253
tereliusbea89592016-06-08 07:20:29 -0700254void RtcEventLogHelperThread::ProcessEvents() {
terelius4311ba52016-04-22 12:40:37 -0700255 ControlMessage message;
256
257 while (true) {
tereliusbea89592016-06-08 07:20:29 -0700258 bool message_received = false;
terelius4311ba52016-04-22 12:40:37 -0700259 // Process control messages.
260 while (message_queue_->Remove(&message)) {
261 switch (message.message_type) {
262 case ControlMessage::START_FILE:
tommia6219cc2016-06-15 10:30:14 -0700263 if (!file_->is_open()) {
terelius4311ba52016-04-22 12:40:37 -0700264 max_size_bytes_ = message.max_size_bytes;
265 start_time_ = message.start_time;
266 stop_time_ = message.stop_time;
267 file_.swap(message.file);
268 StartLogFile();
269 } else {
270 // Already started. Ignore message and close file handle.
271 message.file->CloseFile();
272 }
tereliusbea89592016-06-08 07:20:29 -0700273 message_received = true;
terelius4311ba52016-04-22 12:40:37 -0700274 break;
275 case ControlMessage::STOP_FILE:
tommia6219cc2016-06-15 10:30:14 -0700276 if (file_->is_open()) {
terelius4311ba52016-04-22 12:40:37 -0700277 stop_time_ = message.stop_time;
278 LogToFile(); // Log remaining events from message queues.
279 }
280 // LogToFile might stop on it's own so we need to recheck the state.
tommia6219cc2016-06-15 10:30:14 -0700281 if (file_->is_open()) {
terelius4311ba52016-04-22 12:40:37 -0700282 StopLogFile();
283 }
tereliusbea89592016-06-08 07:20:29 -0700284 file_finished_.Set();
285 message_received = true;
terelius4311ba52016-04-22 12:40:37 -0700286 break;
287 case ControlMessage::TERMINATE_THREAD:
tommia6219cc2016-06-15 10:30:14 -0700288 if (file_->is_open()) {
terelius4311ba52016-04-22 12:40:37 -0700289 StopLogFile();
290 }
291 return;
292 }
293 }
294
tereliusbea89592016-06-08 07:20:29 -0700295 // Write events to file or memory.
tommia6219cc2016-06-15 10:30:14 -0700296 if (file_->is_open()) {
tereliusbea89592016-06-08 07:20:29 -0700297 message_received |= LogToFile();
terelius4311ba52016-04-22 12:40:37 -0700298 } else {
tereliusbea89592016-06-08 07:20:29 -0700299 message_received |= LogToMemory();
terelius4311ba52016-04-22 12:40:37 -0700300 }
301
302 // Accumulate a new batch of events instead of processing them one at a
303 // time.
tereliusbea89592016-06-08 07:20:29 -0700304 if (message_received) {
305 wake_periodically_.Wait(100);
306 } else {
307 wake_from_hibernation_.Wait(rtc::Event::kForever);
308 }
terelius4311ba52016-04-22 12:40:37 -0700309 }
310}
311
312bool RtcEventLogHelperThread::ThreadOutputFunction(void* obj) {
313 RtcEventLogHelperThread* helper = static_cast<RtcEventLogHelperThread*>(obj);
tereliusbea89592016-06-08 07:20:29 -0700314 helper->ProcessEvents();
terelius4311ba52016-04-22 12:40:37 -0700315 return false;
316}
317
318} // namespace webrtc
319
320#endif // ENABLE_RTC_EVENT_LOG