Continued adventures in application troubleshooting aka using strace
in the most brute force sort of way.
Have you ever had one of those problems where you know the payoff isn’t worth the work, but you can’t step away because you have to solve it? That is where I am at right now. If you read my earlier post on troubleshooting, you know that I am working on getting a itch.io game to work in Linux. In that post, I fixed a few missing libraries, but then I hit a dead end. Something in my setup still wasn’t quite right.
As I also noted in that post, this is not the only way to approach solving this issue. It may not even be the best, but it is a way. My real goal here is to convince you that you shouldn’t fear the potential complexity of these tools or even your own lack of knowledge. Get in there and play around. It is the best way to learn.
So, to start, let’s set the system context. I am running run Fedora 28 on an AMD Ryzen 1700X with an Nvidia GTX 1080.
The logs reported thus:
Unable to find a supported OpenGL core profile
Failed to create valid graphics context: please ensure you meet the minimum requirements
E.g. OpenGL core profile 3.2 or later for OpenGL Core renderer
Vulkan detection: 0
No supported renderers found, exiting
Now, this is a bit surprising as I thought I had installed all the OpenGL libraries I needed. Indeed, I double checked that mesa-libGL and its corollaries were installed in both 32bit and 64bit versions. They indeed were.
I then ran ldconfig -p | grep GL
to confirm that the OpenGL libraries were there.
$ ldconfig -p | grep GL
libQt5OpenGL.so.5 (libc6,x86-64) => /lib64/libQt5OpenGL.so.5
libQtOpenGL.so.4 (libc6,x86-64) => /lib64/libQtOpenGL.so.4
libOpenGL.so.0 (libc6,x86-64) => /lib64/libOpenGL.so.0
libGLdispatch.so.0 (libc6,x86-64) => /lib64/libGLdispatch.so.0
libGLdispatch.so.0 (libc6) => /lib/libGLdispatch.so.0
libGLX_nvidia.so.0 (libc6,x86-64) => /lib64/libGLX_nvidia.so.0
libGLX_mesa.so.0 (libc6,x86-64) => /lib64/libGLX_mesa.so.0
libGLX_mesa.so.0 (libc6) => /lib/libGLX_mesa.so.0
libGLX.so.0 (libc6,x86-64) => /lib64/libGLX.so.0
libGLX.so.0 (libc6) => /lib/libGLX.so.0
libGLU.so.1 (libc6,x86-64) => /lib64/libGLU.so.1
libGLU.so.1 (libc6) => /lib/libGLU.so.1
libGLEW.so.2.0 (libc6,x86-64) => /lib64/libGLEW.so.2.0
libGLESv2_nvidia.so.2 (libc6,x86-64) => /lib64/libGLESv2_nvidia.so.2
libGLESv2.so.2 (libc6,x86-64) => /lib64/libGLESv2.so.2
libGLESv1_CM_nvidia.so.1 (libc6,x86-64) => /lib64/libGLESv1_CM_nvidia.so.1
libGLESv1_CM.so.1 (libc6,x86-64) => /lib64/libGLESv1_CM.so.1
libGL.so.1 (libc6,x86-64) => /lib64/libGL.so.1
libGL.so.1 (libc6) => /lib/libGL.so.1
libEGL_nvidia.so.0 (libc6,x86-64) => /lib64/libEGL_nvidia.so.0
libEGL_mesa.so.0 (libc6,x86-64) => /lib64/libEGL_mesa.so.0
libEGL.so.1 (libc6,x86-64) => /lib64/libEGL.so.1
I know, now, that there is a problem in this list . At the time, though, it looked good.
So, this is where I found myself. No error message save a brief log file comment on OpenGL and nothing else.
Do I give up? Nope.
Is this a good way to spend my (valuable?) time? Probably not.
Am I going to dig deeper into this problem, anyway? You got it.
It’s time for strace
. Now, this is not an strace
tutorial . I am not going to show you all the cool things it can do. I am going to show how I used it in a very brute force sort of way to find out what was going wrong.
The strace
tool allows us to see what an application is doing as it runs. We can see how it interacts with the system (what system calls it makes) and where it may run into to problems. This makes it a handy little tool, but it also means that it dumps a lot of information at you. In this case, I just want to know what happens with the OpenGL libraries. As you can see from the ldconfig
output, not all OpenGL libraries have OpenGL in their name, but they do (usually) have GL somewhere in their name. So let’s focus our attention on lines with GL in them. Will I still get some extraneous line? Probably, but I can sort through that.
strace ./"I woke up.x86" 2>&1 | grep GL
This command launches and traces the I Woke Up game as it runs and inevitably crashes. By searching only for those transactions with a GL, I can better track the problem I am looking.
A momentary aside
Now, you might be asking what does 2>&1
mean? Good question. In your terminal, output can be sent via two different file handles: stdout
aka 1 and stderr
aka 2. Think of these like channels. Your terminal is tuned in to both channels, so you see both stdout
and stderr
in your terminal. Terminal applications, however, are often tuned to stdout
and ignore stderr
. This is usually a good thing except when you are troubleshooting. The strace
application sends its output to stderr
which means you can see it in the terminal but grep
won’t. The 2>&1
tells the program to redirect all of the channel 2 output (stderr
) to channel 1 (stdout
). This will allow grep to find only those lines you care about.
Here is my output. Notice anything interesting (HINT: see all the -1 ENOENT (No such file or directory) lines? Those might be a problem.)
$ strace ./"I woke up.x86" 2>&1 | grep GL
openat(AT_FDCWD, "/lib/libGL.so.1", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/lib/libGLX.so.0", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/lib/libGLdispatch.so.0", O_RDONLY|O_CLOEXEC) = 4
writev(3, [{iov_base="b0\3\0\3\0\1\0", iov_len=8}, {iov_base="GLX", iov_len=3}, {iov_base="\0", iov_len=1}], 3) = 12
writev(3, [{iov_base="b0\3\0\3\0\1\0", iov_len=8}, {iov_base="GLX", iov_len=3}, {iov_base="\0", iov_len=1}], 3) = 12
openat(AT_FDCWD, "/lib/tls/i686/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/i686/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i686/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i686/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/i686/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/i686/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/i686/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/i686/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/sse2/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/libGLX_nvidia.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/sse2/libGLX_indirect.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/libGLX_indirect.so.0", O_RDONLY|O_CLOEXEC) = 4
writev(3, [{iov_base="b\23\3\0\3\0\0\0", iov_len=8}, {iov_base="GLX", iov_len=3}, {iov_base="\0", iov_len=1}], 3) = 12
writev(3, [{iov_base="b\0\3\0\3\0\0\0GLX\0", iov_len=12}], 1) = 12
write(2, "libGL error: ", 13libGL error: ) = 13
write(2, "libGL error: ", 13libGL error: ) = 13
read(5, " /usr/lib/libGLX_me"..., 1024) = 1024
writev(3, [{iov_base="b\25\3\0\3\0\0\0", iov_len=8}, {iov_base="GLX", iov_len=3}, {iov_base="\0", iov_len=1}], 3) = 12
Apparently, I am missing the 32bit libraries for the Nvidia driver. Now, Fedora doesn’t include the Nvidia driver because the Nvidia driver remains closed-source. This means you will need to load your libraries from whatever repository you installed them from. You can find what repo and package that is by typing sudo dnf whatprovides libGLX_nvidia.so.0
. Make sure you have installed both the 64bit and 32bit libraries. I use Negativo17’s Nvidia Repo and needed to install nvidia-driver-libs.i686
.
Once you have installed, you can confirm that the libraries are in place by running lddconfig
.
$ dconfig -p | grep GL
libQt5OpenGL.so.5 (libc6,x86-64) => /lib64/libQt5OpenGL.so.5
libQtOpenGL.so.4 (libc6,x86-64) => /lib64/libQtOpenGL.so.4
libOpenGL.so.0 (libc6,x86-64) => /lib64/libOpenGL.so.0
libOpenGL.so.0 (libc6) => /lib/libOpenGL.so.0
libGLdispatch.so.0 (libc6,x86-64) => /lib64/libGLdispatch.so.0
libGLdispatch.so.0 (libc6) => /lib/libGLdispatch.so.0
libGLX_nvidia.so.0 (libc6,x86-64) => /lib64/libGLX_nvidia.so.0
libGLX_nvidia.so.0 (libc6) => /lib/libGLX_nvidia.so.0
libGLX_mesa.so.0 (libc6,x86-64) => /lib64/libGLX_mesa.so.0
libGLX_mesa.so.0 (libc6) => /lib/libGLX_mesa.so.0
libGLX.so.0 (libc6,x86-64) => /lib64/libGLX.so.0
libGLX.so.0 (libc6) => /lib/libGLX.so.0
libGLU.so.1 (libc6,x86-64) => /lib64/libGLU.so.1
libGLU.so.1 (libc6) => /lib/libGLU.so.1
libGLEW.so.2.0 (libc6,x86-64) => /lib64/libGLEW.so.2.0
libGLESv2_nvidia.so.2 (libc6,x86-64) => /lib64/libGLESv2_nvidia.so.2
libGLESv2_nvidia.so.2 (libc6) => /lib/libGLESv2_nvidia.so.2
libGLESv2.so.2 (libc6,x86-64) => /lib64/libGLESv2.so.2
libGLESv2.so.2 (libc6) => /lib/libGLESv2.so.2
libGLESv1_CM_nvidia.so.1 (libc6,x86-64) => /lib64/libGLESv1_CM_nvidia.so.1
libGLESv1_CM_nvidia.so.1 (libc6) => /lib/libGLESv1_CM_nvidia.so.1
libGLESv1_CM.so.1 (libc6,x86-64) => /lib64/libGLESv1_CM.so.1
libGLESv1_CM.so.1 (libc6) => /lib/libGLESv1_CM.so.1
libGL.so.1 (libc6,x86-64) => /lib64/libGL.so.1
libGL.so.1 (libc6) => /lib/libGL.so.1
libEGL_nvidia.so.0 (libc6,x86-64) => /lib64/libEGL_nvidia.so.0
libEGL_nvidia.so.0 (libc6) => /lib/libEGL_nvidia.so.0
libEGL_mesa.so.0 (libc6,x86-64) => /lib64/libEGL_mesa.so.0
libEGL_mesa.so.0 (libc6) => /lib/libEGL_mesa.so.0
libEGL.so.1 (libc6,x86-64) => /lib64/libEGL.so.1
libEGL.so.1 (libc6) => /lib/libEGL.so.1
Notice how the nvidia libraries now have (libc,x86-64) and (libc6) entries? Things are looking a lot better now.
The real proof is in the running, though. Sure enough, the game launches and runs without error. Result!!
Was this a lot of effort for a game? Yes, it was. At a certain point, though, the game wasn’t the point. We should understand the tools we have and the systems we use everyday. Black boxes are inevitable. We cannot live in a non-black-boxed world. We’d get nothing done. If we accept that everything is a black box, however, we cede control. When this comes to computers, this means we cede control over the very tools we use for communication and expression. This isn’t a call to code. Nothing I did here involved you writing in a programming language. Rather, this is an invitation to take back that really cool hunk of plastic and metal that sits on you desk or in your lap. Computers can be awesome. More importantly, their users are awesome. Learning to take control of that machine just gives you more ways of proving your excellence.