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"
25 NS_IMPL_ISUPPORTS_INHERITED(GfxInfo
, GfxInfoBase
, nsIGfxInfoDebug
)
28 // these global variables will be set when firing the glxtest process
29 int glxtest_pipe
= -1;
30 pid_t glxtest_pid
= 0;
46 mHasTextureFromPixmap
= false;
47 return GfxInfoBase::Init();
53 // to understand this function, see bug 639842. We retrieve the OpenGL driver information in a
54 // separate process to protect against bad drivers.
56 // if glxtest_pipe == -1, that means that we already read the information
57 if (glxtest_pipe
== -1)
60 enum { buf_size
= 1024 };
62 ssize_t bytesread
= read(glxtest_pipe
,
64 buf_size
-1); // -1 because we'll append a zero
68 // bytesread < 0 would mean that the above read() call failed.
69 // This should never happen. If it did, the outcome would be to blacklist anyway.
73 // let buf be a zero-terminated string
76 // Wait for the glxtest process to finish. This serves 2 purposes:
77 // * avoid having a zombie glxtest process laying around
78 // * get the glxtest process status info.
79 int glxtest_status
= 0;
80 bool wait_for_glxtest_process
= true;
81 bool waiting_for_glxtest_process_failed
= false;
82 int waitpid_errno
= 0;
83 while(wait_for_glxtest_process
) {
84 wait_for_glxtest_process
= false;
85 if (waitpid(glxtest_pid
, &glxtest_status
, 0) == -1) {
86 waitpid_errno
= errno
;
87 if (waitpid_errno
== EINTR
) {
88 wait_for_glxtest_process
= true;
91 // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
92 // as per bug 227246. This shouldn't matter, as we still seem to get the data
93 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
94 waiting_for_glxtest_process_failed
= (waitpid_errno
!= ECHILD
);
99 bool exited_with_error_code
= !waiting_for_glxtest_process_failed
&&
100 WIFEXITED(glxtest_status
) &&
101 WEXITSTATUS(glxtest_status
) != EXIT_SUCCESS
;
102 bool received_signal
= !waiting_for_glxtest_process_failed
&&
103 WIFSIGNALED(glxtest_status
);
105 bool error
= waiting_for_glxtest_process_failed
|| exited_with_error_code
|| received_signal
;
107 nsCString textureFromPixmap
;
108 nsCString
*stringToFill
= nullptr;
112 char *line
= NS_strtok("\n", &bufptr
);
116 stringToFill
->Assign(line
);
117 stringToFill
= nullptr;
119 else if(!strcmp(line
, "VENDOR"))
120 stringToFill
= &mVendor
;
121 else if(!strcmp(line
, "RENDERER"))
122 stringToFill
= &mRenderer
;
123 else if(!strcmp(line
, "VERSION"))
124 stringToFill
= &mVersion
;
125 else if(!strcmp(line
, "TFP"))
126 stringToFill
= &textureFromPixmap
;
130 if (!strcmp(textureFromPixmap
.get(), "TRUE"))
131 mHasTextureFromPixmap
= true;
133 // only useful for Linux kernel version check for FGLRX driver.
134 // assumes X client == X server, which is sad.
135 struct utsname unameobj
;
136 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");
144 mVendor
.Assign(spoofedVendor
);
145 const char *spoofedRenderer
= PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
147 mRenderer
.Assign(spoofedRenderer
);
148 const char *spoofedVersion
= PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
150 mVersion
.Assign(spoofedVersion
);
151 const char *spoofedOS
= PR_GetEnv("MOZ_GFX_SPOOF_OS");
153 mOS
.Assign(spoofedOS
);
154 const char *spoofedOSRelease
= PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
155 if (spoofedOSRelease
)
156 mOSRelease
.Assign(spoofedOSRelease
);
160 mRenderer
.IsEmpty() ||
161 mVersion
.IsEmpty() ||
163 mOSRelease
.IsEmpty())
165 mAdapterDescription
.AppendLiteral("GLXtest process failed");
166 if (waiting_for_glxtest_process_failed
)
167 mAdapterDescription
.AppendPrintf(" (waitpid failed with errno=%d for pid %d)", waitpid_errno
, glxtest_pid
);
168 if (exited_with_error_code
)
169 mAdapterDescription
.AppendPrintf(" (exited with status %d)", WEXITSTATUS(glxtest_status
));
171 mAdapterDescription
.AppendPrintf(" (received signal %d)", WTERMSIG(glxtest_status
));
173 mAdapterDescription
.AppendLiteral(": ");
174 mAdapterDescription
.Append(nsDependentCString(buf
));
175 mAdapterDescription
.Append('\n');
178 CrashReporter::AppendAppNotesToCrashReport(mAdapterDescription
);
182 mAdapterDescription
.Append(mVendor
);
183 mAdapterDescription
.AppendLiteral(" -- ");
184 mAdapterDescription
.Append(mRenderer
);
187 note
.AppendLiteral("OpenGL: ");
188 note
.Append(mAdapterDescription
);
189 note
.AppendLiteral(" -- ");
190 note
.Append(mVersion
);
191 if (mHasTextureFromPixmap
)
192 note
.AppendLiteral(" -- texture_from_pixmap");
195 CrashReporter::AppendAppNotesToCrashReport(note
);
197 // determine the major OpenGL version. That's the first integer in the version string.
198 mGLMajorVersion
= strtol(mVersion
.get(), 0, 10);
200 // determine driver type (vendor) and where in the version string
201 // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
202 const char *whereToReadVersionNumbers
= nullptr;
203 const char *Mesa_in_version_string
= strstr(mVersion
.get(), "Mesa");
204 if (Mesa_in_version_string
) {
206 // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
207 // there is no actual driver version info.
208 whereToReadVersionNumbers
= Mesa_in_version_string
+ strlen("Mesa");
209 if (strcasestr(mVendor
.get(), "nouveau"))
211 if (strcasestr(mRenderer
.get(), "intel")) // yes, intel is in the renderer string
213 if (strcasestr(mRenderer
.get(), "llvmpipe"))
215 if (strcasestr(mRenderer
.get(), "software rasterizer"))
217 } else if (strstr(mVendor
.get(), "NVIDIA Corporation")) {
219 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
220 // note that here the vendor and version strings behave differently, that's why we don't put this above
221 // alongside Mesa_in_version_string.
222 const char *NVIDIA_in_version_string
= strstr(mVersion
.get(), "NVIDIA");
223 if (NVIDIA_in_version_string
)
224 whereToReadVersionNumbers
= NVIDIA_in_version_string
+ strlen("NVIDIA");
225 } else if (strstr(mVendor
.get(), "ATI Technologies Inc")) {
227 // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
228 // that can at least give a rough idea of how old the driver is.
229 whereToReadVersionNumbers
= mVersion
.get();
232 // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
233 if (whereToReadVersionNumbers
) {
234 // copy into writable buffer, for tokenization
235 strncpy(buf
, whereToReadVersionNumbers
, buf_size
);
238 // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
239 // been initialized as 0 anyways
240 char *token
= NS_strtok(".", &bufptr
);
242 mMajorVersion
= strtol(token
, 0, 10);
243 token
= NS_strtok(".", &bufptr
);
245 mMinorVersion
= strtol(token
, 0, 10);
246 token
= NS_strtok(".", &bufptr
);
248 mRevisionVersion
= strtol(token
, 0, 10);
254 static inline uint64_t version(uint32_t major
, uint32_t minor
, uint32_t revision
= 0)
256 return (uint64_t(major
) << 32) + (uint64_t(minor
) << 16) + uint64_t(revision
);
259 const nsTArray
<GfxDriverInfo
>&
260 GfxInfo::GetGfxDriverInfo()
263 //if (!sDriverInfo->Length()) {
270 GfxInfo::GetFeatureStatusImpl(int32_t aFeature
,
272 nsAString
& aSuggestedDriverVersion
,
273 const nsTArray
<GfxDriverInfo
>& aDriverInfo
,
274 nsACString
& aFailureId
,
275 OperatingSystem
* aOS
/* = nullptr */)
278 NS_ENSURE_ARG_POINTER(aStatus
);
279 *aStatus
= nsIGfxInfo::FEATURE_STATUS_UNKNOWN
;
280 aSuggestedDriverVersion
.SetIsVoid(true);
281 OperatingSystem os
= OperatingSystem::Linux
;
285 if (sShutdownOccurred
) {
291 if (mGLMajorVersion
== 1) {
292 // We're on OpenGL 1. In most cases that indicates really old hardware.
293 // We better block them, rather than rely on them to fail gracefully, because they don't!
295 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
296 aFailureId
= "FEATURE_FAILURE_OPENGL_1";
300 // Don't evaluate any special cases if we're checking the downloaded blocklist.
301 if (!aDriverInfo
.Length()) {
302 // Blacklist software GL implementations from using layers acceleration.
303 // On the test infrastructure, we'll force-enable layers acceleration.
304 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
&&
305 (mIsLlvmpipe
|| mIsOldSwrast
) &&
306 !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL"))
308 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
309 aFailureId
= "FEATURE_FAILURE_SOFTWARE_GL";
313 if (aFeature
== nsIGfxInfo::FEATURE_WEBRENDER
) {
314 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION
;
315 aFailureId
= "FEATURE_UNQUALIFIED_WEBRENDER_LINUX";
319 // Only check features relevant to Linux.
320 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
||
321 aFeature
== nsIGfxInfo::FEATURE_WEBGL_OPENGL
||
322 aFeature
== nsIGfxInfo::FEATURE_WEBGL2
||
323 aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
) {
325 // whitelist the linux test slaves' current configuration.
326 // this is necessary as they're still using the slightly outdated 190.42 driver.
327 // this isn't a huge risk, as at least this is the exact setting in which we do continuous testing,
328 // and this only affects GeForce 9400 cards on linux on this precise driver version, which is very few users.
329 // We do the same thing on Windows XP, see in widget/windows/GfxInfo.cpp
331 !strcmp(mRenderer
.get(), "GeForce 9400/PCI/SSE2") &&
332 !strcmp(mVersion
.get(), "3.2.0 NVIDIA 190.42"))
334 *aStatus
= nsIGfxInfo::FEATURE_STATUS_OK
;
339 if (mIsNouveau
&& version(mMajorVersion
, mMinorVersion
) < version(8,0)) {
340 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
341 aFailureId
= "FEATURE_FAILURE_MESA_1";
342 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.0");
344 else if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(7,10,3)) {
345 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
346 aFailureId
= "FEATURE_FAILURE_MESA_2";
347 aSuggestedDriverVersion
.AssignLiteral("Mesa 7.10.3");
349 else if (mIsOldSwrast
) {
350 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
351 aFailureId
= "FEATURE_FAILURE_SW_RAST";
353 else if (mIsLlvmpipe
&& version(mMajorVersion
, mMinorVersion
) < version(9, 1)) {
354 // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
355 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
356 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
357 aFailureId
= "FEATURE_FAILURE_MESA_3";
359 else if (aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
)
361 if (mIsIntel
&& version(mMajorVersion
, mMinorVersion
) < version(8,1)) {
362 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
363 aFailureId
= "FEATURE_FAILURE_MESA_4";
364 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.1");
368 } else if (mIsNVIDIA
) {
369 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(257,21)) {
370 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
371 aFailureId
= "FEATURE_FAILURE_OLD_NV";
372 aSuggestedDriverVersion
.AssignLiteral("NVIDIA 257.21");
374 } else if (mIsFGLRX
) {
375 // FGLRX does not report a driver version number, so we have the OpenGL version instead.
376 // by requiring OpenGL 3, we effectively require recent drivers.
377 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(3, 0)) {
378 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
379 aFailureId
= "FEATURE_FAILURE_OLD_FGLRX";
380 aSuggestedDriverVersion
.AssignLiteral("<Something recent>");
382 // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
383 bool unknownOS
= mOS
.IsEmpty() || mOSRelease
.IsEmpty();
384 bool badOS
= mOS
.Find("Linux", true) != -1 &&
385 mOSRelease
.Find("2.6.32") != -1;
386 if (unknownOS
|| badOS
) {
387 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION
;
388 aFailureId
= "FEATURE_FAILURE_OLD_OS";
391 // like on windows, let's block unknown vendors. Think of virtual machines.
392 // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
393 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
398 return GfxInfoBase::GetFeatureStatusImpl(aFeature
, aStatus
, aSuggestedDriverVersion
, aDriverInfo
, aFailureId
, &os
);
403 GfxInfo::GetD2DEnabled(bool *aEnabled
)
405 return NS_ERROR_FAILURE
;
409 GfxInfo::GetDWriteEnabled(bool *aEnabled
)
411 return NS_ERROR_FAILURE
;
415 GfxInfo::GetDWriteVersion(nsAString
& aDwriteVersion
)
417 return NS_ERROR_FAILURE
;
421 GfxInfo::GetCleartypeParameters(nsAString
& aCleartypeParams
)
423 return NS_ERROR_FAILURE
;
427 GfxInfo::GetAdapterDescription(nsAString
& aAdapterDescription
)
430 AppendASCIItoUTF16(mAdapterDescription
, aAdapterDescription
);
435 GfxInfo::GetAdapterDescription2(nsAString
& aAdapterDescription
)
437 return NS_ERROR_FAILURE
;
441 GfxInfo::GetAdapterRAM(nsAString
& aAdapterRAM
)
443 aAdapterRAM
.Truncate();
448 GfxInfo::GetAdapterRAM2(nsAString
& aAdapterRAM
)
450 return NS_ERROR_FAILURE
;
454 GfxInfo::GetAdapterDriver(nsAString
& aAdapterDriver
)
456 aAdapterDriver
.Truncate();
461 GfxInfo::GetAdapterDriver2(nsAString
& aAdapterDriver
)
463 return NS_ERROR_FAILURE
;
467 GfxInfo::GetAdapterDriverVersion(nsAString
& aAdapterDriverVersion
)
470 CopyASCIItoUTF16(mVersion
, aAdapterDriverVersion
);
475 GfxInfo::GetAdapterDriverVersion2(nsAString
& aAdapterDriverVersion
)
477 return NS_ERROR_FAILURE
;
481 GfxInfo::GetAdapterDriverDate(nsAString
& aAdapterDriverDate
)
483 aAdapterDriverDate
.Truncate();
488 GfxInfo::GetAdapterDriverDate2(nsAString
& aAdapterDriverDate
)
490 return NS_ERROR_FAILURE
;
494 GfxInfo::GetAdapterVendorID(nsAString
& aAdapterVendorID
)
497 CopyUTF8toUTF16(mVendor
, aAdapterVendorID
);
502 GfxInfo::GetAdapterVendorID2(nsAString
& aAdapterVendorID
)
504 return NS_ERROR_FAILURE
;
508 GfxInfo::GetAdapterDeviceID(nsAString
& aAdapterDeviceID
)
511 CopyUTF8toUTF16(mRenderer
, aAdapterDeviceID
);
516 GfxInfo::GetAdapterDeviceID2(nsAString
& aAdapterDeviceID
)
518 return NS_ERROR_FAILURE
;
522 GfxInfo::GetAdapterSubsysID(nsAString
& aAdapterSubsysID
)
524 return NS_ERROR_FAILURE
;
528 GfxInfo::GetAdapterSubsysID2(nsAString
& aAdapterSubsysID
)
530 return NS_ERROR_FAILURE
;
534 GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active
)
536 return NS_ERROR_FAILURE
;
541 // Implement nsIGfxInfoDebug
542 // We don't support spoofing anything on Linux
544 NS_IMETHODIMP
GfxInfo::SpoofVendorID(const nsAString
& aVendorID
)
546 CopyUTF16toUTF8(aVendorID
, mVendor
);
550 NS_IMETHODIMP
GfxInfo::SpoofDeviceID(const nsAString
& aDeviceID
)
552 CopyUTF16toUTF8(aDeviceID
, mRenderer
);
556 NS_IMETHODIMP
GfxInfo::SpoofDriverVersion(const nsAString
& aDriverVersion
)
558 CopyUTF16toUTF8(aDriverVersion
, mVersion
);
562 NS_IMETHODIMP
GfxInfo::SpoofOSVersion(uint32_t aVersion
)
564 // We don't support OS versioning on Linux. There's just "Linux".
570 } // end namespace widget
571 } // end namespace mozilla