blob: 27877b0bd981f9fd9d43169fdc261953642d22e8 [file] [log] [blame]
// 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 <stdint.h>
#include <utility>
#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_t 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<GPUDevice> 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<SystemInfoHandler> 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<SystemInfoHandler> 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) {
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<scoped_refptr<GPUDevice>> devices;
devices.push_back(GPUDeviceToProtocol(gpu_info.gpu));
for (const auto& device : gpu_info.secondary_gpus)
devices.push_back(GPUDeviceToProtocol(device));
scoped_ptr<base::DictionaryValue> aux_attributes(new base::DictionaryValue);
AuxGPUInfoEnumerator enumerator(aux_attributes.get());
gpu_info.EnumerateFields(&enumerator);
scoped_refptr<GPUInfo> gpu =
GPUInfo::Create()
->set_devices(devices)
->set_aux_attributes(std::move(aux_attributes))
->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