webrtc_logging_handler_host.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/media/webrtc_logging_handler_host.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/cpu.h"
12#include "base/file_util.h"
13#include "base/logging.h"
14#include "base/prefs/pref_service.h"
15#include "base/strings/string_number_conversions.h"
16#include "base/sys_info.h"
17#include "base/time/time.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/chromeos/settings/cros_settings.h"
20#include "chrome/browser/media/webrtc_log_list.h"
21#include "chrome/browser/media/webrtc_log_uploader.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/common/chrome_switches.h"
24#include "chrome/common/media/webrtc_logging_messages.h"
25#include "chrome/common/partial_circular_buffer.h"
26#include "chrome/common/pref_names.h"
27#include "chromeos/settings/cros_settings_names.h"
28#include "content/public/browser/browser_thread.h"
29#include "content/public/browser/content_browser_client.h"
30#include "content/public/browser/gpu_data_manager.h"
31#include "content/public/browser/render_process_host.h"
32#include "gpu/config/gpu_info.h"
33#include "net/base/address_family.h"
34#include "net/url_request/url_request_context_getter.h"
35
36#if defined(OS_LINUX)
37#include "base/linux_util.h"
38#endif
39
40#if defined(OS_MACOSX)
41#include "base/mac/mac_util.h"
42#endif
43
44#if defined(OS_CHROMEOS)
45#include "chromeos/system/statistics_provider.h"
46#endif
47
48using base::IntToString;
49using content::BrowserThread;
50
51
52#if defined(OS_ANDROID)
53const size_t kWebRtcLogSize = 1 * 1024 * 1024;  // 1 MB
54#else
55const size_t kWebRtcLogSize = 6 * 1024 * 1024;  // 6 MB
56#endif
57
58namespace {
59
60const char kLogNotStoppedOrNoLogOpen[] =
61    "Logging not stopped or no log open.";
62
63// For privacy reasons when logging IP addresses. The returned "sensitive
64// string" is for release builds a string with the end stripped away. Last
65// octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
66// "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
67// not stripped.
68std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) {
69#if defined(NDEBUG)
70  std::string sensitive_address;
71  switch (net::GetAddressFamily(address)) {
72    case net::ADDRESS_FAMILY_IPV4: {
73      sensitive_address = net::IPAddressToString(address);
74      size_t find_pos = sensitive_address.rfind('.');
75      if (find_pos == std::string::npos)
76        return std::string();
77      sensitive_address.resize(find_pos);
78      sensitive_address += ".x";
79      break;
80    }
81    case net::ADDRESS_FAMILY_IPV6: {
82      // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
83      // that the end has been stripped out.
84      net::IPAddressNumber sensitive_address_number = address;
85      sensitive_address_number.resize(net::kIPv6AddressSize - 10);
86      sensitive_address_number.resize(net::kIPv6AddressSize, 0);
87      sensitive_address = net::IPAddressToString(sensitive_address_number);
88      break;
89    }
90    case net::ADDRESS_FAMILY_UNSPECIFIED: {
91      break;
92    }
93  }
94  return sensitive_address;
95#else
96  return net::IPAddressToString(address);
97#endif
98}
99
100void FormatMetaDataAsLogMessage(
101    const MetaDataMap& meta_data,
102    std::string* message) {
103  for (MetaDataMap::const_iterator it = meta_data.begin();
104       it != meta_data.end(); ++it) {
105    *message += it->first + ": " + it->second + '\n';
106  }
107  // Remove last '\n'.
108  message->resize(message->size() - 1);
109}
110
111}  // namespace
112
113WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
114    : BrowserMessageFilter(WebRtcLoggingMsgStart),
115      profile_(profile),
116      logging_state_(CLOSED),
117      upload_log_on_render_close_(false) {
118  DCHECK(profile_);
119}
120
121WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
122
123void WebRtcLoggingHandlerHost::SetMetaData(
124    const MetaDataMap& meta_data,
125    const GenericDoneCallback& callback) {
126  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
127  DCHECK(!callback.is_null());
128
129  std::string error_message;
130  if (logging_state_ == CLOSED) {
131    meta_data_ = meta_data;
132  } else if (logging_state_ == STARTED) {
133    meta_data_ = meta_data;
134    std::string meta_data_message;
135    FormatMetaDataAsLogMessage(meta_data_, &meta_data_message);
136    LogToCircularBuffer(meta_data_message);
137  } else {
138    error_message = "Meta data must be set before stop or upload.";
139  }
140  bool success = error_message.empty();
141  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
142                                   base::Bind(callback, success,
143                                              error_message));
144}
145
146void WebRtcLoggingHandlerHost::StartLogging(
147    const GenericDoneCallback& callback) {
148  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
149  DCHECK(!callback.is_null());
150
151  start_callback_ = callback;
152  if (logging_state_ != CLOSED) {
153    FireGenericDoneCallback(&start_callback_, false, "A log is already open");
154    return;
155  }
156  logging_state_ = STARTING;
157  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
158      &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
159}
160
161void WebRtcLoggingHandlerHost::StopLogging(
162    const GenericDoneCallback& callback) {
163  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
164  DCHECK(!callback.is_null());
165
166  stop_callback_ = callback;
167  if (logging_state_ != STARTED) {
168    FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
169    return;
170  }
171  logging_state_ = STOPPING;
172  Send(new WebRtcLoggingMsg_StopLogging());
173}
174
175void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) {
176  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
177  DCHECK(!callback.is_null());
178
179  if (logging_state_ != STOPPED) {
180    if (!callback.is_null()) {
181      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
182          base::Bind(callback, false, "", kLogNotStoppedOrNoLogOpen));
183    }
184    return;
185  }
186  upload_callback_ = callback;
187  content::BrowserThread::PostTaskAndReplyWithResult(
188      content::BrowserThread::FILE,
189      FROM_HERE,
190      base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists,
191                 this),
192      base::Bind(&WebRtcLoggingHandlerHost::TriggerUploadLog, this));
193}
194
195void WebRtcLoggingHandlerHost::UploadLogDone() {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
197  logging_state_ = CLOSED;
198}
199
200void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) {
201  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
202  DCHECK(!callback.is_null());
203
204  GenericDoneCallback discard_callback = callback;
205  if (logging_state_ != STOPPED) {
206    FireGenericDoneCallback(&discard_callback, false,
207                            kLogNotStoppedOrNoLogOpen);
208    return;
209  }
210  g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
211  circular_buffer_.reset();
212  log_buffer_.reset();
213  logging_state_ = CLOSED;
214  FireGenericDoneCallback(&discard_callback, true, "");
215}
216
217void WebRtcLoggingHandlerHost::LogMessage(const std::string& message) {
218  BrowserThread::PostTask(
219      BrowserThread::IO,
220      FROM_HERE,
221      base::Bind(
222          &WebRtcLoggingHandlerHost::AddLogMessageFromBrowser, this, message));
223}
224
225void WebRtcLoggingHandlerHost::OnChannelClosing() {
226  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227  if (logging_state_ == STARTED || logging_state_ == STOPPED) {
228    if (upload_log_on_render_close_) {
229      logging_state_ = STOPPED;
230      content::BrowserThread::PostTaskAndReplyWithResult(
231          content::BrowserThread::FILE,
232          FROM_HERE,
233          base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists,
234                     this),
235          base::Bind(&WebRtcLoggingHandlerHost::TriggerUploadLog, this));
236    } else {
237      g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
238    }
239  }
240  content::BrowserMessageFilter::OnChannelClosing();
241}
242
243void WebRtcLoggingHandlerHost::OnDestruct() const {
244  BrowserThread::DeleteOnIOThread::Destruct(this);
245}
246
247bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message,
248                                                 bool* message_was_ok) {
249  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
250  bool handled = true;
251  IPC_BEGIN_MESSAGE_MAP_EX(WebRtcLoggingHandlerHost, message, *message_was_ok)
252    IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessage, OnAddLogMessage)
253    IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped,
254                        OnLoggingStoppedInRenderer)
255    IPC_MESSAGE_UNHANDLED(handled = false)
256  IPC_END_MESSAGE_MAP_EX()
257
258  return handled;
259}
260
261void WebRtcLoggingHandlerHost::AddLogMessageFromBrowser(
262    const std::string& message) {
263  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
264  if (logging_state_ == STARTED)
265    LogToCircularBuffer(message);
266}
267
268void WebRtcLoggingHandlerHost::OnAddLogMessage(const std::string& message) {
269  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270  if (logging_state_ == STARTED || logging_state_ == STOPPING)
271    LogToCircularBuffer(message);
272}
273
274void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
275  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
276  if (logging_state_ != STOPPING) {
277    // If an out-of-order response is received, stop_callback_ may be invalid,
278    // and must not be invoked.
279    DLOG(ERROR) << "OnLoggingStoppedInRenderer invoked in state "
280                << logging_state_;
281    BadMessageReceived();
282    return;
283  }
284  logging_state_ = STOPPED;
285  FireGenericDoneCallback(&stop_callback_, true, "");
286}
287
288void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
289  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290  if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) {
291    logging_state_ = CLOSED;
292      FireGenericDoneCallback(
293          &start_callback_, false, "Cannot start, maybe the maximum number of "
294          "simultaneuos logs has been reached.");
295    return;
296  }
297  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
298      &WebRtcLoggingHandlerHost::DoStartLogging, this));
299}
300
301void WebRtcLoggingHandlerHost::DoStartLogging() {
302  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
303
304  log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
305  circular_buffer_.reset(
306    new PartialCircularBuffer(log_buffer_.get(),
307                              kWebRtcLogSize,
308                              kWebRtcLogSize / 2,
309                              false));
310
311  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
312      &WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread, this));
313}
314
315void WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread() {
316  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
317
318  net::NetworkInterfaceList network_list;
319  net::GetNetworkList(&network_list,
320                      net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
321
322  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
323      &WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread, this, network_list));
324}
325
326void WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread(
327    const net::NetworkInterfaceList& network_list) {
328  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
329
330  // Log start time (current time). We don't use base/i18n/time_formatting.h
331  // here because we don't want the format of the current locale.
332  base::Time::Exploded now = {0};
333  base::Time::Now().LocalExplode(&now);
334  LogToCircularBuffer(base::StringPrintf(
335      "Start %d-%02d-%02d %02d:%02d:%02d", now.year, now.month,
336      now.day_of_month, now.hour, now.minute, now.second));
337
338  // Write metadata if received before logging started.
339  if (!meta_data_.empty()) {
340    std::string info;
341    FormatMetaDataAsLogMessage(meta_data_, &info);
342    LogToCircularBuffer(info);
343  }
344
345  // OS
346  LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
347                      base::SysInfo::OperatingSystemVersion() + " " +
348                      base::SysInfo::OperatingSystemArchitecture());
349#if defined(OS_LINUX)
350  LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
351#endif
352
353  // CPU
354  base::CPU cpu;
355  LogToCircularBuffer(
356      "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) +
357      "." + IntToString(cpu.stepping()) + ", x" +
358      IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
359      IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
360  std::string cpu_brand = cpu.cpu_brand();
361  // Workaround for crbug.com/249713.
362  // TODO(grunell): Remove workaround when bug is fixed.
363  size_t null_pos = cpu_brand.find('\0');
364  if (null_pos != std::string::npos)
365    cpu_brand.erase(null_pos);
366  LogToCircularBuffer("Cpu brand: " + cpu_brand);
367
368  // Computer model
369  std::string computer_model = "Not available";
370#if defined(OS_MACOSX)
371  computer_model = base::mac::GetModelIdentifier();
372#elif defined(OS_CHROMEOS)
373  chromeos::system::StatisticsProvider::GetInstance()->
374      GetMachineStatistic(chromeos::system::kHardwareClassKey, &computer_model);
375#endif
376  LogToCircularBuffer("Computer model: " + computer_model);
377
378  // GPU
379  gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
380  LogToCircularBuffer("Gpu: machine-model='" + gpu_info.machine_model +
381                      "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) +
382                      ", device-id=" + IntToString(gpu_info.gpu.device_id) +
383                      ", driver-vendor='" + gpu_info.driver_vendor +
384                      "', driver-version=" + gpu_info.driver_version);
385
386  // Network interfaces
387  LogToCircularBuffer("Discovered " + IntToString(network_list.size()) +
388                      " network interfaces:");
389  for (net::NetworkInterfaceList::const_iterator it = network_list.begin();
390       it != network_list.end(); ++it) {
391    LogToCircularBuffer("Name: " + it->friendly_name + ", Address: " +
392                        IPAddressToSensitiveString(it->address));
393  }
394
395  NotifyLoggingStarted();
396}
397
398void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
399  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
400  Send(new WebRtcLoggingMsg_StartLogging());
401  logging_state_ = STARTED;
402  FireGenericDoneCallback(&start_callback_, true, "");
403}
404
405void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string& message) {
406  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
407  DCHECK(circular_buffer_.get());
408  circular_buffer_->Write(message.c_str(), message.length());
409  const char eol = '\n';
410  circular_buffer_->Write(&eol, 1);
411}
412
413base::FilePath WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists() {
414  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
415  base::FilePath log_dir_path =
416      WebRtcLogList::GetWebRtcLogDirectoryForProfile(profile_->GetPath());
417  base::File::Error error;
418  if (!base::CreateDirectoryAndGetError(log_dir_path, &error)) {
419    DLOG(ERROR) << "Could not create WebRTC log directory, error: " << error;
420    return base::FilePath();
421  }
422  return log_dir_path;
423}
424
425void WebRtcLoggingHandlerHost::TriggerUploadLog(
426    const base::FilePath& log_directory) {
427  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
428  DCHECK(logging_state_ == STOPPED);
429
430  logging_state_ = UPLOADING;
431  WebRtcLogUploadDoneData upload_done_data;
432
433  upload_done_data.log_path = log_directory;
434  upload_done_data.callback = upload_callback_;
435  upload_done_data.host = this;
436  upload_callback_.Reset();
437
438  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
439      &WebRtcLogUploader::LoggingStoppedDoUpload,
440      base::Unretained(g_browser_process->webrtc_log_uploader()),
441      Passed(&log_buffer_),
442      kWebRtcLogSize,
443      meta_data_,
444      upload_done_data));
445
446  meta_data_.clear();
447  circular_buffer_.reset();
448}
449
450void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
451    GenericDoneCallback* callback, bool success,
452    const std::string& error_message) {
453  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
454  DCHECK(!(*callback).is_null());
455  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
456                                   base::Bind(*callback, success,
457                                              error_message));
458  (*callback).Reset();
459}
460