Commit 21ea8634 authored by stransky's avatar stransky
Browse files

Bug 1787182 [Linux] Implement glxtest test as binary r=emilio

glxtest is run later when Firefox already spawns threads. Recently glxtest runs in forked process
which doesn't work correctly in multi-thread environment, so we need to move glxtest to different binary file
and launch it as stand alone code.

Differential Revision: https://phabricator.services.mozilla.com/D173486
parent aa36e083
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@
#endif
#ifdef MOZ_GTK
@BINPATH@/vaapitest
@BINPATH@/glxtest
#endif

; [Components]
+155 −146
Original line number Diff line number Diff line
@@ -24,16 +24,16 @@
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <vector>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>

#if defined(MOZ_ASAN) || defined(FUZZING)
#  include <signal.h>
#endif

#include "mozilla/Unused.h"
#include "nsAppRunner.h"  // for IsWaylandEnabled on IsX11EGLEnabled
#include "stdint.h"
#include "nsExceptionHandler.h"

#ifdef __SUNPRO_CC
#  include <stdio.h>
#endif
@@ -44,16 +44,9 @@
#  include <X11/extensions/Xrandr.h>
#endif

#ifdef MOZ_WAYLAND
#  include "mozilla/widget/mozwayland.h"
#  include "prlink.h"
#endif

#include <vector>
#include <sys/wait.h>
#include "nsString.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"

#ifdef MOZ_X11
// stuff from glx.h
@@ -173,6 +166,19 @@ typedef struct _drmDevice {

#define EXIT_FAILURE_BUFFER_TOO_SMALL 2

// An alternative to mozilla::Unused for use in (a) C code and (b) code where
// linking with unused.o is difficult.
#define MOZ_UNUSED(expr) \
  do {                   \
    if (expr) {          \
      (void)0;           \
    }                    \
  } while (0)

// Print VA-API test results to stdout and logging to stderr
#define OUTPUT_PIPE 1
#define LOG_PIPE 2

namespace mozilla {
namespace widget {
// the read end of the pipe, which will be used by GfxInfo
@@ -182,16 +188,11 @@ extern pid_t glxtest_pid;
}  // namespace widget
}  // namespace mozilla

// the write end of the pipe, which we're going to write to
static int write_end_of_the_pipe = -1;

// our buffer, size and used length
static char* glxtest_buf = nullptr;
static int glxtest_bufsize = 0;
static int glxtest_length = 0;

static char* glxtest_render_device_path = nullptr;

// C++ standard collides with C standard in that it doesn't allow casting void*
// to function pointer types. So the work-around is to convert first to size_t.
// http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
@@ -231,14 +232,9 @@ static void record_warning(const char* str) {
}

static void record_flush() {
  mozilla::Unused << write(write_end_of_the_pipe, glxtest_buf, glxtest_length);
  if (auto* debugFile = getenv("MOZ_GFX_DEBUG_FILE")) {
    auto fd = open(debugFile, O_CREAT | O_WRONLY | O_TRUNC,
                   S_IRUSR | S_IRGRP | S_IROTH);
    if (fd != -1) {
      mozilla::Unused << write(fd, glxtest_buf, glxtest_length);
      close(fd);
    }
  MOZ_UNUSED(write(OUTPUT_PIPE, glxtest_buf, glxtest_length));
  if (getenv("MOZ_GFX_DEBUG")) {
    MOZ_UNUSED(write(LOG_PIPE, glxtest_buf, glxtest_length));
  }
}

@@ -259,24 +255,6 @@ static int x_error_handler(Display*, XErrorEvent* ev) {
// care about leaking memory
extern "C" {

static void close_logging() {
  // we want to redirect to /dev/null stdout, stderr, and while we're at it,
  // any PR logging file descriptors. To that effect, we redirect all positive
  // file descriptors up to what open() returns here. In particular, 1 is stdout
  // and 2 is stderr.
  int fd = open("/dev/null", O_WRONLY);
  for (int i = 1; i < fd; i++) {
    dup2(fd, i);
  }
  close(fd);

  if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
    const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
    mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
    exit(EXIT_FAILURE);
  }
}

#define PCI_FILL_IDENT 0x0001
#define PCI_FILL_CLASS 0x0020
#define PCI_BASE_CLASS_DISPLAY 0x03
@@ -296,6 +274,7 @@ static void get_pci_status() {
    record_warning("libpci missing");
    return;
  }
  auto release = mozilla::MakeScopeExit([&] { dlclose(libpci); });

  typedef struct pci_dev {
    struct pci_dev* next;
@@ -344,7 +323,6 @@ static void get_pci_status() {

  pci_access* pacc = pci_alloc();
  if (!pacc) {
    dlclose(libpci);
    record_warning("libpci alloc failed");
    return;
  }
@@ -362,13 +340,11 @@ static void get_pci_status() {
  }

  pci_cleanup(pacc);
  dlclose(libpci);
}

#ifdef MOZ_WAYLAND
static void set_render_device_path(const char* render_device_path) {
  record_value("DRM_RENDERDEVICE\n%s\n", render_device_path);
  glxtest_render_device_path = strdup(render_device_path);
}

static bool device_has_name(const drmDevice* device, const char* name) {
@@ -389,6 +365,7 @@ static bool get_render_name(const char* name) {
    record_warning("Failed to open libdrm");
    return false;
  }
  auto release = mozilla::MakeScopeExit([&] { dlclose(libdrm); });

  typedef int (*DRMGETDEVICES2)(uint32_t, drmDevicePtr*, int);
  DRMGETDEVICES2 drmGetDevices2 =
@@ -401,7 +378,6 @@ static bool get_render_name(const char* name) {
  if (!drmGetDevices2 || !drmFreeDevice) {
    record_warning(
        "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
    dlclose(libdrm);
    return false;
  }

@@ -409,20 +385,17 @@ static bool get_render_name(const char* name) {
  int devices_len = drmGetDevices2(flags, nullptr, 0);
  if (devices_len < 0) {
    record_warning("drmGetDevices2 failed");
    dlclose(libdrm);
    return false;
  }
  drmDevice** devices = (drmDevice**)calloc(devices_len, sizeof(drmDevice*));
  if (!devices) {
    record_warning("Allocation error");
    dlclose(libdrm);
    return false;
  }
  devices_len = drmGetDevices2(flags, devices, devices_len);
  if (devices_len < 0) {
    free(devices);
    record_warning("drmGetDevices2 failed");
    dlclose(libdrm);
    return false;
  }

@@ -474,8 +447,6 @@ static bool get_render_name(const char* name) {
    drmFreeDevice(&devices[i]);
  }
  free(devices);

  dlclose(libdrm);
  return result;
}
#endif
@@ -498,6 +469,11 @@ static bool get_egl_gl_status(EGLDisplay dpy,
  PFNEGLCREATECONTEXTPROC eglCreateContext =
      cast<PFNEGLCREATECONTEXTPROC>(eglGetProcAddress("eglCreateContext"));

  typedef EGLBoolean (*PFNEGLDESTROYCONTEXTPROC)(EGLDisplay dpy,
                                                 EGLContext ctx);
  PFNEGLDESTROYCONTEXTPROC eglDestroyContext =
      cast<PFNEGLDESTROYCONTEXTPROC>(eglGetProcAddress("eglDestroyContext"));

  typedef EGLBoolean (*PFNEGLMAKECURRENTPROC)(
      EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context);
  PFNEGLMAKECURRENTPROC eglMakeCurrent =
@@ -515,8 +491,8 @@ static bool get_egl_gl_status(EGLDisplay dpy,
      cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
          eglGetProcAddress("eglQueryDisplayAttribEXT"));

  if (!eglChooseConfig || !eglCreateContext || !eglMakeCurrent ||
      !eglQueryDeviceStringEXT) {
  if (!eglChooseConfig || !eglCreateContext || !eglDestroyContext ||
      !eglMakeCurrent || !eglQueryDeviceStringEXT) {
    record_warning("libEGL missing methods for GL test");
    return false;
  }
@@ -570,9 +546,11 @@ static bool get_egl_gl_status(EGLDisplay dpy,
  }

  if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx) == EGL_FALSE) {
    eglDestroyContext(dpy, ectx);
    record_warning("eglMakeCurrent returned an error");
    return false;
  }
  eglDestroyContext(dpy, ectx);

  // Implementations disagree about whether eglGetProcAddress or dlsym
  // should be used for getting functions from the actual API, see
@@ -595,6 +573,11 @@ static bool get_egl_gl_status(EGLDisplay dpy,
      return false;
    }
  }
  auto release = mozilla::MakeScopeExit([&] {
    if (libgl) {
      dlclose(libgl);
    }
  });

  const GLubyte* versionString = glGetString(GL_VERSION);
  const GLubyte* vendorString = glGetString(GL_VENDOR);
@@ -605,9 +588,6 @@ static bool get_egl_gl_status(EGLDisplay dpy,
                 vendorString, rendererString, versionString);
  } else {
    record_warning("EGL glGetString returned null");
    if (libgl) {
      dlclose(libgl);
    }
    return false;
  }

@@ -633,25 +613,34 @@ static bool get_egl_gl_status(EGLDisplay dpy,
#endif
    }
  }

  if (libgl) {
    dlclose(libgl);
  }
  return true;
}

static bool get_egl_status(EGLNativeDisplayType native_dpy) {
  EGLDisplay dpy = nullptr;

  typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
  PFNEGLTERMINATEPROC eglTerminate = nullptr;

  void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
  if (!libegl) {
    record_warning("libEGL missing");
    return false;
  }
  auto release = mozilla::MakeScopeExit([&] {
    if (dpy) {
      eglTerminate(dpy);
    }
    // Unload libegl here causes ASAN/MemLeaks failures on Linux
    // as libegl may not be meant to be unloaded runtime.
    // See 1304156 for reference.
    // dlclose(libegl);
  });

  PFNEGLGETPROCADDRESS eglGetProcAddress =
      cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));

  if (!eglGetProcAddress) {
    dlclose(libegl);
    record_warning("no eglGetProcAddress");
    return false;
  }
@@ -664,27 +653,21 @@ static bool get_egl_status(EGLNativeDisplayType native_dpy) {
                                             EGLint * minor);
  PFNEGLINITIALIZEPROC eglInitialize =
      cast<PFNEGLINITIALIZEPROC>(eglGetProcAddress("eglInitialize"));

  typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
  PFNEGLTERMINATEPROC eglTerminate =
      cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));
  eglTerminate = cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));

  if (!eglGetDisplay || !eglInitialize || !eglTerminate) {
    dlclose(libegl);
    record_warning("libEGL missing methods");
    return false;
  }

  EGLDisplay dpy = eglGetDisplay(native_dpy);
  dpy = eglGetDisplay(native_dpy);
  if (!dpy) {
    dlclose(libegl);
    record_warning("libEGL no display");
    return false;
  }

  EGLint major, minor;
  if (!eglInitialize(dpy, &major, &minor)) {
    dlclose(libegl);
    record_warning("libEGL initialize failed");
    return false;
  }
@@ -700,15 +683,7 @@ static bool get_egl_status(EGLNativeDisplayType native_dpy) {
    }
  }

  if (!get_egl_gl_status(dpy, eglGetProcAddress)) {
    eglTerminate(dpy);
    dlclose(libegl);
    return false;
  }

  eglTerminate(dpy);
  dlclose(libegl);
  return true;
  return get_egl_gl_status(dpy, eglGetProcAddress);
}

#ifdef MOZ_X11
@@ -725,22 +700,49 @@ static void get_xrandr_info(Display* dpy) {

  Window root = RootWindow(dpy, DefaultScreen(dpy));
  XRRProviderResources* pr = XRRGetProviderResources(dpy, root);
  if (!pr) {
    return;
  }
  XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
  if (!res) {
    XRRFreeProviderResources(pr);
    return;
  }
  if (pr->nproviders != 0) {
    record_value("DDX_DRIVER\n");
    for (int i = 0; i < pr->nproviders; i++) {
      XRRProviderInfo* info = XRRGetProviderInfo(dpy, res, pr->providers[i]);
      if (info) {
        record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
        XRRFreeProviderInfo(info);
      }
    }
  }
  XRRFreeScreenResources(res);
  XRRFreeProviderResources(pr);
}

static void glxtest() {
void glxtest() {
  Display* dpy = nullptr;
  void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
  if (!libgl) {
    record_error(LIBGL_FILENAME " missing");
    return;
  }
  auto release = mozilla::MakeScopeExit([&] {
    if (dpy) {
#  ifdef MOZ_ASAN
      XCloseDisplay(dpy);
#  else
      // This XSync call wanted to be instead:
      //   XCloseDisplay(dpy);
      // but this can cause 1-minute stalls on certain setups using Nouveau, see
      // bug 973192
      XSync(dpy, False);
#  endif
    }
    dlclose(libgl);
  });

  typedef void* (*PFNGLXGETPROCADDRESS)(const char*);
  PFNGLXGETPROCADDRESS glXGetProcAddress =
@@ -788,7 +790,7 @@ static void glxtest() {
  }

  ///// Open a connection to the X server /////
  Display* dpy = XOpenDisplay(nullptr);
  dpy = XOpenDisplay(nullptr);
  if (!dpy) {
    record_error("Unable to open a connection to the X server");
    return;
@@ -903,28 +905,24 @@ static void glxtest() {
  glXDestroyContext(dpy, context);
  XDestroyWindow(dpy, window);
  XFreeColormap(dpy, swa.colormap);

#  ifdef NS_FREE_PERMANENT_DATA  // conditionally defined in nscore.h, don't
                                 // forget to #include it above
  XCloseDisplay(dpy);
#  else
  // This XSync call wanted to be instead:
  //   XCloseDisplay(dpy);
  // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
  // 973192
  XSync(dpy, False);
#  endif

  dlclose(libgl);
  XFree(vInfo);

  record_value("TEST_TYPE\nGLX\n");
}

static bool x11_egltest() {
bool x11_egltest() {
  Display* dpy = XOpenDisplay(nullptr);
  if (!dpy) {
    return false;
  }
#  ifdef MOZ_ASAN
  auto release = mozilla::MakeScopeExit([&] {
    // Bug 1715245: Closing the display connection here crashes on NV prop.
    // drivers. Just leave it open, the process will exit shortly after anyway.
    XCloseDisplay(dpy);
  });
#  endif

  XSetErrorHandler(x_error_handler);

  if (!get_egl_status(dpy)) {
@@ -943,20 +941,28 @@ static bool x11_egltest() {
  // Get monitor and DDX driver information
  get_xrandr_info(dpy);

  // Bug 1715245: Closing the display connection here crashes on NV prop.
  // drivers. Just leave it open, the process will exit shortly after anyway.
  // XCloseDisplay(dpy);

  record_value("TEST_TYPE\nEGL\n");
  return true;
}
#endif

#ifdef MOZ_WAYLAND
static void wayland_egltest() {
void wayland_egltest() {
  static auto sWlDisplayConnect = (struct wl_display * (*)(const char*))
      dlsym(RTLD_DEFAULT, "wl_display_connect");
  static auto sWlDisplayRoundtrip =
      (int (*)(struct wl_display*))dlsym(RTLD_DEFAULT, "wl_display_roundtrip");
  static auto sWlDisplayDisconnect = (void (*)(struct wl_display*))dlsym(
      RTLD_DEFAULT, "wl_display_disconnect");

  if (!sWlDisplayConnect || !sWlDisplayRoundtrip || !sWlDisplayDisconnect) {
    record_error("Missing Wayland libraries");
    return;
  }

  // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
  // exist but fails with record_error if something actually went wrong
  struct wl_display* dpy = wl_display_connect(nullptr);
  struct wl_display* dpy = sWlDisplayConnect(nullptr);
  if (!dpy) {
    record_error("Could not connect to wayland socket");
    return;
@@ -968,14 +974,14 @@ static void wayland_egltest() {

  // This is enough to crash some broken NVIDIA prime + Wayland setups, see
  // https://github.com/NVIDIA/egl-wayland/issues/41 and bug 1768260.
  wl_display_roundtrip(dpy);
  sWlDisplayRoundtrip(dpy);

  wl_display_disconnect(dpy);
  sWlDisplayDisconnect(dpy);
  record_value("TEST_TYPE\nEGL\n");
}
#endif

int childgltest() {
int childgltest(bool aWayland) {
  enum { bufsize = 2048 };
  char buf[bufsize];

@@ -988,19 +994,18 @@ int childgltest() {
  get_pci_status();

#ifdef MOZ_WAYLAND
  if (IsWaylandEnabled()) {
  if (aWayland) {
    wayland_egltest();
  } else
  }
#endif
  {
#ifdef MOZ_X11
  if (!aWayland) {
    // TODO: --display command line argument is not properly handled
    if (!x11_egltest()) {
      glxtest();
    }
#endif
  }

#endif
  // Finally write buffered data to the pipe.
  record_flush();

@@ -1014,44 +1019,48 @@ int childgltest() {

}  // extern "C"

/** \returns true in the child glxtest process, false in the parent process */
bool fire_glxtest_process() {
  int pfd[2];
  if (pipe(pfd) == -1) {
    perror("pipe");
    return false;
static void PrintUsage() {
  printf(
      "Firefox OpenGL probe utility\n"
      "\n"
      "usage: glxtest [options]\n"
      "\n"
      "Options:\n"
      "\n"
      "  -h --help                 show this message\n"
      "  -w --wayland              probe OpenGL/EGL on Wayland (default is "
      "X11)\n"
      "\n");
}

int main(int argc, char** argv) {
  struct option longOptions[] = {{"help", no_argument, NULL, 'h'},
                                 {"wayland", no_argument, NULL, 'w'},
                                 {NULL, 0, NULL, 0}};
  const char* shortOptions = "hw";
  int c;
  bool wayland = false;
  while ((c = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
    switch (c) {
      case 'w':
        wayland = true;
        break;
      case 'h':
        PrintUsage();
        return 0;
      default:
        break;
    }
  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
    close(pfd[0]);
    close(pfd[1]);
    return false;
  }
  // The child exits early to avoid running the full shutdown sequence and avoid
  // conflicting with threads we have already spawned (like the profiler).
  if (pid == 0) {
    close(pfd[0]);
    write_end_of_the_pipe = pfd[1];
    close_logging();
    // This process is expected to be crashy, and we
    // don't want the user to see its crashes. That's the whole reason for
    // doing this in a separate process.
    if (CrashReporter::GetEnabled()) {
      CrashReporter::UnsetExceptionHandler();
  if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
    const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
    (void)write(OUTPUT_PIPE, msg, strlen(msg));
    exit(EXIT_FAILURE);
  }
#if defined(MOZ_ASAN) || defined(FUZZING)
  // If handle_segv=1 (default), then glxtest crash will print a sanitizer
  // report which can confuse the harness in fuzzing automation.
  signal(SIGSEGV, SIG_DFL);
#endif
    int rv = childgltest();
    close(pfd[1]);
    _exit(rv);
  }

  close(pfd[1]);
  mozilla::widget::glxtest_pipe = pfd[0];
  mozilla::widget::glxtest_pid = pid;
  return false;
  return childgltest(wayland);
}
+26 −0
Original line number Diff line number Diff line
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

with Files("**"):
    BUG_COMPONENT = ("Core", "Widget: Gtk")

Program("glxtest")
SOURCES += [
    "glxtest.cpp",
]

if CONFIG["MOZ_ASAN"]:
    SOURCES += [
        "../../../mozglue/build/AsanOptions.cpp",
    ]

CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
CXXFLAGS += CONFIG["MOZ_PANGO_CFLAGS"]
OS_LIBS += CONFIG["MOZ_X11_LIBS"]
OS_LIBS += CONFIG["MOZ_GTK3_LIBS"]

NO_PGO = True
DisableStlWrapping()
+1 −5
Original line number Diff line number Diff line
@@ -153,11 +153,6 @@ SOURCES += [
    "ProfileReset.cpp",
]

if CONFIG["MOZ_X11"] or CONFIG["MOZ_WAYLAND"]:
    UNIFIED_SOURCES += [
        "glxtest.cpp",
    ]

if CONFIG["MOZ_INSTRUMENT_EVENT_LOOP"]:
    UNIFIED_SOURCES += [
        "EventTracer.cpp",
@@ -186,6 +181,7 @@ FINAL_LIBRARY = "xul"

if CONFIG["MOZ_X11"] or CONFIG["MOZ_WAYLAND"]:
    DEFINES["USE_GLX_TEST"] = True
    DIRS += ["glxtest"]

for var in (
    "MOZ_APP_NAME",
+42 −11
Original line number Diff line number Diff line
@@ -254,6 +254,10 @@
#  include "nsIStringBundle.h"
#endif

#ifdef USE_GLX_TEST
#  include "mozilla/GUniquePtr.h"
#endif

extern uint32_t gRestartMode;
extern void InstallSignalHandlers(const char* ProgramName);

@@ -321,11 +325,6 @@ nsString gProcessStartupShortcut;

#if defined(MOZ_WIDGET_GTK)
#  include <glib.h>
#  if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
#    define CLEANUP_MEMORY 1
#    define PANGO_ENABLE_BACKEND
#    include <pango/pangofc-fontmap.h>
#  endif
#  include "mozilla/WidgetUtilsGtk.h"
#  include <gtk/gtk.h>
#  ifdef MOZ_WAYLAND
@@ -335,7 +334,6 @@ nsString gProcessStartupShortcut;
#  ifdef MOZ_X11
#    include <gdk/gdkx.h>
#  endif /* MOZ_X11 */
#  include <fontconfig/fontconfig.h>
#endif
#include "BinaryPath.h"

@@ -3620,7 +3618,41 @@ static DWORD WINAPI InitDwriteBG(LPVOID lpdwThreadParam) {
#endif

#ifdef USE_GLX_TEST
bool fire_glxtest_process();
namespace mozilla::widget {
// the read end of the pipe, which will be used by GfxInfo
extern int glxtest_pipe;
// the PID of the glxtest process, to pass to waitpid()
extern pid_t glxtest_pid;
}  // namespace mozilla::widget

void fire_glxtest_process() {
  if (!gAppData || !gAppData->xreDirectory) {
    return;
  }

  nsAutoCString glxTestBinary;
  gAppData->xreDirectory->GetNativePath(glxTestBinary);
  glxTestBinary.Append("/");
  glxTestBinary.Append("glxtest");

  char* argv[] = {strdup(glxTestBinary.get()),
                  IsWaylandEnabled() ? strdup("-w") : nullptr, nullptr};
  auto freeArgv = mozilla::MakeScopeExit([&] {
    for (auto& arg : argv) {
      free(arg);
    }
  });

  GUniquePtr<GError> err;
  g_spawn_async_with_pipes(
      nullptr, argv, nullptr,
      GSpawnFlags(G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD), nullptr,
      nullptr, &mozilla::widget::glxtest_pid, nullptr,
      &mozilla::widget::glxtest_pipe, nullptr, getter_Transfers(err));
  if (err) {
    Output(true, "Failed to probe graphics hardware! %s\n", err->message);
  }
}
#endif

#include "GeckoProfiler.h"
@@ -4424,6 +4456,8 @@ bool IsWaylandEnabled() {
  return false;
#  endif
}
#else
bool IsWaylandEnabled() { return false; }
#endif

#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
@@ -4702,10 +4736,7 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
      saveDisplayArg = true;
    }

    bool waylandEnabled = false;
#  if defined(MOZ_WAYLAND)
    waylandEnabled = IsWaylandEnabled();
#  endif
    bool waylandEnabled = IsWaylandEnabled();
    // On Wayland disabled builds read X11 DISPLAY env exclusively
    // and don't care about different displays.
    if (!waylandEnabled && !display_name) {
Loading