1 // Copyright (c) 2012 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 "content/browser/gpu/gpu_internals_ui.h"
7 #if defined(OS_LINUX) && defined(USE_X11)
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/command_line.h"
16 #include "base/environment.h"
17 #include "base/i18n/time_formatting.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/sys_info.h"
21 #include "base/values.h"
22 #include "content/browser/gpu/compositor_util.h"
23 #include "content/browser/gpu/gpu_data_manager_impl.h"
24 #include "content/grit/content_resources.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/gpu_data_manager_observer.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_ui.h"
29 #include "content/public/browser/web_ui_data_source.h"
30 #include "content/public/browser/web_ui_message_handler.h"
31 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/url_constants.h"
34 #include "gpu/config/gpu_feature_type.h"
35 #include "gpu/config/gpu_info.h"
36 #include "third_party/angle/src/common/version.h"
37 #include "ui/gl/gpu_switching_manager.h"
40 #include "ui/base/win/shell.h"
43 #if defined(OS_LINUX) && defined(USE_X11)
44 #include "ui/base/x/x11_util.h"
45 #include "ui/gfx/x/x11_atom_cache.h"
51 WebUIDataSource
* CreateGpuHTMLSource() {
52 WebUIDataSource
* source
= WebUIDataSource::Create(kChromeUIGpuHost
);
54 source
->SetJsonPath("strings.js");
55 source
->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS
);
56 source
->SetDefaultResource(IDR_GPU_INTERNALS_HTML
);
60 base::DictionaryValue
* NewDescriptionValuePair(const std::string
& desc
,
61 const std::string
& value
) {
62 base::DictionaryValue
* dict
= new base::DictionaryValue();
63 dict
->SetString("description", desc
);
64 dict
->SetString("value", value
);
68 base::DictionaryValue
* NewDescriptionValuePair(const std::string
& desc
,
70 base::DictionaryValue
* dict
= new base::DictionaryValue();
71 dict
->SetString("description", desc
);
72 dict
->Set("value", value
);
77 // Output DxDiagNode tree as nested array of {description,value} pairs
78 base::ListValue
* DxDiagNodeToList(const gpu::DxDiagNode
& node
) {
79 base::ListValue
* list
= new base::ListValue();
80 for (std::map
<std::string
, std::string
>::const_iterator it
=
82 it
!= node
.values
.end();
84 list
->Append(NewDescriptionValuePair(it
->first
, it
->second
));
87 for (std::map
<std::string
, gpu::DxDiagNode
>::const_iterator it
=
88 node
.children
.begin();
89 it
!= node
.children
.end();
91 base::ListValue
* sublist
= DxDiagNodeToList(it
->second
);
92 list
->Append(NewDescriptionValuePair(it
->first
, sublist
));
98 std::string
GPUDeviceToString(const gpu::GPUInfo::GPUDevice
& gpu
) {
99 std::string vendor
= base::StringPrintf("0x%04x", gpu
.vendor_id
);
100 if (!gpu
.vendor_string
.empty())
101 vendor
+= " [" + gpu
.vendor_string
+ "]";
102 std::string device
= base::StringPrintf("0x%04x", gpu
.device_id
);
103 if (!gpu
.device_string
.empty())
104 device
+= " [" + gpu
.device_string
+ "]";
105 return base::StringPrintf("VENDOR = %s, DEVICE= %s%s",
106 vendor
.c_str(), device
.c_str(), gpu
.active
? " *ACTIVE*" : "");
109 base::DictionaryValue
* GpuInfoAsDictionaryValue() {
110 gpu::GPUInfo gpu_info
= GpuDataManagerImpl::GetInstance()->GetGPUInfo();
111 base::ListValue
* basic_info
= new base::ListValue();
112 basic_info
->Append(NewDescriptionValuePair(
113 "Initialization time",
114 base::Int64ToString(gpu_info
.initialization_time
.InMilliseconds())));
115 basic_info
->Append(NewDescriptionValuePair(
116 "In-process GPU", new base::FundamentalValue(gpu_info
.in_process_gpu
)));
117 basic_info
->Append(NewDescriptionValuePair(
118 "Sandboxed", new base::FundamentalValue(gpu_info
.sandboxed
)));
119 basic_info
->Append(NewDescriptionValuePair(
120 "GPU0", GPUDeviceToString(gpu_info
.gpu
)));
121 for (size_t i
= 0; i
< gpu_info
.secondary_gpus
.size(); ++i
) {
122 basic_info
->Append(NewDescriptionValuePair(
123 base::StringPrintf("GPU%d", static_cast<int>(i
+ 1)),
124 GPUDeviceToString(gpu_info
.secondary_gpus
[i
])));
126 basic_info
->Append(NewDescriptionValuePair(
127 "Optimus", new base::FundamentalValue(gpu_info
.optimus
)));
128 basic_info
->Append(NewDescriptionValuePair(
129 "AMD switchable", new base::FundamentalValue(gpu_info
.amd_switchable
)));
130 if (gpu_info
.lenovo_dcute
) {
131 basic_info
->Append(NewDescriptionValuePair(
132 "Lenovo dCute", new base::FundamentalValue(true)));
134 if (gpu_info
.display_link_version
.IsValid()) {
135 basic_info
->Append(NewDescriptionValuePair(
136 "DisplayLink Version", gpu_info
.display_link_version
.GetString()));
139 std::string compositor
=
140 ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
142 NewDescriptionValuePair("Desktop compositing", compositor
));
143 if (GpuDataManagerImpl::GetInstance()->ShouldUseWarp()) {
144 basic_info
->Append(NewDescriptionValuePair("Using WARP",
145 new base::FundamentalValue(true)));
149 std::string disabled_extensions
;
150 GpuDataManagerImpl::GetInstance()->GetDisabledExtensions(
151 &disabled_extensions
);
154 NewDescriptionValuePair("Driver vendor", gpu_info
.driver_vendor
));
155 basic_info
->Append(NewDescriptionValuePair("Driver version",
156 gpu_info
.driver_version
));
157 basic_info
->Append(NewDescriptionValuePair("Driver date",
158 gpu_info
.driver_date
));
159 basic_info
->Append(NewDescriptionValuePair("Pixel shader version",
160 gpu_info
.pixel_shader_version
));
161 basic_info
->Append(NewDescriptionValuePair("Vertex shader version",
162 gpu_info
.vertex_shader_version
));
163 basic_info
->Append(NewDescriptionValuePair("Max. MSAA samples",
164 gpu_info
.max_msaa_samples
));
165 basic_info
->Append(NewDescriptionValuePair("Machine model name",
166 gpu_info
.machine_model_name
));
167 basic_info
->Append(NewDescriptionValuePair("Machine model version",
168 gpu_info
.machine_model_version
));
169 basic_info
->Append(NewDescriptionValuePair("GL_VENDOR",
170 gpu_info
.gl_vendor
));
171 basic_info
->Append(NewDescriptionValuePair("GL_RENDERER",
172 gpu_info
.gl_renderer
));
173 basic_info
->Append(NewDescriptionValuePair("GL_VERSION",
174 gpu_info
.gl_version
));
175 basic_info
->Append(NewDescriptionValuePair("GL_EXTENSIONS",
176 gpu_info
.gl_extensions
));
177 basic_info
->Append(NewDescriptionValuePair("Disabled Extensions",
178 disabled_extensions
));
179 basic_info
->Append(NewDescriptionValuePair("Window system binding vendor",
180 gpu_info
.gl_ws_vendor
));
181 basic_info
->Append(NewDescriptionValuePair("Window system binding version",
182 gpu_info
.gl_ws_version
));
183 basic_info
->Append(NewDescriptionValuePair("Window system binding extensions",
184 gpu_info
.gl_ws_extensions
));
185 #if defined(OS_LINUX) && defined(USE_X11)
186 basic_info
->Append(NewDescriptionValuePair("Window manager",
187 ui::GuessWindowManagerName()));
189 scoped_ptr
<base::Environment
> env(base::Environment::Create());
191 const char kXDGCurrentDesktop
[] = "XDG_CURRENT_DESKTOP";
192 if (env
->GetVar(kXDGCurrentDesktop
, &value
))
193 basic_info
->Append(NewDescriptionValuePair(kXDGCurrentDesktop
, value
));
194 const char kGDMSession
[] = "GDMSESSION";
195 if (env
->GetVar(kGDMSession
, &value
))
196 basic_info
->Append(NewDescriptionValuePair(kGDMSession
, value
));
197 const char* kAtomsToCache
[] = {
201 ui::X11AtomCache
atom_cache(gfx::GetXDisplay(), kAtomsToCache
);
202 std::string compositing_manager
= XGetSelectionOwner(
204 atom_cache
.GetAtom("_NET_WM_CM_S0")) != None
? "Yes" : "No";
206 NewDescriptionValuePair("Compositing manager", compositing_manager
));
209 std::string direct_rendering
= gpu_info
.direct_rendering
? "Yes" : "No";
211 NewDescriptionValuePair("Direct rendering", direct_rendering
));
213 std::string reset_strategy
=
214 base::StringPrintf("0x%04x", gpu_info
.gl_reset_notification_strategy
);
215 basic_info
->Append(NewDescriptionValuePair(
216 "Reset notification strategy", reset_strategy
));
218 basic_info
->Append(NewDescriptionValuePair(
219 "GPU process crash count",
220 new base::FundamentalValue(gpu_info
.process_crash_count
)));
222 base::DictionaryValue
* info
= new base::DictionaryValue();
223 info
->Set("basic_info", basic_info
);
226 scoped_ptr
<base::Value
> dx_info
= base::Value::CreateNullValue();
227 if (gpu_info
.dx_diagnostics
.children
.size())
228 dx_info
.reset(DxDiagNodeToList(gpu_info
.dx_diagnostics
));
229 info
->Set("diagnostics", dx_info
.Pass());
235 // This class receives javascript messages from the renderer.
236 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
237 // this class's methods are expected to run on the UI thread.
238 class GpuMessageHandler
239 : public WebUIMessageHandler
,
240 public base::SupportsWeakPtr
<GpuMessageHandler
>,
241 public GpuDataManagerObserver
,
242 public ui::GpuSwitchingObserver
{
245 ~GpuMessageHandler() override
;
247 // WebUIMessageHandler implementation.
248 void RegisterMessages() override
;
250 // GpuDataManagerObserver implementation.
251 void OnGpuInfoUpdate() override
;
253 // ui::GpuSwitchingObserver implementation.
254 void OnGpuSwitched() override
;
257 void OnBrowserBridgeInitialized(const base::ListValue
* list
);
258 void OnCallAsync(const base::ListValue
* list
);
260 // Submessages dispatched from OnCallAsync
261 base::Value
* OnRequestClientInfo(const base::ListValue
* list
);
262 base::Value
* OnRequestLogMessages(const base::ListValue
* list
);
265 // True if observing the GpuDataManager (re-attaching as observer would
269 DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler
);
272 ////////////////////////////////////////////////////////////////////////////////
276 ////////////////////////////////////////////////////////////////////////////////
278 GpuMessageHandler::GpuMessageHandler()
279 : observing_(false) {
282 GpuMessageHandler::~GpuMessageHandler() {
283 ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this);
284 GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
287 /* BrowserBridge.callAsync prepends a requestID to these messages. */
288 void GpuMessageHandler::RegisterMessages() {
289 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
291 web_ui()->RegisterMessageCallback("browserBridgeInitialized",
292 base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized
,
293 base::Unretained(this)));
294 web_ui()->RegisterMessageCallback("callAsync",
295 base::Bind(&GpuMessageHandler::OnCallAsync
,
296 base::Unretained(this)));
299 void GpuMessageHandler::OnCallAsync(const base::ListValue
* args
) {
300 DCHECK_GE(args
->GetSize(), static_cast<size_t>(2));
301 // unpack args into requestId, submessage and submessageArgs
303 const base::Value
* requestId
;
304 ok
= args
->Get(0, &requestId
);
307 std::string submessage
;
308 ok
= args
->GetString(1, &submessage
);
311 base::ListValue
* submessageArgs
= new base::ListValue();
312 for (size_t i
= 2; i
< args
->GetSize(); ++i
) {
313 const base::Value
* arg
;
314 ok
= args
->Get(i
, &arg
);
317 base::Value
* argCopy
= arg
->DeepCopy();
318 submessageArgs
->Append(argCopy
);
321 // call the submessage handler
322 base::Value
* ret
= NULL
;
323 if (submessage
== "requestClientInfo") {
324 ret
= OnRequestClientInfo(submessageArgs
);
325 } else if (submessage
== "requestLogMessages") {
326 ret
= OnRequestLogMessages(submessageArgs
);
327 } else { // unrecognized submessage
329 delete submessageArgs
;
332 delete submessageArgs
;
334 // call BrowserBridge.onCallAsyncReply with result
336 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
341 web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
346 void GpuMessageHandler::OnBrowserBridgeInitialized(
347 const base::ListValue
* args
) {
348 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
350 // Watch for changes in GPUInfo
352 GpuDataManagerImpl::GetInstance()->AddObserver(this);
353 ui::GpuSwitchingManager::GetInstance()->AddObserver(this);
357 // Tell GpuDataManager it should have full GpuInfo. If the
358 // Gpu process has not run yet, this will trigger its launch.
359 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
361 // Run callback immediately in case the info is ready and no update in the
366 base::Value
* GpuMessageHandler::OnRequestClientInfo(
367 const base::ListValue
* list
) {
368 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
370 base::DictionaryValue
* dict
= new base::DictionaryValue();
372 dict
->SetString("version", GetContentClient()->GetProduct());
373 dict
->SetString("command_line",
374 base::CommandLine::ForCurrentProcess()->GetCommandLineString());
375 dict
->SetString("operating_system",
376 base::SysInfo::OperatingSystemName() + " " +
377 base::SysInfo::OperatingSystemVersion());
378 dict
->SetString("angle_commit_id", ANGLE_COMMIT_HASH
);
379 dict
->SetString("graphics_backend", "Skia");
380 dict
->SetString("blacklist_version",
381 GpuDataManagerImpl::GetInstance()->GetBlacklistVersion());
382 dict
->SetString("driver_bug_list_version",
383 GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion());
388 base::Value
* GpuMessageHandler::OnRequestLogMessages(const base::ListValue
*) {
389 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
391 return GpuDataManagerImpl::GetInstance()->GetLogMessages();
394 void GpuMessageHandler::OnGpuInfoUpdate() {
396 scoped_ptr
<base::DictionaryValue
> gpu_info_val(GpuInfoAsDictionaryValue());
399 // Add in blacklisting features
400 base::DictionaryValue
* feature_status
= new base::DictionaryValue
;
401 feature_status
->Set("featureStatus", GetFeatureStatus());
402 feature_status
->Set("problems", GetProblems());
403 base::ListValue
* workarounds
= new base::ListValue();
404 for (const std::string
& workaround
: GetDriverBugWorkarounds())
405 workarounds
->AppendString(workaround
);
406 feature_status
->Set("workarounds", workarounds
);
407 gpu_info_val
->Set("featureStatus", feature_status
);
409 // Send GPU Info to javascript.
410 web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
411 *(gpu_info_val
.get()));
414 void GpuMessageHandler::OnGpuSwitched() {
415 GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
421 ////////////////////////////////////////////////////////////////////////////////
425 ////////////////////////////////////////////////////////////////////////////////
427 GpuInternalsUI::GpuInternalsUI(WebUI
* web_ui
)
428 : WebUIController(web_ui
) {
429 web_ui
->AddMessageHandler(new GpuMessageHandler());
431 // Set up the chrome://gpu/ source.
432 BrowserContext
* browser_context
=
433 web_ui
->GetWebContents()->GetBrowserContext();
434 WebUIDataSource::Add(browser_context
, CreateGpuHTMLSource());
437 } // namespace content