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.
5 #include "chrome/browser/media/webrtc_logging_handler_host.h"
10 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/sys_info.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chromeos/settings/cros_settings.h"
18 #include "chrome/browser/media/webrtc_log_upload_list.h"
19 #include "chrome/browser/media/webrtc_log_uploader.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/media/webrtc_logging_messages.h"
23 #include "chrome/common/partial_circular_buffer.h"
24 #include "chrome/common/pref_names.h"
25 #include "chromeos/settings/cros_settings_names.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/content_browser_client.h"
28 #include "content/public/browser/gpu_data_manager.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "gpu/config/gpu_info.h"
31 #include "net/base/address_family.h"
32 #include "net/url_request/url_request_context_getter.h"
35 #include "base/linux_util.h"
38 #if defined(OS_MACOSX)
39 #include "base/mac/mac_util.h"
42 #if defined(OS_CHROMEOS)
43 #include "chromeos/system/statistics_provider.h"
46 using base::IntToString;
47 using content::BrowserThread;
50 #if defined(OS_ANDROID)
51 const size_t kWebRtcLogSize = 1 * 1024 * 1024; // 1 MB
53 const size_t kWebRtcLogSize = 6 * 1024 * 1024; // 6 MB
58 const char kLogNotStoppedOrNoLogOpen[] =
59 "Logging not stopped or no log open.";
61 // For privacy reasons when logging IP addresses. The returned "sensitive
62 // string" is for release builds a string with the end stripped away. Last
63 // octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
64 // "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
66 std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) {
68 std::string sensitive_address;
69 switch (net::GetAddressFamily(address)) {
70 case net::ADDRESS_FAMILY_IPV4: {
71 sensitive_address = net::IPAddressToString(address);
72 size_t find_pos = sensitive_address.rfind('.');
73 if (find_pos == std::string::npos)
75 sensitive_address.resize(find_pos);
76 sensitive_address += ".x";
79 case net::ADDRESS_FAMILY_IPV6: {
80 // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
81 // that the end has been stripped out.
82 net::IPAddressNumber sensitive_address_number = address;
83 sensitive_address_number.resize(net::kIPv6AddressSize - 10);
84 sensitive_address_number.resize(net::kIPv6AddressSize, 0);
85 sensitive_address = net::IPAddressToString(sensitive_address_number);
88 case net::ADDRESS_FAMILY_UNSPECIFIED: {
92 return sensitive_address;
94 return net::IPAddressToString(address);
98 void FormatMetaDataAsLogMessage(
99 const MetaDataMap& meta_data,
100 std::string* message) {
101 for (MetaDataMap::const_iterator it = meta_data.begin();
102 it != meta_data.end(); ++it) {
103 *message += it->first + ": " + it->second + '\n';
106 message->resize(message->size() - 1);
111 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
113 logging_state_(CLOSED),
114 upload_log_on_render_close_(false) {
118 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
120 void WebRtcLoggingHandlerHost::SetMetaData(
121 const MetaDataMap& meta_data,
122 const GenericDoneCallback& callback) {
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
124 DCHECK(!callback.is_null());
126 std::string error_message;
127 if (logging_state_ == CLOSED) {
128 meta_data_ = meta_data;
129 } else if (logging_state_ == STARTED) {
130 meta_data_ = meta_data;
131 std::string meta_data_message;
132 FormatMetaDataAsLogMessage(meta_data_, &meta_data_message);
133 LogToCircularBuffer(meta_data_message);
135 error_message = "Meta data must be set before stop or upload.";
137 bool success = error_message.empty();
138 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
139 base::Bind(callback, success,
143 void WebRtcLoggingHandlerHost::StartLogging(
144 const GenericDoneCallback& callback) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146 DCHECK(!callback.is_null());
148 start_callback_ = callback;
149 if (logging_state_ != CLOSED) {
150 FireGenericDoneCallback(&start_callback_, false, "A log is already open");
153 logging_state_ = STARTING;
154 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
155 &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
158 void WebRtcLoggingHandlerHost::StopLogging(
159 const GenericDoneCallback& callback) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
161 DCHECK(!callback.is_null());
163 stop_callback_ = callback;
164 if (logging_state_ != STARTED) {
165 FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
168 logging_state_ = STOPPING;
169 Send(new WebRtcLoggingMsg_StopLogging());
172 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) {
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
174 DCHECK(!callback.is_null());
176 if (logging_state_ != STOPPED) {
177 if (!callback.is_null()) {
178 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
179 base::Bind(callback, false, "", kLogNotStoppedOrNoLogOpen));
183 upload_callback_ = callback;
187 void WebRtcLoggingHandlerHost::UploadLogDone() {
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189 logging_state_ = CLOSED;
192 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
194 DCHECK(!callback.is_null());
196 GenericDoneCallback discard_callback = callback;
197 if (logging_state_ != STOPPED) {
198 FireGenericDoneCallback(&discard_callback, false,
199 kLogNotStoppedOrNoLogOpen);
202 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
203 circular_buffer_.reset();
205 logging_state_ = CLOSED;
206 FireGenericDoneCallback(&discard_callback, true, "");
209 void WebRtcLoggingHandlerHost::LogMessage(const std::string& message) {
210 BrowserThread::PostTask(
214 &WebRtcLoggingHandlerHost::AddLogMessageFromBrowser, this, message));
217 void WebRtcLoggingHandlerHost::OnChannelClosing() {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
219 if (logging_state_ == STARTED || logging_state_ == STOPPED) {
220 if (upload_log_on_render_close_) {
221 logging_state_ = STOPPED;
224 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
227 content::BrowserMessageFilter::OnChannelClosing();
230 void WebRtcLoggingHandlerHost::OnDestruct() const {
231 BrowserThread::DeleteOnIOThread::Destruct(this);
234 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message,
235 bool* message_was_ok) {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
238 IPC_BEGIN_MESSAGE_MAP_EX(WebRtcLoggingHandlerHost, message, *message_was_ok)
239 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessage, OnAddLogMessage)
240 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped,
241 OnLoggingStoppedInRenderer)
242 IPC_MESSAGE_UNHANDLED(handled = false)
243 IPC_END_MESSAGE_MAP_EX()
248 void WebRtcLoggingHandlerHost::AddLogMessageFromBrowser(
249 const std::string& message) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
251 if (logging_state_ == STARTED)
252 LogToCircularBuffer(message);
255 void WebRtcLoggingHandlerHost::OnAddLogMessage(const std::string& message) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
257 if (logging_state_ == STARTED || logging_state_ == STOPPING)
258 LogToCircularBuffer(message);
261 void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
263 if (logging_state_ != STOPPING) {
264 // If an out-of-order response is received, stop_callback_ may be invalid,
265 // and must not be invoked.
266 DLOG(ERROR) << "OnLoggingStoppedInRenderer invoked in state "
268 BadMessageReceived();
271 logging_state_ = STOPPED;
272 FireGenericDoneCallback(&stop_callback_, true, "");
275 void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
277 if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) {
278 logging_state_ = CLOSED;
279 FireGenericDoneCallback(
280 &start_callback_, false, "Cannot start, maybe the maximum number of "
281 "simultaneuos logs has been reached.");
284 system_request_context_ = g_browser_process->system_request_context();
285 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
286 &WebRtcLoggingHandlerHost::DoStartLogging, this));
289 void WebRtcLoggingHandlerHost::DoStartLogging() {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
292 log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
293 circular_buffer_.reset(
294 new PartialCircularBuffer(log_buffer_.get(),
299 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
300 &WebRtcLoggingHandlerHost::LogMachineInfoOnFileThread, this));
303 void WebRtcLoggingHandlerHost::LogMachineInfoOnFileThread() {
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
306 net::NetworkInterfaceList network_list;
307 net::GetNetworkList(&network_list,
308 net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
310 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
311 &WebRtcLoggingHandlerHost::LogMachineInfoOnIOThread, this, network_list));
314 void WebRtcLoggingHandlerHost::LogMachineInfoOnIOThread(
315 const net::NetworkInterfaceList& network_list) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
318 // Write metadata if received before logging started.
319 if (!meta_data_.empty()) {
321 FormatMetaDataAsLogMessage(meta_data_, &info);
322 LogToCircularBuffer(info);
326 LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
327 base::SysInfo::OperatingSystemVersion() + " " +
328 base::SysInfo::OperatingSystemArchitecture());
329 #if defined(OS_LINUX)
330 LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
336 "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) +
337 "." + IntToString(cpu.stepping()) + ", x" +
338 IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
339 IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
340 std::string cpu_brand = cpu.cpu_brand();
341 // Workaround for crbug.com/249713.
342 // TODO(grunell): Remove workaround when bug is fixed.
343 size_t null_pos = cpu_brand.find('\0');
344 if (null_pos != std::string::npos)
345 cpu_brand.erase(null_pos);
346 LogToCircularBuffer("Cpu brand: " + cpu_brand);
349 std::string computer_model = "Not available";
350 #if defined(OS_MACOSX)
351 computer_model = base::mac::GetModelIdentifier();
352 #elif defined(OS_CHROMEOS)
353 chromeos::system::StatisticsProvider::GetInstance()->
354 GetMachineStatistic(chromeos::system::kHardwareClassKey, &computer_model);
356 LogToCircularBuffer("Computer model: " + computer_model);
359 gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
360 LogToCircularBuffer("Gpu: machine-model='" + gpu_info.machine_model +
361 "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) +
362 ", device-id=" + IntToString(gpu_info.gpu.device_id) +
363 ", driver-vendor='" + gpu_info.driver_vendor +
364 "', driver-version=" + gpu_info.driver_version);
366 // Network interfaces
367 LogToCircularBuffer("Discovered " + IntToString(network_list.size()) +
368 " network interfaces:");
369 for (net::NetworkInterfaceList::const_iterator it = network_list.begin();
370 it != network_list.end(); ++it) {
371 LogToCircularBuffer("Name: " + it->name + ", Address: " +
372 IPAddressToSensitiveString(it->address));
375 NotifyLoggingStarted();
378 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
380 Send(new WebRtcLoggingMsg_StartLogging());
381 logging_state_ = STARTED;
382 FireGenericDoneCallback(&start_callback_, true, "");
385 void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string& message) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
387 DCHECK(circular_buffer_.get());
388 circular_buffer_->Write(message.c_str(), message.length());
389 const char eol = '\n';
390 circular_buffer_->Write(&eol, 1);
393 void WebRtcLoggingHandlerHost::TriggerUploadLog() {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
395 DCHECK(logging_state_ == STOPPED);
397 logging_state_ = UPLOADING;
398 WebRtcLogUploadDoneData upload_done_data;
399 upload_done_data.upload_list_path =
400 WebRtcLogUploadList::GetFilePathForProfile(profile_);
401 upload_done_data.callback = upload_callback_;
402 upload_done_data.host = this;
403 upload_callback_.Reset();
405 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
406 &WebRtcLogUploader::LoggingStoppedDoUpload,
407 base::Unretained(g_browser_process->webrtc_log_uploader()),
408 system_request_context_,
409 Passed(&log_buffer_),
415 circular_buffer_.reset();
418 void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
419 GenericDoneCallback* callback, bool success,
420 const std::string& error_message) {
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
422 DCHECK(!(*callback).is_null());
423 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
424 base::Bind(*callback, success,