1/*
2 *  Copyright 2012 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 "third_party/webrtc/overrides/webrtc/base/logging.h"
12
13#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
14#include <CoreServices/CoreServices.h>
15#endif  // OS_MACOSX
16
17#include <iomanip>
18
19#include "base/atomicops.h"
20#include "base/strings/string_util.h"
21#include "base/threading/platform_thread.h"
22#include "third_party/webrtc/base/ipaddress.h"
23#include "third_party/webrtc/base/stream.h"
24#include "third_party/webrtc/base/stringencode.h"
25#include "third_party/webrtc/base/stringutils.h"
26#include "third_party/webrtc/base/timeutils.h"
27
28// From this file we can't use VLOG since it expands into usage of the __FILE__
29// macro (for correct filtering). The actual logging call from DIAGNOSTIC_LOG in
30// ~DiagnosticLogMessage. Note that the second parameter to the LAZY_STREAM
31// macro is true since the filter check has already been done for
32// DIAGNOSTIC_LOG.
33#define LOG_LAZY_STREAM_DIRECT(file_name, line_number, sev) \
34  LAZY_STREAM(logging::LogMessage(file_name, line_number, \
35                                  -sev).stream(), true)
36
37namespace rtc {
38
39void (*g_logging_delegate_function)(const std::string&) = NULL;
40void (*g_extra_logging_init_function)(
41    void (*logging_delegate_function)(const std::string&)) = NULL;
42#ifndef NDEBUG
43COMPILE_ASSERT(sizeof(base::subtle::Atomic32) == sizeof(base::PlatformThreadId),
44               atomic32_not_same_size_as_platformthreadid);
45base::subtle::Atomic32 g_init_logging_delegate_thread_id = 0;
46#endif
47
48/////////////////////////////////////////////////////////////////////////////
49// Constant Labels
50/////////////////////////////////////////////////////////////////////////////
51
52const char* FindLabel(int value, const ConstantLabel entries[]) {
53  for (int i = 0; entries[i].label; ++i) {
54    if (value == entries[i].value) return entries[i].label;
55  }
56  return 0;
57}
58
59std::string ErrorName(int err, const ConstantLabel* err_table) {
60  if (err == 0)
61    return "No error";
62
63  if (err_table != 0) {
64    if (const char * value = FindLabel(err, err_table))
65      return value;
66  }
67
68  char buffer[16];
69  base::snprintf(buffer, sizeof(buffer), "0x%08x", err);
70  return buffer;
71}
72
73/////////////////////////////////////////////////////////////////////////////
74// Log helper functions
75/////////////////////////////////////////////////////////////////////////////
76
77// Generates extra information for LOG_E.
78static std::string GenerateExtra(LogErrorContext err_ctx,
79                                 int err,
80                                 const char* module) {
81  if (err_ctx != ERRCTX_NONE) {
82    std::ostringstream tmp;
83    tmp << ": ";
84    tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
85    switch (err_ctx) {
86      case ERRCTX_ERRNO:
87        tmp << " " << strerror(err);
88        break;
89#if defined(WEBRTC_WIN)
90      case ERRCTX_HRESULT: {
91        char msgbuf[256];
92        DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
93        HMODULE hmod = GetModuleHandleA(module);
94        if (hmod)
95          flags |= FORMAT_MESSAGE_FROM_HMODULE;
96        if (DWORD len = FormatMessageA(
97            flags, hmod, err,
98            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
99            msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) {
100          while ((len > 0) &&
101              isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
102            msgbuf[--len] = 0;
103          }
104          tmp << " " << msgbuf;
105        }
106        break;
107      }
108#endif  // OS_WIN
109#if defined(WEBRTC_IOS)
110      case ERRCTX_OSSTATUS:
111        tmp << " " << "Unknown LibJingle error: " << err;
112        break;
113#elif defined(WEBRTC_MAC)
114      case ERRCTX_OSSTATUS: {
115        tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error");
116        if (const char* desc = GetMacOSStatusCommentString(err)) {
117          tmp << ": " << desc;
118        }
119        break;
120      }
121#endif  // OS_MACOSX
122      default:
123        break;
124    }
125    return tmp.str();
126  }
127  return "";
128}
129
130DiagnosticLogMessage::DiagnosticLogMessage(const char* file,
131                                           int line,
132                                           LoggingSeverity severity,
133                                           bool log_to_chrome,
134                                           LogErrorContext err_ctx,
135                                           int err)
136    : file_name_(file),
137      line_(line),
138      severity_(severity),
139      log_to_chrome_(log_to_chrome) {
140  extra_ = GenerateExtra(err_ctx, err, NULL);
141}
142
143DiagnosticLogMessage::DiagnosticLogMessage(const char* file,
144                                           int line,
145                                           LoggingSeverity severity,
146                                           bool log_to_chrome,
147                                           LogErrorContext err_ctx,
148                                           int err,
149                                           const char* module)
150    : file_name_(file),
151      line_(line),
152      severity_(severity),
153      log_to_chrome_(log_to_chrome) {
154  extra_ = GenerateExtra(err_ctx, err, module);
155}
156
157DiagnosticLogMessage::~DiagnosticLogMessage() {
158  print_stream_ << extra_;
159  const std::string& str = print_stream_.str();
160  if (log_to_chrome_)
161    LOG_LAZY_STREAM_DIRECT(file_name_, line_, severity_) << str;
162  if (g_logging_delegate_function && severity_ <= LS_INFO) {
163    g_logging_delegate_function(str);
164  }
165}
166
167// static
168void LogMessage::LogToDebug(int min_sev) {
169  logging::SetMinLogLevel(min_sev);
170}
171
172// Note: this function is a copy from the overriden libjingle implementation.
173void LogMultiline(LoggingSeverity level, const char* label, bool input,
174                  const void* data, size_t len, bool hex_mode,
175                  LogMultilineState* state) {
176  if (!LOG_CHECK_LEVEL_V(level))
177    return;
178
179  const char * direction = (input ? " << " : " >> ");
180
181  // NULL data means to flush our count of unprintable characters.
182  if (!data) {
183    if (state && state->unprintable_count_[input]) {
184      LOG_V(level) << label << direction << "## "
185                   << state->unprintable_count_[input]
186                   << " consecutive unprintable ##";
187      state->unprintable_count_[input] = 0;
188    }
189    return;
190  }
191
192  // The ctype classification functions want unsigned chars.
193  const unsigned char* udata = static_cast<const unsigned char*>(data);
194
195  if (hex_mode) {
196    const size_t LINE_SIZE = 24;
197    char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
198    while (len > 0) {
199      memset(asc_line, ' ', sizeof(asc_line));
200      memset(hex_line, ' ', sizeof(hex_line));
201      size_t line_len = _min(len, LINE_SIZE);
202      for (size_t i = 0; i < line_len; ++i) {
203        unsigned char ch = udata[i];
204        asc_line[i] = isprint(ch) ? ch : '.';
205        hex_line[i*2 + i/4] = hex_encode(ch >> 4);
206        hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
207      }
208      asc_line[sizeof(asc_line)-1] = 0;
209      hex_line[sizeof(hex_line)-1] = 0;
210      LOG_V(level) << label << direction
211                   << asc_line << " " << hex_line << " ";
212      udata += line_len;
213      len -= line_len;
214    }
215    return;
216  }
217
218  size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0;
219
220  const unsigned char* end = udata + len;
221  while (udata < end) {
222    const unsigned char* line = udata;
223    const unsigned char* end_of_line = strchrn<unsigned char>(udata,
224                                                              end - udata,
225                                                              '\n');
226    if (!end_of_line) {
227      udata = end_of_line = end;
228    } else {
229      udata = end_of_line + 1;
230    }
231
232    bool is_printable = true;
233
234    // If we are in unprintable mode, we need to see a line of at least
235    // kMinPrintableLine characters before we'll switch back.
236    const ptrdiff_t kMinPrintableLine = 4;
237    if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) {
238      is_printable = false;
239    } else {
240      // Determine if the line contains only whitespace and printable
241      // characters.
242      bool is_entirely_whitespace = true;
243      for (const unsigned char* pos = line; pos < end_of_line; ++pos) {
244        if (isspace(*pos))
245          continue;
246        is_entirely_whitespace = false;
247        if (!isprint(*pos)) {
248          is_printable = false;
249          break;
250        }
251      }
252      // Treat an empty line following unprintable data as unprintable.
253      if (consecutive_unprintable && is_entirely_whitespace) {
254        is_printable = false;
255      }
256    }
257    if (!is_printable) {
258      consecutive_unprintable += (udata - line);
259      continue;
260    }
261    // Print out the current line, but prefix with a count of prior unprintable
262    // characters.
263    if (consecutive_unprintable) {
264      LOG_V(level) << label << direction << "## " << consecutive_unprintable
265                  << " consecutive unprintable ##";
266      consecutive_unprintable = 0;
267    }
268    // Strip off trailing whitespace.
269    while ((end_of_line > line) && isspace(*(end_of_line-1))) {
270      --end_of_line;
271    }
272    // Filter out any private data
273    std::string substr(reinterpret_cast<const char*>(line), end_of_line - line);
274    std::string::size_type pos_private = substr.find("Email");
275    if (pos_private == std::string::npos) {
276      pos_private = substr.find("Passwd");
277    }
278    if (pos_private == std::string::npos) {
279      LOG_V(level) << label << direction << substr;
280    } else {
281      LOG_V(level) << label << direction << "## omitted for privacy ##";
282    }
283  }
284
285  if (state) {
286    state->unprintable_count_[input] = consecutive_unprintable;
287  }
288}
289
290void InitDiagnosticLoggingDelegateFunction(
291    void (*delegate)(const std::string&)) {
292#ifndef NDEBUG
293  // Ensure that this function is always called from the same thread.
294  base::subtle::NoBarrier_CompareAndSwap(&g_init_logging_delegate_thread_id, 0,
295      static_cast<base::subtle::Atomic32>(base::PlatformThread::CurrentId()));
296  DCHECK_EQ(
297      g_init_logging_delegate_thread_id,
298      static_cast<base::subtle::Atomic32>(base::PlatformThread::CurrentId()));
299#endif
300  CHECK(delegate);
301  // This function may be called with the same argument several times if the
302  // page is reloaded or there are several PeerConnections on one page with
303  // logging enabled. This is OK, we simply don't have to do anything.
304  if (delegate == g_logging_delegate_function)
305    return;
306  CHECK(!g_logging_delegate_function);
307#ifdef NDEBUG
308  IPAddress::set_strip_sensitive(true);
309#endif
310  g_logging_delegate_function = delegate;
311
312  if (g_extra_logging_init_function)
313    g_extra_logging_init_function(delegate);
314}
315
316void SetExtraLoggingInit(
317    void (*function)(void (*delegate)(const std::string&))) {
318  CHECK(function);
319  CHECK(!g_extra_logging_init_function);
320  g_extra_logging_init_function = function;
321}
322
323}  // namespace rtc
324