Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media / webrtc_logging_handler_host.cc
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/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"
33
34 #if defined(OS_LINUX)
35 #include "base/linux_util.h"
36 #endif
37
38 #if defined(OS_MACOSX)
39 #include "base/mac/mac_util.h"
40 #endif
41
42 #if defined(OS_CHROMEOS)
43 #include "chromeos/system/statistics_provider.h"
44 #endif
45
46 using base::IntToString;
47 using content::BrowserThread;
48
49
50 #if defined(OS_ANDROID)
51 const size_t kWebRtcLogSize = 1 * 1024 * 1024;  // 1 MB
52 #else
53 const size_t kWebRtcLogSize = 6 * 1024 * 1024;  // 6 MB
54 #endif
55
56 namespace {
57
58 const char kLogNotStoppedOrNoLogOpen[] =
59     "Logging not stopped or no log open.";
60
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
65 // not stripped.
66 std::string IPAddressToSensitiveString(const net::IPAddressNumber& address) {
67 #if defined(NDEBUG)
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)
74         return std::string();
75       sensitive_address.resize(find_pos);
76       sensitive_address += ".x";
77       break;
78     }
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);
86       break;
87     }
88     case net::ADDRESS_FAMILY_UNSPECIFIED: {
89       break;
90     }
91   }
92   return sensitive_address;
93 #else
94   return net::IPAddressToString(address);
95 #endif
96 }
97
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';
104   }
105   // Remove last '\n'.
106   message->resize(message->size() - 1);
107 }
108
109 }  // namespace
110
111 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
112     : profile_(profile),
113       logging_state_(CLOSED),
114       upload_log_on_render_close_(false) {
115   DCHECK(profile_);
116 }
117
118 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
119
120 void WebRtcLoggingHandlerHost::SetMetaData(
121     const MetaDataMap& meta_data,
122     const GenericDoneCallback& callback) {
123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
124   DCHECK(!callback.is_null());
125
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);
134   } else {
135     error_message = "Meta data must be set before stop or upload.";
136   }
137   bool success = error_message.empty();
138   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
139                                    base::Bind(callback, success,
140                                               error_message));
141 }
142
143 void WebRtcLoggingHandlerHost::StartLogging(
144     const GenericDoneCallback& callback) {
145   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146   DCHECK(!callback.is_null());
147
148   start_callback_ = callback;
149   if (logging_state_ != CLOSED) {
150     FireGenericDoneCallback(&start_callback_, false, "A log is already open");
151     return;
152   }
153   logging_state_ = STARTING;
154   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
155       &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
156 }
157
158 void WebRtcLoggingHandlerHost::StopLogging(
159     const GenericDoneCallback& callback) {
160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
161   DCHECK(!callback.is_null());
162
163   stop_callback_ = callback;
164   if (logging_state_ != STARTED) {
165     FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
166     return;
167   }
168   logging_state_ = STOPPING;
169   Send(new WebRtcLoggingMsg_StopLogging());
170 }
171
172 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) {
173   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
174   DCHECK(!callback.is_null());
175
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));
180     }
181     return;
182   }
183   upload_callback_ = callback;
184   TriggerUploadLog();
185 }
186
187 void WebRtcLoggingHandlerHost::UploadLogDone() {
188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189   logging_state_ = CLOSED;
190 }
191
192 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) {
193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
194   DCHECK(!callback.is_null());
195
196   GenericDoneCallback discard_callback = callback;
197   if (logging_state_ != STOPPED) {
198     FireGenericDoneCallback(&discard_callback, false,
199                             kLogNotStoppedOrNoLogOpen);
200     return;
201   }
202   g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
203   circular_buffer_.reset();
204   log_buffer_.reset();
205   logging_state_ = CLOSED;
206   FireGenericDoneCallback(&discard_callback, true, "");
207 }
208
209 void WebRtcLoggingHandlerHost::LogMessage(const std::string& message) {
210   BrowserThread::PostTask(
211       BrowserThread::IO,
212       FROM_HERE,
213       base::Bind(
214           &WebRtcLoggingHandlerHost::AddLogMessageFromBrowser, this, message));
215 }
216
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;
222       TriggerUploadLog();
223     } else {
224       g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
225     }
226   }
227   content::BrowserMessageFilter::OnChannelClosing();
228 }
229
230 void WebRtcLoggingHandlerHost::OnDestruct() const {
231   BrowserThread::DeleteOnIOThread::Destruct(this);
232 }
233
234 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message,
235                                                  bool* message_was_ok) {
236   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237   bool handled = true;
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()
244
245   return handled;
246 }
247
248 void WebRtcLoggingHandlerHost::AddLogMessageFromBrowser(
249     const std::string& message) {
250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
251   if (logging_state_ == STARTED)
252     LogToCircularBuffer(message);
253 }
254
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);
259 }
260
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 "
267                 << logging_state_;
268     BadMessageReceived();
269     return;
270   }
271   logging_state_ = STOPPED;
272   FireGenericDoneCallback(&stop_callback_, true, "");
273 }
274
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.");
282     return;
283   }
284   system_request_context_ = g_browser_process->system_request_context();
285   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
286       &WebRtcLoggingHandlerHost::DoStartLogging, this));
287 }
288
289 void WebRtcLoggingHandlerHost::DoStartLogging() {
290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
291
292   log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
293   circular_buffer_.reset(
294     new PartialCircularBuffer(log_buffer_.get(),
295                               kWebRtcLogSize,
296                               kWebRtcLogSize / 2,
297                               false));
298
299   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
300       &WebRtcLoggingHandlerHost::LogMachineInfoOnFileThread, this));
301 }
302
303 void WebRtcLoggingHandlerHost::LogMachineInfoOnFileThread() {
304   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
305
306   net::NetworkInterfaceList network_list;
307   net::GetNetworkList(&network_list,
308                       net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
309
310   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
311       &WebRtcLoggingHandlerHost::LogMachineInfoOnIOThread, this, network_list));
312 }
313
314 void WebRtcLoggingHandlerHost::LogMachineInfoOnIOThread(
315     const net::NetworkInterfaceList& network_list) {
316   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
317
318   // Write metadata if received before logging started.
319   if (!meta_data_.empty()) {
320     std::string info;
321     FormatMetaDataAsLogMessage(meta_data_, &info);
322     LogToCircularBuffer(info);
323   }
324
325   // OS
326   LogToCircularBuffer(base::SysInfo::OperatingSystemName() + " " +
327                       base::SysInfo::OperatingSystemVersion() + " " +
328                       base::SysInfo::OperatingSystemArchitecture());
329 #if defined(OS_LINUX)
330   LogToCircularBuffer("Linux distribution: " + base::GetLinuxDistro());
331 #endif
332
333   // CPU
334   base::CPU cpu;
335   LogToCircularBuffer(
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);
347
348   // Computer model
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);
355 #endif
356   LogToCircularBuffer("Computer model: " + computer_model);
357
358   // GPU
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);
365
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));
373   }
374
375   NotifyLoggingStarted();
376 }
377
378 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
379   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
380   Send(new WebRtcLoggingMsg_StartLogging());
381   logging_state_ = STARTED;
382   FireGenericDoneCallback(&start_callback_, true, "");
383 }
384
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);
391 }
392
393 void WebRtcLoggingHandlerHost::TriggerUploadLog() {
394   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
395   DCHECK(logging_state_ == STOPPED);
396
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();
404
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_),
410       kWebRtcLogSize,
411       meta_data_,
412       upload_done_data));
413
414   meta_data_.clear();
415   circular_buffer_.reset();
416 }
417
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,
425                                               error_message));
426   (*callback).Reset();
427 }