Coverage Report

Created: 2018-09-25 14:53

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