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/base/net_util.h" 33 #include "net/url_request/url_request_context_getter.h" 34 35 #if defined(OS_LINUX) 36 #include "base/linux_util.h" 37 #endif 38 39 #if defined(OS_MACOSX) 40 #include "base/mac/mac_util.h" 41 #endif 42 43 using base::IntToString; 44 using content::BrowserThread; 45 46 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 52 53 namespace { 54 55 const char kLogNotStoppedOrNoLogOpen[] = 56 "Logging not stopped or no log open."; 57 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; 75 } 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; 84 } 85 case net::ADDRESS_FAMILY_UNSPECIFIED: { 86 break; 87 } 88 } 89 return sensitive_address; 90 #else 91 return net::IPAddressToString(address); 92 #endif 93 } 94 95 } // namespace 96 97 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile) 98 : profile_(profile), 99 logging_state_(CLOSED), 100 upload_log_on_render_close_(false) { 101 DCHECK(profile_); 102 } 103 104 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {} 105 106 void WebRtcLoggingHandlerHost::SetMetaData( 107 const std::map<std::string, std::string>& meta_data, 108 const GenericDoneCallback& callback) { 109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 110 DCHECK(!callback.is_null()); 111 112 bool success = false; 113 std::string error_message; 114 if (logging_state_ == CLOSED) { 115 meta_data_ = meta_data; 116 success = true; 117 } else { 118 error_message = "Meta data must be set before starting"; 119 } 120 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 121 base::Bind(callback, success, 122 error_message)); 123 } 124 125 void WebRtcLoggingHandlerHost::StartLogging( 126 const GenericDoneCallback& callback) { 127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 128 DCHECK(!callback.is_null()); 129 130 start_callback_ = callback; 131 if (logging_state_ != CLOSED) { 132 FireGenericDoneCallback(&start_callback_, false, "A log is already open"); 133 return; 134 } 135 logging_state_ = STARTING; 136 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 137 &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this)); 138 } 139 140 void WebRtcLoggingHandlerHost::StopLogging( 141 const GenericDoneCallback& callback) { 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 143 DCHECK(!callback.is_null()); 144 145 stop_callback_ = callback; 146 if (logging_state_ != STARTED) { 147 FireGenericDoneCallback(&stop_callback_, false, "Logging not started"); 148 return; 149 } 150 logging_state_ = STOPPING; 151 Send(new WebRtcLoggingMsg_StopLogging()); 152 } 153 154 void WebRtcLoggingHandlerHost::UploadLog(const UploadDoneCallback& callback) { 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 156 DCHECK(!callback.is_null()); 157 158 if (logging_state_ != STOPPED) { 159 if (!callback.is_null()) { 160 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 161 base::Bind(callback, false, "", kLogNotStoppedOrNoLogOpen)); 162 } 163 return; 164 } 165 upload_callback_ = callback; 166 TriggerUploadLog(); 167 } 168 169 void WebRtcLoggingHandlerHost::UploadLogDone() { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 171 logging_state_ = CLOSED; 172 } 173 174 void WebRtcLoggingHandlerHost::DiscardLog(const GenericDoneCallback& callback) { 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 176 DCHECK(!callback.is_null()); 177 178 GenericDoneCallback discard_callback = callback; 179 if (logging_state_ != STOPPED) { 180 FireGenericDoneCallback(&discard_callback, false, 181 kLogNotStoppedOrNoLogOpen); 182 return; 183 } 184 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload(); 185 circular_buffer_.reset(); 186 log_buffer_.reset(); 187 logging_state_ = CLOSED; 188 FireGenericDoneCallback(&discard_callback, true, ""); 189 } 190 191 void WebRtcLoggingHandlerHost::OnChannelClosing() { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 193 if (logging_state_ == STARTED || logging_state_ == STOPPED) { 194 if (upload_log_on_render_close_) { 195 logging_state_ = STOPPED; 196 TriggerUploadLog(); 197 } else { 198 g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload(); 199 } 200 } 201 content::BrowserMessageFilter::OnChannelClosing(); 202 } 203 204 void WebRtcLoggingHandlerHost::OnDestruct() const { 205 BrowserThread::DeleteOnIOThread::Destruct(this); 206 } 207 208 bool WebRtcLoggingHandlerHost::OnMessageReceived(const IPC::Message& message, 209 bool* message_was_ok) { 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 211 bool handled = true; 212 IPC_BEGIN_MESSAGE_MAP_EX(WebRtcLoggingHandlerHost, message, *message_was_ok) 213 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_AddLogMessage, OnAddLogMessage) 214 IPC_MESSAGE_HANDLER(WebRtcLoggingMsg_LoggingStopped, 215 OnLoggingStoppedInRenderer) 216 IPC_MESSAGE_UNHANDLED(handled = false) 217 IPC_END_MESSAGE_MAP_EX() 218 219 return handled; 220 } 221 222 void WebRtcLoggingHandlerHost::OnAddLogMessage(const std::string& message) { 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 224 if (logging_state_ == STARTED || logging_state_ == STOPPING) { 225 DCHECK(circular_buffer_.get()); 226 circular_buffer_->Write(message.c_str(), message.length()); 227 const char eol = '\n'; 228 circular_buffer_->Write(&eol, 1); 229 } 230 } 231 232 void WebRtcLoggingHandlerHost::OnLoggingStoppedInRenderer() { 233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 234 logging_state_ = STOPPED; 235 FireGenericDoneCallback(&stop_callback_, true, ""); 236 } 237 238 void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() { 239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 240 if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) { 241 logging_state_ = CLOSED; 242 FireGenericDoneCallback( 243 &start_callback_, false, "Cannot start, maybe the maximum number of " 244 "simultaneuos logs has been reached."); 245 return; 246 } 247 system_request_context_ = g_browser_process->system_request_context(); 248 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 249 &WebRtcLoggingHandlerHost::DoStartLogging, this)); 250 } 251 252 void WebRtcLoggingHandlerHost::DoStartLogging() { 253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 254 255 log_buffer_.reset(new unsigned char[kWebRtcLogSize]); 256 circular_buffer_.reset( 257 new PartialCircularBuffer(log_buffer_.get(), 258 kWebRtcLogSize, 259 kWebRtcLogSize / 2, 260 false)); 261 262 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 263 &WebRtcLoggingHandlerHost::LogMachineInfo, this)); 264 } 265 266 void WebRtcLoggingHandlerHost::LogMachineInfo() { 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 268 269 // Meta data 270 std::string info; 271 std::map<std::string, std::string>::iterator it = meta_data_.begin(); 272 for (; it != meta_data_.end(); ++it) { 273 info = it->first + ": " + it->second + '\n'; 274 circular_buffer_->Write(info.c_str(), info.length()); 275 } 276 277 // OS 278 info = base::SysInfo::OperatingSystemName() + " " + 279 base::SysInfo::OperatingSystemVersion() + " " + 280 base::SysInfo::OperatingSystemArchitecture() + '\n'; 281 circular_buffer_->Write(info.c_str(), info.length()); 282 #if defined(OS_LINUX) 283 info = "Linux distribution: " + base::GetLinuxDistro() + '\n'; 284 circular_buffer_->Write(info.c_str(), info.length()); 285 #endif 286 287 // CPU 288 base::CPU cpu; 289 info = "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) + 290 "." + IntToString(cpu.stepping()) + 291 ", x" + IntToString(base::SysInfo::NumberOfProcessors()) + ", " + 292 IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB" + '\n'; 293 circular_buffer_->Write(info.c_str(), info.length()); 294 std::string cpu_brand = cpu.cpu_brand(); 295 // Workaround for crbug.com/249713. 296 // TODO(grunell): Remove workaround when bug is fixed. 297 size_t null_pos = cpu_brand.find('\0'); 298 if (null_pos != std::string::npos) 299 cpu_brand.erase(null_pos); 300 info = "Cpu brand: " + cpu_brand + '\n'; 301 circular_buffer_->Write(info.c_str(), info.length()); 302 303 // Computer model 304 #if defined(OS_MACOSX) 305 info = "Computer model: " + base::mac::GetModelIdentifier() + '\n'; 306 #else 307 info = "Computer model: Not available\n"; 308 #endif 309 circular_buffer_->Write(info.c_str(), info.length()); 310 311 // GPU 312 gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo(); 313 info = "Gpu: machine-model='" + gpu_info.machine_model + 314 "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) + 315 ", device-id=" + IntToString(gpu_info.gpu.device_id) + 316 ", driver-vendor='" + gpu_info.driver_vendor + 317 "', driver-version=" + gpu_info.driver_version + '\n'; 318 circular_buffer_->Write(info.c_str(), info.length()); 319 320 // Network interfaces 321 net::NetworkInterfaceList network_list; 322 net::GetNetworkList(&network_list); 323 info = "Discovered " + IntToString(network_list.size()) + 324 " network interfaces:" + '\n'; 325 circular_buffer_->Write(info.c_str(), info.length()); 326 for (net::NetworkInterfaceList::iterator it = network_list.begin(); 327 it != network_list.end(); ++it) { 328 info = "Name: " + it->name + 329 ", Address: " + IPAddressToSensitiveString(it->address) + '\n'; 330 circular_buffer_->Write(info.c_str(), info.length()); 331 } 332 333 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 334 &WebRtcLoggingHandlerHost::NotifyLoggingStarted, this)); 335 } 336 337 void WebRtcLoggingHandlerHost::NotifyLoggingStarted() { 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 339 Send(new WebRtcLoggingMsg_StartLogging()); 340 logging_state_ = STARTED; 341 FireGenericDoneCallback(&start_callback_, true, ""); 342 } 343 344 void WebRtcLoggingHandlerHost::TriggerUploadLog() { 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 346 DCHECK(logging_state_ == STOPPED); 347 348 logging_state_ = UPLOADING; 349 WebRtcLogUploadDoneData upload_done_data; 350 upload_done_data.upload_list_path = 351 WebRtcLogUploadList::GetFilePathForProfile(profile_); 352 upload_done_data.callback = upload_callback_; 353 upload_done_data.host = this; 354 upload_callback_.Reset(); 355 356 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 357 &WebRtcLogUploader::LoggingStoppedDoUpload, 358 base::Unretained(g_browser_process->webrtc_log_uploader()), 359 system_request_context_, 360 Passed(&log_buffer_), 361 kWebRtcLogSize, 362 meta_data_, 363 upload_done_data)); 364 365 meta_data_.clear(); 366 circular_buffer_.reset(); 367 } 368 369 void WebRtcLoggingHandlerHost::FireGenericDoneCallback( 370 GenericDoneCallback* callback, bool success, 371 const std::string& error_message) { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 373 DCHECK(!(*callback).is_null()); 374 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 375 base::Bind(*callback, success, 376 error_message)); 377 (*callback).Reset(); 378 } 379