LCOV - code coverage report
Current view: top level - vcl/opengl/x11 - X11DeviceInfo.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 109 149 73.2 %
Date: 2015-06-13 12:38:46 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : 
      10             : #include "opengl/x11/X11DeviceInfo.hxx"
      11             : 
      12             : #include <vcl/opengl/glxtest.hxx>
      13             : #include <rtl/ustring.hxx>
      14             : 
      15             : #include <unistd.h>
      16             : #include <sys/types.h>
      17             : #include <sys/wait.h>
      18             : #include <errno.h>
      19             : #include <sys/utsname.h>
      20             : 
      21             : namespace glx {
      22             : 
      23             : static int glxtest_pipe = 0;
      24             : 
      25             : static pid_t glxtest_pid = 0;
      26             : 
      27             : }
      28             : 
      29         127 : pid_t* getGlxPid()
      30             : {
      31         127 :     return &glx::glxtest_pid;
      32             : }
      33             : 
      34         127 : int* getGlxPipe()
      35             : {
      36         127 :     return &glx::glxtest_pipe;
      37             : }
      38             : 
      39             : namespace {
      40             : 
      41             : const char*
      42          88 : strspnp_wrapper(const char* aDelims, const char* aStr)
      43             : {
      44             :   const char* d;
      45          88 :   do
      46             :   {
      47         176 :       for (d = aDelims; *d != '\0'; ++d)
      48             :       {
      49          88 :           if (*aStr == *d)
      50             :           {
      51           0 :               ++aStr;
      52           0 :               break;
      53             :           }
      54             :       }
      55             :   } while (*d);
      56             : 
      57          88 :   return aStr;
      58             : }
      59             : 
      60          96 : char* strtok_wrapper(const char* aDelims, char** aStr)
      61             : {
      62          96 :     if (!*aStr)
      63             :     {
      64           8 :         return nullptr;
      65             :     }
      66             : 
      67          88 :     char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
      68             : 
      69          88 :     if (!*ret)
      70             :     {
      71           8 :         *aStr = ret;
      72           8 :         return nullptr;
      73             :     }
      74             : 
      75          80 :     char* i = ret;
      76         792 :     do
      77             :     {
      78        1656 :         for (const char* d = aDelims; *d != '\0'; ++d)
      79             :         {
      80         864 :             if (*i == *d) {
      81          72 :                 *i = '\0';
      82          72 :                 *aStr = ++i;
      83          72 :                 return ret;
      84             :             }
      85             :         }
      86         792 :         ++i;
      87             :     } while (*i);
      88             : 
      89           8 :     *aStr = nullptr;
      90           8 :     return ret;
      91             : }
      92             : 
      93          16 : uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
      94             : {
      95          16 :     return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
      96             : }
      97             : 
      98             : }
      99             : 
     100          12 : X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
     101             :     mbIsMesa(false),
     102             :     mbIsNVIDIA(false),
     103             :     mbIsFGLRX(false),
     104             :     mbIsNouveau(false),
     105             :     mbIsIntel(false),
     106             :     mbIsOldSwrast(false),
     107             :     mbIsLlvmpipe(false),
     108             :     mbHasTextureFromPixmap(false),
     109             :     mnGLMajorVersion(0),
     110             :     mnMajorVersion(0),
     111             :     mnMinorVersion(0),
     112          12 :     mnRevisionVersion(0)
     113             : {
     114          12 :     GetData();
     115          12 : }
     116             : 
     117          12 : X11OpenGLDeviceInfo::~X11OpenGLDeviceInfo()
     118             : {
     119          12 : }
     120             : 
     121          12 : void X11OpenGLDeviceInfo::GetData()
     122             : {
     123          12 :     if (!glx::glxtest_pipe)
     124          16 :        return;
     125             : 
     126             :     // to understand this function, see bug moz#639842. We retrieve the OpenGL driver information in a
     127             :     // separate process to protect against bad drivers.
     128             :     enum { buf_size = 1024 };
     129             :     char buf[buf_size];
     130             :     ssize_t bytesread = read(glx::glxtest_pipe,
     131             :             &buf,
     132           8 :             buf_size-1); // -1 because we'll append a zero
     133           8 :     close(glx::glxtest_pipe);
     134           8 :     glx::glxtest_pipe = 0;
     135             : 
     136             :     // bytesread < 0 would mean that the above read() call failed.
     137             :     // This should never happen. If it did, the outcome would be to blacklist anyway.
     138           8 :     if (bytesread < 0)
     139           0 :         bytesread = 0;
     140             : 
     141             :     // let buf be a zero-terminated string
     142           8 :     buf[bytesread] = 0;
     143             : 
     144             :     // Wait for the glxtest process to finish. This serves 2 purposes:
     145             :     // * avoid having a zombie glxtest process laying around
     146             :     // * get the glxtest process status info.
     147           8 :     int glxtest_status = 0;
     148           8 :     bool wait_for_glxtest_process = true;
     149           8 :     bool waiting_for_glxtest_process_failed = false;
     150           8 :     int waitpid_errno = 0;
     151          24 :     while(wait_for_glxtest_process)
     152             :     {
     153           8 :         wait_for_glxtest_process = false;
     154           8 :         if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1)
     155             :         {
     156           0 :             waitpid_errno = errno;
     157           0 :             if (waitpid_errno == EINTR)
     158             :             {
     159           0 :                 wait_for_glxtest_process = true;
     160             :             }
     161             :             else
     162             :             {
     163             :                 // Bug moz#718629
     164             :                 // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
     165             :                 // as per bug moz#227246. This shouldn't matter, as we still seem to get the data
     166             :                 // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
     167           0 :                 waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
     168             :             }
     169             :         }
     170             :     }
     171             : 
     172          16 :     bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
     173          16 :         WIFEXITED(glxtest_status) &&
     174          16 :         WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
     175          16 :     bool received_signal = !waiting_for_glxtest_process_failed &&
     176          16 :         WIFSIGNALED(glxtest_status);
     177             : 
     178           8 :     bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
     179             : 
     180           8 :     OString textureFromPixmap;
     181           8 :     OString *stringToFill = nullptr;
     182           8 :     char *bufptr = buf;
     183           8 :     if (!error)
     184             :     {
     185             :         while(true)
     186             :         {
     187          72 :             char *line = strtok_wrapper("\n", &bufptr);
     188          72 :             if (!line)
     189           8 :                 break;
     190          64 :             if (stringToFill) {
     191          32 :                 *stringToFill = OString(line);
     192          32 :                 stringToFill = nullptr;
     193             :             }
     194          32 :             else if(!strcmp(line, "VENDOR"))
     195           8 :                 stringToFill = &maVendor;
     196          24 :             else if(!strcmp(line, "RENDERER"))
     197           8 :                 stringToFill = &maRenderer;
     198          16 :             else if(!strcmp(line, "VERSION"))
     199           8 :                 stringToFill = &maVersion;
     200           8 :             else if(!strcmp(line, "TFP"))
     201           8 :                 stringToFill = &textureFromPixmap;
     202          64 :         }
     203             :     }
     204             : 
     205           8 :     if (!strcmp(textureFromPixmap.getStr(), "TRUE"))
     206           8 :         mbHasTextureFromPixmap = true;
     207             : 
     208             :     // only useful for Linux kernel version check for FGLRX driver.
     209             :     // assumes X client == X server, which is sad.
     210             :     struct utsname unameobj;
     211           8 :     if (!uname(&unameobj))
     212             :     {
     213           8 :         maOS = OString(unameobj.sysname);
     214           8 :         maOSRelease = OString(unameobj.release);
     215             :     }
     216             : 
     217             :     // determine the major OpenGL version. That's the first integer in the version string.
     218           8 :     mnGLMajorVersion = strtol(maVersion.getStr(), 0, 10);
     219             : 
     220             :     // determine driver type (vendor) and where in the version string
     221             :     // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
     222           8 :     const char *whereToReadVersionNumbers = nullptr;
     223           8 :     const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
     224           8 :     if (Mesa_in_version_string)
     225             :     {
     226           0 :         mbIsMesa = true;
     227             :         // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
     228             :         // there is no actual driver version info.
     229           0 :         whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
     230           0 :         if (strcasestr(maVendor.getStr(), "nouveau"))
     231           0 :             mbIsNouveau = true;
     232           0 :         if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
     233           0 :             mbIsIntel = true;
     234           0 :         if (strcasestr(maRenderer.getStr(), "llvmpipe"))
     235           0 :             mbIsLlvmpipe = true;
     236           0 :         if (strcasestr(maRenderer.getStr(), "software rasterizer"))
     237           0 :             mbIsOldSwrast = true;
     238             :     }
     239           8 :     else if (strstr(maVendor.getStr(), "NVIDIA Corporation"))
     240             :     {
     241           8 :         mbIsNVIDIA = true;
     242             :         // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
     243             :         // note that here the vendor and version strings behave differently, that's why we don't put this above
     244             :         // alongside Mesa_in_version_string.
     245           8 :         const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
     246           8 :         if (NVIDIA_in_version_string)
     247           8 :             whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
     248             :     }
     249           0 :     else if (strstr(maVendor.getStr(), "ATI Technologies Inc"))
     250             :     {
     251           0 :         mbIsFGLRX = true;
     252             :         // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
     253             :         // that can at least give a rough idea of how old the driver is.
     254           0 :         whereToReadVersionNumbers = maVersion.getStr();
     255             :     }
     256             : 
     257             :     // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
     258           8 :     if (whereToReadVersionNumbers)
     259             :     {
     260             :         // copy into writable buffer, for tokenization
     261           8 :         strncpy(buf, whereToReadVersionNumbers, buf_size-1);
     262           8 :         buf[buf_size-1] = 0;
     263           8 :         bufptr = buf;
     264             : 
     265             :         // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
     266             :         // been initialized as 0 anyways
     267           8 :         char *token = strtok_wrapper(".", &bufptr);
     268           8 :         if (token)
     269             :         {
     270           8 :             mnMajorVersion = strtol(token, 0, 10);
     271           8 :             token = strtok_wrapper(".", &bufptr);
     272           8 :             if (token)
     273             :             {
     274           8 :                 mnMinorVersion = strtol(token, 0, 10);
     275           8 :                 token = strtok_wrapper(".", &bufptr);
     276           8 :                 if (token)
     277           0 :                     mnRevisionVersion = strtol(token, 0, 10);
     278             :             }
     279             :         }
     280           8 :     }
     281             : }
     282             : 
     283          12 : bool X11OpenGLDeviceInfo::isDeviceBlocked()
     284             : {
     285             :     // don't even try to use OpenGL 1.x
     286          12 :     if (mnGLMajorVersion == 1)
     287           0 :         return true;
     288             : 
     289             :     SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
     290             :     SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
     291             :     SAL_INFO("vcl.opengl", "Version: " << maVersion);
     292             :     SAL_INFO("vcl.opengl", "OS: " << maOS);
     293             :     SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
     294             : 
     295          12 :     if (mbIsMesa)
     296             :     {
     297           0 :         if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0))
     298             :         {
     299             :             SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
     300           0 :             return true;
     301             :         }
     302           0 :         else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3))
     303             :         {
     304             :             SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
     305           0 :             return true;
     306             :         }
     307           0 :         else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
     308             :         {
     309             :             SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
     310           0 :             return true;
     311             :         }
     312           0 :         else if (mbIsOldSwrast)
     313             :         {
     314             :             SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
     315           0 :             return true;
     316             :         }
     317           0 :         else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1))
     318             :         {
     319             :             // bug moz#791905, Mesa bug 57733, fixed in Mesa 9.1 according to
     320             :             // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
     321             :             SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
     322           0 :             return true;
     323             :         }
     324             :     }
     325          12 :     else if (mbIsNVIDIA)
     326             :     {
     327           8 :         if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21))
     328             :         {
     329             :             SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
     330           0 :             return true;
     331             :         }
     332             :     }
     333           4 :     else if (mbIsFGLRX)
     334             :     {
     335             :         // FGLRX does not report a driver version number, so we have the OpenGL version instead.
     336             :         // by requiring OpenGL 3, we effectively require recent drivers.
     337           0 :         if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0))
     338             :         {
     339             :             SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
     340           0 :             return true;
     341             :         }
     342             :         // Bug moz#724640: FGLRX + Linux 2.6.32 is a crashy combo
     343           0 :         bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
     344           0 :         bool badOS = maOS.indexOf("Linux") != -1 &&
     345           0 :             maOSRelease.indexOf("2.6.32") != -1;
     346           0 :         if (unknownOS || badOS)
     347             :         {
     348             :             SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
     349           0 :             return true;
     350             :         }
     351             :     }
     352             :     else
     353             :     {
     354             :         // like on windows, let's block unknown vendors. Think of virtual machines.
     355             :         // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
     356             :         SAL_WARN("vcl.opengl", "unknown vendor => blocked");
     357           4 :         return true;
     358             :     }
     359             : 
     360           8 :     return false;
     361             : }
     362             : 
     363             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11