| OLD | NEW | 
 | (Empty) | 
|    1 /* |  | 
|    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  |  | 
|   20 namespace webrtc { |  | 
|   21  |  | 
|   22 namespace { |  | 
|   23 const int kEventsInHistory = 10000; |  | 
|   24  |  | 
|   25 bool 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. |  | 
|   35 RtcEventLogHelperThread::RtcEventLogHelperThread( |  | 
|   36     SwapQueue<ControlMessage>* message_queue, |  | 
|   37     SwapQueue<std::unique_ptr<rtclog::Event>>* event_queue, |  | 
|   38     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_(), |  | 
|   52       wake_periodically_(false, false), |  | 
|   53       wake_from_hibernation_(false, false), |  | 
|   54       file_finished_(false, false), |  | 
|   55       clock_(clock) { |  | 
|   56   RTC_DCHECK(message_queue_); |  | 
|   57   RTC_DCHECK(event_queue_); |  | 
|   58   RTC_DCHECK(clock_); |  | 
|   59   thread_.Start(); |  | 
|   60 } |  | 
|   61  |  | 
|   62 RtcEventLogHelperThread::~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   } |  | 
|   75   wake_from_hibernation_.Set(); |  | 
|   76   wake_periodically_.Set();  // Wake up the output thread. |  | 
|   77   thread_.Stop();   // Wait for the thread to terminate. |  | 
|   78 } |  | 
|   79  |  | 
|   80 void RtcEventLogHelperThread::WaitForFileFinished() { |  | 
|   81   wake_from_hibernation_.Set(); |  | 
|   82   wake_periodically_.Set(); |  | 
|   83   file_finished_.Wait(rtc::Event::kForever); |  | 
|   84 } |  | 
|   85  |  | 
|   86 void RtcEventLogHelperThread::SignalNewEvent() { |  | 
|   87   wake_from_hibernation_.Set(); |  | 
|   88 } |  | 
|   89  |  | 
|   90 bool 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  |  | 
|  109 bool RtcEventLogHelperThread::LogToMemory() { |  | 
|  110   RTC_DCHECK(!file_->is_open()); |  | 
|  111   bool message_received = false; |  | 
|  112  |  | 
|  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_); |  | 
|  127     message_received = true; |  | 
|  128   } |  | 
|  129   return message_received; |  | 
|  130 } |  | 
|  131  |  | 
|  132 void RtcEventLogHelperThread::StartLogFile() { |  | 
|  133   RTC_DCHECK(file_->is_open()); |  | 
|  134   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. |  | 
|  157   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. |  | 
|  160     RTC_DCHECK(!file_->is_open()); |  | 
|  161     return; |  | 
|  162   } |  | 
|  163   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) { |  | 
|  171     RTC_DCHECK(file_->is_open()); |  | 
|  172     StopLogFile(); |  | 
|  173   } |  | 
|  174 } |  | 
|  175  |  | 
|  176 bool RtcEventLogHelperThread::LogToFile() { |  | 
|  177   RTC_DCHECK(file_->is_open()); |  | 
|  178   output_string_.clear(); |  | 
|  179   bool message_received = false; |  | 
|  180  |  | 
|  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     } |  | 
|  198     message_received = true; |  | 
|  199   } |  | 
|  200  |  | 
|  201   // Write string to file. |  | 
|  202   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. |  | 
|  205     RTC_DCHECK(!file_->is_open()); |  | 
|  206     return message_received; |  | 
|  207   } |  | 
|  208   written_bytes_ += output_string_.size(); |  | 
|  209  |  | 
|  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) { |  | 
|  216     RTC_DCHECK(file_->is_open()); |  | 
|  217     StopLogFile(); |  | 
|  218   } |  | 
|  219   return message_received; |  | 
|  220 } |  | 
|  221  |  | 
|  222 void RtcEventLogHelperThread::StopLogFile() { |  | 
|  223   RTC_DCHECK(file_->is_open()); |  | 
|  224   output_string_.clear(); |  | 
|  225  |  | 
|  226   rtclog::Event end_event; |  | 
|  227   // 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())); |  | 
|  232   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_) { |  | 
|  237     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. |  | 
|  240       RTC_DCHECK(!file_->is_open()); |  | 
|  241     } |  | 
|  242     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(); |  | 
|  251   RTC_DCHECK(!file_->is_open()); |  | 
|  252 } |  | 
|  253  |  | 
|  254 void RtcEventLogHelperThread::ProcessEvents() { |  | 
|  255   ControlMessage message; |  | 
|  256  |  | 
|  257   while (true) { |  | 
|  258     bool message_received = false; |  | 
|  259     // Process control messages. |  | 
|  260     while (message_queue_->Remove(&message)) { |  | 
|  261       switch (message.message_type) { |  | 
|  262         case ControlMessage::START_FILE: |  | 
|  263           if (!file_->is_open()) { |  | 
|  264             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           } |  | 
|  273           message_received = true; |  | 
|  274           break; |  | 
|  275         case ControlMessage::STOP_FILE: |  | 
|  276           if (file_->is_open()) { |  | 
|  277             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. |  | 
|  281           if (file_->is_open()) { |  | 
|  282             StopLogFile(); |  | 
|  283           } |  | 
|  284           file_finished_.Set(); |  | 
|  285           message_received = true; |  | 
|  286           break; |  | 
|  287         case ControlMessage::TERMINATE_THREAD: |  | 
|  288           if (file_->is_open()) { |  | 
|  289             StopLogFile(); |  | 
|  290           } |  | 
|  291           return; |  | 
|  292       } |  | 
|  293     } |  | 
|  294  |  | 
|  295     // Write events to file or memory. |  | 
|  296     if (file_->is_open()) { |  | 
|  297       message_received |= LogToFile(); |  | 
|  298     } else { |  | 
|  299       message_received |= LogToMemory(); |  | 
|  300     } |  | 
|  301  |  | 
|  302     // Accumulate a new batch of events instead of processing them one at a |  | 
|  303     // time. |  | 
|  304     if (message_received) { |  | 
|  305       wake_periodically_.Wait(100); |  | 
|  306     } else { |  | 
|  307       wake_from_hibernation_.Wait(rtc::Event::kForever); |  | 
|  308     } |  | 
|  309   } |  | 
|  310 } |  | 
|  311  |  | 
|  312 bool RtcEventLogHelperThread::ThreadOutputFunction(void* obj) { |  | 
|  313   RtcEventLogHelperThread* helper = static_cast<RtcEventLogHelperThread*>(obj); |  | 
|  314   helper->ProcessEvents(); |  | 
|  315   return false; |  | 
|  316 } |  | 
|  317  |  | 
|  318 }  // namespace webrtc |  | 
|  319  |  | 
|  320 #endif  // ENABLE_RTC_EVENT_LOG |  | 
| OLD | NEW |