1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
12 #include <sys/utsname.h>
13 #include "nsCRTGlue.h"
14 #include "nsExceptionHandler.h"
15 #include "nsICrashReporter.h"
18 #include "GfxInfoX11.h"
24 NS_IMPL_ISUPPORTS_INHERITED(GfxInfo
, GfxInfoBase
, nsIGfxInfoDebug
)
27 // these global variables will be set when firing the glxtest process
28 int glxtest_pipe
= -1;
29 pid_t glxtest_pid
= 0;
31 nsresult
GfxInfo::Init() {
43 mHasTextureFromPixmap
= false;
44 return GfxInfoBase::Init();
47 void GfxInfo::AddCrashReportAnnotations() {
48 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID
,
50 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID
,
52 CrashReporter::AnnotateCrashReport(
53 CrashReporter::Annotation::AdapterDriverVersion
, mVersion
);
56 void GfxInfo::GetData() {
57 // to understand this function, see bug 639842. We retrieve the OpenGL driver
58 // information in a separate process to protect against bad drivers.
60 // if glxtest_pipe == -1, that means that we already read the information
61 if (glxtest_pipe
== -1) return;
63 enum { buf_size
= 1024 };
65 ssize_t bytesread
= read(glxtest_pipe
, &buf
,
66 buf_size
- 1); // -1 because we'll append a zero
70 // bytesread < 0 would mean that the above read() call failed.
71 // This should never happen. If it did, the outcome would be to blacklist
73 if (bytesread
< 0) bytesread
= 0;
75 // let buf be a zero-terminated string
78 // Wait for the glxtest process to finish. This serves 2 purposes:
79 // * avoid having a zombie glxtest process laying around
80 // * get the glxtest process status info.
81 int glxtest_status
= 0;
82 bool wait_for_glxtest_process
= true;
83 bool waiting_for_glxtest_process_failed
= false;
84 int waitpid_errno
= 0;
85 while (wait_for_glxtest_process
) {
86 wait_for_glxtest_process
= false;
87 if (waitpid(glxtest_pid
, &glxtest_status
, 0) == -1) {
88 waitpid_errno
= errno
;
89 if (waitpid_errno
== EINTR
) {
90 wait_for_glxtest_process
= true;
93 // ECHILD happens when the glxtest process got reaped got reaped after a
94 // PR_CreateProcess as per bug 227246. This shouldn't matter, as we
95 // still seem to get the data from the pipe, and if we didn't, the
96 // outcome would be to blacklist anyway.
97 waiting_for_glxtest_process_failed
= (waitpid_errno
!= ECHILD
);
102 bool exited_with_error_code
= !waiting_for_glxtest_process_failed
&&
103 WIFEXITED(glxtest_status
) &&
104 WEXITSTATUS(glxtest_status
) != EXIT_SUCCESS
;
105 bool received_signal
=
106 !waiting_for_glxtest_process_failed
&& WIFSIGNALED(glxtest_status
);
108 bool error
= waiting_for_glxtest_process_failed
|| exited_with_error_code
||
111 nsCString textureFromPixmap
;
112 nsCString
*stringToFill
= nullptr;
116 char *line
= NS_strtok("\n", &bufptr
);
119 stringToFill
->Assign(line
);
120 stringToFill
= nullptr;
121 } else if (!strcmp(line
, "VENDOR"))
122 stringToFill
= &mVendor
;
123 else if (!strcmp(line
, "RENDERER"))
124 stringToFill
= &mRenderer
;
125 else if (!strcmp(line
, "VERSION"))
126 stringToFill
= &mVersion
;
127 else if (!strcmp(line
, "TFP"))
128 stringToFill
= &textureFromPixmap
;
132 if (!strcmp(textureFromPixmap
.get(), "TRUE")) mHasTextureFromPixmap
= true;
134 // only useful for Linux kernel version check for FGLRX driver.
135 // assumes X client == X server, which is sad.
136 struct utsname unameobj
;
137 if (uname(&unameobj
) >= 0) {
138 mOS
.Assign(unameobj
.sysname
);
139 mOSRelease
.Assign(unameobj
.release
);
142 const char *spoofedVendor
= PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
143 if (spoofedVendor
) mVendor
.Assign(spoofedVendor
);
144 const char *spoofedRenderer
= PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
145 if (spoofedRenderer
) mRenderer
.Assign(spoofedRenderer
);
146 const char *spoofedVersion
= PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
147 if (spoofedVersion
) mVersion
.Assign(spoofedVersion
);
148 const char *spoofedOS
= PR_GetEnv("MOZ_GFX_SPOOF_OS");
149 if (spoofedOS
) mOS
.Assign(spoofedOS
);
150 const char *spoofedOSRelease
= PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
151 if (spoofedOSRelease
) mOSRelease
.Assign(spoofedOSRelease
);
153 if (error
|| mVendor
.IsEmpty() || mRenderer
.IsEmpty() || mVersion
.IsEmpty() ||
154 mOS
.IsEmpty() || mOSRelease
.IsEmpty()) {
155 mAdapterDescription
.AppendLiteral("GLXtest process failed");
156 if (waiting_for_glxtest_process_failed
)
157 mAdapterDescription
.AppendPrintf(
158 " (waitpid failed with errno=%d for pid %d)", waitpid_errno
,
160 if (exited_with_error_code
)
161 mAdapterDescription
.AppendPrintf(" (exited with status %d)",
162 WEXITSTATUS(glxtest_status
));
164 mAdapterDescription
.AppendPrintf(" (received signal %d)",
165 WTERMSIG(glxtest_status
));
167 mAdapterDescription
.AppendLiteral(": ");
168 mAdapterDescription
.Append(nsDependentCString(buf
));
169 mAdapterDescription
.Append('\n');
172 CrashReporter::AppendAppNotesToCrashReport(mAdapterDescription
);
176 mAdapterDescription
.Append(mVendor
);
177 mAdapterDescription
.AppendLiteral(" -- ");
178 mAdapterDescription
.Append(mRenderer
);
180 AddCrashReportAnnotations();
182 // determine the major OpenGL version. That's the first integer in the version
184 mGLMajorVersion
= strtol(mVersion
.get(), 0, 10);
186 // determine driver type (vendor) and where in the version string
187 // the actual driver version numbers should be expected to be found
188 // (whereToReadVersionNumbers)
189 const char *whereToReadVersionNumbers
= nullptr;
190 const char *Mesa_in_version_string
= strstr(mVersion
.get(), "Mesa");
191 if (Mesa_in_version_string
) {
193 // with Mesa, the version string contains "Mesa major.minor" and that's all
194 // the version information we get: there is no actual driver version info.
195 whereToReadVersionNumbers
= Mesa_in_version_string
+ strlen("Mesa");
196 if (strcasestr(mVendor
.get(), "nouveau")) mIsNouveau
= true;
197 if (strcasestr(mRenderer
.get(),
198 "intel")) // yes, intel is in the renderer string
200 if (strcasestr(mRenderer
.get(), "llvmpipe")) mIsLlvmpipe
= true;
201 if (strcasestr(mRenderer
.get(), "software rasterizer")) mIsOldSwrast
= true;
202 } else if (strstr(mVendor
.get(), "NVIDIA Corporation")) {
204 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
205 // note that here the vendor and version strings behave differently, that's
206 // why we don't put this above alongside Mesa_in_version_string.
207 const char *NVIDIA_in_version_string
= strstr(mVersion
.get(), "NVIDIA");
208 if (NVIDIA_in_version_string
)
209 whereToReadVersionNumbers
= NVIDIA_in_version_string
+ strlen("NVIDIA");
210 } else if (strstr(mVendor
.get(), "ATI Technologies Inc")) {
212 // with the FGLRX driver, the version string only gives a OpenGL version :/
213 // so let's return that. that can at least give a rough idea of how old the
215 whereToReadVersionNumbers
= mVersion
.get();
218 // read major.minor version numbers of the driver (not to be confused with the
220 if (whereToReadVersionNumbers
) {
221 // copy into writable buffer, for tokenization
222 strncpy(buf
, whereToReadVersionNumbers
, buf_size
);
225 // now try to read major.minor version numbers. In case of failure,
226 // gracefully exit: these numbers have been initialized as 0 anyways
227 char *token
= NS_strtok(".", &bufptr
);
229 mMajorVersion
= strtol(token
, 0, 10);
230 token
= NS_strtok(".", &bufptr
);
232 mMinorVersion
= strtol(token
, 0, 10);
233 token
= NS_strtok(".", &bufptr
);
234 if (token
) mRevisionVersion
= strtol(token
, 0, 10);
240 static inline uint64_t version(uint32_t major
, uint32_t minor
,
241 uint32_t revision
= 0) {
242 return (uint64_t(major
) << 32) + (uint64_t(minor
) << 16) + uint64_t(revision
);
245 const nsTArray
<GfxDriverInfo
> &GfxInfo::GetGfxDriverInfo() {
247 // if (!sDriverInfo->Length()) {
253 nsresult
GfxInfo::GetFeatureStatusImpl(
254 int32_t aFeature
, int32_t *aStatus
, nsAString
&aSuggestedDriverVersion
,
255 const nsTArray
<GfxDriverInfo
> &aDriverInfo
, nsACString
&aFailureId
,
256 OperatingSystem
*aOS
/* = nullptr */)
259 NS_ENSURE_ARG_POINTER(aStatus
);
260 *aStatus
= nsIGfxInfo::FEATURE_STATUS_UNKNOWN
;
261 aSuggestedDriverVersion
.SetIsVoid(true);
262 OperatingSystem os
= OperatingSystem::Linux
;
265 if (sShutdownOccurred
) {
271 if (mGLMajorVersion
== 1) {
272 // We're on OpenGL 1. In most cases that indicates really old hardware.
273 // We better block them, rather than rely on them to fail gracefully,
274 // because they don't! see bug 696636
275 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
276 aFailureId
= "FEATURE_FAILURE_OPENGL_1";
280 // Don't evaluate any special cases if we're checking the downloaded
282 if (!aDriverInfo
.Length()) {
283 // Blacklist software GL implementations from using layers acceleration.
284 // On the test infrastructure, we'll force-enable layers acceleration.
285 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
&&
286 (mIsLlvmpipe
|| mIsOldSwrast
) &&
287 !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL")) {
288 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
289 aFailureId
= "FEATURE_FAILURE_SOFTWARE_GL";
293 if (aFeature
== nsIGfxInfo::FEATURE_WEBRENDER
) {
294 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION
;
295 aFailureId
= "FEATURE_UNQUALIFIED_WEBRENDER_LINUX";
299 // Only check features relevant to Linux.
300 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
||
301 aFeature
== nsIGfxInfo::FEATURE_WEBGL_OPENGL
||
302 aFeature
== nsIGfxInfo::FEATURE_WEBGL2
||
303 aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
) {
304 // whitelist the linux test slaves' current configuration.
305 // this is necessary as they're still using the slightly outdated 190.42
306 // driver. this isn't a huge risk, as at least this is the exact setting
307 // in which we do continuous testing, and this only affects GeForce 9400
308 // cards on linux on this precise driver version, which is very few users.
309 // We do the same thing on Windows XP, see in widget/windows/GfxInfo.cpp
310 if (mIsNVIDIA
&& !strcmp(mRenderer
.get(), "GeForce 9400/PCI/SSE2") &&
311 !strcmp(mVersion
.get(), "3.2.0 NVIDIA 190.42")) {
312 *aStatus
= nsIGfxInfo::FEATURE_STATUS_OK
;
318 version(mMajorVersion
, mMinorVersion
) < version(8, 0)) {
319 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
320 aFailureId
= "FEATURE_FAILURE_MESA_1";
321 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.0");
322 } else if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) <
324 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
325 aFailureId
= "FEATURE_FAILURE_MESA_2";
326 aSuggestedDriverVersion
.AssignLiteral("Mesa 7.10.3");
327 } else if (mIsOldSwrast
) {
328 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
329 aFailureId
= "FEATURE_FAILURE_SW_RAST";
330 } else if (mIsLlvmpipe
&&
331 version(mMajorVersion
, mMinorVersion
) < version(9, 1)) {
332 // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
333 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
334 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
335 aFailureId
= "FEATURE_FAILURE_MESA_3";
336 } else if (aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
) {
338 version(mMajorVersion
, mMinorVersion
) < version(8, 1)) {
339 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
340 aFailureId
= "FEATURE_FAILURE_MESA_4";
341 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.1");
345 } else if (mIsNVIDIA
) {
346 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) <
348 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
349 aFailureId
= "FEATURE_FAILURE_OLD_NV";
350 aSuggestedDriverVersion
.AssignLiteral("NVIDIA 257.21");
352 } else if (mIsFGLRX
) {
353 // FGLRX does not report a driver version number, so we have the OpenGL
354 // version instead. by requiring OpenGL 3, we effectively require recent
356 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) <
358 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
359 aFailureId
= "FEATURE_FAILURE_OLD_FGLRX";
360 aSuggestedDriverVersion
.AssignLiteral("<Something recent>");
362 // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
363 bool unknownOS
= mOS
.IsEmpty() || mOSRelease
.IsEmpty();
365 mOS
.Find("Linux", true) != -1 && mOSRelease
.Find("2.6.32") != -1;
366 if (unknownOS
|| badOS
) {
367 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION
;
368 aFailureId
= "FEATURE_FAILURE_OLD_OS";
371 // like on windows, let's block unknown vendors. Think of virtual
372 // machines. Also, this case is hit whenever the GLXtest probe failed to
373 // get driver info or crashed.
374 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
379 return GfxInfoBase::GetFeatureStatusImpl(
380 aFeature
, aStatus
, aSuggestedDriverVersion
, aDriverInfo
, aFailureId
, &os
);
384 GfxInfo::GetD2DEnabled(bool *aEnabled
) { return NS_ERROR_FAILURE
; }
387 GfxInfo::GetDWriteEnabled(bool *aEnabled
) { return NS_ERROR_FAILURE
; }
390 GfxInfo::GetDWriteVersion(nsAString
&aDwriteVersion
) {
391 return NS_ERROR_FAILURE
;
395 GfxInfo::GetCleartypeParameters(nsAString
&aCleartypeParams
) {
396 return NS_ERROR_FAILURE
;
400 GfxInfo::GetAdapterDescription(nsAString
&aAdapterDescription
) {
402 AppendASCIItoUTF16(mAdapterDescription
, aAdapterDescription
);
407 GfxInfo::GetAdapterDescription2(nsAString
&aAdapterDescription
) {
408 return NS_ERROR_FAILURE
;
412 GfxInfo::GetAdapterRAM(nsAString
&aAdapterRAM
) {
413 aAdapterRAM
.Truncate();
418 GfxInfo::GetAdapterRAM2(nsAString
&aAdapterRAM
) { return NS_ERROR_FAILURE
; }
421 GfxInfo::GetAdapterDriver(nsAString
&aAdapterDriver
) {
422 aAdapterDriver
.Truncate();
427 GfxInfo::GetAdapterDriver2(nsAString
&aAdapterDriver
) {
428 return NS_ERROR_FAILURE
;
432 GfxInfo::GetAdapterDriverVersion(nsAString
&aAdapterDriverVersion
) {
434 CopyASCIItoUTF16(mVersion
, aAdapterDriverVersion
);
439 GfxInfo::GetAdapterDriverVersion2(nsAString
&aAdapterDriverVersion
) {
440 return NS_ERROR_FAILURE
;
444 GfxInfo::GetAdapterDriverDate(nsAString
&aAdapterDriverDate
) {
445 aAdapterDriverDate
.Truncate();
450 GfxInfo::GetAdapterDriverDate2(nsAString
&aAdapterDriverDate
) {
451 return NS_ERROR_FAILURE
;
455 GfxInfo::GetAdapterVendorID(nsAString
&aAdapterVendorID
) {
457 CopyUTF8toUTF16(mVendor
, aAdapterVendorID
);
462 GfxInfo::GetAdapterVendorID2(nsAString
&aAdapterVendorID
) {
463 return NS_ERROR_FAILURE
;
467 GfxInfo::GetAdapterDeviceID(nsAString
&aAdapterDeviceID
) {
469 CopyUTF8toUTF16(mRenderer
, aAdapterDeviceID
);
474 GfxInfo::GetAdapterDeviceID2(nsAString
&aAdapterDeviceID
) {
475 return NS_ERROR_FAILURE
;
479 GfxInfo::GetAdapterSubsysID(nsAString
&aAdapterSubsysID
) {
480 return NS_ERROR_FAILURE
;
484 GfxInfo::GetAdapterSubsysID2(nsAString
&aAdapterSubsysID
) {
485 return NS_ERROR_FAILURE
;
489 GfxInfo::GetIsGPU2Active(bool *aIsGPU2Active
) { return NS_ERROR_FAILURE
; }
493 // Implement nsIGfxInfoDebug
494 // We don't support spoofing anything on Linux
496 NS_IMETHODIMP
GfxInfo::SpoofVendorID(const nsAString
&aVendorID
) {
497 CopyUTF16toUTF8(aVendorID
, mVendor
);
501 NS_IMETHODIMP
GfxInfo::SpoofDeviceID(const nsAString
&aDeviceID
) {
502 CopyUTF16toUTF8(aDeviceID
, mRenderer
);
506 NS_IMETHODIMP
GfxInfo::SpoofDriverVersion(const nsAString
&aDriverVersion
) {
507 CopyUTF16toUTF8(aDriverVersion
, mVersion
);
511 NS_IMETHODIMP
GfxInfo::SpoofOSVersion(uint32_t aVersion
) {
512 // We don't support OS versioning on Linux. There's just "Linux".
518 } // end namespace widget
519 } // end namespace mozilla