Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media / webrtc_logging_handler_host.cc
blob611bc520ae000b0391379c2253f77e16db739441
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"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/cpu.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/base/net_util.h"
33 #include "net/url_request/url_request_context_getter.h"
35 #if defined(OS_LINUX)
36 #include "base/linux_util.h"
37 #endif
39 #if defined(OS_MACOSX)
40 #include "base/mac/mac_util.h"
41 #endif
43 using base::IntToString;
44 using content::BrowserThread;
47 #if defined(OS_ANDROID)
48 const size_t kWebRtcLogSize = 1 * 1024 * 1024; // 1 MB
49 #else
50 const size_t kWebRtcLogSize = 6 * 1024 * 1024; // 6 MB
51 #endif
53 namespace {
55 const char kLogNotStoppedOrNoLogOpen[] =
56 "Logging not stopped or no log open.";
58 // For privacy reasons when logging IP addresses. The returned "sensitive
59 // string" is for release builds a string with the end stripped away. Last
60 // octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
61 // "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
62 // not stripped.
63 std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) {
64 #if defined(NDEBUG)
65 std::string sensitive_address;
66 switch (net::GetAddressFamily(address)) {
67 case net::ADDRESS_FAMILY_IPV4: {
68 sensitive_address = net::IPAddressToString(address);
69 size_t find_pos = sensitive_address.rfind('.');
70 if (find_pos == std::string::npos)
71 return std::string();
72 sensitive_address.resize(find_pos);
73 sensitive_address += ".x";
74 break;
76 case net::ADDRESS_FAMILY_IPV6: {
77 // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
78 // that the end has been stripped out.
79 net::IPAddressNumber sensitive_address_number = address;
80 sensitive_address_number.resize(net::kIPv6AddressSize - 10);
81 sensitive_address_number.resize(net::kIPv6AddressSize, 0);
82 sensitive_address = net::IPAddressToString(sensitive_address_number);
83 break;
85 case net::ADDRESS_FAMILY_UNSPECIFIED: {
86 break;
89 return sensitive_address;
90 #else
91 return net::IPAddressToString(address);
92 #endif
95 void FormatMetaDataAsLogMessage(
96 const MetaDataMap& meta_data,
97 std::string* message) {
98 for (MetaDataMap::const_iterator it = meta_data.begin();
99 it != meta_data.end(); ++it) {
100 *message += it->first + ": " + it->second + '\n';
102 // Remove last '\n'.
103 message->resize(message->size() - 1);
106 } // namespace
108 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
109 : profile_(profile),
110 logging_state_(CLOSED),
111 upload_log_on_render_close_(false) {
112 DCHECK(profile_);
115 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
117 void WebRtcLoggingHandlerHost::SetMetaData(
118 const MetaDataMap& meta_data,
119 const GenericDoneCallback& callback) {
120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
121 DCHECK(!callback.is_null());
123 std::string error_message;
124 if (logging_state_ == CLOSED) {
125 meta_data_ = meta_data;
126 } else if (logging_state_ == STARTED) {
127 meta_data_ = meta_data;
128 std::string meta_data_message;
129 FormatMetaDataAsLogMessage(meta_data_, &meta_data_message);
130 LogToCircularBuffer(meta_data_message);
131 } else {
132 error_message = "Meta data must be set before stop or upload.";
134 bool success = error_message.empty();
135 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
136 base::Bind(callback, success,
137 error_message));
140 void WebRtcLoggingHandlerHost::StartLogging(
141 const GenericDoneCallback& callback) {
142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143 DCHECK(!callback.is_null());
145 start_callback_ = callback;
146 if (logging_state_ != CLOSED) {
147 FireGenericDoneCallback(&start_callback_, false, "A log is already open");
148 return;
150 logging_state_ = STARTING;
151 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
152 &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
155 void WebRtcLoggingHandlerHost::StopLogging(
156 const GenericDoneCallback& callback) {
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
158 DCHECK(!callback.is_null());
160 stop_callback_ = callback;
161 if (logging_state_ != STARTED) {
162 FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
163 return;
165 logging_state_ = STOPPING;
166 Send(new WebRtcLoggingMsg_StopLogging());
169 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
171 DCHECK(!callback.is_null());
173 if (logging_state_ != STOPPED) {
174 if (!callback.is_null()) {
175 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
176 base::Bind(callback, false, "", kLogNotStoppedOrNoLogOpen));
178 return;
180 upload_callback_ = callback;
181 TriggerUploadLog();
184 void WebRtcLoggingHandlerHost::UploadLogDone() {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
186 logging_state_ = CLOSED;
189 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
191 DCHECK(!callback.is_null());
193 GenericDoneCallback discard_callback = callback;
194 if (logging_state_ != STOPPED) {
195 FireGenericDoneCallback(&discard_callback, false,
196 kLogNotStoppedOrNoLogOpen);
197 return;
199 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
200 circular_buffer_.reset();
201 log_buffer_.reset();
202 logging_state_ = CLOSED;
203 FireGenericDoneCallback(&discard_callback, true, "");
206 void WebRtcLoggingHandlerHost::OnChannelClosing() {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
208 if (logging_state_ == STARTED || logging_state_ == STOPPED) {
209 if (upload_log_on_render_close_) {
210 logging_state_ = STOPPED;
211 TriggerUploadLog();
212 } else {
213 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
216 content::BrowserMessageFilter::OnChannelClosing();
219 void WebRtcLoggingHandlerHost::OnDestruct() const {
220 BrowserThread::DeleteOnIOThread::Destruct(this);
223 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message,
224 bool* message_was_ok) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226 bool handled = true;
227 IPC_BEGIN_MESSAGE_MAP_EX(WebRtcLoggingHandlerHost, message, *message_was_ok)
228 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessage, OnAddLogMessage)
229 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped,
230 OnLoggingStoppedInRenderer)
231 IPC_MESSAGE_UNHANDLED(handled = false)
232 IPC_END_MESSAGE_MAP_EX()
234 return handled;
237 void WebRtcLoggingHandlerHost::OnAddLogMessage(const std::string& message) {
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
239 if (logging_state_ == STARTED || logging_state_ == STOPPING)
240 LogToCircularBuffer(message);
243 void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
245 if (logging_state_ != STOPPING) {
246 // If an out-of-order response is received, stop_callback_ may be invalid,
247 // and must not be invoked.
248 DLOG(ERROR) << "OnLoggingStoppedInRenderer invoked in state "
249 << logging_state_;
250 BadMessageReceived();
251 return;
253 logging_state_ = STOPPED;
254 FireGenericDoneCallback(&stop_callback_, true, "");
257 void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
259 if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) {
260 logging_state_ = CLOSED;
261 FireGenericDoneCallback(
262 &start_callback_, false, "Cannot start, maybe the maximum number of "
263 "simultaneuos logs has been reached.");
264 return;
266 system_request_context_ = g_browser_process->system_request_context();
267 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
268 &WebRtcLoggingHandlerHost::DoStartLogging, this));
271 void WebRtcLoggingHandlerHost::DoStartLogging() {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
274 log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
275 circular_buffer_.reset(
276 new PartialCircularBuffer(log_buffer_.get(),
277 kWebRtcLogSize,
278 kWebRtcLogSize / 2,
279 false));
281 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
282 &WebRtcLoggingHandlerHost::LogMachineInfo, this));
285 void WebRtcLoggingHandlerHost::LogMachineInfo() {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
288 // Write metadata if received before logging started.
289 if (!meta_data_.empty()) {
290 std::string info;
291 FormatMetaDataAsLogMessage(meta_data_, &info);
292 LogToCircularBuffer(info);
295 // OS
296 LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
297 base::SysInfo::OperatingSystemVersion() + " " +
298 base::SysInfo::OperatingSystemArchitecture());
299 #if defined(OS_LINUX)
300 LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
301 #endif
303 // CPU
304 base::CPU cpu;
305 LogToCircularBuffer(
306 "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) +
307 "." + IntToString(cpu.stepping()) + ", x" +
308 IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
309 IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
310 std::string cpu_brand = cpu.cpu_brand();
311 // Workaround for crbug.com/249713.
312 // TODO(grunell): Remove workaround when bug is fixed.
313 size_t null_pos = cpu_brand.find('\0');
314 if (null_pos != std::string::npos)
315 cpu_brand.erase(null_pos);
316 LogToCircularBuffer("Cpu brand: " + cpu_brand);
318 // Computer model
319 #if defined(OS_MACOSX)
320 LogToCircularBuffer("Computer model: " + base::mac::GetModelIdentifier());
321 #else
322 LogToCircularBuffer("Computer model: Not available");
323 #endif
325 // GPU
326 gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
327 LogToCircularBuffer("Gpu: machine-model='" + gpu_info.machine_model +
328 "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) +
329 ", device-id=" + IntToString(gpu_info.gpu.device_id) +
330 ", driver-vendor='" + gpu_info.driver_vendor +
331 "', driver-version=" + gpu_info.driver_version);
333 // Network interfaces
334 net::NetworkInterfaceList network_list;
335 net::GetNetworkList(&network_list,
336 net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
337 LogToCircularBuffer("Discovered " + IntToString(network_list.size()) +
338 " network interfaces:");
339 for (net::NetworkInterfaceList::iterator it = network_list.begin();
340 it != network_list.end(); ++it) {
341 LogToCircularBuffer("Name: " + it->name + ", Address: " +
342 IPAddressToSensitiveString(it->address));
345 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
346 &WebRtcLoggingHandlerHost::NotifyLoggingStarted, this));
349 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
351 Send(new WebRtcLoggingMsg_StartLogging());
352 logging_state_ = STARTED;
353 FireGenericDoneCallback(&start_callback_, true, "");
356 void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string& message) {
357 DCHECK(circular_buffer_.get());
358 circular_buffer_->Write(message.c_str(), message.length());
359 const char eol = '\n';
360 circular_buffer_->Write(&eol, 1);
363 void WebRtcLoggingHandlerHost::TriggerUploadLog() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
365 DCHECK(logging_state_ == STOPPED);
367 logging_state_ = UPLOADING;
368 WebRtcLogUploadDoneData upload_done_data;
369 upload_done_data.upload_list_path =
370 WebRtcLogUploadList::GetFilePathForProfile(profile_);
371 upload_done_data.callback = upload_callback_;
372 upload_done_data.host = this;
373 upload_callback_.Reset();
375 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
376 &WebRtcLogUploader::LoggingStoppedDoUpload,
377 base::Unretained(g_browser_process->webrtc_log_uploader()),
378 system_request_context_,
379 Passed(&log_buffer_),
380 kWebRtcLogSize,
381 meta_data_,
382 upload_done_data));
384 meta_data_.clear();
385 circular_buffer_.reset();
388 void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
389 GenericDoneCallback* callback, bool success,
390 const std::string& error_message) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
392 DCHECK(!(*callback).is_null());
393 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
394 base::Bind(*callback, success,
395 error_message));
396 (*callback).Reset();