toolkit/xre/glxtest.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim: sw=2 ts=8 et :
michael@0 3 */
michael@0 4 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 7
michael@0 8
michael@0 9 //////////////////////////////////////////////////////////////////////////////
michael@0 10 //
michael@0 11 // Explanation: See bug 639842. Safely getting GL driver info on X11 is hard, because the only way to do
michael@0 12 // that is to create a GL context and call glGetString(), but with bad drivers,
michael@0 13 // just creating a GL context may crash.
michael@0 14 //
michael@0 15 // This file implements the idea to do that in a separate process.
michael@0 16 //
michael@0 17 // The only non-static function here is fire_glxtest_process(). It creates a pipe, publishes its 'read' end as the
michael@0 18 // mozilla::widget::glxtest_pipe global variable, forks, and runs that GLX probe in the child process,
michael@0 19 // which runs the glxtest() static function. This creates a X connection, a GLX context, calls glGetString, and writes that
michael@0 20 // to the 'write' end of the pipe.
michael@0 21
michael@0 22 #include <cstdio>
michael@0 23 #include <cstdlib>
michael@0 24 #include <unistd.h>
michael@0 25 #include <dlfcn.h>
michael@0 26 #include "nscore.h"
michael@0 27 #include <fcntl.h>
michael@0 28 #include "stdint.h"
michael@0 29
michael@0 30 #ifdef __SUNPRO_CC
michael@0 31 #include <stdio.h>
michael@0 32 #endif
michael@0 33
michael@0 34 #include "X11/Xlib.h"
michael@0 35 #include "X11/Xutil.h"
michael@0 36
michael@0 37 // stuff from glx.h
michael@0 38 typedef struct __GLXcontextRec *GLXContext;
michael@0 39 typedef XID GLXPixmap;
michael@0 40 typedef XID GLXDrawable;
michael@0 41 /* GLX 1.3 and later */
michael@0 42 typedef struct __GLXFBConfigRec *GLXFBConfig;
michael@0 43 typedef XID GLXFBConfigID;
michael@0 44 typedef XID GLXContextID;
michael@0 45 typedef XID GLXWindow;
michael@0 46 typedef XID GLXPbuffer;
michael@0 47 #define GLX_RGBA 4
michael@0 48 #define GLX_RED_SIZE 8
michael@0 49 #define GLX_GREEN_SIZE 9
michael@0 50 #define GLX_BLUE_SIZE 10
michael@0 51
michael@0 52 // stuff from gl.h
michael@0 53 typedef uint8_t GLubyte;
michael@0 54 typedef uint32_t GLenum;
michael@0 55 #define GL_VENDOR 0x1F00
michael@0 56 #define GL_RENDERER 0x1F01
michael@0 57 #define GL_VERSION 0x1F02
michael@0 58
michael@0 59 namespace mozilla {
michael@0 60 namespace widget {
michael@0 61 // the read end of the pipe, which will be used by GfxInfo
michael@0 62 extern int glxtest_pipe;
michael@0 63 // the PID of the glxtest process, to pass to waitpid()
michael@0 64 extern pid_t glxtest_pid;
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 // the write end of the pipe, which we're going to write to
michael@0 69 static int write_end_of_the_pipe = -1;
michael@0 70
michael@0 71 // C++ standard collides with C standard in that it doesn't allow casting void* to function pointer types.
michael@0 72 // So the work-around is to convert first to size_t.
michael@0 73 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
michael@0 74 template<typename func_ptr_type>
michael@0 75 static func_ptr_type cast(void *ptr)
michael@0 76 {
michael@0 77 return reinterpret_cast<func_ptr_type>(
michael@0 78 reinterpret_cast<size_t>(ptr)
michael@0 79 );
michael@0 80 }
michael@0 81
michael@0 82 static void fatal_error(const char *str)
michael@0 83 {
michael@0 84 write(write_end_of_the_pipe, str, strlen(str));
michael@0 85 write(write_end_of_the_pipe, "\n", 1);
michael@0 86 _exit(EXIT_FAILURE);
michael@0 87 }
michael@0 88
michael@0 89 static int
michael@0 90 x_error_handler(Display *, XErrorEvent *ev)
michael@0 91 {
michael@0 92 enum { bufsize = 1024 };
michael@0 93 char buf[bufsize];
michael@0 94 int length = snprintf(buf, bufsize,
michael@0 95 "X error occurred in GLX probe, error_code=%d, request_code=%d, minor_code=%d\n",
michael@0 96 ev->error_code,
michael@0 97 ev->request_code,
michael@0 98 ev->minor_code);
michael@0 99 write(write_end_of_the_pipe, buf, length);
michael@0 100 _exit(EXIT_FAILURE);
michael@0 101 return 0;
michael@0 102 }
michael@0 103
michael@0 104
michael@0 105 // glxtest is declared inside extern "C" so that the name is not mangled.
michael@0 106 // The name is used in build/valgrind/x86_64-redhat-linux-gnu.sup to suppress
michael@0 107 // memory leak errors because we run it inside a short lived fork and we don't
michael@0 108 // care about leaking memory
michael@0 109 extern "C" {
michael@0 110
michael@0 111 void glxtest()
michael@0 112 {
michael@0 113 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
michael@0 114 // any PR logging file descriptors. To that effect, we redirect all positive
michael@0 115 // file descriptors up to what open() returns here. In particular, 1 is stdout and 2 is stderr.
michael@0 116 int fd = open("/dev/null", O_WRONLY);
michael@0 117 for (int i = 1; i < fd; i++)
michael@0 118 dup2(fd, i);
michael@0 119 close(fd);
michael@0 120
michael@0 121 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER"))
michael@0 122 fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined");
michael@0 123
michael@0 124 ///// Open libGL and load needed symbols /////
michael@0 125 #ifdef __OpenBSD__
michael@0 126 #define LIBGL_FILENAME "libGL.so"
michael@0 127 #else
michael@0 128 #define LIBGL_FILENAME "libGL.so.1"
michael@0 129 #endif
michael@0 130 void *libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
michael@0 131 if (!libgl)
michael@0 132 fatal_error("Unable to load " LIBGL_FILENAME);
michael@0 133
michael@0 134 typedef void* (* PFNGLXGETPROCADDRESS) (const char *);
michael@0 135 PFNGLXGETPROCADDRESS glXGetProcAddress = cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
michael@0 136
michael@0 137 if (!glXGetProcAddress)
michael@0 138 fatal_error("Unable to find glXGetProcAddress in " LIBGL_FILENAME);
michael@0 139
michael@0 140 typedef GLXFBConfig* (* PFNGLXQUERYEXTENSION) (Display *, int *, int *);
michael@0 141 PFNGLXQUERYEXTENSION glXQueryExtension = cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
michael@0 142
michael@0 143 typedef GLXFBConfig* (* PFNGLXQUERYVERSION) (Display *, int *, int *);
michael@0 144 PFNGLXQUERYVERSION glXQueryVersion = cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
michael@0 145
michael@0 146 typedef XVisualInfo* (* PFNGLXCHOOSEVISUAL) (Display *, int, int *);
michael@0 147 PFNGLXCHOOSEVISUAL glXChooseVisual = cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
michael@0 148
michael@0 149 typedef GLXContext (* PFNGLXCREATECONTEXT) (Display *, XVisualInfo *, GLXContext, Bool);
michael@0 150 PFNGLXCREATECONTEXT glXCreateContext = cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
michael@0 151
michael@0 152 typedef Bool (* PFNGLXMAKECURRENT) (Display*, GLXDrawable, GLXContext);
michael@0 153 PFNGLXMAKECURRENT glXMakeCurrent = cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
michael@0 154
michael@0 155 typedef void (* PFNGLXDESTROYCONTEXT) (Display*, GLXContext);
michael@0 156 PFNGLXDESTROYCONTEXT glXDestroyContext = cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
michael@0 157
michael@0 158 typedef GLubyte* (* PFNGLGETSTRING) (GLenum);
michael@0 159 PFNGLGETSTRING glGetString = cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
michael@0 160
michael@0 161 if (!glXQueryExtension ||
michael@0 162 !glXQueryVersion ||
michael@0 163 !glXChooseVisual ||
michael@0 164 !glXCreateContext ||
michael@0 165 !glXMakeCurrent ||
michael@0 166 !glXDestroyContext ||
michael@0 167 !glGetString)
michael@0 168 {
michael@0 169 fatal_error("glXGetProcAddress couldn't find required functions");
michael@0 170 }
michael@0 171 ///// Open a connection to the X server /////
michael@0 172 Display *dpy = XOpenDisplay(nullptr);
michael@0 173 if (!dpy)
michael@0 174 fatal_error("Unable to open a connection to the X server");
michael@0 175
michael@0 176 ///// Check that the GLX extension is present /////
michael@0 177 if (!glXQueryExtension(dpy, nullptr, nullptr))
michael@0 178 fatal_error("GLX extension missing");
michael@0 179
michael@0 180 XSetErrorHandler(x_error_handler);
michael@0 181
michael@0 182 ///// Get a visual /////
michael@0 183 int attribs[] = {
michael@0 184 GLX_RGBA,
michael@0 185 GLX_RED_SIZE, 1,
michael@0 186 GLX_GREEN_SIZE, 1,
michael@0 187 GLX_BLUE_SIZE, 1,
michael@0 188 None };
michael@0 189 XVisualInfo *vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
michael@0 190 if (!vInfo)
michael@0 191 fatal_error("No visuals found");
michael@0 192
michael@0 193 // using a X11 Window instead of a GLXPixmap does not crash
michael@0 194 // fglrx in indirect rendering. bug 680644
michael@0 195 Window window;
michael@0 196 XSetWindowAttributes swa;
michael@0 197 swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
michael@0 198 vInfo->visual, AllocNone);
michael@0 199
michael@0 200 swa.border_pixel = 0;
michael@0 201 window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen),
michael@0 202 0, 0, 16, 16,
michael@0 203 0, vInfo->depth, InputOutput, vInfo->visual,
michael@0 204 CWBorderPixel | CWColormap, &swa);
michael@0 205
michael@0 206 ///// Get a GL context and make it current //////
michael@0 207 GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
michael@0 208 glXMakeCurrent(dpy, window, context);
michael@0 209
michael@0 210 ///// Look for this symbol to determine texture_from_pixmap support /////
michael@0 211 void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
michael@0 212
michael@0 213 ///// Get GL vendor/renderer/versions strings /////
michael@0 214 enum { bufsize = 1024 };
michael@0 215 char buf[bufsize];
michael@0 216 const GLubyte *vendorString = glGetString(GL_VENDOR);
michael@0 217 const GLubyte *rendererString = glGetString(GL_RENDERER);
michael@0 218 const GLubyte *versionString = glGetString(GL_VERSION);
michael@0 219
michael@0 220 if (!vendorString || !rendererString || !versionString)
michael@0 221 fatal_error("glGetString returned null");
michael@0 222
michael@0 223 int length = snprintf(buf, bufsize,
michael@0 224 "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
michael@0 225 vendorString,
michael@0 226 rendererString,
michael@0 227 versionString,
michael@0 228 glXBindTexImageEXT ? "TRUE" : "FALSE");
michael@0 229 if (length >= bufsize)
michael@0 230 fatal_error("GL strings length too large for buffer size");
michael@0 231
michael@0 232 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
michael@0 233 ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
michael@0 234 ///// possible. Also we want to check that we're able to do that too without generating X errors.
michael@0 235 glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
michael@0 236 glXDestroyContext(dpy, context);
michael@0 237 XDestroyWindow(dpy, window);
michael@0 238 XFreeColormap(dpy, swa.colormap);
michael@0 239
michael@0 240 #ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't forget to #include it above
michael@0 241 XCloseDisplay(dpy);
michael@0 242 #else
michael@0 243 // This XSync call wanted to be instead:
michael@0 244 // XCloseDisplay(dpy);
michael@0 245 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug 973192
michael@0 246 XSync(dpy, False);
michael@0 247 #endif
michael@0 248
michael@0 249 dlclose(libgl);
michael@0 250
michael@0 251 ///// Finally write data to the pipe
michael@0 252 write(write_end_of_the_pipe, buf, length);
michael@0 253 }
michael@0 254
michael@0 255 }
michael@0 256
michael@0 257 /** \returns true in the child glxtest process, false in the parent process */
michael@0 258 bool fire_glxtest_process()
michael@0 259 {
michael@0 260 int pfd[2];
michael@0 261 if (pipe(pfd) == -1) {
michael@0 262 perror("pipe");
michael@0 263 return false;
michael@0 264 }
michael@0 265 pid_t pid = fork();
michael@0 266 if (pid < 0) {
michael@0 267 perror("fork");
michael@0 268 close(pfd[0]);
michael@0 269 close(pfd[1]);
michael@0 270 return false;
michael@0 271 }
michael@0 272 // The child exits early to avoid running the full shutdown sequence and avoid conflicting with threads
michael@0 273 // we have already spawned (like the profiler).
michael@0 274 if (pid == 0) {
michael@0 275 close(pfd[0]);
michael@0 276 write_end_of_the_pipe = pfd[1];
michael@0 277 glxtest();
michael@0 278 close(pfd[1]);
michael@0 279 _exit(0);
michael@0 280 }
michael@0 281
michael@0 282 close(pfd[1]);
michael@0 283 mozilla::widget::glxtest_pipe = pfd[0];
michael@0 284 mozilla::widget::glxtest_pid = pid;
michael@0 285 return false;
michael@0 286 }

mercurial