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"
16 #include "GfxInfoX11.h"
18 #ifdef MOZ_CRASHREPORTER
19 #include "nsExceptionHandler.h"
20 #include "nsICrashReporter.h"
27 NS_IMPL_ISUPPORTS_INHERITED(GfxInfo
, GfxInfoBase
, nsIGfxInfoDebug
)
30 // these global variables will be set when firing the glxtest process
32 pid_t glxtest_pid
= 0;
48 mHasTextureFromPixmap
= false;
49 return GfxInfoBase::Init();
55 // to understand this function, see bug 639842. We retrieve the OpenGL driver information in a
56 // separate process to protect against bad drivers.
58 // if glxtest_pipe == 0, that means that we already read the information
62 enum { buf_size
= 1024 };
64 ssize_t bytesread
= read(glxtest_pipe
,
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 anyway.
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 PR_CreateProcess
94 // as per bug 227246. This shouldn't matter, as we still seem to get the data
95 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
96 waiting_for_glxtest_process_failed
= (waitpid_errno
!= ECHILD
);
101 bool exited_with_error_code
= !waiting_for_glxtest_process_failed
&&
102 WIFEXITED(glxtest_status
) &&
103 WEXITSTATUS(glxtest_status
) != EXIT_SUCCESS
;
104 bool received_signal
= !waiting_for_glxtest_process_failed
&&
105 WIFSIGNALED(glxtest_status
);
107 bool error
= waiting_for_glxtest_process_failed
|| exited_with_error_code
|| received_signal
;
109 nsCString textureFromPixmap
;
110 nsCString
*stringToFill
= nullptr;
114 char *line
= NS_strtok("\n", &bufptr
);
118 stringToFill
->Assign(line
);
119 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"))
133 mHasTextureFromPixmap
= true;
135 // only useful for Linux kernel version check for FGLRX driver.
136 // assumes X client == X server, which is sad.
137 struct utsname unameobj
;
138 if (!uname(&unameobj
))
140 mOS
.Assign(unameobj
.sysname
);
141 mOSRelease
.Assign(unameobj
.release
);
144 const char *spoofedVendor
= PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
146 mVendor
.Assign(spoofedVendor
);
147 const char *spoofedRenderer
= PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
149 mRenderer
.Assign(spoofedRenderer
);
150 const char *spoofedVersion
= PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
152 mVersion
.Assign(spoofedVersion
);
153 const char *spoofedOS
= PR_GetEnv("MOZ_GFX_SPOOF_OS");
155 mOS
.Assign(spoofedOS
);
156 const char *spoofedOSRelease
= PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
157 if (spoofedOSRelease
)
158 mOSRelease
.Assign(spoofedOSRelease
);
162 mRenderer
.IsEmpty() ||
163 mVersion
.IsEmpty() ||
165 mOSRelease
.IsEmpty())
167 mAdapterDescription
.AppendLiteral("GLXtest process failed");
168 if (waiting_for_glxtest_process_failed
)
169 mAdapterDescription
.AppendPrintf(" (waitpid failed with errno=%d for pid %d)", waitpid_errno
, glxtest_pid
);
170 if (exited_with_error_code
)
171 mAdapterDescription
.AppendPrintf(" (exited with status %d)", WEXITSTATUS(glxtest_status
));
173 mAdapterDescription
.AppendPrintf(" (received signal %d)", WTERMSIG(glxtest_status
));
175 mAdapterDescription
.AppendLiteral(": ");
176 mAdapterDescription
.Append(nsDependentCString(buf
));
177 mAdapterDescription
.Append('\n');
179 #ifdef MOZ_CRASHREPORTER
180 CrashReporter::AppendAppNotesToCrashReport(mAdapterDescription
);
185 mAdapterDescription
.Append(mVendor
);
186 mAdapterDescription
.AppendLiteral(" -- ");
187 mAdapterDescription
.Append(mRenderer
);
190 note
.AppendLiteral("OpenGL: ");
191 note
.Append(mAdapterDescription
);
192 note
.AppendLiteral(" -- ");
193 note
.Append(mVersion
);
194 if (mHasTextureFromPixmap
)
195 note
.AppendLiteral(" -- texture_from_pixmap");
197 #ifdef MOZ_CRASHREPORTER
198 CrashReporter::AppendAppNotesToCrashReport(note
);
201 // determine the major OpenGL version. That's the first integer in the version string.
202 mGLMajorVersion
= strtol(mVersion
.get(), 0, 10);
204 // determine driver type (vendor) and where in the version string
205 // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
206 const char *whereToReadVersionNumbers
= nullptr;
207 const char *Mesa_in_version_string
= strstr(mVersion
.get(), "Mesa");
208 if (Mesa_in_version_string
) {
210 // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
211 // there is no actual driver version info.
212 whereToReadVersionNumbers
= Mesa_in_version_string
+ strlen("Mesa");
213 if (strcasestr(mVendor
.get(), "nouveau"))
215 if (strcasestr(mRenderer
.get(), "intel")) // yes, intel is in the renderer string
217 if (strcasestr(mRenderer
.get(), "llvmpipe"))
219 if (strcasestr(mRenderer
.get(), "software rasterizer"))
221 } else if (strstr(mVendor
.get(), "NVIDIA Corporation")) {
223 // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
224 // note that here the vendor and version strings behave differently, that's why we don't put this above
225 // alongside Mesa_in_version_string.
226 const char *NVIDIA_in_version_string
= strstr(mVersion
.get(), "NVIDIA");
227 if (NVIDIA_in_version_string
)
228 whereToReadVersionNumbers
= NVIDIA_in_version_string
+ strlen("NVIDIA");
229 } else if (strstr(mVendor
.get(), "ATI Technologies Inc")) {
231 // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
232 // that can at least give a rough idea of how old the driver is.
233 whereToReadVersionNumbers
= mVersion
.get();
236 // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
237 if (whereToReadVersionNumbers
) {
238 // copy into writable buffer, for tokenization
239 strncpy(buf
, whereToReadVersionNumbers
, buf_size
);
242 // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
243 // been initialized as 0 anyways
244 char *token
= NS_strtok(".", &bufptr
);
246 mMajorVersion
= strtol(token
, 0, 10);
247 token
= NS_strtok(".", &bufptr
);
249 mMinorVersion
= strtol(token
, 0, 10);
250 token
= NS_strtok(".", &bufptr
);
252 mRevisionVersion
= strtol(token
, 0, 10);
258 static inline uint64_t version(uint32_t major
, uint32_t minor
, uint32_t revision
= 0)
260 return (uint64_t(major
) << 32) + (uint64_t(minor
) << 16) + uint64_t(revision
);
263 const nsTArray
<GfxDriverInfo
>&
264 GfxInfo::GetGfxDriverInfo()
267 //if (!mDriverInfo->Length()) {
274 GfxInfo::GetFeatureStatusImpl(int32_t aFeature
,
276 nsAString
& aSuggestedDriverVersion
,
277 const nsTArray
<GfxDriverInfo
>& aDriverInfo
,
278 OperatingSystem
* aOS
/* = nullptr */)
283 NS_ENSURE_ARG_POINTER(aStatus
);
284 *aStatus
= nsIGfxInfo::FEATURE_STATUS_UNKNOWN
;
285 aSuggestedDriverVersion
.SetIsVoid(true);
286 OperatingSystem os
= DRIVER_OS_LINUX
;
290 if (mGLMajorVersion
== 1) {
291 // We're on OpenGL 1. In most cases that indicates really old hardware.
292 // We better block them, rather than rely on them to fail gracefully, because they don't!
294 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
298 // Don't evaluate any special cases if we're checking the downloaded blocklist.
299 if (!aDriverInfo
.Length()) {
300 // Only check features relevant to Linux.
301 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
||
302 aFeature
== nsIGfxInfo::FEATURE_WEBGL_OPENGL
||
303 aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
) {
305 // Disable OpenGL layers when we don't have texture_from_pixmap because it regresses performance.
306 if (aFeature
== nsIGfxInfo::FEATURE_OPENGL_LAYERS
&& !mHasTextureFromPixmap
) {
307 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
308 aSuggestedDriverVersion
.AssignLiteral("<Anything with EXT_texture_from_pixmap support>");
312 // whitelist the linux test slaves' current configuration.
313 // this is necessary as they're still using the slightly outdated 190.42 driver.
314 // this isn't a huge risk, as at least this is the exact setting in which we do continuous testing,
315 // and this only affects GeForce 9400 cards on linux on this precise driver version, which is very few users.
316 // We do the same thing on Windows XP, see in widget/windows/GfxInfo.cpp
318 !strcmp(mRenderer
.get(), "GeForce 9400/PCI/SSE2") &&
319 !strcmp(mVersion
.get(), "3.2.0 NVIDIA 190.42"))
321 *aStatus
= nsIGfxInfo::FEATURE_STATUS_OK
;
326 if (mIsNouveau
&& version(mMajorVersion
, mMinorVersion
) < version(8,0)) {
327 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
328 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.0");
330 else if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(7,10,3)) {
331 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
332 aSuggestedDriverVersion
.AssignLiteral("Mesa 7.10.3");
334 else if (mIsOldSwrast
) {
335 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
337 else if (mIsLlvmpipe
&& version(mMajorVersion
, mMinorVersion
) < version(9, 1)) {
338 // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
339 // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
340 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
342 else if (aFeature
== nsIGfxInfo::FEATURE_WEBGL_MSAA
)
344 if (mIsIntel
&& version(mMajorVersion
, mMinorVersion
) < version(8,1)) {
345 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
346 aSuggestedDriverVersion
.AssignLiteral("Mesa 8.1");
350 } else if (mIsNVIDIA
) {
351 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(257,21)) {
352 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
353 aSuggestedDriverVersion
.AssignLiteral("NVIDIA 257.21");
355 } else if (mIsFGLRX
) {
356 // FGLRX does not report a driver version number, so we have the OpenGL version instead.
357 // by requiring OpenGL 3, we effectively require recent drivers.
358 if (version(mMajorVersion
, mMinorVersion
, mRevisionVersion
) < version(3, 0)) {
359 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION
;
360 aSuggestedDriverVersion
.AssignLiteral("<Something recent>");
362 // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
363 bool unknownOS
= mOS
.IsEmpty() || mOSRelease
.IsEmpty();
364 bool badOS
= mOS
.Find("Linux", true) != -1 &&
365 mOSRelease
.Find("2.6.32") != -1;
366 if (unknownOS
|| badOS
) {
367 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION
;
370 // like on windows, let's block unknown vendors. Think of virtual machines.
371 // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
372 *aStatus
= nsIGfxInfo::FEATURE_BLOCKED_DEVICE
;
377 return GfxInfoBase::GetFeatureStatusImpl(aFeature
, aStatus
, aSuggestedDriverVersion
, aDriverInfo
, &os
);
382 GfxInfo::GetD2DEnabled(bool *aEnabled
)
384 return NS_ERROR_FAILURE
;
388 GfxInfo::GetDWriteEnabled(bool *aEnabled
)
390 return NS_ERROR_FAILURE
;
393 /* readonly attribute DOMString DWriteVersion; */
395 GfxInfo::GetDWriteVersion(nsAString
& aDwriteVersion
)
397 return NS_ERROR_FAILURE
;
400 /* readonly attribute DOMString cleartypeParameters; */
402 GfxInfo::GetCleartypeParameters(nsAString
& aCleartypeParams
)
404 return NS_ERROR_FAILURE
;
407 /* readonly attribute DOMString adapterDescription; */
409 GfxInfo::GetAdapterDescription(nsAString
& aAdapterDescription
)
412 AppendASCIItoUTF16(mAdapterDescription
, aAdapterDescription
);
416 /* readonly attribute DOMString adapterDescription2; */
418 GfxInfo::GetAdapterDescription2(nsAString
& aAdapterDescription
)
420 return NS_ERROR_FAILURE
;
423 /* readonly attribute DOMString adapterRAM; */
425 GfxInfo::GetAdapterRAM(nsAString
& aAdapterRAM
)
427 aAdapterRAM
.Truncate();
431 /* readonly attribute DOMString adapterRAM2; */
433 GfxInfo::GetAdapterRAM2(nsAString
& aAdapterRAM
)
435 return NS_ERROR_FAILURE
;
438 /* readonly attribute DOMString adapterDriver; */
440 GfxInfo::GetAdapterDriver(nsAString
& aAdapterDriver
)
442 aAdapterDriver
.Truncate();
446 /* readonly attribute DOMString adapterDriver2; */
448 GfxInfo::GetAdapterDriver2(nsAString
& aAdapterDriver
)
450 return NS_ERROR_FAILURE
;
453 /* readonly attribute DOMString adapterDriverVersion; */
455 GfxInfo::GetAdapterDriverVersion(nsAString
& aAdapterDriverVersion
)
458 CopyASCIItoUTF16(mVersion
, aAdapterDriverVersion
);
462 /* readonly attribute DOMString adapterDriverVersion2; */
464 GfxInfo::GetAdapterDriverVersion2(nsAString
& aAdapterDriverVersion
)
466 return NS_ERROR_FAILURE
;
469 /* readonly attribute DOMString adapterDriverDate; */
471 GfxInfo::GetAdapterDriverDate(nsAString
& aAdapterDriverDate
)
473 aAdapterDriverDate
.Truncate();
477 /* readonly attribute DOMString adapterDriverDate2; */
479 GfxInfo::GetAdapterDriverDate2(nsAString
& aAdapterDriverDate
)
481 return NS_ERROR_FAILURE
;
484 /* readonly attribute DOMString adapterVendorID; */
486 GfxInfo::GetAdapterVendorID(nsAString
& aAdapterVendorID
)
489 CopyUTF8toUTF16(mVendor
, aAdapterVendorID
);
493 /* readonly attribute DOMString adapterVendorID2; */
495 GfxInfo::GetAdapterVendorID2(nsAString
& aAdapterVendorID
)
497 return NS_ERROR_FAILURE
;
500 /* readonly attribute DOMString adapterDeviceID; */
502 GfxInfo::GetAdapterDeviceID(nsAString
& aAdapterDeviceID
)
505 CopyUTF8toUTF16(mRenderer
, aAdapterDeviceID
);
509 /* readonly attribute DOMString adapterDeviceID2; */
511 GfxInfo::GetAdapterDeviceID2(nsAString
& aAdapterDeviceID
)
513 return NS_ERROR_FAILURE
;
516 /* readonly attribute DOMString adapterSubsysID; */
518 GfxInfo::GetAdapterSubsysID(nsAString
& aAdapterSubsysID
)
520 return NS_ERROR_FAILURE
;
523 /* readonly attribute DOMString adapterSubsysID2; */
525 GfxInfo::GetAdapterSubsysID2(nsAString
& aAdapterSubsysID
)
527 return NS_ERROR_FAILURE
;
530 /* readonly attribute boolean isGPU2Active; */
532 GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active
)
534 return NS_ERROR_FAILURE
;
539 // Implement nsIGfxInfoDebug
540 // We don't support spoofing anything on Linux
542 /* void spoofVendorID (in DOMString aVendorID); */
543 NS_IMETHODIMP
GfxInfo::SpoofVendorID(const nsAString
& aVendorID
)
545 CopyUTF16toUTF8(aVendorID
, mVendor
);
549 /* void spoofDeviceID (in unsigned long aDeviceID); */
550 NS_IMETHODIMP
GfxInfo::SpoofDeviceID(const nsAString
& aDeviceID
)
552 CopyUTF16toUTF8(aDeviceID
, mRenderer
);
556 /* void spoofDriverVersion (in DOMString aDriverVersion); */
557 NS_IMETHODIMP
GfxInfo::SpoofDriverVersion(const nsAString
& aDriverVersion
)
559 CopyUTF16toUTF8(aDriverVersion
, mVersion
);
563 /* void spoofOSVersion (in unsigned long aVersion); */
564 NS_IMETHODIMP
GfxInfo::SpoofOSVersion(uint32_t aVersion
)
566 // We don't support OS versioning on Linux. There's just "Linux".
572 } // end namespace widget
573 } // end namespace mozilla