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 "chrome/common/pref_names.h"
29 #include "chromeos/settings/cros_settings_names.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/content_browser_client.h"
32 #include "content/public/browser/gpu_data_manager.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "gpu/config/gpu_info.h"
35 #include "net/base/address_family.h"
36 #include "net/url_request/url_request_context_getter.h"
39 #include "base/linux_util.h"
42 #if defined(OS_MACOSX)
43 #include "base/mac/mac_util.h"
46 #if defined(OS_CHROMEOS)
47 #include "chromeos/system/statistics_provider.h"
50 using base::IntToString
;
51 using content::BrowserThread
;
55 #if defined(OS_ANDROID)
56 const size_t kWebRtcLogSize
= 1 * 1024 * 1024; // 1 MB
58 const size_t kWebRtcLogSize
= 6 * 1024 * 1024; // 6 MB
61 const char kLogNotStoppedOrNoLogOpen
[] =
62 "Logging not stopped or no log open.";
64 // For privacy reasons when logging IP addresses. The returned "sensitive
65 // string" is for release builds a string with the end stripped away. Last
66 // octet for IPv4 and last 80 bits (5 groups) for IPv6. String will be
67 // "1.2.3.x" and "1.2.3::" respectively. For debug builds, the string is
69 std::string
IPAddressToSensitiveString(const net::IPAddressNumber
& address
) {
71 std::string sensitive_address
;
72 switch (net::GetAddressFamily(address
)) {
73 case net::ADDRESS_FAMILY_IPV4
: {
74 sensitive_address
= net::IPAddressToString(address
);
75 size_t find_pos
= sensitive_address
.rfind('.');
76 if (find_pos
== std::string::npos
)
78 sensitive_address
.resize(find_pos
);
79 sensitive_address
+= ".x";
82 case net::ADDRESS_FAMILY_IPV6
: {
83 // TODO(grunell): Create a string of format "1:2:3:x:x:x:x:x" to clarify
84 // that the end has been stripped out.
85 net::IPAddressNumber sensitive_address_number
= address
;
86 sensitive_address_number
.resize(net::kIPv6AddressSize
- 10);
87 sensitive_address_number
.resize(net::kIPv6AddressSize
, 0);
88 sensitive_address
= net::IPAddressToString(sensitive_address_number
);
91 case net::ADDRESS_FAMILY_UNSPECIFIED
: {
95 return sensitive_address
;
97 return net::IPAddressToString(address
);
101 void FormatMetaDataAsLogMessage(
102 const MetaDataMap
& meta_data
,
103 std::string
* message
) {
104 for (MetaDataMap::const_iterator it
= meta_data
.begin();
105 it
!= meta_data
.end(); ++it
) {
106 *message
+= it
->first
+ ": " + it
->second
+ '\n';
109 message
->resize(message
->size() - 1);
114 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile
* profile
)
115 : BrowserMessageFilter(WebRtcLoggingMsgStart
),
117 logging_state_(CLOSED
),
118 upload_log_on_render_close_(false) {
122 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
124 void WebRtcLoggingHandlerHost::SetMetaData(
125 const MetaDataMap
& meta_data
,
126 const GenericDoneCallback
& callback
) {
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
128 DCHECK(!callback
.is_null());
130 std::string error_message
;
131 if (logging_state_
== CLOSED
) {
132 meta_data_
= meta_data
;
133 } else if (logging_state_
== STARTED
) {
134 meta_data_
= meta_data
;
135 std::string meta_data_message
;
136 FormatMetaDataAsLogMessage(meta_data_
, &meta_data_message
);
137 LogToCircularBuffer(meta_data_message
);
139 error_message
= "Meta data must be set before stop or upload.";
141 bool success
= error_message
.empty();
142 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
143 base::Bind(callback
, success
,
147 void WebRtcLoggingHandlerHost::StartLogging(
148 const GenericDoneCallback
& callback
) {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
150 DCHECK(!callback
.is_null());
152 start_callback_
= callback
;
153 if (logging_state_
!= CLOSED
) {
154 FireGenericDoneCallback(&start_callback_
, false, "A log is already open");
157 logging_state_
= STARTING
;
158 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
159 &WebRtcLoggingHandlerHost::StartLoggingIfAllowed
, this));
162 void WebRtcLoggingHandlerHost::StopLogging(
163 const GenericDoneCallback
& callback
) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
165 DCHECK(!callback
.is_null());
167 stop_callback_
= callback
;
168 if (logging_state_
!= STARTED
) {
169 FireGenericDoneCallback(&stop_callback_
, false, "Logging not started");
172 logging_state_
= STOPPING
;
173 Send(new WebRtcLoggingMsg_StopLogging());
176 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback
& callback
) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
178 DCHECK(!callback
.is_null());
180 if (logging_state_
!= STOPPED
) {
181 if (!callback
.is_null()) {
182 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
183 base::Bind(callback
, false, "", kLogNotStoppedOrNoLogOpen
));
188 upload_callback_
= callback
;
189 logging_state_
= UPLOADING
;
190 content::BrowserThread::PostTaskAndReplyWithResult(
191 content::BrowserThread::FILE,
193 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
195 base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload
, this));
198 void WebRtcLoggingHandlerHost::UploadLogDone() {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
200 logging_state_
= CLOSED
;
203 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback
& callback
) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
205 DCHECK(!callback
.is_null());
207 GenericDoneCallback discard_callback
= callback
;
208 if (logging_state_
!= STOPPED
) {
209 FireGenericDoneCallback(&discard_callback
, false,
210 kLogNotStoppedOrNoLogOpen
);
213 g_browser_process
->webrtc_log_uploader()->LoggingStoppedDontUpload();
214 circular_buffer_
.reset();
216 logging_state_
= CLOSED
;
217 rtp_dump_handler_
.reset();
218 stop_rtp_dump_callback_
.Reset();
219 FireGenericDoneCallback(&discard_callback
, true, "");
222 void WebRtcLoggingHandlerHost::LogMessage(const std::string
& message
) {
223 BrowserThread::PostTask(
227 &WebRtcLoggingHandlerHost::AddLogMessageFromBrowser
,
229 WebRtcLoggingMessageData(base::Time::Now(), message
)));
232 void WebRtcLoggingHandlerHost::StartRtpDump(
234 const GenericDoneCallback
& callback
,
235 const content::RenderProcessHost::WebRtcStopRtpDumpCallback
&
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
238 DCHECK(stop_rtp_dump_callback_
.is_null() ||
239 stop_rtp_dump_callback_
.Equals(stop_callback
));
241 stop_rtp_dump_callback_
= stop_callback
;
243 if (!rtp_dump_handler_
) {
244 content::BrowserThread::PostTaskAndReplyWithResult(
245 content::BrowserThread::FILE,
247 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
249 base::Bind(&WebRtcLoggingHandlerHost::CreateRtpDumpHandlerAndStart
,
256 GenericDoneCallback start_callback
= callback
;
257 DoStartRtpDump(type
, &start_callback
);
260 void WebRtcLoggingHandlerHost::StopRtpDump(
262 const GenericDoneCallback
& callback
) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
264 DCHECK(!callback
.is_null());
266 if (!rtp_dump_handler_
) {
267 GenericDoneCallback stop_callback
= callback
;
268 FireGenericDoneCallback(
269 &stop_callback
, false, "RTP dump has not been started.");
273 if (!stop_rtp_dump_callback_
.is_null()) {
274 BrowserThread::PostTask(
277 base::Bind(stop_rtp_dump_callback_
,
278 type
== RTP_DUMP_INCOMING
|| type
== RTP_DUMP_BOTH
,
279 type
== RTP_DUMP_OUTGOING
|| type
== RTP_DUMP_BOTH
));
282 rtp_dump_handler_
->StopDump(type
, callback
);
285 void WebRtcLoggingHandlerHost::OnRtpPacket(scoped_ptr
<uint8
[]> packet_header
,
286 size_t header_length
,
287 size_t packet_length
,
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
291 BrowserThread::PostTask(
294 base::Bind(&WebRtcLoggingHandlerHost::DumpRtpPacketOnIOThread
,
296 base::Passed(&packet_header
),
302 void WebRtcLoggingHandlerHost::DumpRtpPacketOnIOThread(
303 scoped_ptr
<uint8
[]> packet_header
,
304 size_t header_length
,
305 size_t packet_length
,
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
309 // |rtp_dump_handler_| could be NULL if we are waiting for the FILE thread to
310 // create/ensure the log directory.
311 if (rtp_dump_handler_
) {
312 rtp_dump_handler_
->OnRtpPacket(
313 packet_header
.get(), header_length
, packet_length
, incoming
);
317 void WebRtcLoggingHandlerHost::OnChannelClosing() {
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
319 if (logging_state_
== STARTED
|| logging_state_
== STOPPED
) {
320 if (upload_log_on_render_close_
) {
321 logging_state_
= UPLOADING
;
322 logging_started_time_
= base::Time();
324 content::BrowserThread::PostTaskAndReplyWithResult(
325 content::BrowserThread::FILE,
327 base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists
,
329 base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload
, this));
331 g_browser_process
->webrtc_log_uploader()->LoggingStoppedDontUpload();
334 content::BrowserMessageFilter::OnChannelClosing();
337 void WebRtcLoggingHandlerHost::OnDestruct() const {
338 BrowserThread::DeleteOnIOThread::Destruct(this);
341 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message
& message
) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
344 IPC_BEGIN_MESSAGE_MAP(WebRtcLoggingHandlerHost
, message
)
345 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessages
, OnAddLogMessages
)
346 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped
,
347 OnLoggingStoppedInRenderer
)
348 IPC_MESSAGE_UNHANDLED(handled
= false)
349 IPC_END_MESSAGE_MAP()
354 void WebRtcLoggingHandlerHost::AddLogMessageFromBrowser(
355 const WebRtcLoggingMessageData
& message
) {
356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
357 if (logging_state_
== STARTED
)
358 LogToCircularBuffer(message
.Format(logging_started_time_
));
361 void WebRtcLoggingHandlerHost::OnAddLogMessages(
362 const std::vector
<WebRtcLoggingMessageData
>& messages
) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
364 if (logging_state_
== STARTED
|| logging_state_
== STOPPING
) {
365 for (size_t i
= 0; i
< messages
.size(); ++i
) {
366 LogToCircularBuffer(messages
[i
].Format(logging_started_time_
));
371 void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
373 if (logging_state_
!= STOPPING
) {
374 // If an out-of-order response is received, stop_callback_ may be invalid,
375 // and must not be invoked.
376 DLOG(ERROR
) << "OnLoggingStoppedInRenderer invoked in state "
378 BadMessageReceived();
381 logging_started_time_
= base::Time();
382 logging_state_
= STOPPED
;
383 FireGenericDoneCallback(&stop_callback_
, true, "");
386 void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
388 if (!g_browser_process
->webrtc_log_uploader()->ApplyForStartLogging()) {
389 logging_state_
= CLOSED
;
390 FireGenericDoneCallback(
391 &start_callback_
, false, "Cannot start, maybe the maximum number of "
392 "simultaneuos logs has been reached.");
395 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
396 &WebRtcLoggingHandlerHost::DoStartLogging
, this));
399 void WebRtcLoggingHandlerHost::DoStartLogging() {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
402 log_buffer_
.reset(new unsigned char[kWebRtcLogSize
]);
403 circular_buffer_
.reset(
404 new PartialCircularBuffer(log_buffer_
.get(),
409 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, base::Bind(
410 &WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread
, this));
413 void WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread() {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
416 net::NetworkInterfaceList network_list
;
417 net::GetNetworkList(&network_list
,
418 net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES
);
420 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
421 &WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread
, this, network_list
));
424 void WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread(
425 const net::NetworkInterfaceList
& network_list
) {
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
428 // Log start time (current time). We don't use base/i18n/time_formatting.h
429 // here because we don't want the format of the current locale.
430 base::Time::Exploded now
= {0};
431 base::Time::Now().LocalExplode(&now
);
432 LogToCircularBuffer(base::StringPrintf(
433 "Start %d-%02d-%02d %02d:%02d:%02d", now
.year
, now
.month
,
434 now
.day_of_month
, now
.hour
, now
.minute
, now
.second
));
436 // Write metadata if received before logging started.
437 if (!meta_data_
.empty()) {
439 FormatMetaDataAsLogMessage(meta_data_
, &info
);
440 LogToCircularBuffer(info
);
444 chrome::VersionInfo version_info
;
445 LogToCircularBuffer("Chrome version: " + version_info
.Version() + " " +
446 chrome::VersionInfo::GetVersionStringModifier());
449 LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
450 base::SysInfo::OperatingSystemVersion() + " " +
451 base::SysInfo::OperatingSystemArchitecture());
452 #if defined(OS_LINUX)
453 LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
459 "Cpu: " + IntToString(cpu
.family()) + "." + IntToString(cpu
.model()) +
460 "." + IntToString(cpu
.stepping()) + ", x" +
461 IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
462 IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
463 std::string cpu_brand
= cpu
.cpu_brand();
464 // Workaround for crbug.com/249713.
465 // TODO(grunell): Remove workaround when bug is fixed.
466 size_t null_pos
= cpu_brand
.find('\0');
467 if (null_pos
!= std::string::npos
)
468 cpu_brand
.erase(null_pos
);
469 LogToCircularBuffer("Cpu brand: " + cpu_brand
);
472 std::string computer_model
= "Not available";
473 #if defined(OS_MACOSX)
474 computer_model
= base::mac::GetModelIdentifier();
475 #elif defined(OS_CHROMEOS)
476 chromeos::system::StatisticsProvider::GetInstance()->
477 GetMachineStatistic(chromeos::system::kHardwareClassKey
, &computer_model
);
479 LogToCircularBuffer("Computer model: " + computer_model
);
482 gpu::GPUInfo gpu_info
= content::GpuDataManager::GetInstance()->GetGPUInfo();
484 "Gpu: machine-model-name=" + gpu_info
.machine_model_name
+
485 ", machine-model-version=" + gpu_info
.machine_model_version
+
486 ", vendor-id=" + IntToString(gpu_info
.gpu
.vendor_id
) +
487 ", device-id=" + IntToString(gpu_info
.gpu
.device_id
) +
488 ", driver-vendor=" + gpu_info
.driver_vendor
+
489 ", driver-version=" + gpu_info
.driver_version
);
491 "OpenGL: gl-vendor=" + gpu_info
.gl_vendor
+
492 ", gl-renderer=" + gpu_info
.gl_renderer
+
493 ", gl-version=" + gpu_info
.gl_version
);
495 // Network interfaces
496 LogToCircularBuffer("Discovered " + IntToString(network_list
.size()) +
497 " network interfaces:");
498 for (net::NetworkInterfaceList::const_iterator it
= network_list
.begin();
499 it
!= network_list
.end(); ++it
) {
500 LogToCircularBuffer("Name: " + it
->friendly_name
+ ", Address: " +
501 IPAddressToSensitiveString(it
->address
));
504 NotifyLoggingStarted();
507 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
509 Send(new WebRtcLoggingMsg_StartLogging());
510 logging_started_time_
= base::Time::Now();
511 logging_state_
= STARTED
;
512 FireGenericDoneCallback(&start_callback_
, true, "");
515 void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string
& message
) {
516 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
517 DCHECK(circular_buffer_
.get());
518 circular_buffer_
->Write(message
.c_str(), message
.length());
519 const char eol
= '\n';
520 circular_buffer_
->Write(&eol
, 1);
523 base::FilePath
WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists() {
524 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
525 base::FilePath log_dir_path
=
526 WebRtcLogList::GetWebRtcLogDirectoryForProfile(profile_
->GetPath());
527 base::File::Error error
;
528 if (!base::CreateDirectoryAndGetError(log_dir_path
, &error
)) {
529 DLOG(ERROR
) << "Could not create WebRTC log directory, error: " << error
;
530 return base::FilePath();
535 void WebRtcLoggingHandlerHost::TriggerUpload(
536 const base::FilePath
& log_directory
) {
537 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
538 DCHECK_EQ(logging_state_
, UPLOADING
);
540 if (rtp_dump_handler_
) {
541 BrowserThread::PostTask(
544 base::Bind(stop_rtp_dump_callback_
, true, true));
546 rtp_dump_handler_
->StopOngoingDumps(
547 base::Bind(&WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps
,
553 DoUploadLogAndRtpDumps(log_directory
);
556 void WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps(
557 const base::FilePath
& log_directory
) {
558 WebRtcLogUploadDoneData upload_done_data
;
559 upload_done_data
.log_path
= log_directory
;
561 if (rtp_dump_handler_
) {
562 WebRtcRtpDumpHandler::ReleasedDumps
rtp_dumps(
563 rtp_dump_handler_
->ReleaseDumps());
564 upload_done_data
.incoming_rtp_dump
= rtp_dumps
.incoming_dump_path
;
565 upload_done_data
.outgoing_rtp_dump
= rtp_dumps
.outgoing_dump_path
;
567 rtp_dump_handler_
.reset();
568 stop_rtp_dump_callback_
.Reset();
571 upload_done_data
.callback
= upload_callback_
;
572 upload_done_data
.host
= this;
573 upload_callback_
.Reset();
575 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
, base::Bind(
576 &WebRtcLogUploader::LoggingStoppedDoUpload
,
577 base::Unretained(g_browser_process
->webrtc_log_uploader()),
578 Passed(&log_buffer_
),
584 circular_buffer_
.reset();
587 void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
588 GenericDoneCallback
* callback
,
590 const std::string
& error_message
) {
591 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
592 DCHECK(!(*callback
).is_null());
593 content::BrowserThread::PostTask(
594 content::BrowserThread::UI
,
596 base::Bind(*callback
, success
, error_message
));
600 void WebRtcLoggingHandlerHost::CreateRtpDumpHandlerAndStart(
602 GenericDoneCallback callback
,
603 const base::FilePath
& dump_dir
) {
604 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
606 // |rtp_dump_handler_| may be non-NULL if StartRtpDump is called again before
607 // GetLogDirectoryAndEnsureExists returns on the FILE thread for a previous
609 if (!rtp_dump_handler_
)
610 rtp_dump_handler_
.reset(new WebRtcRtpDumpHandler(dump_dir
));
612 DoStartRtpDump(type
, &callback
);
615 void WebRtcLoggingHandlerHost::DoStartRtpDump(RtpDumpType type
,
616 GenericDoneCallback
* callback
) {
617 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
618 DCHECK(rtp_dump_handler_
);
622 bool result
= rtp_dump_handler_
->StartDump(type
, &error
);
623 FireGenericDoneCallback(callback
, result
, error
);