• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/gpu/gpu_internals_ui.h"
6 
7 #if defined(OS_LINUX) && defined(USE_X11)
8 #include <X11/Xlib.h>
9 #endif
10 
11 #include <string>
12 
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 
38 #if defined(OS_WIN)
39 #include "ui/base/win/shell.h"
40 #endif
41 
42 #if defined(OS_LINUX) && defined(USE_X11)
43 #include "ui/base/x/x11_util.h"
44 #include "ui/gfx/x/x11_atom_cache.h"
45 #endif
46 
47 namespace content {
48 namespace {
49 
CreateGpuHTMLSource()50 WebUIDataSource* CreateGpuHTMLSource() {
51   WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost);
52 
53   source->SetJsonPath("strings.js");
54   source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS);
55   source->SetDefaultResource(IDR_GPU_INTERNALS_HTML);
56   return source;
57 }
58 
NewDescriptionValuePair(const std::string & desc,const std::string & value)59 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc,
60     const std::string& value) {
61   base::DictionaryValue* dict = new base::DictionaryValue();
62   dict->SetString("description", desc);
63   dict->SetString("value", value);
64   return dict;
65 }
66 
NewDescriptionValuePair(const std::string & desc,base::Value * value)67 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc,
68     base::Value* value) {
69   base::DictionaryValue* dict = new base::DictionaryValue();
70   dict->SetString("description", desc);
71   dict->Set("value", value);
72   return dict;
73 }
74 
75 #if defined(OS_WIN)
76 // Output DxDiagNode tree as nested array of {description,value} pairs
DxDiagNodeToList(const gpu::DxDiagNode & node)77 base::ListValue* DxDiagNodeToList(const gpu::DxDiagNode& node) {
78   base::ListValue* list = new base::ListValue();
79   for (std::map<std::string, std::string>::const_iterator it =
80       node.values.begin();
81       it != node.values.end();
82       ++it) {
83     list->Append(NewDescriptionValuePair(it->first, it->second));
84   }
85 
86   for (std::map<std::string, gpu::DxDiagNode>::const_iterator it =
87       node.children.begin();
88       it != node.children.end();
89       ++it) {
90     base::ListValue* sublist = DxDiagNodeToList(it->second);
91     list->Append(NewDescriptionValuePair(it->first, sublist));
92   }
93   return list;
94 }
95 #endif
96 
GPUDeviceToString(const gpu::GPUInfo::GPUDevice & gpu)97 std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) {
98   std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id);
99   if (!gpu.vendor_string.empty())
100     vendor += " [" + gpu.vendor_string + "]";
101   std::string device = base::StringPrintf("0x%04x", gpu.device_id);
102   if (!gpu.device_string.empty())
103     device += " [" + gpu.device_string + "]";
104   return base::StringPrintf("VENDOR = %s, DEVICE= %s%s",
105       vendor.c_str(), device.c_str(), gpu.active ? " *ACTIVE*" : "");
106 }
107 
GpuInfoAsDictionaryValue()108 base::DictionaryValue* GpuInfoAsDictionaryValue() {
109   gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
110   base::ListValue* basic_info = new base::ListValue();
111   basic_info->Append(NewDescriptionValuePair(
112       "Initialization time",
113       base::Int64ToString(gpu_info.initialization_time.InMilliseconds())));
114   basic_info->Append(NewDescriptionValuePair(
115       "Sandboxed", new base::FundamentalValue(gpu_info.sandboxed)));
116   basic_info->Append(NewDescriptionValuePair(
117       "GPU0", GPUDeviceToString(gpu_info.gpu)));
118   for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) {
119     basic_info->Append(NewDescriptionValuePair(
120         base::StringPrintf("GPU%d", static_cast<int>(i + 1)),
121         GPUDeviceToString(gpu_info.secondary_gpus[i])));
122   }
123   basic_info->Append(NewDescriptionValuePair(
124       "Optimus", new base::FundamentalValue(gpu_info.optimus)));
125   basic_info->Append(NewDescriptionValuePair(
126       "AMD switchable", new base::FundamentalValue(gpu_info.amd_switchable)));
127   if (gpu_info.lenovo_dcute) {
128     basic_info->Append(NewDescriptionValuePair(
129         "Lenovo dCute", new base::FundamentalValue(true)));
130   }
131   if (gpu_info.display_link_version.IsValid()) {
132     basic_info->Append(NewDescriptionValuePair(
133         "DisplayLink Version", gpu_info.display_link_version.GetString()));
134   }
135 #if defined(OS_WIN)
136   std::string compositor =
137       ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
138   basic_info->Append(
139       NewDescriptionValuePair("Desktop compositing", compositor));
140   if (GpuDataManagerImpl::GetInstance()->ShouldUseWarp()) {
141     basic_info->Append(NewDescriptionValuePair("Using WARP",
142         new base::FundamentalValue(true)));
143   }
144 #endif
145 
146   basic_info->Append(
147       NewDescriptionValuePair("Driver vendor", gpu_info.driver_vendor));
148   basic_info->Append(NewDescriptionValuePair("Driver version",
149                                              gpu_info.driver_version));
150   basic_info->Append(NewDescriptionValuePair("Driver date",
151                                              gpu_info.driver_date));
152   basic_info->Append(NewDescriptionValuePair("Pixel shader version",
153                                              gpu_info.pixel_shader_version));
154   basic_info->Append(NewDescriptionValuePair("Vertex shader version",
155                                              gpu_info.vertex_shader_version));
156   basic_info->Append(NewDescriptionValuePair("Machine model name",
157                                              gpu_info.machine_model_name));
158   basic_info->Append(NewDescriptionValuePair("Machine model version",
159                                              gpu_info.machine_model_version));
160   basic_info->Append(NewDescriptionValuePair("GL_VENDOR",
161                                              gpu_info.gl_vendor));
162   basic_info->Append(NewDescriptionValuePair("GL_RENDERER",
163                                              gpu_info.gl_renderer));
164   basic_info->Append(NewDescriptionValuePair("GL_VERSION",
165                                              gpu_info.gl_version));
166   basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS",
167                                              gpu_info.gl_extensions));
168   basic_info->Append(NewDescriptionValuePair("Window system binding vendor",
169                                              gpu_info.gl_ws_vendor));
170   basic_info->Append(NewDescriptionValuePair("Window system binding version",
171                                              gpu_info.gl_ws_version));
172   basic_info->Append(NewDescriptionValuePair("Window system binding extensions",
173                                              gpu_info.gl_ws_extensions));
174 #if defined(OS_LINUX) && defined(USE_X11)
175   basic_info->Append(NewDescriptionValuePair("Window manager",
176                                              ui::GuessWindowManagerName()));
177   {
178     scoped_ptr<base::Environment> env(base::Environment::Create());
179     std::string value;
180     const char kXDGCurrentDesktop[] = "XDG_CURRENT_DESKTOP";
181     if (env->GetVar(kXDGCurrentDesktop, &value))
182       basic_info->Append(NewDescriptionValuePair(kXDGCurrentDesktop, value));
183     const char kGDMSession[] = "GDMSESSION";
184     if (env->GetVar(kGDMSession, &value))
185       basic_info->Append(NewDescriptionValuePair(kGDMSession, value));
186     const char* kAtomsToCache[] = {
187         "_NET_WM_CM_S0",
188         NULL
189     };
190     ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache);
191     std::string compositing_manager = XGetSelectionOwner(
192         gfx::GetXDisplay(),
193         atom_cache.GetAtom("_NET_WM_CM_S0")) != None ? "Yes" : "No";
194     basic_info->Append(
195         NewDescriptionValuePair("Compositing manager", compositing_manager));
196   }
197 #endif
198   std::string direct_rendering = gpu_info.direct_rendering ? "Yes" : "No";
199   basic_info->Append(
200       NewDescriptionValuePair("Direct rendering", direct_rendering));
201 
202   std::string reset_strategy =
203       base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy);
204   basic_info->Append(NewDescriptionValuePair(
205       "Reset notification strategy", reset_strategy));
206 
207   basic_info->Append(NewDescriptionValuePair(
208       "GPU process crash count",
209       new base::FundamentalValue(gpu_info.process_crash_count)));
210 
211   base::DictionaryValue* info = new base::DictionaryValue();
212   info->Set("basic_info", basic_info);
213 
214 #if defined(OS_WIN)
215   base::ListValue* perf_info = new base::ListValue();
216   perf_info->Append(NewDescriptionValuePair(
217       "Graphics",
218       base::StringPrintf("%.1f", gpu_info.performance_stats.graphics)));
219   perf_info->Append(NewDescriptionValuePair(
220       "Gaming",
221       base::StringPrintf("%.1f", gpu_info.performance_stats.gaming)));
222   perf_info->Append(NewDescriptionValuePair(
223       "Overall",
224       base::StringPrintf("%.1f", gpu_info.performance_stats.overall)));
225   info->Set("performance_info", perf_info);
226 
227   base::Value* dx_info = gpu_info.dx_diagnostics.children.size() ?
228     DxDiagNodeToList(gpu_info.dx_diagnostics) :
229     base::Value::CreateNullValue();
230   info->Set("diagnostics", dx_info);
231 #endif
232 
233   return info;
234 }
235 
236 // This class receives javascript messages from the renderer.
237 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
238 // this class's methods are expected to run on the UI thread.
239 class GpuMessageHandler
240     : public WebUIMessageHandler,
241       public base::SupportsWeakPtr<GpuMessageHandler>,
242       public GpuDataManagerObserver {
243  public:
244   GpuMessageHandler();
245   virtual ~GpuMessageHandler();
246 
247   // WebUIMessageHandler implementation.
248   virtual void RegisterMessages() OVERRIDE;
249 
250   // GpuDataManagerObserver implementation.
251   virtual void OnGpuInfoUpdate() OVERRIDE;
252   virtual void OnGpuSwitching() OVERRIDE;
253 
254   // Messages
255   void OnBrowserBridgeInitialized(const base::ListValue* list);
256   void OnCallAsync(const base::ListValue* list);
257 
258   // Submessages dispatched from OnCallAsync
259   base::Value* OnRequestClientInfo(const base::ListValue* list);
260   base::Value* OnRequestLogMessages(const base::ListValue* list);
261 
262  private:
263   // True if observing the GpuDataManager (re-attaching as observer would
264   // DCHECK).
265   bool observing_;
266 
267   DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler);
268 };
269 
270 ////////////////////////////////////////////////////////////////////////////////
271 //
272 // GpuMessageHandler
273 //
274 ////////////////////////////////////////////////////////////////////////////////
275 
GpuMessageHandler()276 GpuMessageHandler::GpuMessageHandler()
277     : observing_(false) {
278 }
279 
~GpuMessageHandler()280 GpuMessageHandler::~GpuMessageHandler() {
281   GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
282 }
283 
284 /* BrowserBridge.callAsync prepends a requestID to these messages. */
RegisterMessages()285 void GpuMessageHandler::RegisterMessages() {
286   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 
288   web_ui()->RegisterMessageCallback("browserBridgeInitialized",
289       base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized,
290                  base::Unretained(this)));
291   web_ui()->RegisterMessageCallback("callAsync",
292       base::Bind(&GpuMessageHandler::OnCallAsync,
293                  base::Unretained(this)));
294 }
295 
OnCallAsync(const base::ListValue * args)296 void GpuMessageHandler::OnCallAsync(const base::ListValue* args) {
297   DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
298   // unpack args into requestId, submessage and submessageArgs
299   bool ok;
300   const base::Value* requestId;
301   ok = args->Get(0, &requestId);
302   DCHECK(ok);
303 
304   std::string submessage;
305   ok = args->GetString(1, &submessage);
306   DCHECK(ok);
307 
308   base::ListValue* submessageArgs = new base::ListValue();
309   for (size_t i = 2; i < args->GetSize(); ++i) {
310     const base::Value* arg;
311     ok = args->Get(i, &arg);
312     DCHECK(ok);
313 
314     base::Value* argCopy = arg->DeepCopy();
315     submessageArgs->Append(argCopy);
316   }
317 
318   // call the submessage handler
319   base::Value* ret = NULL;
320   if (submessage == "requestClientInfo") {
321     ret = OnRequestClientInfo(submessageArgs);
322   } else if (submessage == "requestLogMessages") {
323     ret = OnRequestLogMessages(submessageArgs);
324   } else {  // unrecognized submessage
325     NOTREACHED();
326     delete submessageArgs;
327     return;
328   }
329   delete submessageArgs;
330 
331   // call BrowserBridge.onCallAsyncReply with result
332   if (ret) {
333     web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
334         *requestId,
335         *ret);
336     delete ret;
337   } else {
338     web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
339         *requestId);
340   }
341 }
342 
OnBrowserBridgeInitialized(const base::ListValue * args)343 void GpuMessageHandler::OnBrowserBridgeInitialized(
344     const base::ListValue* args) {
345   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 
347   // Watch for changes in GPUInfo
348   if (!observing_)
349     GpuDataManagerImpl::GetInstance()->AddObserver(this);
350   observing_ = true;
351 
352   // Tell GpuDataManager it should have full GpuInfo. If the
353   // Gpu process has not run yet, this will trigger its launch.
354   GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
355 
356   // Run callback immediately in case the info is ready and no update in the
357   // future.
358   OnGpuInfoUpdate();
359 }
360 
OnRequestClientInfo(const base::ListValue * list)361 base::Value* GpuMessageHandler::OnRequestClientInfo(
362     const base::ListValue* list) {
363   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 
365   base::DictionaryValue* dict = new base::DictionaryValue();
366 
367   dict->SetString("version", GetContentClient()->GetProduct());
368   dict->SetString("command_line",
369       base::CommandLine::ForCurrentProcess()->GetCommandLineString());
370   dict->SetString("operating_system",
371                   base::SysInfo::OperatingSystemName() + " " +
372                   base::SysInfo::OperatingSystemVersion());
373   dict->SetString("angle_commit_id", ANGLE_COMMIT_HASH);
374   dict->SetString("graphics_backend", "Skia");
375   dict->SetString("blacklist_version",
376       GpuDataManagerImpl::GetInstance()->GetBlacklistVersion());
377   dict->SetString("driver_bug_list_version",
378       GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion());
379 
380   return dict;
381 }
382 
OnRequestLogMessages(const base::ListValue *)383 base::Value* GpuMessageHandler::OnRequestLogMessages(const base::ListValue*) {
384   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
385 
386   return GpuDataManagerImpl::GetInstance()->GetLogMessages();
387 }
388 
OnGpuInfoUpdate()389 void GpuMessageHandler::OnGpuInfoUpdate() {
390   // Get GPU Info.
391   scoped_ptr<base::DictionaryValue> gpu_info_val(GpuInfoAsDictionaryValue());
392 
393   // Add in blacklisting features
394   base::DictionaryValue* feature_status = new base::DictionaryValue;
395   feature_status->Set("featureStatus", GetFeatureStatus());
396   feature_status->Set("problems", GetProblems());
397   feature_status->Set("workarounds", GetDriverBugWorkarounds());
398   if (feature_status)
399     gpu_info_val->Set("featureStatus", feature_status);
400 
401   // Send GPU Info to javascript.
402   web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
403       *(gpu_info_val.get()));
404 }
405 
OnGpuSwitching()406 void GpuMessageHandler::OnGpuSwitching() {
407   GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
408 }
409 
410 }  // namespace
411 
412 
413 ////////////////////////////////////////////////////////////////////////////////
414 //
415 // GpuInternalsUI
416 //
417 ////////////////////////////////////////////////////////////////////////////////
418 
GpuInternalsUI(WebUI * web_ui)419 GpuInternalsUI::GpuInternalsUI(WebUI* web_ui)
420     : WebUIController(web_ui) {
421   web_ui->AddMessageHandler(new GpuMessageHandler());
422 
423   // Set up the chrome://gpu/ source.
424   BrowserContext* browser_context =
425       web_ui->GetWebContents()->GetBrowserContext();
426   WebUIDataSource::Add(browser_context, CreateGpuHTMLSource());
427 }
428 
429 }  // namespace content
430