Backed out 5 changesets (bug 1787182) for causing leaks on linux asan. CLOSED TREE

Backed out changeset 34056df4e1d5 (bug 1787182)
Backed out changeset fea9dc3de652 (bug 1787182)
Backed out changeset 7e710b6500f0 (bug 1787182)
Backed out changeset fe70bf105370 (bug 1787182)
Backed out changeset 70439dbc72fd (bug 1787182)
This commit is contained in:
Iulian Moraru 2023-03-27 14:18:06 +03:00
Родитель 0a04242ea2
Коммит 5234db201c
10 изменённых файлов: 408 добавлений и 667 удалений

Просмотреть файл

@ -158,10 +158,6 @@
@BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@
#endif
#endif
#ifdef MOZ_GTK
@BINPATH@/vaapitest
@BINPATH@/glxtest
#endif
; [Components]
#ifdef ACCESSIBILITY

Просмотреть файл

@ -24,15 +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"
#ifdef __SUNPRO_CC
# include <stdio.h>
#endif
@ -43,6 +43,13 @@
# include <X11/extensions/Xrandr.h>
#endif
#ifdef MOZ_WAYLAND
# include "mozilla/widget/mozwayland.h"
# include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
# include "prlink.h"
# include "va/va.h"
#endif
#ifdef MOZ_X11
// stuff from glx.h
typedef struct __GLXcontextRec* GLXContext;
@ -161,10 +168,6 @@ 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
@ -174,11 +177,27 @@ extern pid_t glxtest_pid;
} // namespace widget
} // namespace mozilla
#ifdef MOZ_WAYLAND
// bits to use decoding childvaapitest() return values.
constexpr int CODEC_HW_H264 = 1 << 4;
constexpr int CODEC_HW_VP8 = 1 << 5;
constexpr int CODEC_HW_VP9 = 1 << 6;
constexpr int CODEC_HW_AV1 = 1 << 7;
# define NVIDIA_VENDOR_STRING "NVIDIA Corporation"
static bool run_vaapi_test = false;
#endif
// 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/
@ -218,9 +237,14 @@ static void record_warning(const char* str) {
}
static void record_flush() {
(void)write(OUTPUT_PIPE, glxtest_buf, glxtest_length);
if (getenv("MOZ_GFX_DEBUG")) {
(void)write(LOG_PIPE, glxtest_buf, glxtest_length);
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);
}
}
}
@ -232,6 +256,7 @@ 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
@ -241,6 +266,24 @@ 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
@ -332,6 +375,7 @@ 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) {
@ -574,6 +618,13 @@ static bool get_egl_gl_status(EGLDisplay dpy,
return false;
}
#ifdef MOZ_WAYLAND
// Enable VA-API testing on EGL/non-NVIDIA setup (Bug 1799747).
if (!strstr((const char*)vendorString, NVIDIA_VENDOR_STRING)) {
run_vaapi_test = true;
}
#endif
EGLDeviceEXT device;
if (eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, (EGLAttrib*)&device) ==
EGL_TRUE) {
@ -688,26 +739,14 @@ 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);
}
record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
}
}
XRRFreeScreenResources(res);
XRRFreeProviderResources(pr);
}
static void glxtest() {
@ -723,7 +762,6 @@ static void glxtest() {
if (!glXGetProcAddress) {
record_error("no glXGetProcAddress");
dlclose(libgl);
return;
}
@ -760,7 +798,6 @@ static void glxtest() {
!glXCreateContext || !glXMakeCurrent || !glXDestroyContext ||
!glGetString) {
record_error(LIBGL_FILENAME " missing methods");
dlclose(libgl);
return;
}
@ -768,14 +805,12 @@ 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;
}
@ -792,7 +827,6 @@ static void glxtest() {
vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
if (!vInfo) {
record_error("No visuals found");
dlclose(libgl);
return;
}
}
@ -934,21 +968,9 @@ 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 = sWlDisplayConnect(nullptr);
struct wl_display* dpy = wl_display_connect(nullptr);
if (!dpy) {
record_error("Could not connect to wayland socket");
return;
@ -960,14 +982,246 @@ 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.
sWlDisplayRoundtrip(dpy);
wl_display_roundtrip(dpy);
sWlDisplayDisconnect(dpy);
wl_display_disconnect(dpy);
record_value("TEST_TYPE\nEGL\n");
}
static constexpr struct {
VAProfile mVAProfile;
nsLiteralCString mName;
} kVAAPiProfileName[] = {
# define MAP(v) \
{ VAProfile##v, nsLiteralCString(#v) }
MAP(H264ConstrainedBaseline),
MAP(H264Main),
MAP(H264High),
MAP(VP8Version0_3),
MAP(VP9Profile0),
MAP(VP9Profile2),
MAP(AV1Profile0),
MAP(AV1Profile1),
# undef MAP
};
static const char* VAProfileName(VAProfile aVAProfile) {
for (const auto& profile : kVAAPiProfileName) {
if (profile.mVAProfile == aVAProfile) {
return profile.mName.get();
}
}
return nullptr;
}
int childvaapitest() {
int renderDeviceFD = -1;
VAProfile* profiles = nullptr;
VAEntrypoint* entryPoints = nullptr;
PRLibrary* libDrm = nullptr;
VADisplay display = nullptr;
auto autoRelease = mozilla::MakeScopeExit([&] {
if (renderDeviceFD > -1) {
close(renderDeviceFD);
}
delete[] profiles;
delete[] entryPoints;
if (display) {
vaTerminate(display);
}
if (libDrm) {
PR_UnloadLibrary(libDrm);
}
});
renderDeviceFD = open(glxtest_render_device_path, O_RDWR);
if (renderDeviceFD == -1) {
return 3;
}
PRLibSpec lspec;
lspec.type = PR_LibSpec_Pathname;
const char* libName = "libva-drm.so.2";
lspec.value.pathname = libName;
libDrm = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
if (!libDrm) {
return 4;
}
static auto sVaGetDisplayDRM =
(void* (*)(int fd))PR_FindSymbol(libDrm, "vaGetDisplayDRM");
if (!sVaGetDisplayDRM) {
return 5;
}
display = sVaGetDisplayDRM(renderDeviceFD);
if (!display) {
return 6;
}
int major, minor;
VAStatus status = vaInitialize(display, &major, &minor);
if (status != VA_STATUS_SUCCESS) {
return 7;
}
int maxProfiles = vaMaxNumProfiles(display);
int maxEntryPoints = vaMaxNumEntrypoints(display);
if (MOZ_UNLIKELY(maxProfiles <= 0 || maxEntryPoints <= 0)) {
return 8;
}
profiles = new VAProfile[maxProfiles];
int numProfiles = 0;
status = vaQueryConfigProfiles(display, profiles, &numProfiles);
if (status != VA_STATUS_SUCCESS) {
return 9;
}
numProfiles = std::min(numProfiles, maxProfiles);
entryPoints = new VAEntrypoint[maxEntryPoints];
int codecs = 0;
bool foundProfile = false;
for (int p = 0; p < numProfiles; p++) {
VAProfile profile = profiles[p];
// Check only supported profiles
if (!VAProfileName(profile)) {
continue;
}
int numEntryPoints = 0;
status = vaQueryConfigEntrypoints(display, profile, entryPoints,
&numEntryPoints);
if (status != VA_STATUS_SUCCESS) {
continue;
}
numEntryPoints = std::min(numEntryPoints, maxEntryPoints);
for (int entry = 0; entry < numEntryPoints; entry++) {
if (entryPoints[entry] != VAEntrypointVLD) {
continue;
}
VAConfigID config = VA_INVALID_ID;
status = vaCreateConfig(display, profile, entryPoints[entry], nullptr, 0,
&config);
if (status == VA_STATUS_SUCCESS) {
const char* profstr = VAProfileName(profile);
// VAProfileName returns null on failure, making the below calls safe
if (!strncmp(profstr, "H264", 4)) {
codecs |= CODEC_HW_H264;
} else if (!strncmp(profstr, "VP8", 3)) {
codecs |= CODEC_HW_VP8;
} else if (!strncmp(profstr, "VP9", 3)) {
codecs |= CODEC_HW_VP9;
} else if (!strncmp(profstr, "AV1", 3)) {
codecs |= CODEC_HW_AV1;
} else {
char warnbuf[128];
SprintfBuf(warnbuf, 128, "VA-API test unknown profile: %s", profstr);
record_warning(warnbuf);
}
vaDestroyConfig(display, config);
foundProfile = true;
}
}
}
if (foundProfile) {
return codecs;
}
return 10;
}
static void vaapitest() {
if (!glxtest_render_device_path) {
return;
}
pid_t vaapitest_pid = fork();
if (vaapitest_pid == 0) {
# 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 vaapirv = childvaapitest();
_exit(vaapirv);
} else if (vaapitest_pid > 0) {
int vaapitest_status = 0;
bool wait_for_vaapitest_process = true;
while (wait_for_vaapitest_process) {
if (waitpid(vaapitest_pid, &vaapitest_status, 0) == -1) {
wait_for_vaapitest_process = false;
record_warning(
"VA-API test failed: waiting for VA-API process failed.");
} else if (WIFEXITED(vaapitest_status) || WIFSIGNALED(vaapitest_status)) {
wait_for_vaapitest_process = false;
}
}
if (WIFEXITED(vaapitest_status)) {
// Note that WEXITSTATUS only returns least significant 8 bits
// of the exit code, despite returning type int.
int exitcode = WEXITSTATUS(vaapitest_status);
int codecs = exitcode & 0b1111'0000;
exitcode &= 0b0000'1111;
switch (exitcode) {
case 0:
char codecstr[80];
record_value("VAAPI_SUPPORTED\nTRUE\n");
SprintfBuf(codecstr, 80, "%d", codecs);
record_value("VAAPI_HWCODECS\n");
record_value(codecstr);
record_value("\n");
break;
case 3:
record_warning(
"VA-API test failed: opening render device path failed.");
break;
case 4:
record_warning(
"VA-API test failed: missing or old libva-drm library.");
break;
case 5:
record_warning("VA-API test failed: missing vaGetDisplayDRM.");
break;
case 6:
record_warning("VA-API test failed: failed to get vaGetDisplayDRM.");
break;
case 7:
record_warning(
"VA-API test failed: failed to initialise VAAPI connection.");
break;
case 8:
record_warning(
"VA-API test failed: wrong VAAPI profiles/entry point nums.");
break;
case 9:
record_warning("VA-API test failed: vaQueryConfigProfiles() failed.");
break;
case 10:
record_warning(
"VA-API test failed: no supported VAAPI profile found.");
break;
default:
record_warning(
"VA-API test failed: Something unexpected went wrong.");
break;
}
} else {
record_warning(
"VA-API test failed: process crashed. Please check your VA-API "
"drivers.");
}
} else {
record_warning("VA-API test failed: Could not fork process.");
}
}
#endif
int childgltest(bool aWayland) {
int childgltest() {
enum { bufsize = 2048 };
char buf[bufsize];
@ -980,16 +1234,22 @@ int childgltest(bool aWayland) {
get_pci_status();
#ifdef MOZ_WAYLAND
if (aWayland) {
if (IsWaylandEnabled()) {
wayland_egltest();
}
} else
#endif
{
#ifdef MOZ_X11
if (!aWayland) {
// TODO: --display command line argument is not properly handled
if (!x11_egltest()) {
glxtest();
}
#endif
}
#ifdef MOZ_WAYLAND
if (run_vaapi_test) {
vaapitest();
}
#endif
@ -1006,48 +1266,38 @@ int childgltest(bool aWayland) {
} // extern "C"
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;
}
/** \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;
}
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);
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();
#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);
// 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);
int rv = childgltest();
close(pfd[1]);
_exit(rv);
}
close(pfd[1]);
mozilla::widget::glxtest_pipe = pfd[0];
mozilla::widget::glxtest_pid = pid;
return false;
}

Просмотреть файл

@ -1,20 +0,0 @@
# -*- 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,6 +153,19 @@ SOURCES += [
"ProfileReset.cpp",
]
if CONFIG["MOZ_X11"] or CONFIG["MOZ_WAYLAND"]:
UNIFIED_SOURCES += [
"glxtest.cpp",
]
if CONFIG["MOZ_WAYLAND"]:
LOCAL_INCLUDES += [
"/media/mozva",
]
USE_LIBS += [
"mozva",
]
if CONFIG["MOZ_INSTRUMENT_EVENT_LOOP"]:
UNIFIED_SOURCES += [
"EventTracer.cpp",
@ -181,7 +194,6 @@ 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,10 +254,6 @@
# include "nsIStringBundle.h"
#endif
#ifdef USE_GLX_TEST
# include "mozilla/GUniquePtr.h"
#endif
extern uint32_t gRestartMode;
extern void InstallSignalHandlers(const char* ProgramName);
@ -325,6 +321,11 @@ 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
@ -334,6 +335,7 @@ nsString gProcessStartupShortcut;
# ifdef MOZ_X11
# include <gdk/gdkx.h>
# endif /* MOZ_X11 */
# include <fontconfig/fontconfig.h>
#endif
#include "BinaryPath.h"
@ -3618,40 +3620,7 @@ static DWORD WINAPI InitDwriteBG(LPVOID lpdwThreadParam) {
#endif
#ifdef USE_GLX_TEST
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);
}
}
bool fire_glxtest_process();
#endif
#include "GeckoProfiler.h"
@ -3980,6 +3949,17 @@ int XREMain::XRE_mainInit(bool* aExitFlag) {
mozilla::startup::IncreaseDescriptorLimits();
#ifdef USE_GLX_TEST
// bug 639842 - it's very important to fire this process BEFORE we set up
// error handling. indeed, 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.
//
// This call will cause a fork and the fork will terminate itself separately
// from the usual shutdown sequence
fire_glxtest_process();
#endif
SetupErrorHandling(gArgv[0]);
#ifdef CAIRO_HAS_DWRITE_FONT
@ -5141,10 +5121,6 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
// Flush any pending page load events.
mozilla::glean_pings::Pageload.Submit("startup"_ns);
#ifdef USE_GLX_TEST
fire_glxtest_process();
#endif
return 0;
}

Просмотреть файл

@ -15,15 +15,10 @@
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <glib.h>
#include <fcntl.h>
#include "mozilla/gfx/Logging.h"
#include "mozilla/SSE.h"
#include "mozilla/Telemetry.h"
#include "mozilla/XREAppData.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/GUniquePtr.h"
#include "nsCRTGlue.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
@ -34,13 +29,9 @@
#include "prenv.h"
#include "WidgetUtilsGtk.h"
#include "MediaCodecsSupport.h"
#include "nsAppRunner.h"
// How long we wait for data from glxtest/vaapi test process in milliseconds.
#define GFX_TEST_TIMEOUT 4000
#define VAAPI_TEST_TIMEOUT 2000
#define VAAPI_PROBE_BINARY "vaapitest"
// How long we wait for data from glxtest process in milliseconds.
#define GLXTEST_TIMEOUT 4000
#define EXIT_STATUS_BUFFER_TOO_SMALL 2
#ifdef DEBUG
@ -54,7 +45,7 @@ NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
#endif
// these global variables will be set when firing the glxtest process
int glxtest_pipe = -2;
int glxtest_pipe = -1;
pid_t glxtest_pid = 0;
// bits to use decoding codec information returned from glxtest
@ -73,6 +64,7 @@ nsresult GfxInfo::Init() {
mIsXWayland = false;
mHasMultipleGPUs = false;
mGlxTestError = false;
mIsVAAPISupported = false;
return GfxInfoBase::Init();
}
@ -116,7 +108,7 @@ void GfxInfo::GetData() {
}
const TimeStamp deadline =
TimeStamp::Now() + TimeDuration::FromMilliseconds(GFX_TEST_TIMEOUT);
TimeStamp::Now() + TimeDuration::FromMilliseconds(GLXTEST_TIMEOUT);
enum { buf_size = 2048 };
char buf[buf_size];
@ -125,7 +117,7 @@ void GfxInfo::GetData() {
struct pollfd pfd {};
pfd.fd = glxtest_pipe;
pfd.events = POLLIN;
auto ret = poll(&pfd, 1, GFX_TEST_TIMEOUT);
auto ret = poll(&pfd, 1, GLXTEST_TIMEOUT);
if (ret <= 0) {
gfxCriticalNote << "glxtest: failed to read data from glxtest, we may "
"fallback to software rendering\n";
@ -163,7 +155,7 @@ void GfxInfo::GetData() {
wait_for_glxtest_process = true;
} else {
// Bug 718629
// ECHILD happens when the glxtest process got reaped after a
// ECHILD happens when the glxtest process got reaped 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.
@ -256,6 +248,29 @@ void GfxInfo::GetData() {
stringToFill = pciDevices.AppendElement();
} else if (!strcmp(line, "DRM_RENDERDEVICE")) {
stringToFill = &drmRenderDevice;
} else if (!strcmp(line, "VAAPI_SUPPORTED")) {
stringToFill = &isVAAPISupported;
} else if (!strcmp(line, "VAAPI_HWCODECS")) {
line = NS_strtok("\n", &bufptr);
if (!line) break;
int codecs = 0;
std::istringstream(line) >> codecs;
if (codecs & CODEC_HW_H264) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::H264HardwareDecode);
}
if (codecs & CODEC_HW_VP8) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::VP8HardwareDecode);
}
if (codecs & CODEC_HW_VP9) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::VP9HardwareDecode);
}
if (codecs & CODEC_HW_AV1) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::AV1HardwareDecode);
}
} else if (!strcmp(line, "TEST_TYPE")) {
stringToFill = &testType;
} else if (!strcmp(line, "WARNING")) {
@ -317,6 +332,7 @@ void GfxInfo::GetData() {
}
mDrmRenderDevice = std::move(drmRenderDevice);
mIsVAAPISupported = isVAAPISupported.Equals("TRUE");
mTestType = std::move(testType);
// Mesa always exposes itself in the GL_VERSION string, but not always the
@ -555,152 +571,6 @@ void GfxInfo::GetData() {
AddCrashReportAnnotations();
}
int fire_vaapi_process(const char* aRenderDevicePath, int* aOutPipe) {
nsAutoCString vaapiTestBinary;
if (!gAppData->xreDirectory) {
return 0;
}
gAppData->xreDirectory->GetNativePath(vaapiTestBinary);
vaapiTestBinary.Append("/");
vaapiTestBinary.Append(VAAPI_PROBE_BINARY);
char* argv[] = {strdup(PromiseFlatCString(vaapiTestBinary).get()),
strdup("-d"), strdup(aRenderDevicePath), nullptr};
auto freeArgv = mozilla::MakeScopeExit([&] {
for (auto& arg : argv) {
free(arg);
}
});
int pid;
GUniquePtr<GError> err;
g_spawn_async_with_pipes(
nullptr, argv, nullptr,
GSpawnFlags(G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD), nullptr,
nullptr, &pid, nullptr, aOutPipe, nullptr, getter_Transfers(err));
if (err) {
gfxCriticalNote << "Failed to probe VA-API hardware! " << err->message
<< "\n";
pid = 0;
}
return pid;
}
static bool MakeFdNonBlocking(int fd) {
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) != -1;
}
void GfxInfo::GetDataVAAPI() {
if (mIsVAAPISupported.isSome()) {
return;
}
mIsVAAPISupported = Some(false);
int vaapiPipe = -1;
int vaapiPID = 0;
gsize vaapiDataLen;
char* vaapiData = nullptr;
GIOChannel* channel = nullptr;
auto free = mozilla::MakeScopeExit([&] {
if (channel) {
g_io_channel_unref(channel);
}
if (vaapiPipe >= 0) {
close(vaapiPipe);
}
if (vaapiData) {
g_free((void*)vaapiData);
}
if (vaapiPID) {
int status;
waitpid(vaapiPID, &status, WNOHANG);
}
});
vaapiPID = fire_vaapi_process(mDrmRenderDevice.get(), &vaapiPipe);
if (!vaapiPID) {
return;
}
struct pollfd pfd {};
pfd.fd = vaapiPipe;
pfd.events = POLLIN;
auto ret = poll(&pfd, 1, VAAPI_TEST_TIMEOUT);
if (ret <= 0) {
return;
}
channel = g_io_channel_unix_new(vaapiPipe);
MakeFdNonBlocking(vaapiPID);
GUniquePtr<GError> error;
do {
error = nullptr;
ret = g_io_channel_read_to_end(channel, &vaapiData, &vaapiDataLen,
getter_Transfers(error));
} while (ret == G_IO_STATUS_AGAIN);
if (error) {
return;
}
int vaapi_exit_code = EXIT_FAILURE;
int vaapi_status = 0;
if (waitpid(vaapiPID, &vaapi_status, WNOHANG) < 0) {
vaapiPID = 0;
return;
}
vaapi_exit_code = WEXITSTATUS(vaapi_status);
if (vaapi_exit_code != EXIT_SUCCESS) {
return;
}
char* bufptr = vaapiData;
char* line;
while ((line = NS_strtok("\n", &bufptr))) {
if (!strcmp(line, "VAAPI_SUPPORTED")) {
line = NS_strtok("\n", &bufptr);
if (!line) {
gfxCriticalNote << "vaapitest: Failed to get VAAPI support\n";
return;
}
mIsVAAPISupported = Some(!strcmp(line, "TRUE"));
} else if (!strcmp(line, "VAAPI_HWCODECS")) {
line = NS_strtok("\n", &bufptr);
if (!line) {
gfxCriticalNote << "vaapitest: Failed to get VAAPI codecs\n";
return;
}
int codecs = 0;
std::istringstream(line) >> codecs;
if (codecs & CODEC_HW_H264) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::H264HardwareDecode);
}
if (codecs & CODEC_HW_VP8) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::VP8HardwareDecode);
}
if (codecs & CODEC_HW_VP9) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::VP9HardwareDecode);
}
if (codecs & CODEC_HW_AV1) {
media::MCSInfo::AddSupport(
media::MediaCodecsSupport::AV1HardwareDecode);
}
} else if (!strcmp(line, "WARNING") || !strcmp(line, "ERROR")) {
gfxCriticalNote << "vaapitest: " << line;
line = NS_strtok("\n", &bufptr);
if (line) {
gfxCriticalNote << "vaapitest: " << line << "\n";
}
return;
}
}
}
const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() {
if (!sDriverInfo->Length()) {
// Mesa 10.0 provides the GLX_MESA_query_renderer extension, which allows us
@ -1120,20 +990,15 @@ nsresult GfxInfo::GetFeatureStatusImpl(
}
}
auto ret = GfxInfoBase::GetFeatureStatusImpl(
aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
// Probe VA-API on supported devices only
if (aFeature == nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING &&
*aStatus == nsIGfxInfo::FEATURE_STATUS_OK) {
GetDataVAAPI();
if (!mIsVAAPISupported.value()) {
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_TEST_FAILED";
}
!mIsVAAPISupported) {
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_TEST_FAILED";
return NS_OK;
}
return ret;
return GfxInfoBase::GetFeatureStatusImpl(
aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
}
NS_IMETHODIMP

Просмотреть файл

@ -112,9 +112,8 @@ class GfxInfo final : public GfxInfoBase {
bool mIsXWayland;
bool mHasMultipleGPUs;
bool mGlxTestError;
mozilla::Maybe<bool> mIsVAAPISupported;
bool mIsVAAPISupported;
void GetDataVAAPI();
void AddCrashReportAnnotations();
};

Просмотреть файл

@ -23,7 +23,7 @@ if CONFIG["COMPILE_ENVIRONMENT"]:
DIRS += ["mozgtk"]
if CONFIG["MOZ_WAYLAND"]:
DIRS += ["wayland", "mozwayland", "vaapitest"]
DIRS += ["wayland", "mozwayland"]
EXPORTS += [
"MozContainer.h",

Просмотреть файл

@ -1,25 +0,0 @@
# -*- 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 = ("Toolkit", "Startup and Profile System")
Program("vaapitest")
SOURCES += [
"vaapitest.cpp",
]
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
CXXFLAGS += CONFIG["MOZ_PANGO_CFLAGS"]
OS_LIBS += CONFIG["MOZ_X11_LIBS"]
OS_LIBS += CONFIG["MOZ_GTK3_LIBS"]
if CONFIG["MOZ_WAYLAND"]:
USE_LIBS += [
"mozva",
]
LOCAL_INCLUDES += [
"/media/mozva",
]

Просмотреть файл

@ -1,312 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#include <cstdio>
#include <cstdlib>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdint.h>
#if defined(MOZ_ASAN) || defined(FUZZING)
# include <signal.h>
#endif
#include "mozilla/ScopeExit.h"
#ifdef __SUNPRO_CC
# include <stdio.h>
#endif
#include "mozilla/widget/mozwayland.h"
#include "prlink.h"
#include "va/va.h"
// Print VA-API test results to stdout and logging to stderr
#define OUTPUT_PIPE 1
#define LOG_PIPE 2
#define EXIT_FAILURE_BUFFER_TOO_SMALL 2
// bits to use decoding childvaapitest() return values.
constexpr int CODEC_HW_H264 = 1 << 4;
constexpr int CODEC_HW_VP8 = 1 << 5;
constexpr int CODEC_HW_VP9 = 1 << 6;
constexpr int CODEC_HW_AV1 = 1 << 7;
// our buffer, size and used length
static char* vaapi_test_buf = nullptr;
static int vaapi_test_bufsize = 0;
static int vaapitest_length = 0;
// 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/
template <typename func_ptr_type>
static func_ptr_type cast(void* ptr) {
return reinterpret_cast<func_ptr_type>(reinterpret_cast<size_t>(ptr));
}
static void record_value(const char* format, ...) {
// Don't add more if the buffer is full.
if (vaapi_test_bufsize <= vaapitest_length) {
return;
}
// Append the new values to the buffer, not to exceed the remaining space.
int remaining = vaapi_test_bufsize - vaapitest_length;
va_list args;
va_start(args, format);
int max_added =
vsnprintf(vaapi_test_buf + vaapitest_length, remaining, format, args);
va_end(args);
// snprintf returns how many char it could have added, not how many it added.
// It is important to get this right since it will control how many chars we
// will attempt to write to the pipe fd.
if (max_added > remaining) {
vaapitest_length += remaining;
} else {
vaapitest_length += max_added;
}
}
static void record_error(const char* str) { record_value("ERROR\n%s\n", str); }
static void record_flush() {
// Write output to stdout
(void)write(OUTPUT_PIPE, vaapi_test_buf, vaapitest_length);
if (getenv("MOZ_GFX_DEBUG")) {
(void)write(LOG_PIPE, vaapi_test_buf, vaapitest_length);
}
}
// childgltest is declared inside extern "C" so that the name is not mangled.
// The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
// memory leak errors because we run it inside a short lived fork and we don't
// care about leaking memory
extern "C" {
static constexpr struct {
VAProfile mVAProfile;
const char* mName;
} kVAAPiProfileName[] = {
#define MAP(v) \
{ VAProfile##v, #v }
MAP(H264ConstrainedBaseline),
MAP(H264Main),
MAP(H264High),
MAP(VP8Version0_3),
MAP(VP9Profile0),
MAP(VP9Profile2),
MAP(AV1Profile0),
MAP(AV1Profile1),
#undef MAP
};
static const char* VAProfileName(VAProfile aVAProfile) {
for (const auto& profile : kVAAPiProfileName) {
if (profile.mVAProfile == aVAProfile) {
return profile.mName;
}
}
return nullptr;
}
void childvaapitest(const char* aRenderDevicePath) {
int renderDeviceFD = -1;
VAProfile* profiles = nullptr;
VAEntrypoint* entryPoints = nullptr;
VADisplay display = nullptr;
void* libDrm = nullptr;
auto autoRelease = mozilla::MakeScopeExit([&] {
if (renderDeviceFD > -1) {
close(renderDeviceFD);
}
free(profiles);
free(entryPoints);
if (display) {
vaTerminate(display);
}
if (libDrm) {
dlclose(libDrm);
}
});
renderDeviceFD = open(aRenderDevicePath, O_RDWR);
if (renderDeviceFD == -1) {
record_error("VA-API test failed: failed to open renderDeviceFD.");
return;
}
libDrm = dlopen("libva-drm.so.2", RTLD_LAZY);
if (!libDrm) {
record_error("VA-API test failed: libva-drm.so.2 is missing.");
return;
}
static auto sVaGetDisplayDRM =
(void* (*)(int fd))dlsym(libDrm, "vaGetDisplayDRM");
if (!sVaGetDisplayDRM) {
record_error("VA-API test failed: sVaGetDisplayDRM is missing.");
return;
}
display = sVaGetDisplayDRM(renderDeviceFD);
if (!display) {
record_error("VA-API test failed: sVaGetDisplayDRM failed.");
return;
}
int major, minor;
VAStatus status = vaInitialize(display, &major, &minor);
if (status != VA_STATUS_SUCCESS) {
fprintf(stderr, "vaInitialize failed %d\n", status);
record_error("VA-API test failed: failed to initialise VAAPI connection.");
return;
} else {
fprintf(stderr, "vaInitialize finished\n");
}
int maxProfiles = vaMaxNumProfiles(display);
int maxEntryPoints = vaMaxNumEntrypoints(display);
if (maxProfiles <= 0 || maxEntryPoints <= 0) {
record_error("VA-API test failed: wrong VAAPI profiles/entry point nums.");
return;
}
profiles = (VAProfile*)malloc(sizeof(VAProfile) * maxProfiles);
int numProfiles = 0;
status = vaQueryConfigProfiles(display, profiles, &numProfiles);
if (status != VA_STATUS_SUCCESS) {
record_error("VA-API test failed: vaQueryConfigProfiles() failed.");
return;
}
numProfiles = std::min(numProfiles, maxProfiles);
entryPoints = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * maxEntryPoints);
int codecs = 0;
bool foundProfile = false;
for (int p = 0; p < numProfiles; p++) {
VAProfile profile = profiles[p];
// Check only supported profiles
if (!VAProfileName(profile)) {
continue;
}
int numEntryPoints = 0;
status = vaQueryConfigEntrypoints(display, profile, entryPoints,
&numEntryPoints);
if (status != VA_STATUS_SUCCESS) {
continue;
}
numEntryPoints = std::min(numEntryPoints, maxEntryPoints);
for (int entry = 0; entry < numEntryPoints; entry++) {
if (entryPoints[entry] != VAEntrypointVLD) {
continue;
}
VAConfigID config = VA_INVALID_ID;
status = vaCreateConfig(display, profile, entryPoints[entry], nullptr, 0,
&config);
if (status == VA_STATUS_SUCCESS) {
const char* profstr = VAProfileName(profile);
// VAProfileName returns null on failure, making the below calls safe
if (!strncmp(profstr, "H264", 4)) {
codecs |= CODEC_HW_H264;
} else if (!strncmp(profstr, "VP8", 3)) {
codecs |= CODEC_HW_VP8;
} else if (!strncmp(profstr, "VP9", 3)) {
codecs |= CODEC_HW_VP9;
} else if (!strncmp(profstr, "AV1", 3)) {
codecs |= CODEC_HW_AV1;
} else {
record_value("WARNING\nVA-API test unknown profile: %s\n", profstr);
}
vaDestroyConfig(display, config);
foundProfile = true;
}
}
}
if (foundProfile) {
record_value("VAAPI_SUPPORTED\nTRUE\n");
record_value("VAAPI_HWCODECS\n%d\n", codecs);
} else {
record_value("VAAPI_SUPPORTED\nFALSE\n");
}
}
int vaapitest(const char* aRenderDevicePath) {
enum { bufsize = 2048 };
char buf[bufsize];
// We save it as a global so that the X error handler can flush the buffer
// before early exiting.
vaapi_test_buf = buf;
vaapi_test_bufsize = bufsize;
childvaapitest(aRenderDevicePath);
// Finally write buffered data to the pipe.
record_flush();
// If we completely filled the buffer, we need to tell the parent.
if (vaapitest_length >= vaapi_test_bufsize) {
return EXIT_FAILURE_BUFFER_TOO_SMALL;
}
return EXIT_SUCCESS;
}
} // extern "C"
static void PrintUsage() {
printf(
"Firefox VA-API probe utility\n"
"\n"
"usage: vaapitest [options]\n"
"\n"
"Options:\n"
"\n"
" -h --help show this message\n"
" -d --drm drm_device probe VA-API on drm_device (may be "
"/dev/dri/renderD128)\n"
"\n");
}
int main(int argc, char** argv) {
struct option longOptions[] = {{"help", no_argument, NULL, 'h'},
{"drm", required_argument, NULL, 'd'},
{NULL, 0, NULL, 0}};
const char* shortOptions = "hd:";
int c;
const char* drmDevice = nullptr;
while ((c = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
switch (c) {
case 'd':
drmDevice = optarg;
break;
case 'h':
default:
break;
}
}
if (drmDevice) {
#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 vaapitest(drmDevice);
}
PrintUsage();
return 0;
}