Bug 1753720 [wpt PR 32698] - [FedCM] Perform CSP checks on the provider URL, a=testonly
[gecko.git] / toolkit / xre / glxtest.cpp
blobab6533d070a16c7f36848ea6af673de7ccb40b57
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
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/. */
8 //////////////////////////////////////////////////////////////////////////////
9 //
10 // Explanation: See bug 639842. Safely getting GL driver info on X11 is hard,
11 // because the only way to do that is to create a GL context and call
12 // glGetString(), but with bad drivers, just creating a GL context may crash.
14 // This file implements the idea to do that in a separate process.
16 // The only non-static function here is fire_glxtest_process(). It creates a
17 // pipe, publishes its 'read' end as the mozilla::widget::glxtest_pipe global
18 // variable, forks, and runs that GLX probe in the child process, which runs the
19 // childgltest() static function. This creates a X connection, a GLX context,
20 // calls glGetString, and writes that to the 'write' end of the pipe.
22 #include <cstdio>
23 #include <cstdlib>
24 #include <dlfcn.h>
25 #include <fcntl.h>
26 #include <unistd.h>
28 #include "mozilla/Unused.h"
29 #include "nsAppRunner.h" // for IsWaylandEnabled on IsX11EGLEnabled
30 #include "stdint.h"
32 #ifdef __SUNPRO_CC
33 # include <stdio.h>
34 #endif
36 #ifdef MOZ_X11
37 # include "X11/Xlib.h"
38 # include "X11/Xutil.h"
39 # include <X11/extensions/Xrandr.h>
40 #endif
42 #ifdef MOZ_WAYLAND
43 # include "mozilla/widget/mozwayland.h"
44 # include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
45 #endif
47 #ifdef MOZ_X11
48 // stuff from glx.h
49 typedef struct __GLXcontextRec* GLXContext;
50 typedef XID GLXPixmap;
51 typedef XID GLXDrawable;
52 /* GLX 1.3 and later */
53 typedef struct __GLXFBConfigRec* GLXFBConfig;
54 typedef XID GLXFBConfigID;
55 typedef XID GLXContextID;
56 typedef XID GLXWindow;
57 typedef XID GLXPbuffer;
58 # define GLX_RGBA 4
59 # define GLX_RED_SIZE 8
60 # define GLX_GREEN_SIZE 9
61 # define GLX_BLUE_SIZE 10
62 # define GLX_DOUBLEBUFFER 5
63 #endif
65 // stuff from gl.h
66 typedef uint8_t GLubyte;
67 typedef uint32_t GLenum;
68 #define GL_VENDOR 0x1F00
69 #define GL_RENDERER 0x1F01
70 #define GL_VERSION 0x1F02
72 // GLX_MESA_query_renderer
73 // clang-format off
74 #define GLX_RENDERER_VENDOR_ID_MESA 0x8183
75 #define GLX_RENDERER_DEVICE_ID_MESA 0x8184
76 #define GLX_RENDERER_VERSION_MESA 0x8185
77 #define GLX_RENDERER_ACCELERATED_MESA 0x8186
78 #define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
79 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188
80 #define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189
81 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A
82 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
83 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
84 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
85 #define GLX_RENDERER_ID_MESA 0x818E
86 // clang-format on
88 // stuff from egl.h
89 typedef intptr_t EGLAttrib;
90 typedef int EGLBoolean;
91 typedef void* EGLConfig;
92 typedef void* EGLContext;
93 typedef void* EGLDeviceEXT;
94 typedef void* EGLDisplay;
95 typedef int EGLint;
96 typedef void* EGLNativeDisplayType;
97 typedef void* EGLSurface;
98 typedef void* (*PFNEGLGETPROCADDRESS)(const char*);
100 #define EGL_NO_CONTEXT nullptr
101 #define EGL_NO_SURFACE nullptr
102 #define EGL_FALSE 0
103 #define EGL_TRUE 1
104 #define EGL_BLUE_SIZE 0x3022
105 #define EGL_GREEN_SIZE 0x3023
106 #define EGL_RED_SIZE 0x3024
107 #define EGL_NONE 0x3038
108 #define EGL_VENDOR 0x3053
109 #define EGL_EXTENSIONS 0x3055
110 #define EGL_CONTEXT_CLIENT_VERSION 0x3098
111 #define EGL_OPENGL_API 0x30A2
112 #define EGL_DEVICE_EXT 0x322C
113 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
114 #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
116 // stuff from xf86drm.h
117 #define DRM_NODE_RENDER 2
118 #define DRM_NODE_MAX 3
120 typedef struct _drmPciDeviceInfo {
121 uint16_t vendor_id;
122 uint16_t device_id;
123 uint16_t subvendor_id;
124 uint16_t subdevice_id;
125 uint8_t revision_id;
126 } drmPciDeviceInfo, *drmPciDeviceInfoPtr;
128 typedef struct _drmDevice {
129 char** nodes;
130 int available_nodes;
131 int bustype;
132 union {
133 void* pci;
134 void* usb;
135 void* platform;
136 void* host1x;
137 } businfo;
138 union {
139 drmPciDeviceInfoPtr pci;
140 void* usb;
141 void* platform;
142 void* host1x;
143 } deviceinfo;
144 } drmDevice, *drmDevicePtr;
146 // Open libGL and load needed symbols
147 #if defined(__OpenBSD__) || defined(__NetBSD__)
148 # define LIBGL_FILENAME "libGL.so"
149 # define LIBGLES_FILENAME "libGLESv2.so"
150 # define LIBEGL_FILENAME "libEGL.so"
151 # define LIBDRM_FILENAME "libdrm.so"
152 #else
153 # define LIBGL_FILENAME "libGL.so.1"
154 # define LIBGLES_FILENAME "libGLESv2.so.2"
155 # define LIBEGL_FILENAME "libEGL.so.1"
156 # define LIBDRM_FILENAME "libdrm.so.2"
157 #endif
159 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
161 namespace mozilla {
162 namespace widget {
163 // the read end of the pipe, which will be used by GfxInfo
164 extern int glxtest_pipe;
165 // the PID of the glxtest process, to pass to waitpid()
166 extern pid_t glxtest_pid;
167 } // namespace widget
168 } // namespace mozilla
170 // the write end of the pipe, which we're going to write to
171 static int write_end_of_the_pipe = -1;
173 // our buffer, size and used length
174 static char* glxtest_buf = nullptr;
175 static int glxtest_bufsize = 0;
176 static int glxtest_length = 0;
178 // C++ standard collides with C standard in that it doesn't allow casting void*
179 // to function pointer types. So the work-around is to convert first to size_t.
180 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
181 template <typename func_ptr_type>
182 static func_ptr_type cast(void* ptr) {
183 return reinterpret_cast<func_ptr_type>(reinterpret_cast<size_t>(ptr));
186 static void record_value(const char* format, ...) {
187 // Don't add more if the buffer is full.
188 if (glxtest_bufsize <= glxtest_length) {
189 return;
192 // Append the new values to the buffer, not to exceed the remaining space.
193 int remaining = glxtest_bufsize - glxtest_length;
194 va_list args;
195 va_start(args, format);
196 int max_added =
197 vsnprintf(glxtest_buf + glxtest_length, remaining, format, args);
198 va_end(args);
200 // snprintf returns how many char it could have added, not how many it added.
201 // It is important to get this right since it will control how many chars we
202 // will attempt to write to the pipe fd.
203 if (max_added > remaining) {
204 glxtest_length += remaining;
205 } else {
206 glxtest_length += max_added;
210 static void record_error(const char* str) { record_value("ERROR\n%s\n", str); }
212 static void record_warning(const char* str) {
213 record_value("WARNING\n%s\n", str);
216 static void record_flush() {
217 mozilla::Unused << write(write_end_of_the_pipe, glxtest_buf, glxtest_length);
220 #ifdef MOZ_X11
221 static int x_error_handler(Display*, XErrorEvent* ev) {
222 record_value(
223 "ERROR\nX error, error_code=%d, "
224 "request_code=%d, minor_code=%d\n",
225 ev->error_code, ev->request_code, ev->minor_code);
226 record_flush();
227 _exit(EXIT_FAILURE);
228 return 0;
230 #endif
232 // childgltest is declared inside extern "C" so that the name is not mangled.
233 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
234 // memory leak errors because we run it inside a short lived fork and we don't
235 // care about leaking memory
236 extern "C" {
238 static void close_logging() {
239 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
240 // any PR logging file descriptors. To that effect, we redirect all positive
241 // file descriptors up to what open() returns here. In particular, 1 is stdout
242 // and 2 is stderr.
243 int fd = open("/dev/null", O_WRONLY);
244 for (int i = 1; i < fd; i++) {
245 dup2(fd, i);
247 close(fd);
249 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
250 const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
251 mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
252 exit(EXIT_FAILURE);
256 #define PCI_FILL_IDENT 0x0001
257 #define PCI_FILL_CLASS 0x0020
258 #define PCI_BASE_CLASS_DISPLAY 0x03
260 static int get_pci_status() {
261 void* libpci = dlopen("libpci.so.3", RTLD_LAZY);
262 if (!libpci) {
263 libpci = dlopen("libpci.so", RTLD_LAZY);
265 if (!libpci) {
266 record_warning("libpci missing");
267 return 0;
270 typedef struct pci_dev {
271 struct pci_dev* next;
272 uint16_t domain_16;
273 uint8_t bus, dev, func;
274 unsigned int known_fields;
275 uint16_t vendor_id, device_id;
276 uint16_t device_class;
277 } pci_dev;
279 typedef struct pci_access {
280 unsigned int method;
281 int writeable;
282 int buscentric;
283 char* id_file_name;
284 int free_id_name;
285 int numeric_ids;
286 unsigned int id_lookup_mode;
287 int debugging;
288 void* error;
289 void* warning;
290 void* debug;
291 pci_dev* devices;
292 } pci_access;
294 typedef pci_access* (*PCIALLOC)(void);
295 PCIALLOC pci_alloc = cast<PCIALLOC>(dlsym(libpci, "pci_alloc"));
297 typedef void (*PCIINIT)(pci_access*);
298 PCIINIT pci_init = cast<PCIINIT>(dlsym(libpci, "pci_init"));
300 typedef void (*PCICLEANUP)(pci_access*);
301 PCICLEANUP pci_cleanup = cast<PCICLEANUP>(dlsym(libpci, "pci_cleanup"));
303 typedef void (*PCISCANBUS)(pci_access*);
304 PCISCANBUS pci_scan_bus = cast<PCISCANBUS>(dlsym(libpci, "pci_scan_bus"));
306 typedef void (*PCIFILLINFO)(pci_dev*, int);
307 PCIFILLINFO pci_fill_info = cast<PCIFILLINFO>(dlsym(libpci, "pci_fill_info"));
309 if (!pci_alloc || !pci_cleanup || !pci_scan_bus || !pci_fill_info) {
310 dlclose(libpci);
311 record_warning("libpci missing methods");
312 return 0;
315 pci_access* pacc = pci_alloc();
316 if (!pacc) {
317 dlclose(libpci);
318 record_warning("libpci alloc failed");
319 return 0;
322 pci_init(pacc);
323 pci_scan_bus(pacc);
325 int count = 0;
326 for (pci_dev* dev = pacc->devices; dev; dev = dev->next) {
327 pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
328 if (dev->device_class >> 8 == PCI_BASE_CLASS_DISPLAY && dev->vendor_id &&
329 dev->device_id) {
330 ++count;
331 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
332 dev->vendor_id, dev->device_id);
336 pci_cleanup(pacc);
337 dlclose(libpci);
338 return count;
341 #ifdef MOZ_WAYLAND
342 static bool device_has_name(const drmDevice* device, const char* name) {
343 for (size_t i = 0; i < DRM_NODE_MAX; i++) {
344 if (!(device->available_nodes & (1 << i))) {
345 continue;
347 if (strcmp(device->nodes[i], name) == 0) {
348 return true;
351 return false;
354 static bool get_render_name(const char* name) {
355 void* libdrm = dlopen(LIBDRM_FILENAME, RTLD_LAZY);
356 if (!libdrm) {
357 record_warning("Failed to open libdrm");
358 return false;
361 typedef int (*DRMGETDEVICES2)(uint32_t, drmDevicePtr*, int);
362 DRMGETDEVICES2 drmGetDevices2 =
363 cast<DRMGETDEVICES2>(dlsym(libdrm, "drmGetDevices2"));
365 typedef void (*DRMFREEDEVICE)(drmDevicePtr*);
366 DRMFREEDEVICE drmFreeDevice =
367 cast<DRMFREEDEVICE>(dlsym(libdrm, "drmFreeDevice"));
369 if (!drmGetDevices2 || !drmFreeDevice) {
370 record_warning(
371 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
372 dlclose(libdrm);
373 return false;
376 uint32_t flags = 0;
377 int devices_len = drmGetDevices2(flags, nullptr, 0);
378 if (devices_len < 0) {
379 record_warning("drmGetDevices2 failed");
380 dlclose(libdrm);
381 return false;
383 drmDevice** devices = (drmDevice**)calloc(devices_len, sizeof(drmDevice*));
384 if (!devices) {
385 record_warning("Allocation error");
386 dlclose(libdrm);
387 return false;
389 devices_len = drmGetDevices2(flags, devices, devices_len);
390 if (devices_len < 0) {
391 free(devices);
392 record_warning("drmGetDevices2 failed");
393 dlclose(libdrm);
394 return false;
397 const drmDevice* match = nullptr;
398 for (int i = 0; i < devices_len; i++) {
399 if (device_has_name(devices[i], name)) {
400 match = devices[i];
401 break;
405 bool result = false;
406 if (!match) {
407 record_warning("Cannot find DRM device");
408 } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
409 record_warning("DRM device has no render node");
410 } else {
411 record_value("DRM_RENDERDEVICE\n%s\n", match->nodes[DRM_NODE_RENDER]);
412 record_value(
413 "MESA_VENDOR_ID\n0x%04x\n"
414 "MESA_DEVICE_ID\n0x%04x\n",
415 match->deviceinfo.pci->vendor_id, match->deviceinfo.pci->device_id);
416 result = true;
419 for (int i = 0; i < devices_len; i++) {
420 drmFreeDevice(&devices[i]);
422 free(devices);
424 dlclose(libdrm);
425 return result;
427 #endif
429 static bool get_gles_status(EGLDisplay dpy,
430 PFNEGLGETPROCADDRESS eglGetProcAddress) {
431 typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
432 EGLDisplay dpy, EGLint const* attrib_list, EGLConfig* configs,
433 EGLint config_size, EGLint* num_config);
434 PFNEGLCHOOSECONFIGPROC eglChooseConfig =
435 cast<PFNEGLCHOOSECONFIGPROC>(eglGetProcAddress("eglChooseConfig"));
437 typedef EGLBoolean (*PFNEGLBINDAPIPROC)(EGLint api);
438 PFNEGLBINDAPIPROC eglBindAPI =
439 cast<PFNEGLBINDAPIPROC>(eglGetProcAddress("eglBindAPI"));
441 typedef EGLContext (*PFNEGLCREATECONTEXTPROC)(
442 EGLDisplay dpy, EGLConfig config, EGLContext share_context,
443 EGLint const* attrib_list);
444 PFNEGLCREATECONTEXTPROC eglCreateContext =
445 cast<PFNEGLCREATECONTEXTPROC>(eglGetProcAddress("eglCreateContext"));
447 typedef EGLBoolean (*PFNEGLMAKECURRENTPROC)(
448 EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context);
449 PFNEGLMAKECURRENTPROC eglMakeCurrent =
450 cast<PFNEGLMAKECURRENTPROC>(eglGetProcAddress("eglMakeCurrent"));
452 typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC)(EGLDeviceEXT device,
453 EGLint name);
454 PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
455 cast<PFNEGLQUERYDEVICESTRINGEXTPROC>(
456 eglGetProcAddress("eglQueryDeviceStringEXT"));
458 typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC)(
459 EGLDisplay dpy, EGLint name, EGLAttrib * value);
460 PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
461 cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
462 eglGetProcAddress("eglQueryDisplayAttribEXT"));
464 if (!eglChooseConfig || !eglCreateContext || !eglMakeCurrent ||
465 !eglQueryDeviceStringEXT) {
466 record_warning("libEGL missing methods for GL test");
467 return false;
470 typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
471 PFNGLGETSTRING glGetString =
472 cast<PFNGLGETSTRING>(eglGetProcAddress("glGetString"));
474 EGLint config_attrs[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
475 EGL_BLUE_SIZE, 8, EGL_NONE};
477 EGLConfig config;
478 EGLint num_config;
479 if (eglChooseConfig(dpy, config_attrs, &config, 1, &num_config) ==
480 EGL_FALSE) {
481 record_warning("eglChooseConfig returned an error");
482 return false;
485 if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
486 record_warning("eglBindAPI returned an error");
487 return false;
490 EGLint ctx_attrs[] = {EGL_NONE};
491 EGLContext ectx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attrs);
492 if (!ectx) {
493 record_warning("eglCreateContext returned an error");
494 return false;
497 if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx) == EGL_FALSE) {
498 record_warning("eglMakeCurrent returned an error");
499 return false;
502 // Implementations disagree about whether eglGetProcAddress or dlsym
503 // should be used for getting functions from the actual API, see
504 // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
505 void* libgl = nullptr;
506 if (!glGetString) {
507 libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
508 if (!libgl) {
509 libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
510 if (!libgl) {
511 record_warning(LIBGL_FILENAME " and " LIBGLES_FILENAME " missing");
512 return false;
516 glGetString = cast<PFNGLGETSTRING>(dlsym(libgl, "glGetString"));
517 if (!glGetString) {
518 dlclose(libgl);
519 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
520 return false;
524 const GLubyte* versionString = glGetString(GL_VERSION);
525 const GLubyte* vendorString = glGetString(GL_VENDOR);
526 const GLubyte* rendererString = glGetString(GL_RENDERER);
528 if (versionString && vendorString && rendererString) {
529 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
530 vendorString, rendererString, versionString);
531 } else {
532 record_warning("EGL glGetString returned null");
533 if (libgl) {
534 dlclose(libgl);
536 return false;
539 EGLDeviceEXT device;
540 if (eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, (EGLAttrib*)&device) ==
541 EGL_TRUE) {
542 const char* deviceExtensions =
543 eglQueryDeviceStringEXT(device, EGL_EXTENSIONS);
544 if (deviceExtensions &&
545 strstr(deviceExtensions, "EGL_MESA_device_software")) {
546 record_value("MESA_ACCELERATED\nFALSE\n");
547 } else {
548 #ifdef MOZ_WAYLAND
549 const char* deviceString =
550 eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
551 if (!deviceString || !get_render_name(deviceString)) {
552 const char* renderNodeString =
553 eglQueryDeviceStringEXT(device, EGL_DRM_RENDER_NODE_FILE_EXT);
554 if (renderNodeString) {
555 record_value("DRM_RENDERDEVICE\n%s\n", renderNodeString);
558 #endif
562 if (libgl) {
563 dlclose(libgl);
565 return true;
568 static bool get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test,
569 bool require_driver) {
570 void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
571 if (!libegl) {
572 record_warning("libEGL missing");
573 return false;
576 PFNEGLGETPROCADDRESS eglGetProcAddress =
577 cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));
579 if (!eglGetProcAddress) {
580 dlclose(libegl);
581 record_warning("no eglGetProcAddress");
582 return false;
585 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
586 PFNEGLGETDISPLAYPROC eglGetDisplay =
587 cast<PFNEGLGETDISPLAYPROC>(eglGetProcAddress("eglGetDisplay"));
589 typedef EGLBoolean (*PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major,
590 EGLint * minor);
591 PFNEGLINITIALIZEPROC eglInitialize =
592 cast<PFNEGLINITIALIZEPROC>(eglGetProcAddress("eglInitialize"));
594 typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
595 PFNEGLTERMINATEPROC eglTerminate =
596 cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));
598 if (!eglGetDisplay || !eglInitialize || !eglTerminate) {
599 dlclose(libegl);
600 record_warning("libEGL missing methods");
601 return false;
604 EGLDisplay dpy = eglGetDisplay(native_dpy);
605 if (!dpy) {
606 dlclose(libegl);
607 record_warning("libEGL no display");
608 return false;
611 EGLint major, minor;
612 if (!eglInitialize(dpy, &major, &minor)) {
613 dlclose(libegl);
614 record_warning("libEGL initialize failed");
615 return false;
618 typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC)(EGLDisplay dpy);
619 PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName =
620 cast<PFNEGLGETDISPLAYDRIVERNAMEPROC>(
621 eglGetProcAddress("eglGetDisplayDriverName"));
622 if (eglGetDisplayDriverName) {
623 // TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
624 // and instead a proprietary GL, most likely NVIDIA's. The PCI device list
625 // in combination with the vendor name is very likely sufficient to identify
626 // the device.
627 const char* driDriver = eglGetDisplayDriverName(dpy);
628 if (driDriver) {
629 record_value("DRI_DRIVER\n%s\n", driDriver);
631 } else if (require_driver) {
632 record_warning("libEGL missing eglGetDisplayDriverName");
633 eglTerminate(dpy);
634 dlclose(libegl);
635 return false;
638 if (gles_test && !get_gles_status(dpy, eglGetProcAddress)) {
639 eglTerminate(dpy);
640 dlclose(libegl);
641 return false;
644 eglTerminate(dpy);
645 dlclose(libegl);
646 return true;
649 #ifdef MOZ_X11
650 static void get_xrandr_info(Display* dpy) {
651 // When running on remote X11 the xrandr version may be stuck on an ancient
652 // version. There are still setups using remote X11 out there, so make sure we
653 // don't crash.
654 int eventBase, errorBase, major, minor;
655 if (!XRRQueryExtension(dpy, &eventBase, &errorBase) ||
656 !XRRQueryVersion(dpy, &major, &minor) ||
657 !(major > 1 || (major == 1 && minor >= 4))) {
658 return;
661 Window root = RootWindow(dpy, DefaultScreen(dpy));
662 XRRProviderResources* pr = XRRGetProviderResources(dpy, root);
663 XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
664 RROutput primary = XRRGetOutputPrimary(dpy, root);
666 if (res->noutput != 0) {
667 bool foundCRTC = false;
669 for (int i = 0; i < res->noutput; i++) {
670 XRROutputInfo* outputInfo = XRRGetOutputInfo(dpy, res, res->outputs[i]);
671 if (!outputInfo->crtc) {
672 continue;
675 if (!foundCRTC) {
676 record_value("SCREEN_INFO\n");
677 foundCRTC = true;
680 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(dpy, res, outputInfo->crtc);
681 record_value("%dx%d:%d;", crtcInfo->width, crtcInfo->height,
682 res->outputs[i] == primary ? 1 : 0);
685 if (foundCRTC) {
686 record_value("\n");
690 if (pr->nproviders != 0) {
691 record_value("DDX_DRIVER\n");
692 for (int i = 0; i < pr->nproviders; i++) {
693 XRRProviderInfo* info = XRRGetProviderInfo(dpy, res, pr->providers[i]);
694 record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
699 static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
700 void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
701 if (!libgl) {
702 record_error(LIBGL_FILENAME " missing");
703 return;
706 typedef void* (*PFNGLXGETPROCADDRESS)(const char*);
707 PFNGLXGETPROCADDRESS glXGetProcAddress =
708 cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
710 if (!glXGetProcAddress) {
711 record_error("no glXGetProcAddress");
712 return;
715 typedef GLXFBConfig* (*PFNGLXQUERYEXTENSION)(Display*, int*, int*);
716 PFNGLXQUERYEXTENSION glXQueryExtension =
717 cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
719 typedef GLXFBConfig* (*PFNGLXQUERYVERSION)(Display*, int*, int*);
720 PFNGLXQUERYVERSION glXQueryVersion =
721 cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
723 typedef XVisualInfo* (*PFNGLXCHOOSEVISUAL)(Display*, int, int*);
724 PFNGLXCHOOSEVISUAL glXChooseVisual =
725 cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
727 typedef GLXContext (*PFNGLXCREATECONTEXT)(Display*, XVisualInfo*, GLXContext,
728 Bool);
729 PFNGLXCREATECONTEXT glXCreateContext =
730 cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
732 typedef Bool (*PFNGLXMAKECURRENT)(Display*, GLXDrawable, GLXContext);
733 PFNGLXMAKECURRENT glXMakeCurrent =
734 cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
736 typedef void (*PFNGLXDESTROYCONTEXT)(Display*, GLXContext);
737 PFNGLXDESTROYCONTEXT glXDestroyContext =
738 cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
740 typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
741 PFNGLGETSTRING glGetString =
742 cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
744 if (!glXQueryExtension || !glXQueryVersion || !glXChooseVisual ||
745 !glXCreateContext || !glXMakeCurrent || !glXDestroyContext ||
746 !glGetString) {
747 record_error(LIBGL_FILENAME " missing methods");
748 return;
751 ///// Open a connection to the X server /////
752 Display* dpy = XOpenDisplay(nullptr);
753 if (!dpy) {
754 record_error("Unable to open a connection to the X server");
755 return;
758 ///// Check that the GLX extension is present /////
759 if (!glXQueryExtension(dpy, nullptr, nullptr)) {
760 record_error("GLX extension missing");
761 return;
764 XSetErrorHandler(x_error_handler);
766 ///// Get a visual /////
767 int attribs[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
768 1, GLX_BLUE_SIZE, 1, None};
769 XVisualInfo* vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
770 if (!vInfo) {
771 int attribs2[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
772 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER,
773 None};
774 vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
775 if (!vInfo) {
776 record_error("No visuals found");
777 return;
781 // using a X11 Window instead of a GLXPixmap does not crash
782 // fglrx in indirect rendering. bug 680644
783 Window window;
784 XSetWindowAttributes swa;
785 swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
786 vInfo->visual, AllocNone);
788 swa.border_pixel = 0;
789 window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen), 0, 0, 16, 16, 0,
790 vInfo->depth, InputOutput, vInfo->visual,
791 CWBorderPixel | CWColormap, &swa);
793 ///// Get a GL context and make it current //////
794 GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
795 glXMakeCurrent(dpy, window, context);
797 ///// Look for this symbol to determine texture_from_pixmap support /////
798 void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
800 ///// Get GL vendor/renderer/versions strings /////
801 const GLubyte* versionString = glGetString(GL_VERSION);
802 const GLubyte* vendorString = glGetString(GL_VENDOR);
803 const GLubyte* rendererString = glGetString(GL_RENDERER);
805 if (versionString && vendorString && rendererString) {
806 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
807 vendorString, rendererString, versionString,
808 glXBindTexImageEXT ? "TRUE" : "FALSE");
809 *gotGlxInfo = 1;
810 } else {
811 record_error("glGetString returned null");
814 // If GLX_MESA_query_renderer is available, populate additional data.
815 typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)(
816 int attribute, unsigned int* value);
817 PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
818 glXQueryCurrentRendererIntegerMESAProc =
819 cast<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC>(
820 glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
821 if (glXQueryCurrentRendererIntegerMESAProc) {
822 unsigned int vendorId, deviceId, accelerated, videoMemoryMB;
823 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA,
824 &vendorId);
825 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA,
826 &deviceId);
827 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA,
828 &accelerated);
829 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA,
830 &videoMemoryMB);
832 // Truncate IDs to 4 digits- that's all PCI IDs are.
833 vendorId &= 0xFFFF;
834 deviceId &= 0xFFFF;
836 record_value(
837 "MESA_VENDOR_ID\n0x%04x\n"
838 "MESA_DEVICE_ID\n0x%04x\n"
839 "MESA_ACCELERATED\n%s\n"
840 "MESA_VRAM\n%dMB\n",
841 vendorId, deviceId, accelerated ? "TRUE" : "FALSE", videoMemoryMB);
844 // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
845 typedef const char* (*PFNGLXGETSCREENDRIVERPROC)(Display * dpy, int scrNum);
846 PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc =
847 cast<PFNGLXGETSCREENDRIVERPROC>(glXGetProcAddress("glXGetScreenDriver"));
848 if (glXGetScreenDriverProc) {
849 const char* driDriver = glXGetScreenDriverProc(dpy, DefaultScreen(dpy));
850 if (driDriver) {
851 *gotDriDriver = 1;
852 record_value("DRI_DRIVER\n%s\n", driDriver);
856 // Get monitor and DDX driver information
857 get_xrandr_info(dpy);
859 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
860 ///// doesn't need to check GL info) so we might be staying alive for longer
861 ///// than expected, so it's important to consume as little memory as
862 ///// possible. Also we want to check that we're able to do that too without
863 ///// generating X errors.
864 glXMakeCurrent(dpy, None,
865 nullptr); // must release the GL context before destroying it
866 glXDestroyContext(dpy, context);
867 XDestroyWindow(dpy, window);
868 XFreeColormap(dpy, swa.colormap);
870 # ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
871 // forget to #include it above
872 XCloseDisplay(dpy);
873 # else
874 // This XSync call wanted to be instead:
875 // XCloseDisplay(dpy);
876 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
877 // 973192
878 XSync(dpy, False);
879 # endif
881 dlclose(libgl);
884 static bool x11_egltest(int pci_count) {
885 Display* dpy = XOpenDisplay(nullptr);
886 if (!dpy) {
887 return false;
889 XSetErrorHandler(x_error_handler);
891 // Bug 1667621: 30bit "Deep Color" is broken on EGL on Mesa (as of 2021/10).
892 // Disable all non-standard depths for the initial EGL roleout.
893 int screenCount = ScreenCount(dpy);
894 for (int idx = 0; idx < screenCount; idx++) {
895 if (DefaultDepth(dpy, idx) != 24) {
896 return false;
900 // On at least amdgpu open source driver, eglInitialize fails unless
901 // a valid XDisplay pointer is passed as the native display.
902 if (!get_egl_status(dpy, true, pci_count != 1)) {
903 return false;
906 // Get monitor and DDX driver information
907 get_xrandr_info(dpy);
909 // Bug 1715245: Closing the display connection here crashes on NV prop.
910 // drivers. Just leave it open, the process will exit shortly after anyway.
911 // XCloseDisplay(dpy);
913 record_value("TEST_TYPE\nEGL\n");
914 return true;
917 static void glxtest() {
918 int gotGlxInfo = 0;
919 int gotDriDriver = 0;
921 get_glx_status(&gotGlxInfo, &gotDriDriver);
922 if (!gotGlxInfo) {
923 get_egl_status(nullptr, true, false);
924 } else if (!gotDriDriver) {
925 // If we failed to get the driver name from X, try via
926 // EGL_MESA_query_driver. We are probably using Wayland.
927 get_egl_status(nullptr, false, true);
930 record_value("TEST_TYPE\nGLX\n");
932 #endif
934 #ifdef MOZ_WAYLAND
935 typedef void (*print_info_t)(void* info);
936 typedef void (*destroy_info_t)(void* info);
938 struct global_info {
939 struct wl_list link;
941 uint32_t id;
942 uint32_t version;
943 char* interface;
945 print_info_t print;
946 destroy_info_t destroy;
949 struct output_info {
950 struct global_info global;
951 struct wl_list global_link;
953 struct wl_output* output;
955 int32_t version;
957 int32_t scale;
960 struct xdg_output_v1_info {
961 struct wl_list link;
963 struct zxdg_output_v1* xdg_output;
964 struct output_info* output;
966 struct {
967 int32_t width, height;
968 } logical;
971 struct xdg_output_manager_v1_info {
972 struct global_info global;
973 struct zxdg_output_manager_v1* manager;
974 struct weston_info* info;
976 struct wl_list outputs;
979 struct weston_info {
980 struct wl_display* display;
981 struct wl_registry* registry;
983 struct wl_list infos;
984 bool roundtrip_needed;
986 struct wl_list outputs;
987 struct xdg_output_manager_v1_info* xdg_output_manager_v1_info;
990 static void init_global_info(struct weston_info* info,
991 struct global_info* global, uint32_t id,
992 const char* interface, uint32_t version) {
993 global->id = id;
994 global->version = version;
995 global->interface = strdup(interface);
997 wl_list_insert(info->infos.prev, &global->link);
1000 static void print_output_info(void* data) {}
1002 static void destroy_xdg_output_v1_info(struct xdg_output_v1_info* info) {
1003 wl_list_remove(&info->link);
1004 zxdg_output_v1_destroy(info->xdg_output);
1005 free(info);
1008 static int cmpOutputIds(const void* a, const void* b) {
1009 return (((struct xdg_output_v1_info*)a)->output->global.id -
1010 ((struct xdg_output_v1_info*)b)->output->global.id);
1013 static void print_xdg_output_manager_v1_info(void* data) {
1014 struct xdg_output_manager_v1_info* info =
1015 (struct xdg_output_manager_v1_info*)data;
1016 struct xdg_output_v1_info* output;
1018 int screen_count = wl_list_length(&info->outputs);
1019 if (screen_count > 0) {
1020 struct xdg_output_v1_info* infos = (struct xdg_output_v1_info*)calloc(
1021 1, screen_count * sizeof(xdg_output_v1_info));
1023 int pos = 0;
1024 wl_list_for_each(output, &info->outputs, link) {
1025 infos[pos] = *output;
1026 pos++;
1029 if (screen_count > 1) {
1030 qsort(infos, screen_count, sizeof(struct xdg_output_v1_info),
1031 cmpOutputIds);
1034 record_value("SCREEN_INFO\n");
1035 for (int i = 0; i < screen_count; i++) {
1036 record_value("%dx%d:0;", infos[i].logical.width, infos[i].logical.height);
1038 record_value("\n");
1040 free(infos);
1044 static void destroy_xdg_output_manager_v1_info(void* data) {
1045 struct xdg_output_manager_v1_info* info =
1046 (struct xdg_output_manager_v1_info*)data;
1047 struct xdg_output_v1_info *output, *tmp;
1049 zxdg_output_manager_v1_destroy(info->manager);
1051 wl_list_for_each_safe(output, tmp, &info->outputs, link) {
1052 destroy_xdg_output_v1_info(output);
1056 static void handle_xdg_output_v1_logical_position(void* data,
1057 struct zxdg_output_v1* output,
1058 int32_t x, int32_t y) {}
1060 static void handle_xdg_output_v1_logical_size(void* data,
1061 struct zxdg_output_v1* output,
1062 int32_t width, int32_t height) {
1063 struct xdg_output_v1_info* xdg_output = (struct xdg_output_v1_info*)data;
1064 xdg_output->logical.width = width;
1065 xdg_output->logical.height = height;
1068 static void handle_xdg_output_v1_done(void* data,
1069 struct zxdg_output_v1* output) {}
1071 static void handle_xdg_output_v1_name(void* data, struct zxdg_output_v1* output,
1072 const char* name) {}
1074 static void handle_xdg_output_v1_description(void* data,
1075 struct zxdg_output_v1* output,
1076 const char* description) {}
1078 static const struct zxdg_output_v1_listener xdg_output_v1_listener = {
1079 .logical_position = handle_xdg_output_v1_logical_position,
1080 .logical_size = handle_xdg_output_v1_logical_size,
1081 .done = handle_xdg_output_v1_done,
1082 .name = handle_xdg_output_v1_name,
1083 .description = handle_xdg_output_v1_description,
1086 static void add_xdg_output_v1_info(
1087 struct xdg_output_manager_v1_info* manager_info,
1088 struct output_info* output) {
1089 struct xdg_output_v1_info* xdg_output =
1090 (struct xdg_output_v1_info*)calloc(1, sizeof *xdg_output);
1092 wl_list_insert(&manager_info->outputs, &xdg_output->link);
1093 xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
1094 manager_info->manager, output->output);
1095 zxdg_output_v1_add_listener(xdg_output->xdg_output, &xdg_output_v1_listener,
1096 xdg_output);
1098 xdg_output->output = output;
1100 manager_info->info->roundtrip_needed = true;
1103 static void add_xdg_output_manager_v1_info(struct weston_info* info,
1104 uint32_t id, uint32_t version) {
1105 struct output_info* output;
1106 struct xdg_output_manager_v1_info* manager =
1107 (struct xdg_output_manager_v1_info*)calloc(1, sizeof *manager);
1109 wl_list_init(&manager->outputs);
1110 manager->info = info;
1112 init_global_info(info, &manager->global, id,
1113 zxdg_output_manager_v1_interface.name, version);
1114 manager->global.print = print_xdg_output_manager_v1_info;
1115 manager->global.destroy = destroy_xdg_output_manager_v1_info;
1117 manager->manager = (struct zxdg_output_manager_v1*)wl_registry_bind(
1118 info->registry, id, &zxdg_output_manager_v1_interface,
1119 version > 2 ? 2 : version);
1121 wl_list_for_each(output, &info->outputs, global_link) {
1122 add_xdg_output_v1_info(manager, output);
1125 info->xdg_output_manager_v1_info = manager;
1128 static void output_handle_geometry(void* data, struct wl_output* wl_output,
1129 int32_t x, int32_t y, int32_t physical_width,
1130 int32_t physical_height, int32_t subpixel,
1131 const char* make, const char* model,
1132 int32_t output_transform) {}
1134 static void output_handle_mode(void* data, struct wl_output* wl_output,
1135 uint32_t flags, int32_t width, int32_t height,
1136 int32_t refresh) {}
1138 static void output_handle_done(void* data, struct wl_output* wl_output) {}
1140 static void output_handle_scale(void* data, struct wl_output* wl_output,
1141 int32_t scale) {
1142 struct output_info* output = (struct output_info*)data;
1144 output->scale = scale;
1147 static const struct wl_output_listener output_listener = {
1148 output_handle_geometry,
1149 output_handle_mode,
1150 output_handle_done,
1151 output_handle_scale,
1154 static void destroy_output_info(void* data) {
1155 struct output_info* output = (struct output_info*)data;
1157 wl_output_destroy(output->output);
1160 static void add_output_info(struct weston_info* info, uint32_t id,
1161 uint32_t version) {
1162 struct output_info* output = (struct output_info*)calloc(1, sizeof *output);
1164 init_global_info(info, &output->global, id, "wl_output", version);
1165 output->global.print = print_output_info;
1166 output->global.destroy = destroy_output_info;
1168 output->version = MIN(version, 2);
1169 output->scale = 1;
1171 output->output = (struct wl_output*)wl_registry_bind(
1172 info->registry, id, &wl_output_interface, output->version);
1173 wl_output_add_listener(output->output, &output_listener, output);
1175 info->roundtrip_needed = true;
1176 wl_list_insert(&info->outputs, &output->global_link);
1178 if (info->xdg_output_manager_v1_info) {
1179 add_xdg_output_v1_info(info->xdg_output_manager_v1_info, output);
1183 static void global_handler(void* data, struct wl_registry* registry,
1184 uint32_t id, const char* interface,
1185 uint32_t version) {
1186 struct weston_info* info = (struct weston_info*)data;
1188 if (!strcmp(interface, "wl_output")) {
1189 add_output_info(info, id, version);
1190 } else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
1191 add_xdg_output_manager_v1_info(info, id, version);
1195 static void global_remove_handler(void* data, struct wl_registry* registry,
1196 uint32_t name) {}
1198 static const struct wl_registry_listener registry_listener = {
1199 global_handler, global_remove_handler};
1201 static void print_infos(struct wl_list* infos) {
1202 struct global_info* info;
1204 wl_list_for_each(info, infos, link) { info->print(info); }
1207 static void destroy_info(void* data) {
1208 struct global_info* global = (struct global_info*)data;
1210 global->destroy(data);
1211 wl_list_remove(&global->link);
1212 free(global->interface);
1213 free(data);
1216 static void destroy_infos(struct wl_list* infos) {
1217 struct global_info *info, *tmp;
1218 wl_list_for_each_safe(info, tmp, infos, link) { destroy_info(info); }
1221 static void get_wayland_screen_info(struct wl_display* dpy) {
1222 struct weston_info info = {0};
1223 info.display = dpy;
1225 info.xdg_output_manager_v1_info = NULL;
1226 wl_list_init(&info.infos);
1227 wl_list_init(&info.outputs);
1229 info.registry = wl_display_get_registry(info.display);
1230 wl_registry_add_listener(info.registry, &registry_listener, &info);
1232 do {
1233 info.roundtrip_needed = false;
1234 wl_display_roundtrip(info.display);
1235 } while (info.roundtrip_needed);
1237 print_infos(&info.infos);
1238 destroy_infos(&info.infos);
1240 wl_registry_destroy(info.registry);
1243 static void wayland_egltest() {
1244 // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
1245 // exist but fails with record_error if something actually went wrong
1246 struct wl_display* dpy = wl_display_connect(nullptr);
1247 if (!dpy) {
1248 record_error("Could not connect to wayland socket");
1249 return;
1252 if (!get_egl_status((EGLNativeDisplayType)dpy, true, false)) {
1253 record_error("EGL test failed");
1255 get_wayland_screen_info(dpy);
1257 wl_display_disconnect(dpy);
1258 record_value("TEST_TYPE\nEGL\n");
1260 #endif
1262 int childgltest() {
1263 enum { bufsize = 2048 };
1264 char buf[bufsize];
1266 // We save it as a global so that the X error handler can flush the buffer
1267 // before early exiting.
1268 glxtest_buf = buf;
1269 glxtest_bufsize = bufsize;
1271 // Get a list of all GPUs from the PCI bus.
1272 int pci_count = get_pci_status();
1274 #ifdef MOZ_WAYLAND
1275 if (IsWaylandEnabled()) {
1276 wayland_egltest();
1277 } else
1278 #endif
1280 #ifdef MOZ_X11
1281 // TODO: --display command line argument is not properly handled
1282 if (!x11_egltest(pci_count)) {
1283 glxtest();
1285 #endif
1288 // Finally write buffered data to the pipe.
1289 record_flush();
1291 // If we completely filled the buffer, we need to tell the parent.
1292 if (glxtest_length >= glxtest_bufsize) {
1293 return EXIT_FAILURE_BUFFER_TOO_SMALL;
1296 return EXIT_SUCCESS;
1299 } // extern "C"
1301 /** \returns true in the child glxtest process, false in the parent process */
1302 bool fire_glxtest_process() {
1303 int pfd[2];
1304 if (pipe(pfd) == -1) {
1305 perror("pipe");
1306 return false;
1308 pid_t pid = fork();
1309 if (pid < 0) {
1310 perror("fork");
1311 close(pfd[0]);
1312 close(pfd[1]);
1313 return false;
1315 // The child exits early to avoid running the full shutdown sequence and avoid
1316 // conflicting with threads we have already spawned (like the profiler).
1317 if (pid == 0) {
1318 close(pfd[0]);
1319 write_end_of_the_pipe = pfd[1];
1320 close_logging();
1321 int rv = childgltest();
1322 close(pfd[1]);
1323 _exit(rv);
1326 close(pfd[1]);
1327 mozilla::widget::glxtest_pipe = pfd[0];
1328 mozilla::widget::glxtest_pid = pid;
1329 return false;