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/files/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/media/webrtc_rtp_dump_handler.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/chrome_version_info.h"
26 #include "chrome/common/media/webrtc_logging_messages.h"
27 #include "chrome/common/partial_circular_buffer.h"
28 #include "chromeos/settings/cros_settings_names.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/content_browser_client.h"
31 #include "content/public/browser/gpu_data_manager.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "gpu/config/gpu_info.h"
34 #include "net/base/address_family.h"
35 #include "net/url_request/url_request_context_getter.h"
38 #include "base/linux_util.h"
41 #if defined(OS_MACOSX)
42 #include "base/mac/mac_util.h"
45 #if defined(OS_CHROMEOS)
46 #include "chromeos/system/statistics_provider.h"
49 using base::IntToString
;
50 using content::BrowserThread
;
54 #if defined(OS_ANDROID)
55 const size_t kWebRtcLogSize
= 1 * 1024 * 1024; // 1 MB
57 const size_t kWebRtcLogSize
= 6 * 1024 * 1024; // 6 MB
60 const char kLogNotStoppedOrNoLogOpen
[] =
61 "Logging not stopped or no log open.";
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
68 std::string
IPAddressToSensitiveString(const net::IPAddressNumber
& address
) {
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
)
77 sensitive_address
.resize(find_pos
);
78 sensitive_address
+= ".x";
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
);
90 case net::ADDRESS_FAMILY_UNSPECIFIED
: {
94 return sensitive_address
;
96 return net::IPAddressToString(address
);
100 void 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';
108 message
->resize(message
->size() - 1);
113 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile
* profile
)
114 : BrowserMessageFilter(WebRtcLoggingMsgStart
),
116 logging_state_(CLOSED
),
117 upload_log_on_render_close_(false) {
121 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
123 void WebRtcLoggingHandlerHost::SetMetaData(
124 const MetaDataMap
& meta_data
,
125 const GenericDoneCallback
& callback
) {
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
127 DCHECK(!callback
.is_null());
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
);
138 error_message
= "Meta data must be set before stop or upload.";
140 bool success
= error_message
.empty();
141 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
142 base::Bind(callback
, success
,
146 void WebRtcLoggingHandlerHost::StartLogging(
147 const GenericDoneCallback
& callback
) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
149 DCHECK(!callback
.is_null());
151 start_callback_
= callback
;
152 if (logging_state_
!= CLOSED
) {
153 FireGenericDoneCallback(&start_callback_
, false, "A log is already open");
156 logging_state_
= STARTING
;
157 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
158 &WebRtcLoggingHandlerHost::StartLoggingIfAllowed
, this));
161 void WebRtcLoggingHandlerHost::StopLogging(
162 const GenericDoneCallback
& callback
) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
164 DCHECK(!callback
.is_null());
166 stop_callback_
= callback
;
167 if (logging_state_
!= STARTED
) {
168 FireGenericDoneCallback(&stop_callback_
, false, "Logging not started");
171 logging_state_
= STOPPING
;
172 Send(new WebRtcLoggingMsg_StopLogging());
175 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback
& callback
) {
176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
177 DCHECK(!callback
.is_null());
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
));
187 upload_callback_
= callback
;
188 logging_state_
= UPLOADING
;
189 content::BrowserThread::PostTaskAndReplyWithResult(
190 content::BrowserThread::FILE,
192 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
194 base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload
, this));
197 void WebRtcLoggingHandlerHost::UploadLogDone() {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
199 logging_state_
= CLOSED
;
202 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback
& callback
) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
204 DCHECK(!callback
.is_null());
206 GenericDoneCallback discard_callback
= callback
;
207 if (logging_state_
!= STOPPED
) {
208 FireGenericDoneCallback(&discard_callback
, false,
209 kLogNotStoppedOrNoLogOpen
);
212 g_browser_process
->webrtc_log_uploader()->LoggingStoppedDontUpload();
213 circular_buffer_
.reset();
215 logging_state_
= CLOSED
;
216 rtp_dump_handler_
.reset();
217 stop_rtp_dump_callback_
.Reset();
218 FireGenericDoneCallback(&discard_callback
, true, "");
221 void WebRtcLoggingHandlerHost::LogMessage(const std::string
& message
) {
222 BrowserThread::PostTask(
226 &WebRtcLoggingHandlerHost::AddLogMessageFromBrowser
,
228 WebRtcLoggingMessageData(base::Time::Now(), message
)));
231 void WebRtcLoggingHandlerHost::StartRtpDump(
233 const GenericDoneCallback
& callback
,
234 const content::RenderProcessHost::WebRtcStopRtpDumpCallback
&
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
237 DCHECK(stop_rtp_dump_callback_
.is_null() ||
238 stop_rtp_dump_callback_
.Equals(stop_callback
));
240 stop_rtp_dump_callback_
= stop_callback
;
242 if (!rtp_dump_handler_
) {
243 content::BrowserThread::PostTaskAndReplyWithResult(
244 content::BrowserThread::FILE,
246 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
248 base::Bind(&WebRtcLoggingHandlerHost::CreateRtpDumpHandlerAndStart
,
255 GenericDoneCallback start_callback
= callback
;
256 DoStartRtpDump(type
, &start_callback
);
259 void WebRtcLoggingHandlerHost::StopRtpDump(
261 const GenericDoneCallback
& callback
) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
263 DCHECK(!callback
.is_null());
265 if (!rtp_dump_handler_
) {
266 GenericDoneCallback stop_callback
= callback
;
267 FireGenericDoneCallback(
268 &stop_callback
, false, "RTP dump has not been started.");
272 if (!stop_rtp_dump_callback_
.is_null()) {
273 BrowserThread::PostTask(
276 base::Bind(stop_rtp_dump_callback_
,
277 type
== RTP_DUMP_INCOMING
|| type
== RTP_DUMP_BOTH
,
278 type
== RTP_DUMP_OUTGOING
|| type
== RTP_DUMP_BOTH
));
281 rtp_dump_handler_
->StopDump(type
, callback
);
284 void WebRtcLoggingHandlerHost::OnRtpPacket(scoped_ptr
<uint8
[]> packet_header
,
285 size_t header_length
,
286 size_t packet_length
,
288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
290 BrowserThread::PostTask(
293 base::Bind(&WebRtcLoggingHandlerHost::DumpRtpPacketOnIOThread
,
295 base::Passed(&packet_header
),
301 void WebRtcLoggingHandlerHost::DumpRtpPacketOnIOThread(
302 scoped_ptr
<uint8
[]> packet_header
,
303 size_t header_length
,
304 size_t packet_length
,
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
308 // |rtp_dump_handler_| could be NULL if we are waiting for the FILE thread to
309 // create/ensure the log directory.
310 if (rtp_dump_handler_
) {
311 rtp_dump_handler_
->OnRtpPacket(
312 packet_header
.get(), header_length
, packet_length
, incoming
);
316 void WebRtcLoggingHandlerHost::OnChannelClosing() {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
318 if (logging_state_
== STARTED
|| logging_state_
== STOPPED
) {
319 if (upload_log_on_render_close_
) {
320 logging_state_
= UPLOADING
;
321 logging_started_time_
= base::Time();
323 content::BrowserThread::PostTaskAndReplyWithResult(
324 content::BrowserThread::FILE,
326 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
328 base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload
, this));
330 g_browser_process
->webrtc_log_uploader()->LoggingStoppedDontUpload();
333 content::BrowserMessageFilter::OnChannelClosing();
336 void WebRtcLoggingHandlerHost::OnDestruct() const {
337 BrowserThread::DeleteOnIOThread::Destruct(this);
340 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message
& message
) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
343 IPC_BEGIN_MESSAGE_MAP(WebRtcLoggingHandlerHost
, message
)
344 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessages
, OnAddLogMessages
)
345 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped
,
346 OnLoggingStoppedInRenderer
)
347 IPC_MESSAGE_UNHANDLED(handled
= false)
348 IPC_END_MESSAGE_MAP()
353 void WebRtcLoggingHandlerHost::AddLogMessageFromBrowser(
354 const WebRtcLoggingMessageData
& message
) {
355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
356 if (logging_state_
== STARTED
)
357 LogToCircularBuffer(message
.Format(logging_started_time_
));
360 void WebRtcLoggingHandlerHost::OnAddLogMessages(
361 const std::vector
<WebRtcLoggingMessageData
>& messages
) {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
363 if (logging_state_
== STARTED
|| logging_state_
== STOPPING
) {
364 for (size_t i
= 0; i
< messages
.size(); ++i
) {
365 LogToCircularBuffer(messages
[i
].Format(logging_started_time_
));
370 void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
372 if (logging_state_
!= STOPPING
) {
373 // If an out-of-order response is received, stop_callback_ may be invalid,
374 // and must not be invoked.
375 DLOG(ERROR
) << "OnLoggingStoppedInRenderer invoked in state "
377 BadMessageReceived();
380 logging_started_time_
= base::Time();
381 logging_state_
= STOPPED
;
382 FireGenericDoneCallback(&stop_callback_
, true, "");
385 void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
387 if (!g_browser_process
->webrtc_log_uploader()->ApplyForStartLogging()) {
388 logging_state_
= CLOSED
;
389 FireGenericDoneCallback(
390 &start_callback_
, false, "Cannot start, maybe the maximum number of "
391 "simultaneuos logs has been reached.");
394 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
395 &WebRtcLoggingHandlerHost::DoStartLogging
, this));
398 void WebRtcLoggingHandlerHost::DoStartLogging() {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
401 log_buffer_
.reset(new unsigned char[kWebRtcLogSize
]);
402 circular_buffer_
.reset(
403 new PartialCircularBuffer(log_buffer_
.get(),
408 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, base::Bind(
409 &WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread
, this));
412 void WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread() {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
415 net::NetworkInterfaceList network_list
;
416 net::GetNetworkList(&network_list
,
417 net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES
);
419 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
420 &WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread
, this, network_list
));
423 void WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread(
424 const net::NetworkInterfaceList
& network_list
) {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
427 // Log start time (current time). We don't use base/i18n/time_formatting.h
428 // here because we don't want the format of the current locale.
429 base::Time::Exploded now
= {0};
430 base::Time::Now().LocalExplode(&now
);
431 LogToCircularBuffer(base::StringPrintf(
432 "Start %d-%02d-%02d %02d:%02d:%02d", now
.year
, now
.month
,
433 now
.day_of_month
, now
.hour
, now
.minute
, now
.second
));
435 // Write metadata if received before logging started.
436 if (!meta_data_
.empty()) {
438 FormatMetaDataAsLogMessage(meta_data_
, &info
);
439 LogToCircularBuffer(info
);
443 chrome::VersionInfo version_info
;
444 LogToCircularBuffer("Chrome version: " + version_info
.Version() + " " +
445 chrome::VersionInfo::GetVersionStringModifier());
448 LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
449 base::SysInfo::OperatingSystemVersion() + " " +
450 base::SysInfo::OperatingSystemArchitecture());
451 #if defined(OS_LINUX)
452 LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
458 "Cpu: " + IntToString(cpu
.family()) + "." + IntToString(cpu
.model()) +
459 "." + IntToString(cpu
.stepping()) + ", x" +
460 IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
461 IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
462 std::string cpu_brand
= cpu
.cpu_brand();
463 // Workaround for crbug.com/249713.
464 // TODO(grunell): Remove workaround when bug is fixed.
465 size_t null_pos
= cpu_brand
.find('\0');
466 if (null_pos
!= std::string::npos
)
467 cpu_brand
.erase(null_pos
);
468 LogToCircularBuffer("Cpu brand: " + cpu_brand
);
471 std::string computer_model
= "Not available";
472 #if defined(OS_MACOSX)
473 computer_model
= base::mac::GetModelIdentifier();
474 #elif defined(OS_CHROMEOS)
475 chromeos::system::StatisticsProvider::GetInstance()->
476 GetMachineStatistic(chromeos::system::kHardwareClassKey
, &computer_model
);
478 LogToCircularBuffer("Computer model: " + computer_model
);
481 gpu::GPUInfo gpu_info
= content::GpuDataManager::GetInstance()->GetGPUInfo();
483 "Gpu: machine-model-name=" + gpu_info
.machine_model_name
+
484 ", machine-model-version=" + gpu_info
.machine_model_version
+
485 ", vendor-id=" + IntToString(gpu_info
.gpu
.vendor_id
) +
486 ", device-id=" + IntToString(gpu_info
.gpu
.device_id
) +
487 ", driver-vendor=" + gpu_info
.driver_vendor
+
488 ", driver-version=" + gpu_info
.driver_version
);
490 "OpenGL: gl-vendor=" + gpu_info
.gl_vendor
+
491 ", gl-renderer=" + gpu_info
.gl_renderer
+
492 ", gl-version=" + gpu_info
.gl_version
);
494 // Network interfaces
495 LogToCircularBuffer("Discovered " + IntToString(network_list
.size()) +
496 " network interfaces:");
497 for (net::NetworkInterfaceList::const_iterator it
= network_list
.begin();
498 it
!= network_list
.end(); ++it
) {
500 "Name: " + it
->friendly_name
+ ", Address: " +
501 IPAddressToSensitiveString(it
->address
) + ", Type: " +
502 net::NetworkChangeNotifier::ConnectionTypeToString(it
->type
));
505 NotifyLoggingStarted();
508 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
510 Send(new WebRtcLoggingMsg_StartLogging());
511 logging_started_time_
= base::Time::Now();
512 logging_state_
= STARTED
;
513 FireGenericDoneCallback(&start_callback_
, true, "");
516 void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string
& message
) {
517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
518 DCHECK(circular_buffer_
.get());
519 circular_buffer_
->Write(message
.c_str(), message
.length());
520 const char eol
= '\n';
521 circular_buffer_
->Write(&eol
, 1);
524 base::FilePath
WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists() {
525 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
526 base::FilePath log_dir_path
=
527 WebRtcLogList::GetWebRtcLogDirectoryForProfile(profile_
->GetPath());
528 base::File::Error error
;
529 if (!base::CreateDirectoryAndGetError(log_dir_path
, &error
)) {
530 DLOG(ERROR
) << "Could not create WebRTC log directory, error: " << error
;
531 return base::FilePath();
536 void WebRtcLoggingHandlerHost::TriggerUpload(
537 const base::FilePath
& log_directory
) {
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
539 DCHECK_EQ(logging_state_
, UPLOADING
);
541 if (rtp_dump_handler_
) {
542 BrowserThread::PostTask(
545 base::Bind(stop_rtp_dump_callback_
, true, true));
547 rtp_dump_handler_
->StopOngoingDumps(
548 base::Bind(&WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps
,
554 DoUploadLogAndRtpDumps(log_directory
);
557 void WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps(
558 const base::FilePath
& log_directory
) {
559 WebRtcLogUploadDoneData upload_done_data
;
560 upload_done_data
.log_path
= log_directory
;
562 if (rtp_dump_handler_
) {
563 WebRtcRtpDumpHandler::ReleasedDumps
rtp_dumps(
564 rtp_dump_handler_
->ReleaseDumps());
565 upload_done_data
.incoming_rtp_dump
= rtp_dumps
.incoming_dump_path
;
566 upload_done_data
.outgoing_rtp_dump
= rtp_dumps
.outgoing_dump_path
;
568 rtp_dump_handler_
.reset();
569 stop_rtp_dump_callback_
.Reset();
572 upload_done_data
.callback
= upload_callback_
;
573 upload_done_data
.host
= this;
574 upload_callback_
.Reset();
576 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, base::Bind(
577 &WebRtcLogUploader::LoggingStoppedDoUpload
,
578 base::Unretained(g_browser_process
->webrtc_log_uploader()),
579 Passed(&log_buffer_
),
585 circular_buffer_
.reset();
588 void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
589 GenericDoneCallback
* callback
,
591 const std::string
& error_message
) {
592 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
593 DCHECK(!(*callback
).is_null());
594 content::BrowserThread::PostTask(
595 content::BrowserThread::UI
,
597 base::Bind(*callback
, success
, error_message
));
601 void WebRtcLoggingHandlerHost::CreateRtpDumpHandlerAndStart(
603 GenericDoneCallback callback
,
604 const base::FilePath
& dump_dir
) {
605 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
607 // |rtp_dump_handler_| may be non-NULL if StartRtpDump is called again before
608 // GetLogDirectoryAndEnsureExists returns on the FILE thread for a previous
610 if (!rtp_dump_handler_
)
611 rtp_dump_handler_
.reset(new WebRtcRtpDumpHandler(dump_dir
));
613 DoStartRtpDump(type
, &callback
);
616 void WebRtcLoggingHandlerHost::DoStartRtpDump(RtpDumpType type
,
617 GenericDoneCallback
* callback
) {
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
619 DCHECK(rtp_dump_handler_
);
623 bool result
= rtp_dump_handler_
->StartDump(type
, &error
);
624 FireGenericDoneCallback(callback
, result
, error
);