зеркало из https://github.com/mozilla/gecko-dev.git
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
This commit is contained in:
Родитель
08c92637d2
Коммит
facf3ee2b4
|
@ -160,6 +160,7 @@
|
|||
#endif
|
||||
#ifdef MOZ_GTK
|
||||
@BINPATH@/vaapitest
|
||||
@BINPATH@/glxtest
|
||||
#endif
|
||||
|
||||
; [Components]
|
||||
|
|
|
@ -24,16 +24,15 @@
|
|||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <string.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,11 +43,6 @@
|
|||
# include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WAYLAND
|
||||
# include "mozilla/widget/mozwayland.h"
|
||||
# include "prlink.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
// stuff from glx.h
|
||||
typedef struct __GLXcontextRec* GLXContext;
|
||||
|
@ -167,6 +161,10 @@ typedef struct _drmDevice {
|
|||
|
||||
#define EXIT_FAILURE_BUFFER_TOO_SMALL 2
|
||||
|
||||
// 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
|
||||
|
@ -176,16 +174,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/
|
||||
|
@ -225,14 +218,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);
|
||||
}
|
||||
(void)write(OUTPUT_PIPE, glxtest_buf, glxtest_length);
|
||||
if (getenv("MOZ_GFX_DEBUG")) {
|
||||
(void)write(LOG_PIPE, glxtest_buf, glxtest_length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +232,6 @@ static int x_error_handler(Display*, XErrorEvent* ev) {
|
|||
ev->error_code, ev->request_code, ev->minor_code);
|
||||
record_flush();
|
||||
_exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -254,24 +241,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
|
||||
|
@ -363,7 +332,6 @@ static void get_pci_status() {
|
|||
#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) {
|
||||
|
@ -720,14 +688,26 @@ 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]);
|
||||
record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
|
||||
if (info) {
|
||||
record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
|
||||
XRRFreeProviderInfo(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
XRRFreeScreenResources(res);
|
||||
XRRFreeProviderResources(pr);
|
||||
}
|
||||
|
||||
static void glxtest() {
|
||||
|
@ -743,6 +723,7 @@ static void glxtest() {
|
|||
|
||||
if (!glXGetProcAddress) {
|
||||
record_error("no glXGetProcAddress");
|
||||
dlclose(libgl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -779,6 +760,7 @@ static void glxtest() {
|
|||
!glXCreateContext || !glXMakeCurrent || !glXDestroyContext ||
|
||||
!glGetString) {
|
||||
record_error(LIBGL_FILENAME " missing methods");
|
||||
dlclose(libgl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -786,12 +768,14 @@ static void glxtest() {
|
|||
Display* dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
record_error("Unable to open a connection to the X server");
|
||||
dlclose(libgl);
|
||||
return;
|
||||
}
|
||||
|
||||
///// Check that the GLX extension is present /////
|
||||
if (!glXQueryExtension(dpy, nullptr, nullptr)) {
|
||||
record_error("GLX extension missing");
|
||||
dlclose(libgl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -808,6 +792,7 @@ static void glxtest() {
|
|||
vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
|
||||
if (!vInfo) {
|
||||
record_error("No visuals found");
|
||||
dlclose(libgl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -949,9 +934,21 @@ static bool x11_egltest() {
|
|||
|
||||
#ifdef MOZ_WAYLAND
|
||||
static 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;
|
||||
|
@ -963,14 +960,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];
|
||||
|
||||
|
@ -983,18 +980,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();
|
||||
|
@ -1009,44 +1006,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;
|
||||
}
|
||||
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 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
return childgltest(wayland);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# -*- 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",
|
||||
]
|
||||
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()
|
|
@ -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",
|
||||
|
|
|
@ -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,40 @@ 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() {
|
||||
nsAutoCString glxTestBinary;
|
||||
if (!gAppData->xreDirectory) {
|
||||
return;
|
||||
}
|
||||
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"
|
||||
|
|
|
@ -163,7 +163,7 @@ void GfxInfo::GetData() {
|
|||
wait_for_glxtest_process = true;
|
||||
} else {
|
||||
// Bug 718629
|
||||
// ECHILD happens when the glxtest process got reaped got reaped after a
|
||||
// ECHILD happens when the glxtest process got reaped after a
|
||||
// PR_CreateProcess as per bug 227246. This shouldn't matter, as we
|
||||
// still seem to get the data from the pipe, and if we didn't, the
|
||||
// outcome would be to blocklist anyway.
|
||||
|
|
Загрузка…
Ссылка в новой задаче