|  | /* | 
|  | *  Copyright (c) 2012 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/system_wrappers/source/trace_impl.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #include "webrtc/system_wrappers/source/trace_win.h" | 
|  | #else | 
|  | #include "webrtc/system_wrappers/source/trace_posix.h" | 
|  | #endif  // _WIN32 | 
|  |  | 
|  | #include "webrtc/system_wrappers/interface/sleep.h" | 
|  |  | 
|  | #define KEY_LEN_CHARS 31 | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #pragma warning(disable:4355) | 
|  | #endif  // _WIN32 | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | const int Trace::kBoilerplateLength = 71; | 
|  | const int Trace::kTimestampPosition = 13; | 
|  | const int Trace::kTimestampLength = 12; | 
|  | uint32_t Trace::level_filter_ = kTraceDefault; | 
|  |  | 
|  | // Construct On First Use idiom. Avoids "static initialization order fiasco". | 
|  | TraceImpl* TraceImpl::StaticInstance(CountOperation count_operation, | 
|  | const TraceLevel level) { | 
|  | // Sanities to avoid taking lock unless absolutely necessary (for | 
|  | // performance reasons). count_operation == kAddRefNoCreate implies that a | 
|  | // message will be written to file. | 
|  | if ((level != kTraceAll) && (count_operation == kAddRefNoCreate)) { | 
|  | if (!(level & level_filter())) { | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  | TraceImpl* impl = | 
|  | GetStaticInstance<TraceImpl>(count_operation); | 
|  | return impl; | 
|  | } | 
|  |  | 
|  | TraceImpl* TraceImpl::GetTrace(const TraceLevel level) { | 
|  | return StaticInstance(kAddRefNoCreate, level); | 
|  | } | 
|  |  | 
|  | TraceImpl* TraceImpl::CreateInstance() { | 
|  | #if defined(_WIN32) | 
|  | return new TraceWindows(); | 
|  | #else | 
|  | return new TracePosix(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TraceImpl::TraceImpl() | 
|  | : critsect_interface_(CriticalSectionWrapper::CreateCriticalSection()), | 
|  | callback_(NULL), | 
|  | row_count_text_(0), | 
|  | file_count_text_(0), | 
|  | trace_file_(*FileWrapper::Create()), | 
|  | thread_(*ThreadWrapper::CreateThread(TraceImpl::Run, this, | 
|  | kHighestPriority, "Trace")), | 
|  | event_(*EventWrapper::Create()), | 
|  | critsect_array_(CriticalSectionWrapper::CreateCriticalSection()), | 
|  | next_free_idx_(), | 
|  | level_(), | 
|  | length_(), | 
|  | message_queue_(), | 
|  | active_queue_(0) { | 
|  | next_free_idx_[0] = 0; | 
|  | next_free_idx_[1] = 0; | 
|  |  | 
|  | unsigned int tid = 0; | 
|  | thread_.Start(tid); | 
|  |  | 
|  | for (int m = 0; m < WEBRTC_TRACE_NUM_ARRAY; ++m) { | 
|  | for (int n = 0; n < WEBRTC_TRACE_MAX_QUEUE; ++n) { | 
|  | message_queue_[m][n] = new | 
|  | char[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TraceImpl::StopThread() { | 
|  | // Release the worker thread so that it can flush any lingering messages. | 
|  | event_.Set(); | 
|  |  | 
|  | // Allow 10 ms for pending messages to be flushed out. | 
|  | // TODO(hellner): why not use condition variables to do this? Or let the | 
|  | //                worker thread die and let this thread flush remaining | 
|  | //                messages? | 
|  | SleepMs(10); | 
|  |  | 
|  | thread_.SetNotAlive(); | 
|  | // Make sure the thread finishes as quickly as possible (instead of having | 
|  | // to wait for the timeout). | 
|  | event_.Set(); | 
|  | bool stopped = thread_.Stop(); | 
|  |  | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  | trace_file_.Flush(); | 
|  | trace_file_.CloseFile(); | 
|  | return stopped; | 
|  | } | 
|  |  | 
|  | TraceImpl::~TraceImpl() { | 
|  | StopThread(); | 
|  | delete &event_; | 
|  | delete &trace_file_; | 
|  | delete &thread_; | 
|  | delete critsect_interface_; | 
|  | delete critsect_array_; | 
|  |  | 
|  | for (int m = 0; m < WEBRTC_TRACE_NUM_ARRAY; ++m) { | 
|  | for (int n = 0; n < WEBRTC_TRACE_MAX_QUEUE; ++n) { | 
|  | delete [] message_queue_[m][n]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::AddThreadId(char* trace_message) const { | 
|  | uint32_t thread_id = ThreadWrapper::GetThreadId(); | 
|  | // Messages is 12 characters. | 
|  | return sprintf(trace_message, "%10u; ", thread_id); | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::AddLevel(char* sz_message, const TraceLevel level) const { | 
|  | const int kMessageLength = 12; | 
|  | switch (level) { | 
|  | case kTraceTerseInfo: | 
|  | // Add the appropriate amount of whitespace. | 
|  | memset(sz_message, ' ', kMessageLength); | 
|  | sz_message[kMessageLength] = '\0'; | 
|  | break; | 
|  | case kTraceStateInfo: | 
|  | sprintf(sz_message, "STATEINFO ; "); | 
|  | break; | 
|  | case kTraceWarning: | 
|  | sprintf(sz_message, "WARNING   ; "); | 
|  | break; | 
|  | case kTraceError: | 
|  | sprintf(sz_message, "ERROR     ; "); | 
|  | break; | 
|  | case kTraceCritical: | 
|  | sprintf(sz_message, "CRITICAL  ; "); | 
|  | break; | 
|  | case kTraceInfo: | 
|  | sprintf(sz_message, "DEBUGINFO ; "); | 
|  | break; | 
|  | case kTraceModuleCall: | 
|  | sprintf(sz_message, "MODULECALL; "); | 
|  | break; | 
|  | case kTraceMemory: | 
|  | sprintf(sz_message, "MEMORY    ; "); | 
|  | break; | 
|  | case kTraceTimer: | 
|  | sprintf(sz_message, "TIMER     ; "); | 
|  | break; | 
|  | case kTraceStream: | 
|  | sprintf(sz_message, "STREAM    ; "); | 
|  | break; | 
|  | case kTraceApiCall: | 
|  | sprintf(sz_message, "APICALL   ; "); | 
|  | break; | 
|  | case kTraceDebug: | 
|  | sprintf(sz_message, "DEBUG     ; "); | 
|  | break; | 
|  | default: | 
|  | assert(false); | 
|  | return 0; | 
|  | } | 
|  | // All messages are 12 characters. | 
|  | return kMessageLength; | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::AddModuleAndId(char* trace_message, | 
|  | const TraceModule module, | 
|  | const int32_t id) const { | 
|  | // Use long int to prevent problems with different definitions of | 
|  | // int32_t. | 
|  | // TODO(hellner): is this actually a problem? If so, it should be better to | 
|  | //                clean up int32_t | 
|  | const long int idl = id; | 
|  | const int kMessageLength = 25; | 
|  | if (idl != -1) { | 
|  | const unsigned long int id_engine = id >> 16; | 
|  | const unsigned long int id_channel = id & 0xffff; | 
|  |  | 
|  | switch (module) { | 
|  | case kTraceUndefined: | 
|  | // Add the appropriate amount of whitespace. | 
|  | memset(trace_message, ' ', kMessageLength); | 
|  | trace_message[kMessageLength] = '\0'; | 
|  | break; | 
|  | case kTraceVoice: | 
|  | sprintf(trace_message, "       VOICE:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceVideo: | 
|  | sprintf(trace_message, "       VIDEO:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceUtility: | 
|  | sprintf(trace_message, "     UTILITY:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceRtpRtcp: | 
|  | sprintf(trace_message, "    RTP/RTCP:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceTransport: | 
|  | sprintf(trace_message, "   TRANSPORT:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceAudioCoding: | 
|  | sprintf(trace_message, "AUDIO CODING:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceSrtp: | 
|  | sprintf(trace_message, "        SRTP:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceAudioMixerServer: | 
|  | sprintf(trace_message, " AUDIO MIX/S:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceAudioMixerClient: | 
|  | sprintf(trace_message, " AUDIO MIX/C:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceVideoCoding: | 
|  | sprintf(trace_message, "VIDEO CODING:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceVideoMixer: | 
|  | // Print sleep time and API call | 
|  | sprintf(trace_message, "   VIDEO MIX:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceFile: | 
|  | sprintf(trace_message, "        FILE:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceAudioProcessing: | 
|  | sprintf(trace_message, "  AUDIO PROC:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceAudioDevice: | 
|  | sprintf(trace_message, "AUDIO DEVICE:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceVideoRenderer: | 
|  | sprintf(trace_message, "VIDEO RENDER:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceVideoCapture: | 
|  | sprintf(trace_message, "VIDEO CAPTUR:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | case kTraceRemoteBitrateEstimator: | 
|  | sprintf(trace_message, "     BWE RBE:%5ld %5ld;", id_engine, | 
|  | id_channel); | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | switch (module) { | 
|  | case kTraceUndefined: | 
|  | // Add the appropriate amount of whitespace. | 
|  | memset(trace_message, ' ', kMessageLength); | 
|  | trace_message[kMessageLength] = '\0'; | 
|  | break; | 
|  | case kTraceVoice: | 
|  | sprintf(trace_message, "       VOICE:%11ld;", idl); | 
|  | break; | 
|  | case kTraceVideo: | 
|  | sprintf(trace_message, "       VIDEO:%11ld;", idl); | 
|  | break; | 
|  | case kTraceUtility: | 
|  | sprintf(trace_message, "     UTILITY:%11ld;", idl); | 
|  | break; | 
|  | case kTraceRtpRtcp: | 
|  | sprintf(trace_message, "    RTP/RTCP:%11ld;", idl); | 
|  | break; | 
|  | case kTraceTransport: | 
|  | sprintf(trace_message, "   TRANSPORT:%11ld;", idl); | 
|  | break; | 
|  | case kTraceAudioCoding: | 
|  | sprintf(trace_message, "AUDIO CODING:%11ld;", idl); | 
|  | break; | 
|  | case kTraceSrtp: | 
|  | sprintf(trace_message, "        SRTP:%11ld;", idl); | 
|  | break; | 
|  | case kTraceAudioMixerServer: | 
|  | sprintf(trace_message, " AUDIO MIX/S:%11ld;", idl); | 
|  | break; | 
|  | case kTraceAudioMixerClient: | 
|  | sprintf(trace_message, " AUDIO MIX/C:%11ld;", idl); | 
|  | break; | 
|  | case kTraceVideoCoding: | 
|  | sprintf(trace_message, "VIDEO CODING:%11ld;", idl); | 
|  | break; | 
|  | case kTraceVideoMixer: | 
|  | sprintf(trace_message, "   VIDEO MIX:%11ld;", idl); | 
|  | break; | 
|  | case kTraceFile: | 
|  | sprintf(trace_message, "        FILE:%11ld;", idl); | 
|  | break; | 
|  | case kTraceAudioProcessing: | 
|  | sprintf(trace_message, "  AUDIO PROC:%11ld;", idl); | 
|  | break; | 
|  | case kTraceAudioDevice: | 
|  | sprintf(trace_message, "AUDIO DEVICE:%11ld;", idl); | 
|  | break; | 
|  | case kTraceVideoRenderer: | 
|  | sprintf(trace_message, "VIDEO RENDER:%11ld;", idl); | 
|  | break; | 
|  | case kTraceVideoCapture: | 
|  | sprintf(trace_message, "VIDEO CAPTUR:%11ld;", idl); | 
|  | break; | 
|  | case kTraceRemoteBitrateEstimator: | 
|  | sprintf(trace_message, "     BWE RBE:%11ld;", idl); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return kMessageLength; | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::SetTraceFileImpl(const char* file_name_utf8, | 
|  | const bool add_file_counter) { | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  |  | 
|  | trace_file_.Flush(); | 
|  | trace_file_.CloseFile(); | 
|  |  | 
|  | if (file_name_utf8) { | 
|  | if (add_file_counter) { | 
|  | file_count_text_ = 1; | 
|  |  | 
|  | char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize]; | 
|  | CreateFileName(file_name_utf8, file_name_with_counter_utf8, | 
|  | file_count_text_); | 
|  | if (trace_file_.OpenFile(file_name_with_counter_utf8, false, false, | 
|  | true) == -1) { | 
|  | return -1; | 
|  | } | 
|  | } else { | 
|  | file_count_text_ = 0; | 
|  | if (trace_file_.OpenFile(file_name_utf8, false, false, true) == -1) { | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | } | 
|  | row_count_text_ = 0; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::TraceFileImpl( | 
|  | char file_name_utf8[FileWrapper::kMaxFileNameSize]) { | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  | return trace_file_.FileName(file_name_utf8, FileWrapper::kMaxFileNameSize); | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::SetTraceCallbackImpl(TraceCallback* callback) { | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  | callback_ = callback; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t TraceImpl::AddMessage( | 
|  | char* trace_message, | 
|  | const char msg[WEBRTC_TRACE_MAX_MESSAGE_SIZE], | 
|  | const uint16_t written_so_far) const { | 
|  | int length = 0; | 
|  | if (written_so_far >= WEBRTC_TRACE_MAX_MESSAGE_SIZE) { | 
|  | return -1; | 
|  | } | 
|  | // - 2 to leave room for newline and NULL termination. | 
|  | #ifdef _WIN32 | 
|  | length = _snprintf(trace_message, | 
|  | WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2, | 
|  | "%s", msg); | 
|  | if (length < 0) { | 
|  | length = WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2; | 
|  | trace_message[length] = 0; | 
|  | } | 
|  | #else | 
|  | length = snprintf(trace_message, | 
|  | WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2, | 
|  | "%s", msg); | 
|  | if (length < 0 || | 
|  | length > WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2) { | 
|  | length = WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2; | 
|  | trace_message[length] = 0; | 
|  | } | 
|  | #endif | 
|  | // Length with NULL termination. | 
|  | return length + 1; | 
|  | } | 
|  |  | 
|  | void TraceImpl::AddMessageToList( | 
|  | const char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE], | 
|  | const uint16_t length, | 
|  | const TraceLevel level) { | 
|  | // NOTE(andresp): Enabled externally. | 
|  | #ifdef WEBRTC_DIRECT_TRACE | 
|  | if (callback_) { | 
|  | callback_->Print(level, trace_message, length); | 
|  | } | 
|  | return; | 
|  | #endif | 
|  |  | 
|  | CriticalSectionScoped lock(critsect_array_); | 
|  |  | 
|  | if (next_free_idx_[active_queue_] >= WEBRTC_TRACE_MAX_QUEUE) { | 
|  | if (!trace_file_.Open() && !callback_) { | 
|  | // Keep at least the last 1/4 of old messages when not logging. | 
|  | // TODO(hellner): isn't this redundant. The user will make it known | 
|  | //                when to start logging. Why keep messages before | 
|  | //                that? | 
|  | for (int n = 0; n < WEBRTC_TRACE_MAX_QUEUE / 4; ++n) { | 
|  | const int last_quarter_offset = (3 * WEBRTC_TRACE_MAX_QUEUE / 4); | 
|  | memcpy(message_queue_[active_queue_][n], | 
|  | message_queue_[active_queue_][n + last_quarter_offset], | 
|  | WEBRTC_TRACE_MAX_MESSAGE_SIZE); | 
|  | } | 
|  | next_free_idx_[active_queue_] = WEBRTC_TRACE_MAX_QUEUE / 4; | 
|  | } else { | 
|  | // More messages are being written than there is room for in the | 
|  | // buffer. Drop any new messages. | 
|  | // TODO(hellner): its probably better to drop old messages instead | 
|  | //                of new ones. One step further: if this happens | 
|  | //                it's due to writing faster than what can be | 
|  | //                processed. Maybe modify the filter at this point. | 
|  | //                E.g. turn of STREAM. | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | uint16_t idx = next_free_idx_[active_queue_]; | 
|  | next_free_idx_[active_queue_]++; | 
|  |  | 
|  | level_[active_queue_][idx] = level; | 
|  | length_[active_queue_][idx] = length; | 
|  | memcpy(message_queue_[active_queue_][idx], trace_message, length); | 
|  |  | 
|  | if (next_free_idx_[active_queue_] == WEBRTC_TRACE_MAX_QUEUE - 1) { | 
|  | // Logging more messages than can be worked off. Log a warning. | 
|  | const char warning_msg[] = "WARNING MISSING TRACE MESSAGES\n"; | 
|  | level_[active_queue_][next_free_idx_[active_queue_]] = kTraceWarning; | 
|  | length_[active_queue_][next_free_idx_[active_queue_]] = strlen(warning_msg); | 
|  | memcpy(message_queue_[active_queue_][next_free_idx_[active_queue_]], | 
|  | warning_msg, strlen(warning_msg)); | 
|  | next_free_idx_[active_queue_]++; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TraceImpl::Run(void* obj) { | 
|  | return static_cast<TraceImpl*>(obj)->Process(); | 
|  | } | 
|  |  | 
|  | bool TraceImpl::Process() { | 
|  | if (event_.Wait(1000) == kEventSignaled) { | 
|  | // This slightly odd construction is to avoid locking |critsect_interface_| | 
|  | // while calling WriteToFile() since it's locked inside the function. | 
|  | critsect_interface_->Enter(); | 
|  | bool write_to_file = trace_file_.Open() || callback_; | 
|  | critsect_interface_->Leave(); | 
|  | if (write_to_file) { | 
|  | WriteToFile(); | 
|  | } | 
|  | } else { | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  | trace_file_.Flush(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void TraceImpl::WriteToFile() { | 
|  | uint8_t local_queue_active = 0; | 
|  | uint16_t local_next_free_idx = 0; | 
|  |  | 
|  | // There are two buffers. One for reading (for writing to file) and one for | 
|  | // writing (for storing new messages). Let new messages be posted to the | 
|  | // unused buffer so that the current buffer can be flushed safely. | 
|  | { | 
|  | CriticalSectionScoped lock(critsect_array_); | 
|  | local_next_free_idx = next_free_idx_[active_queue_]; | 
|  | next_free_idx_[active_queue_] = 0; | 
|  | local_queue_active = active_queue_; | 
|  | if (active_queue_ == 0) { | 
|  | active_queue_ = 1; | 
|  | } else { | 
|  | active_queue_ = 0; | 
|  | } | 
|  | } | 
|  | if (local_next_free_idx == 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | CriticalSectionScoped lock(critsect_interface_); | 
|  |  | 
|  | for (uint16_t idx = 0; idx < local_next_free_idx; ++idx) { | 
|  | TraceLevel local_level = level_[local_queue_active][idx]; | 
|  | if (callback_) { | 
|  | callback_->Print(local_level, message_queue_[local_queue_active][idx], | 
|  | length_[local_queue_active][idx]); | 
|  | } | 
|  | if (trace_file_.Open()) { | 
|  | if (row_count_text_ > WEBRTC_TRACE_MAX_FILE_SIZE) { | 
|  | // wrap file | 
|  | row_count_text_ = 0; | 
|  | trace_file_.Flush(); | 
|  |  | 
|  | if (file_count_text_ == 0) { | 
|  | trace_file_.Rewind(); | 
|  | } else { | 
|  | char old_file_name[FileWrapper::kMaxFileNameSize]; | 
|  | char new_file_name[FileWrapper::kMaxFileNameSize]; | 
|  |  | 
|  | // get current name | 
|  | trace_file_.FileName(old_file_name, | 
|  | FileWrapper::kMaxFileNameSize); | 
|  | trace_file_.CloseFile(); | 
|  |  | 
|  | file_count_text_++; | 
|  |  | 
|  | UpdateFileName(old_file_name, new_file_name, file_count_text_); | 
|  |  | 
|  | if (trace_file_.OpenFile(new_file_name, false, false, | 
|  | true) == -1) { | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (row_count_text_ ==  0) { | 
|  | char message[WEBRTC_TRACE_MAX_MESSAGE_SIZE + 1]; | 
|  | int32_t length = AddDateTimeInfo(message); | 
|  | if (length != -1) { | 
|  | message[length] = 0; | 
|  | message[length - 1] = '\n'; | 
|  | trace_file_.Write(message, length); | 
|  | row_count_text_++; | 
|  | } | 
|  | length = AddBuildInfo(message); | 
|  | if (length != -1) { | 
|  | message[length + 1] = 0; | 
|  | message[length] = '\n'; | 
|  | message[length - 1] = '\n'; | 
|  | trace_file_.Write(message, length + 1); | 
|  | row_count_text_++; | 
|  | row_count_text_++; | 
|  | } | 
|  | } | 
|  | uint16_t length = length_[local_queue_active][idx]; | 
|  | message_queue_[local_queue_active][idx][length] = 0; | 
|  | message_queue_[local_queue_active][idx][length - 1] = '\n'; | 
|  | trace_file_.Write(message_queue_[local_queue_active][idx], length); | 
|  | row_count_text_++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void TraceImpl::AddImpl(const TraceLevel level, const TraceModule module, | 
|  | const int32_t id, | 
|  | const char msg[WEBRTC_TRACE_MAX_MESSAGE_SIZE]) { | 
|  | if (TraceCheck(level)) { | 
|  | char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; | 
|  | char* message_ptr = trace_message; | 
|  |  | 
|  | int32_t len = 0; | 
|  | int32_t ack_len = 0; | 
|  |  | 
|  | len = AddLevel(message_ptr, level); | 
|  | if (len == -1) { | 
|  | return; | 
|  | } | 
|  | message_ptr += len; | 
|  | ack_len += len; | 
|  |  | 
|  | len = AddTime(message_ptr, level); | 
|  | if (len == -1) { | 
|  | return; | 
|  | } | 
|  | message_ptr += len; | 
|  | ack_len += len; | 
|  |  | 
|  | len = AddModuleAndId(message_ptr, module, id); | 
|  | if (len == -1) { | 
|  | return; | 
|  | } | 
|  | message_ptr += len; | 
|  | ack_len += len; | 
|  |  | 
|  | len = AddThreadId(message_ptr); | 
|  | if (len < 0) { | 
|  | return; | 
|  | } | 
|  | message_ptr += len; | 
|  | ack_len += len; | 
|  |  | 
|  | len = AddMessage(message_ptr, msg, (uint16_t)ack_len); | 
|  | if (len == -1) { | 
|  | return; | 
|  | } | 
|  | ack_len += len; | 
|  | AddMessageToList(trace_message, (uint16_t)ack_len, level); | 
|  |  | 
|  | // Make sure that messages are written as soon as possible. | 
|  | event_.Set(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TraceImpl::TraceCheck(const TraceLevel level) const { | 
|  | return (level & level_filter()) ? true : false; | 
|  | } | 
|  |  | 
|  | bool TraceImpl::UpdateFileName( | 
|  | const char file_name_utf8[FileWrapper::kMaxFileNameSize], | 
|  | char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], | 
|  | const uint32_t new_count) const { | 
|  | int32_t length = (int32_t)strlen(file_name_utf8); | 
|  | if (length < 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int32_t length_without_file_ending = length - 1; | 
|  | while (length_without_file_ending > 0) { | 
|  | if (file_name_utf8[length_without_file_ending] == '.') { | 
|  | break; | 
|  | } else { | 
|  | length_without_file_ending--; | 
|  | } | 
|  | } | 
|  | if (length_without_file_ending == 0) { | 
|  | length_without_file_ending = length; | 
|  | } | 
|  | int32_t length_to_ = length_without_file_ending - 1; | 
|  | while (length_to_ > 0) { | 
|  | if (file_name_utf8[length_to_] == '_') { | 
|  | break; | 
|  | } else { | 
|  | length_to_--; | 
|  | } | 
|  | } | 
|  |  | 
|  | memcpy(file_name_with_counter_utf8, file_name_utf8, length_to_); | 
|  | sprintf(file_name_with_counter_utf8 + length_to_, "_%lu%s", | 
|  | static_cast<long unsigned int>(new_count), | 
|  | file_name_utf8 + length_without_file_ending); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraceImpl::CreateFileName( | 
|  | const char file_name_utf8[FileWrapper::kMaxFileNameSize], | 
|  | char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], | 
|  | const uint32_t new_count) const { | 
|  | int32_t length = (int32_t)strlen(file_name_utf8); | 
|  | if (length < 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int32_t length_without_file_ending = length - 1; | 
|  | while (length_without_file_ending > 0) { | 
|  | if (file_name_utf8[length_without_file_ending] == '.') { | 
|  | break; | 
|  | } else { | 
|  | length_without_file_ending--; | 
|  | } | 
|  | } | 
|  | if (length_without_file_ending == 0) { | 
|  | length_without_file_ending = length; | 
|  | } | 
|  | memcpy(file_name_with_counter_utf8, file_name_utf8, | 
|  | length_without_file_ending); | 
|  | sprintf(file_name_with_counter_utf8 + length_without_file_ending, "_%lu%s", | 
|  | static_cast<long unsigned int>(new_count), | 
|  | file_name_utf8 + length_without_file_ending); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Trace::CreateTrace() { | 
|  | TraceImpl::StaticInstance(kAddRef); | 
|  | } | 
|  |  | 
|  | void Trace::ReturnTrace() { | 
|  | TraceImpl::StaticInstance(kRelease); | 
|  | } | 
|  |  | 
|  | int32_t Trace::TraceFile(char file_name[FileWrapper::kMaxFileNameSize]) { | 
|  | TraceImpl* trace = TraceImpl::GetTrace(); | 
|  | if (trace) { | 
|  | int ret_val = trace->TraceFileImpl(file_name); | 
|  | ReturnTrace(); | 
|  | return ret_val; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int32_t Trace::SetTraceFile(const char* file_name, | 
|  | const bool add_file_counter) { | 
|  | TraceImpl* trace = TraceImpl::GetTrace(); | 
|  | if (trace) { | 
|  | int ret_val = trace->SetTraceFileImpl(file_name, add_file_counter); | 
|  | ReturnTrace(); | 
|  | return ret_val; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int32_t Trace::SetTraceCallback(TraceCallback* callback) { | 
|  | TraceImpl* trace = TraceImpl::GetTrace(); | 
|  | if (trace) { | 
|  | int ret_val = trace->SetTraceCallbackImpl(callback); | 
|  | ReturnTrace(); | 
|  | return ret_val; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | void Trace::Add(const TraceLevel level, const TraceModule module, | 
|  | const int32_t id, const char* msg, ...) { | 
|  | TraceImpl* trace = TraceImpl::GetTrace(level); | 
|  | if (trace) { | 
|  | if (trace->TraceCheck(level)) { | 
|  | char temp_buff[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; | 
|  | char* buff = 0; | 
|  | if (msg) { | 
|  | va_list args; | 
|  | va_start(args, msg); | 
|  | #ifdef _WIN32 | 
|  | _vsnprintf(temp_buff, WEBRTC_TRACE_MAX_MESSAGE_SIZE - 1, msg, args); | 
|  | #else | 
|  | vsnprintf(temp_buff, WEBRTC_TRACE_MAX_MESSAGE_SIZE - 1, msg, args); | 
|  | #endif | 
|  | va_end(args); | 
|  | buff = temp_buff; | 
|  | } | 
|  | trace->AddImpl(level, module, id, buff); | 
|  | } | 
|  | ReturnTrace(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |