// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/devtools/protocol/system_info_handler.h" #include "base/bind.h" #include "content/browser/gpu/compositor_util.h" #include "content/public/browser/gpu_data_manager.h" #include "gpu/config/gpu_info.h" namespace content { namespace devtools { namespace system_info { namespace { using Response = DevToolsProtocolClient::Response; // Give the GPU process a few seconds to provide GPU info. const int kGPUInfoWatchdogTimeoutMs = 5000; class AuxGPUInfoEnumerator : public gpu::GPUInfo::Enumerator { public: AuxGPUInfoEnumerator(base::DictionaryValue* dictionary) : dictionary_(dictionary), in_aux_attributes_(false) { } void AddInt64(const char* name, int64 value) override { if (in_aux_attributes_) dictionary_->SetDouble(name, value); } void AddInt(const char* name, int value) override { if (in_aux_attributes_) dictionary_->SetInteger(name, value); } void AddString(const char* name, const std::string& value) override { if (in_aux_attributes_) dictionary_->SetString(name, value); } void AddBool(const char* name, bool value) override { if (in_aux_attributes_) dictionary_->SetBoolean(name, value); } void AddTimeDeltaInSecondsF(const char* name, const base::TimeDelta& value) override { if (in_aux_attributes_) dictionary_->SetDouble(name, value.InSecondsF()); } void BeginGPUDevice() override {} void EndGPUDevice() override {} void BeginVideoDecodeAcceleratorSupportedProfile() override {} void EndVideoDecodeAcceleratorSupportedProfile() override {} void BeginVideoEncodeAcceleratorSupportedProfile() override {} void EndVideoEncodeAcceleratorSupportedProfile() override {} void BeginAuxAttributes() override { in_aux_attributes_ = true; } void EndAuxAttributes() override { in_aux_attributes_ = false; } private: base::DictionaryValue* dictionary_; bool in_aux_attributes_; }; scoped_refptr GPUDeviceToProtocol( const gpu::GPUInfo::GPUDevice& device) { return GPUDevice::Create()->set_vendor_id(device.vendor_id) ->set_device_id(device.device_id) ->set_vendor_string(device.vendor_string) ->set_device_string(device.device_string); } } // namespace class SystemInfoHandlerGpuObserver : public content::GpuDataManagerObserver { public: SystemInfoHandlerGpuObserver(base::WeakPtr handler, DevToolsCommandId command_id) : handler_(handler), command_id_(command_id), observer_id_(++next_observer_id_) { if (handler_) { handler_->AddActiveObserverId(observer_id_); } } void OnGpuInfoUpdate() override { UnregisterAndSendResponse(); } void OnGpuProcessCrashed(base::TerminationStatus exit_code) override { UnregisterAndSendResponse(); } void UnregisterAndSendResponse() { GpuDataManager::GetInstance()->RemoveObserver(this); if (handler_.get()) { if (handler_->RemoveActiveObserverId(observer_id_)) { handler_->SendGetInfoResponse(command_id_); } } delete this; } int GetObserverId() { return observer_id_; } private: base::WeakPtr handler_; DevToolsCommandId command_id_; int observer_id_; static int next_observer_id_; }; int SystemInfoHandlerGpuObserver::next_observer_id_ = 0; SystemInfoHandler::SystemInfoHandler() : weak_factory_(this) { } SystemInfoHandler::~SystemInfoHandler() { } void SystemInfoHandler::SetClient(scoped_ptr client) { client_.swap(client); } Response SystemInfoHandler::GetInfo(DevToolsCommandId command_id) { std::string reason; if (!GpuDataManager::GetInstance()->GpuAccessAllowed(&reason) || GpuDataManager::GetInstance()->IsEssentialGpuInfoAvailable()) { // The GpuDataManager already has all of the information needed to make // GPU-based blacklisting decisions. Post a task to give it to the // client asynchronously. // // Waiting for complete GPU info in the if-test above seems to // frequently hit internal timeouts in the launching of the unsandboxed // GPU process in debug builds on Windows. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&SystemInfoHandler::SendGetInfoResponse, weak_factory_.GetWeakPtr(), command_id)); } else { // We will be able to get more information from the GpuDataManager. // Register a transient observer with it to call us back when the // information is available. SystemInfoHandlerGpuObserver* observer = new SystemInfoHandlerGpuObserver( weak_factory_.GetWeakPtr(), command_id); BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&SystemInfoHandler::ObserverWatchdogCallback, weak_factory_.GetWeakPtr(), observer->GetObserverId(), command_id), base::TimeDelta::FromMilliseconds(kGPUInfoWatchdogTimeoutMs)); GpuDataManager::GetInstance()->AddObserver(observer); // There's no other method available to request just essential GPU info. GpuDataManager::GetInstance()->RequestCompleteGpuInfoIfNeeded(); } return Response::OK(); } void SystemInfoHandler::SendGetInfoResponse(DevToolsCommandId command_id) { gpu::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo(); std::vector> devices; devices.push_back(GPUDeviceToProtocol(gpu_info.gpu)); for (const auto& device : gpu_info.secondary_gpus) devices.push_back(GPUDeviceToProtocol(device)); scoped_ptr aux_attributes(new base::DictionaryValue); AuxGPUInfoEnumerator enumerator(aux_attributes.get()); gpu_info.EnumerateFields(&enumerator); scoped_refptr gpu = GPUInfo::Create() ->set_devices(devices) ->set_aux_attributes(aux_attributes.Pass()) ->set_feature_status(make_scoped_ptr(GetFeatureStatus())) ->set_driver_bug_workarounds(GetDriverBugWorkarounds()); client_->SendGetInfoResponse( command_id, GetInfoResponse::Create()->set_gpu(gpu) ->set_model_name(gpu_info.machine_model_name) ->set_model_version(gpu_info.machine_model_version)); } void SystemInfoHandler::AddActiveObserverId(int observer_id) { base::AutoLock auto_lock(lock_); active_observers_.insert(observer_id); } bool SystemInfoHandler::RemoveActiveObserverId(int observer_id) { base::AutoLock auto_lock(lock_); int num_removed = active_observers_.erase(observer_id); return (num_removed != 0); } void SystemInfoHandler::ObserverWatchdogCallback(int observer_id, DevToolsCommandId command_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (RemoveActiveObserverId(observer_id)) { SendGetInfoResponse(command_id); // For the time being we want to know about this event in the test logs. LOG(ERROR) << "SystemInfoHandler: request for GPU info timed out!" << " Most recent info sent."; } } } // namespace system_info } // namespace devtools } // namespace content