OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "gpu/ipc/service/gpu_init.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/metrics/histogram_macros.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
11 #include "base/threading/thread_restrictions.h" | |
12 #include "base/trace_event/trace_event.h" | |
13 #include "gpu/command_buffer/service/gpu_switches.h" | |
14 #include "gpu/config/gpu_driver_bug_list.h" | |
15 #include "gpu/config/gpu_info_collector.h" | |
16 #include "gpu/config/gpu_switches.h" | |
17 #include "gpu/config/gpu_util.h" | |
18 #include "gpu/ipc/service/gpu_watchdog_thread.h" | |
19 #include "gpu/ipc/service/switches.h" | |
20 #include "ui/gl/gl_implementation.h" | |
21 #include "ui/gl/gl_switches.h" | |
22 #include "ui/gl/init/gl_factory.h" | |
23 | |
24 namespace gpu { | |
25 | |
26 namespace { | |
27 | |
28 void GetGpuInfoFromCommandLine(gpu::GPUInfo& gpu_info, | |
29 const base::CommandLine& command_line) { | |
30 if (!command_line.HasSwitch(switches::kGpuVendorID) || | |
31 !command_line.HasSwitch(switches::kGpuDeviceID) || | |
32 !command_line.HasSwitch(switches::kGpuDriverVersion)) | |
33 return; | |
34 bool success = base::HexStringToUInt( | |
35 command_line.GetSwitchValueASCII(switches::kGpuVendorID), | |
36 &gpu_info.gpu.vendor_id); | |
37 DCHECK(success); | |
38 success = base::HexStringToUInt( | |
39 command_line.GetSwitchValueASCII(switches::kGpuDeviceID), | |
40 &gpu_info.gpu.device_id); | |
41 DCHECK(success); | |
42 gpu_info.driver_vendor = | |
43 command_line.GetSwitchValueASCII(switches::kGpuDriverVendor); | |
44 gpu_info.driver_version = | |
45 command_line.GetSwitchValueASCII(switches::kGpuDriverVersion); | |
46 gpu_info.driver_date = | |
47 command_line.GetSwitchValueASCII(switches::kGpuDriverDate); | |
48 gpu::ParseSecondaryGpuDevicesFromCommandLine(command_line, &gpu_info); | |
49 | |
50 // Set active gpu device. | |
51 if (command_line.HasSwitch(switches::kGpuActiveVendorID) && | |
52 command_line.HasSwitch(switches::kGpuActiveDeviceID)) { | |
53 uint32_t active_vendor_id = 0; | |
54 uint32_t active_device_id = 0; | |
55 success = base::HexStringToUInt( | |
56 command_line.GetSwitchValueASCII(switches::kGpuActiveVendorID), | |
57 &active_vendor_id); | |
58 DCHECK(success); | |
59 success = base::HexStringToUInt( | |
60 command_line.GetSwitchValueASCII(switches::kGpuActiveDeviceID), | |
61 &active_device_id); | |
62 DCHECK(success); | |
63 if (gpu_info.gpu.vendor_id == active_vendor_id && | |
64 gpu_info.gpu.device_id == active_device_id) { | |
65 gpu_info.gpu.active = true; | |
66 } else { | |
67 for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) { | |
68 if (gpu_info.secondary_gpus[i].vendor_id == active_vendor_id && | |
69 gpu_info.secondary_gpus[i].device_id == active_device_id) { | |
70 gpu_info.secondary_gpus[i].active = true; | |
71 break; | |
72 } | |
73 } | |
74 } | |
75 } | |
76 } | |
77 | |
78 #if !defined(OS_MACOSX) | |
79 void CollectGraphicsInfo(gpu::GPUInfo& gpu_info) { | |
80 TRACE_EVENT0("gpu,startup", "Collect Graphics Info"); | |
81 | |
82 gpu::CollectInfoResult result = gpu::CollectContextGraphicsInfo(&gpu_info); | |
83 switch (result) { | |
84 case gpu::kCollectInfoFatalFailure: | |
85 LOG(ERROR) << "gpu::CollectGraphicsInfo failed (fatal)."; | |
86 break; | |
87 case gpu::kCollectInfoNonFatalFailure: | |
88 DVLOG(1) << "gpu::CollectGraphicsInfo failed (non-fatal)."; | |
89 break; | |
90 case gpu::kCollectInfoNone: | |
91 NOTREACHED(); | |
92 break; | |
93 case gpu::kCollectInfoSuccess: | |
94 break; | |
95 } | |
96 } | |
97 #endif // defined(OS_MACOSX) | |
98 | |
99 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
100 bool CanAccessNvidiaDeviceFile() { | |
101 bool res = true; | |
102 base::ThreadRestrictions::AssertIOAllowed(); | |
103 if (access("/dev/nvidiactl", R_OK) != 0) { | |
104 DVLOG(1) << "NVIDIA device file /dev/nvidiactl access denied"; | |
105 res = false; | |
106 } | |
107 return res; | |
108 } | |
109 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
110 | |
111 } // namespace | |
112 | |
113 GpuInit::GpuInit() {} | |
114 | |
115 GpuInit::~GpuInit() {} | |
116 | |
117 void GpuInit::InitializeAndStartSandbox(const base::CommandLine& command_line) { | |
118 if (command_line.HasSwitch(switches::kSupportsDualGpus)) { | |
119 std::set<int> workarounds; | |
120 gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine(&workarounds, | |
121 command_line); | |
122 gpu::InitializeDualGpusIfSupported(workarounds); | |
123 } | |
124 | |
125 // In addition to disabling the watchdog if the command line switch is | |
126 // present, disable the watchdog on valgrind because the code is expected | |
127 // to run slowly in that case. | |
128 bool enable_watchdog = | |
129 !command_line.HasSwitch(switches::kDisableGpuWatchdog) && | |
130 !RunningOnValgrind(); | |
131 | |
132 // Disable the watchdog in debug builds because they tend to only be run by | |
133 // developers who will not appreciate the watchdog killing the GPU process. | |
134 #ifndef NDEBUG | |
135 enable_watchdog = false; | |
136 #endif | |
137 | |
138 bool delayed_watchdog_enable = false; | |
139 | |
140 #if defined(OS_CHROMEOS) | |
141 // Don't start watchdog immediately, to allow developers to switch to VT2 on | |
142 // startup. | |
143 delayed_watchdog_enable = true; | |
144 #endif | |
145 | |
146 // Start the GPU watchdog only after anything that is expected to be time | |
147 // consuming has completed, otherwise the process is liable to be aborted. | |
148 if (enable_watchdog && !delayed_watchdog_enable) | |
149 watchdog_thread_ = gpu::GpuWatchdogThread::Create(); | |
150 | |
151 // Get vendor_id, device_id, driver_version from browser process through | |
152 // commandline switches. | |
153 GetGpuInfoFromCommandLine(gpu_info_, command_line); | |
154 gpu_info_.in_process_gpu = false; | |
155 | |
156 sandbox_helper_->PreSandboxStartup(); | |
157 | |
158 #if defined(OS_LINUX) | |
159 // On Chrome OS ARM Mali, GPU driver userspace creates threads when | |
160 // initializing a GL context, so start the sandbox early. | |
161 if (command_line.HasSwitch(switches::kGpuSandboxStartEarly)) | |
162 gpu_info_.sandboxed = sandbox_helper_->EnsureSandboxInitialized(); | |
163 #endif // defined(OS_LINUX) | |
164 | |
165 base::TimeTicks before_initialize_one_off = base::TimeTicks::Now(); | |
166 | |
167 // Initialization of the OpenGL bindings may fail, in which case we | |
168 // will need to tear down this process. However, we can not do so | |
169 // safely until the IPC channel is set up, because the detection of | |
170 // early return of a child process is implemented using an IPC | |
171 // channel error. If the IPC channel is not fully set up between the | |
172 // browser and GPU process, and the GPU process crashes or exits | |
173 // early, the browser process will never detect it. For this reason | |
174 // we defer tearing down the GPU process until receiving the | |
175 // GpuMsg_Initialize message from the browser. | |
176 | |
177 // Load and initialize the GL implementation and locate the GL entry points | |
178 // if needed. This initialization may have already happened if running in | |
179 // the browser process, for example. | |
180 bool gl_initialized = gl::GetGLImplementation() != gl::kGLImplementationNone; | |
181 if (!gl_initialized) | |
182 gl_initialized = gl::init::InitializeGLOneOff(); | |
183 | |
184 if (gl_initialized) { | |
185 // We need to collect GL strings (VENDOR, RENDERER) for blacklisting | |
186 // purposes. However, on Mac we don't actually use them. As documented in | |
187 // crbug.com/222934, due to some driver issues, glGetString could take | |
188 // multiple seconds to finish, which in turn cause the GPU process to | |
189 // crash. | |
190 // By skipping the following code on Mac, we don't really lose anything, | |
191 // because the basic GPU information is passed down from browser process | |
192 // and we already registered them through SetGpuInfo() above. | |
193 base::TimeTicks before_collect_context_graphics_info = | |
194 base::TimeTicks::Now(); | |
195 #if !defined(OS_MACOSX) | |
196 CollectGraphicsInfo(gpu_info_); | |
197 | |
198 // Recompute gpu driver bug workarounds. | |
199 // This is necessary on systems where vendor_id/device_id aren't available | |
200 // (Chrome OS, Android) or where workarounds may be dependent on GL_VENDOR | |
201 // and GL_RENDERER strings which are lazily computed (Linux). | |
202 if (!command_line.HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) { | |
203 // TODO: this can not affect disabled extensions, since they're already | |
204 // initialized in the bindings. This should be moved before bindings | |
205 // initialization. However, populating GPUInfo fully works only on | |
206 // Android. Other platforms would need the bindings to query GL strings. | |
207 gpu::ApplyGpuDriverBugWorkarounds( | |
208 gpu_info_, const_cast<base::CommandLine*>(&command_line)); | |
209 } | |
210 #endif // !defined(OS_MACOSX) | |
211 | |
212 base::TimeDelta collect_context_time = | |
213 base::TimeTicks::Now() - before_collect_context_graphics_info; | |
214 UMA_HISTOGRAM_TIMES("GPU.CollectContextGraphicsInfo", collect_context_time); | |
215 } else { // gl_initialized | |
216 VLOG(1) << "gl::init::InitializeGLOneOff failed"; | |
217 } | |
218 | |
219 base::TimeDelta initialize_one_off_time = | |
220 base::TimeTicks::Now() - before_initialize_one_off; | |
221 UMA_HISTOGRAM_MEDIUM_TIMES("GPU.InitializeOneOffMediumTime", | |
222 initialize_one_off_time); | |
223 if (enable_watchdog && delayed_watchdog_enable) | |
224 watchdog_thread_ = gpu::GpuWatchdogThread::Create(); | |
225 | |
226 // OSMesa is expected to run very slowly, so disable the watchdog in that | |
227 // case. | |
228 if (enable_watchdog && | |
229 gl::GetGLImplementation() == gl::kGLImplementationOSMesaGL) { | |
230 watchdog_thread_->Stop(); | |
231 watchdog_thread_ = NULL; | |
232 } | |
233 | |
234 // It is necessary to make the DOA decision before entering the sandbox | |
235 // (because on Linux, it can attempt to access a file). | |
236 dead_on_arrival_ = IsDeadOnArrival(); | |
piman
2016/09/09 03:32:41
How about returning a "success" bool from this fun
sadrul
2016/09/09 15:38:04
Thanks! That makes it much simpler. Moved all of c
| |
237 | |
238 if (!gpu_info_.sandboxed) | |
239 gpu_info_.sandboxed = sandbox_helper_->EnsureSandboxInitialized(); | |
240 } | |
241 | |
242 bool GpuInit::IsDeadOnArrival() const { | |
243 // Initialization of OpenGL binding may fail. | |
244 if (gl::GetGLImplementation() == gl::kGLImplementationNone) | |
245 return true; | |
246 | |
247 #if !defined(OS_MACOSX) | |
248 DCHECK_NE(gpu::kCollectInfoNone, gpu_info_.context_info_state); | |
249 if (gpu_info_.context_info_state == gpu::kCollectInfoFatalFailure) | |
250 return true; | |
251 #endif | |
252 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
253 if (gpu_info_.gpu.vendor_id == 0x10de && // NVIDIA | |
254 gpu_info_.driver_vendor == "NVIDIA" && !CanAccessNvidiaDeviceFile()) | |
255 return true; | |
256 #endif | |
257 return false; | |
258 } | |
259 | |
260 } // namespace gpu | |
OLD | NEW |