blob: 5d071405fc344e4c958b32c42e0aa2b704911d23 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 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#if defined(WEBRTC_WIN)
Tommi23edcff2015-05-25 10:45:43 +020012#if !defined(WIN32_LEAN_AND_MEAN)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013#define WIN32_LEAN_AND_MEAN
Tommi23edcff2015-05-25 10:45:43 +020014#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000015#include <windows.h>
conceptgenesis3f705622016-01-30 14:40:44 -080016#if _MSC_VER < 1900
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017#define snprintf _snprintf
conceptgenesis3f705622016-01-30 14:40:44 -080018#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000019#undef ERROR // wingdi.h
20#endif
21
22#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
23#include <CoreServices/CoreServices.h>
24#elif defined(WEBRTC_ANDROID)
25#include <android/log.h>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000026// Android has a 1024 limit on log inputs. We use 60 chars as an
27// approx for the header/tag portion.
28// See android/system/core/liblog/logd_write.c
29static const int kMaxLogLineSize = 1024 - 60;
30#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID
31
32#include <time.h>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000033#include <limits.h>
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000034
35#include <algorithm>
36#include <iomanip>
37#include <ostream>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000038#include <vector>
39
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040#include "rtc_base/criticalsection.h"
41#include "rtc_base/logging.h"
Tommi9ecdcdf2018-02-24 15:52:11 +010042#include "rtc_base/platform_thread_types.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020043#include "rtc_base/stringencode.h"
44#include "rtc_base/stringutils.h"
45#include "rtc_base/timeutils.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000046
47namespace rtc {
andrew88703d72015-09-07 00:34:56 -070048namespace {
Jonas Olsson2b6f1352018-02-15 11:57:03 +010049// By default, release builds don't log, debug builds at info level
50#if !defined(NDEBUG)
51static LoggingSeverity g_min_sev = LS_INFO;
52static LoggingSeverity g_dbg_sev = LS_INFO;
53#else
54static LoggingSeverity g_min_sev = LS_NONE;
55static LoggingSeverity g_dbg_sev = LS_NONE;
56#endif
andrew88703d72015-09-07 00:34:56 -070057
58// Return the filename portion of the string (that following the last slash).
59const char* FilenameFromPath(const char* file) {
60 const char* end1 = ::strrchr(file, '/');
61 const char* end2 = ::strrchr(file, '\\');
62 if (!end1 && !end2)
63 return file;
64 else
65 return (end1 > end2) ? end1 + 1 : end2 + 1;
66}
67
Jonas Olsson2b6f1352018-02-15 11:57:03 +010068// Global lock for log subsystem, only needed to serialize access to streams_.
69CriticalSection g_log_crit;
andrew88703d72015-09-07 00:34:56 -070070} // namespace
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000071
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000072
73/////////////////////////////////////////////////////////////////////////////
74// LogMessage
75/////////////////////////////////////////////////////////////////////////////
76
andrew88703d72015-09-07 00:34:56 -070077bool LogMessage::log_to_stderr_ = true;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000078
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000079// The list of logging streams currently configured.
80// Note: we explicitly do not clean this up, because of the uncertain ordering
81// of destructors at program exit. Let the person who sets the stream trigger
deadbeef37f5ecf2017-02-27 14:06:41 -080082// cleanup by setting to null, or let it leak (safe at program exit).
danilchap3c6abd22017-09-06 05:46:29 -070083LogMessage::StreamList LogMessage::streams_ RTC_GUARDED_BY(g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000084
85// Boolean options default to false (0)
86bool LogMessage::thread_, LogMessage::timestamp_;
87
Peter Boström225789d2015-10-23 15:20:56 +020088LogMessage::LogMessage(const char* file,
89 int line,
90 LoggingSeverity sev,
91 LogErrorContext err_ctx,
Tommi9ecdcdf2018-02-24 15:52:11 +010092 int err)
93 : severity_(sev) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000094 if (timestamp_) {
Taylor Brandstetter4f0dfbd2016-06-15 17:15:23 -070095 // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp
96 // in log messages represents the real system time.
97 int64_t time = TimeDiff(SystemTimeMillis(), LogStartTime());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000098 // Also ensure WallClockStartTime is initialized, so that it matches
99 // LogStartTime.
100 WallClockStartTime();
101 print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
102 << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
103 << "] ";
104 }
105
106 if (thread_) {
henrikaba35d052015-07-14 17:04:08 +0200107 PlatformThreadId id = CurrentThreadId();
108 print_stream_ << "[" << std::dec << id << "] ";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000109 }
110
deadbeef37f5ecf2017-02-27 14:06:41 -0800111 if (file != nullptr)
Alex Glaznevebed24d2015-09-15 11:05:24 -0700112 print_stream_ << "(" << FilenameFromPath(file) << ":" << line << "): ";
andrew88703d72015-09-07 00:34:56 -0700113
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000114 if (err_ctx != ERRCTX_NONE) {
115 std::ostringstream tmp;
116 tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
117 switch (err_ctx) {
118 case ERRCTX_ERRNO:
119 tmp << " " << strerror(err);
120 break;
kwiberg77eab702016-09-28 17:42:01 -0700121#ifdef WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000122 case ERRCTX_HRESULT: {
123 char msgbuf[256];
Tommi9ecdcdf2018-02-24 15:52:11 +0100124 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM |
125 FORMAT_MESSAGE_IGNORE_INSERTS;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000126 if (DWORD len = FormatMessageA(
Tommi9ecdcdf2018-02-24 15:52:11 +0100127 flags, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
deadbeef37f5ecf2017-02-27 14:06:41 -0800128 msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000129 while ((len > 0) &&
130 isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
131 msgbuf[--len] = 0;
132 }
133 tmp << " " << msgbuf;
134 }
135 break;
136 }
Tommi0eefb4d2015-05-23 09:54:07 +0200137#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000138#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
139 case ERRCTX_OSSTATUS: {
Tommi09ca02e2016-04-24 17:32:48 +0200140 std::string desc(DescriptionFromOSStatus(err));
141 tmp << " " << (desc.empty() ? "Unknown error" : desc.c_str());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000142 break;
143 }
144#endif // WEBRTC_MAC && !defined(WEBRTC_IOS)
145 default:
146 break;
147 }
148 extra_ = tmp.str();
149 }
150}
151
Tommi9ecdcdf2018-02-24 15:52:11 +0100152#if defined(WEBRTC_ANDROID)
jiayl66f0da22015-09-14 15:06:39 -0700153LogMessage::LogMessage(const char* file,
154 int line,
155 LoggingSeverity sev,
Tommi9ecdcdf2018-02-24 15:52:11 +0100156 const char* tag)
deadbeef37f5ecf2017-02-27 14:06:41 -0800157 : LogMessage(file,
158 line,
159 sev,
160 ERRCTX_NONE,
Tommi9ecdcdf2018-02-24 15:52:11 +0100161 0 /* err */) {
jiayl66f0da22015-09-14 15:06:39 -0700162 tag_ = tag;
Jiayang Liue4ba6ce92015-09-21 15:49:24 -0700163 print_stream_ << tag << ": ";
jiayl66f0da22015-09-14 15:06:39 -0700164}
Tommi9ecdcdf2018-02-24 15:52:11 +0100165#endif
jiayl66f0da22015-09-14 15:06:39 -0700166
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000167LogMessage::~LogMessage() {
168 if (!extra_.empty())
169 print_stream_ << " : " << extra_;
170 print_stream_ << std::endl;
171
172 const std::string& str = print_stream_.str();
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100173 if (severity_ >= g_dbg_sev) {
Tommi9ecdcdf2018-02-24 15:52:11 +0100174#if defined(WEBRTC_ANDROID)
jiayl66f0da22015-09-14 15:06:39 -0700175 OutputToDebug(str, severity_, tag_);
Tommi9ecdcdf2018-02-24 15:52:11 +0100176#else
177 OutputToDebug(str, severity_);
178#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000179 }
180
Peter Boström225789d2015-10-23 15:20:56 +0200181 CritScope cs(&g_log_crit);
182 for (auto& kv : streams_) {
183 if (severity_ >= kv.second) {
184 kv.first->OnLogMessage(str);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000185 }
186 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000187}
188
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100189bool LogMessage::Loggable(LoggingSeverity sev) {
190 return sev >= g_min_sev;
191}
192
193int LogMessage::GetMinLogSeverity() {
194 return g_min_sev;
195}
196
197LoggingSeverity LogMessage::GetLogToDebug() {
198 return g_dbg_sev;
199}
Honghai Zhang82d78622016-05-06 11:29:15 -0700200int64_t LogMessage::LogStartTime() {
Taylor Brandstetter4f0dfbd2016-06-15 17:15:23 -0700201 static const int64_t g_start = SystemTimeMillis();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000202 return g_start;
203}
204
Peter Boström0c4e06b2015-10-07 12:23:21 +0200205uint32_t LogMessage::WallClockStartTime() {
deadbeef37f5ecf2017-02-27 14:06:41 -0800206 static const uint32_t g_start_wallclock = time(nullptr);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000207 return g_start_wallclock;
208}
209
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000210void LogMessage::LogThreads(bool on) {
211 thread_ = on;
212}
213
214void LogMessage::LogTimestamps(bool on) {
215 timestamp_ = on;
216}
217
Tommi0eefb4d2015-05-23 09:54:07 +0200218void LogMessage::LogToDebug(LoggingSeverity min_sev) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100219 g_dbg_sev = min_sev;
Peter Boström225789d2015-10-23 15:20:56 +0200220 CritScope cs(&g_log_crit);
Tommi00aac5a2015-05-25 11:25:59 +0200221 UpdateMinLogSeverity();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000222}
223
andrew88703d72015-09-07 00:34:56 -0700224void LogMessage::SetLogToStderr(bool log_to_stderr) {
225 log_to_stderr_ = log_to_stderr;
226}
227
Tommi0eefb4d2015-05-23 09:54:07 +0200228int LogMessage::GetLogToStream(LogSink* stream) {
Peter Boström225789d2015-10-23 15:20:56 +0200229 CritScope cs(&g_log_crit);
Tommi0eefb4d2015-05-23 09:54:07 +0200230 LoggingSeverity sev = LS_NONE;
Peter Boström225789d2015-10-23 15:20:56 +0200231 for (auto& kv : streams_) {
232 if (!stream || stream == kv.first) {
233 sev = std::min(sev, kv.second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000234 }
235 }
236 return sev;
237}
238
Tommi0eefb4d2015-05-23 09:54:07 +0200239void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {
Peter Boström225789d2015-10-23 15:20:56 +0200240 CritScope cs(&g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000241 streams_.push_back(std::make_pair(stream, min_sev));
242 UpdateMinLogSeverity();
243}
244
Tommi0eefb4d2015-05-23 09:54:07 +0200245void LogMessage::RemoveLogToStream(LogSink* stream) {
Peter Boström225789d2015-10-23 15:20:56 +0200246 CritScope cs(&g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000247 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
248 if (stream == it->first) {
249 streams_.erase(it);
250 break;
251 }
252 }
253 UpdateMinLogSeverity();
254}
255
Tommi0eefb4d2015-05-23 09:54:07 +0200256void LogMessage::ConfigureLogging(const char* params) {
257 LoggingSeverity current_level = LS_VERBOSE;
258 LoggingSeverity debug_level = GetLogToDebug();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000259
260 std::vector<std::string> tokens;
261 tokenize(params, ' ', &tokens);
262
Tommi0eefb4d2015-05-23 09:54:07 +0200263 for (const std::string& token : tokens) {
264 if (token.empty())
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000265 continue;
266
267 // Logging features
Tommi0eefb4d2015-05-23 09:54:07 +0200268 if (token == "tstamp") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000269 LogTimestamps();
Tommi0eefb4d2015-05-23 09:54:07 +0200270 } else if (token == "thread") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000271 LogThreads();
272
273 // Logging levels
Tommi0eefb4d2015-05-23 09:54:07 +0200274 } else if (token == "sensitive") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000275 current_level = LS_SENSITIVE;
Tommi0eefb4d2015-05-23 09:54:07 +0200276 } else if (token == "verbose") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000277 current_level = LS_VERBOSE;
Tommi0eefb4d2015-05-23 09:54:07 +0200278 } else if (token == "info") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000279 current_level = LS_INFO;
Tommi0eefb4d2015-05-23 09:54:07 +0200280 } else if (token == "warning") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000281 current_level = LS_WARNING;
Tommi0eefb4d2015-05-23 09:54:07 +0200282 } else if (token == "error") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000283 current_level = LS_ERROR;
Tommi0eefb4d2015-05-23 09:54:07 +0200284 } else if (token == "none") {
285 current_level = LS_NONE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000286
287 // Logging targets
Tommi0eefb4d2015-05-23 09:54:07 +0200288 } else if (token == "debug") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000289 debug_level = current_level;
290 }
291 }
292
293#if defined(WEBRTC_WIN)
Tommi0eefb4d2015-05-23 09:54:07 +0200294 if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000295 // First, attempt to attach to our parent's console... so if you invoke
296 // from the command line, we'll see the output there. Otherwise, create
297 // our own console window.
298 // Note: These methods fail if a console already exists, which is fine.
Tommi9ecdcdf2018-02-24 15:52:11 +0100299 if (!AttachConsole(ATTACH_PARENT_PROCESS))
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000300 ::AllocConsole();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000301 }
Tommi0eefb4d2015-05-23 09:54:07 +0200302#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000303
304 LogToDebug(debug_level);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000305}
306
danilchap3c6abd22017-09-06 05:46:29 -0700307void LogMessage::UpdateMinLogSeverity()
308 RTC_EXCLUSIVE_LOCKS_REQUIRED(g_log_crit) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100309 LoggingSeverity min_sev = g_dbg_sev;
Peter Boström225789d2015-10-23 15:20:56 +0200310 for (auto& kv : streams_) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100311 min_sev = std::min(g_dbg_sev, kv.second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000312 }
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100313 g_min_sev = min_sev;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000314}
315
Tommi9ecdcdf2018-02-24 15:52:11 +0100316#if defined(WEBRTC_ANDROID)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000317void LogMessage::OutputToDebug(const std::string& str,
jiayl66f0da22015-09-14 15:06:39 -0700318 LoggingSeverity severity,
Tommi9ecdcdf2018-02-24 15:52:11 +0100319 const char* tag) {
320#else
321void LogMessage::OutputToDebug(const std::string& str,
322 LoggingSeverity severity) {
323#endif
andrew88703d72015-09-07 00:34:56 -0700324 bool log_to_stderr = log_to_stderr_;
tfarinaa41ab932015-10-30 16:08:48 -0700325#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000326 // On the Mac, all stderr output goes to the Console log and causes clutter.
327 // So in opt builds, don't log to stderr unless the user specifically sets
328 // a preference to do so.
329 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
330 "logToStdErr",
331 kCFStringEncodingUTF8);
332 CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
deadbeef37f5ecf2017-02-27 14:06:41 -0800333 if (key != nullptr && domain != nullptr) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000334 Boolean exists_and_is_valid;
335 Boolean should_log =
336 CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
337 // If the key doesn't exist or is invalid or is false, we will not log to
338 // stderr.
339 log_to_stderr = exists_and_is_valid && should_log;
340 }
deadbeef37f5ecf2017-02-27 14:06:41 -0800341 if (key != nullptr) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000342 CFRelease(key);
343 }
Tommi9ecdcdf2018-02-24 15:52:11 +0100344#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
345
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000346#if defined(WEBRTC_WIN)
347 // Always log to the debugger.
348 // Perhaps stderr should be controlled by a preference, as on Mac?
349 OutputDebugStringA(str.c_str());
350 if (log_to_stderr) {
351 // This handles dynamically allocated consoles, too.
352 if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
353 log_to_stderr = false;
354 DWORD written = 0;
355 ::WriteFile(error_handle, str.data(), static_cast<DWORD>(str.size()),
356 &written, 0);
357 }
358 }
Tommi0eefb4d2015-05-23 09:54:07 +0200359#endif // WEBRTC_WIN
Tommi9ecdcdf2018-02-24 15:52:11 +0100360
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000361#if defined(WEBRTC_ANDROID)
362 // Android's logging facility uses severity to log messages but we
363 // need to map libjingle's severity levels to Android ones first.
364 // Also write to stderr which maybe available to executable started
365 // from the shell.
366 int prio;
367 switch (severity) {
368 case LS_SENSITIVE:
Tommi9ecdcdf2018-02-24 15:52:11 +0100369 __android_log_write(ANDROID_LOG_INFO, tag, "SENSITIVE");
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000370 if (log_to_stderr) {
371 fprintf(stderr, "SENSITIVE");
372 fflush(stderr);
373 }
374 return;
375 case LS_VERBOSE:
376 prio = ANDROID_LOG_VERBOSE;
377 break;
378 case LS_INFO:
379 prio = ANDROID_LOG_INFO;
380 break;
381 case LS_WARNING:
382 prio = ANDROID_LOG_WARN;
383 break;
384 case LS_ERROR:
385 prio = ANDROID_LOG_ERROR;
386 break;
387 default:
388 prio = ANDROID_LOG_UNKNOWN;
389 }
390
391 int size = str.size();
392 int line = 0;
393 int idx = 0;
394 const int max_lines = size / kMaxLogLineSize + 1;
395 if (max_lines == 1) {
Tommi9ecdcdf2018-02-24 15:52:11 +0100396 __android_log_print(prio, tag, "%.*s", size, str.c_str());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000397 } else {
398 while (size > 0) {
399 const int len = std::min(size, kMaxLogLineSize);
400 // Use the size of the string in the format (str may have \0 in the
401 // middle).
Tommi9ecdcdf2018-02-24 15:52:11 +0100402 __android_log_print(prio, tag, "[%d/%d] %.*s", line + 1, max_lines, len,
403 str.c_str() + idx);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000404 idx += len;
405 size -= len;
406 ++line;
407 }
408 }
409#endif // WEBRTC_ANDROID
410 if (log_to_stderr) {
411 fprintf(stderr, "%s", str.c_str());
412 fflush(stderr);
413 }
414}
415
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000416//////////////////////////////////////////////////////////////////////
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000417
418} // namespace rtc