2015년 6월 7일 일요일

[SOLVED] Upgrade from Python 2.7.9 to 2.7.10 breaks Brainworkshop

Note: I originally made this post in June 2015, but in Dec. 2015 I found a workaround to get Brainworkshop 4.8.4/4.8.7 to work with Python 2.7.10+. Please refer to the new post at the following link:
http://eatpeppershothot.blogspot.kr/2015/12/enabling-brainworkshop-487-to-work-with.html

=================================================================
Recently Archlinux made python2-2.7.10-1 available in the main Arch repositories. Unfortunately, however, the Brainworkshop N-back training program doesn't work with the latest version of Python 2.

In Gentoo and Arch, the brainworkshop.pyw source file is launched by the startup script /usr/bin/brainworkshop which looks like the following (I have edited it to correspond to my BW data and config files on Dropbox):



After upgrading from Python 2.7.9 to 2.7.10, however, the following error message is generated:

/usr/bin/brainworkshop: line 8:  5212 Segmentation fault      (core dumped) python2 /usr/share/brainworkshop/brainworkshop.pyw --configfile $BRW_CONFIGFILE --statsfile $BRW_STATFILE --datadir $BRW_DATADIR

The actual Python 2 source file is located at /usr/share/brainworkshop/brainworkshop.pyw (.pyw instead of .py so that a separate console window won't be launched). Even launching brainworkshop.pyw directly without any option flags returns Segmentation fault (core dumped). In Archlinux, core dump files are located in /var/lib/systemd/coredump, but rather than trying to analyze a dump of system memory at the time brainworkshop.pyw crashed, I decided to run gdb to see if I could get more informative error messages through a stack trace. Although gdb is normally run on C/C++ compiled executables, gdb (version 7+) will also work with Python if you invoke it as follows:

[archjun@lenovoS310 ~]$ gdb /usr/bin/python2
GNU gdb (GDB) 7.9.1
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python2...(no debugging symbols found)...done.

Now at the gdb prompt, type run followed by the name of the python program:

(gdb) run /usr/share/brainworkshop/brainworkshop.pyw
Starting program: /usr/bin/python2 /usr/share/brainworkshop/brainworkshop.pyw
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Traceback (most recent call last):
  File "/usr/share/brainworkshop/brainworkshop.pyw", line 1023, in
    window = MyWindow(cfg.WINDOW_WIDTH, cfg.WINDOW_HEIGHT, caption=''.join(caption), style=style, vsync=VSYNC)
  File "/usr/lib/python2.7/site-packages/pyglet/window/xlib/__init__.py", line 163, in __init__
    super(XlibWindow, self).__init__(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/pyglet/window/__init__.py", line 494, in __init__
    display = get_platform().get_default_display()
  File "/usr/lib/python2.7/site-packages/pyglet/window/__init__.py", line 1766, in get_default_display
    return pyglet.canvas.get_display()
  File "/usr/lib/python2.7/site-packages/pyglet/canvas/__init__.py", line 82, in get_display
    return Display()
  File "/usr/lib/python2.7/site-packages/pyglet/canvas/xlib.py", line 83, in __init__
    raise NoSuchDisplayException('Cannot connect to "%s"' % name)
pyglet.canvas.xlib.NoSuchDisplayException: Cannot connect to "None"
[Inferior 1 (process 26382) exited with code 01]

So it seems like Python's pyglet module is the culprit once again in breaking Brainworkshop. In a previous post from February 2015 (pyglet 1.2 breaks Brainworkshop), I described a way to get Brainworkshop working again by making a few edits to the /usr/share/brainworkshop/brainworkshop.pyw source file. I have played around with changing some of the parameters in line 1024

window = MyWindow(cfg.WINDOW_WIDTH, cfg.WINDOW_HEIGHT, caption=''.join(caption), style=style, vsync=VSYNC)

but I have yet to succeed in getting Brainworkshop to work with Python 2.7.10.

Workaround

Luckily, BW still works with Python 2.7.9, so I downgraded to 2.7.9 with pacman -U /var/cache/pacman/pkg/python2-2.7.9-1-x86_64.pkg.tar.xz (if you don't have this package in your pacman package cache, you can still download it from the Arch Linux Archive) and added python2 to IgnorePkg in /etc/pacman.conf as a temporary workaround.

Update 2015-09-07

python2-pyglet in Archlinux was recently upgraded from 1.2.3-1 to 1.2.4-1. Now when trying to run Brainworkshop with Python 2.7.10 I get the following error in brainworkshop.pyw when debugging with gdb:

[archjun@latitude630 ~]$ gdb /usr/bin/python2
GNU gdb (GDB) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python2...(no debugging symbols found)...done.
(gdb) run /usr/share/brainworkshop/brainworkshop.pyw
Starting program: /usr/bin/python2 /usr/share/brainworkshop/brainworkshop.pyw
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffeaef1700 (LWP 8948)]
[Thread 0x7fffeaef1700 (LWP 8948) exited]
[New Thread 0x7fffeaef1700 (LWP 8949)]
[New Thread 0x7fffedd79700 (LWP 8950)]

Program received signal SIGSEGV, Segmentation fault.
0x00007fffde270664 in gdk_pixbuf_loader_write () from /usr/lib/libgdk_pixbuf-2.0.so.0

It looks like we are no longer getting errors from pyglet like we did above with pyglet 1.2.3, but now we are having problems with libgdk_pixbuf-2.0.so.0. Let's take a look at this file:

[archjun@latitude630 ~]$ ls -al /usr/lib/libgdk_pixbuf-2.0.so.0
lrwxrwxrwx 1 root root 29 Aug 20 20:32 /usr/lib/libgdk_pixbuf-2.0.so.0 -> libgdk_pixbuf-2.0.so.0.3100.6

Apparently libgdk_pixbuf-2.0.so.0 is a symlink from the file /usr/lib/libgdk_pixbuf-2.0.so.0.3100.6

What package owns libgdk_pixbuf-2.0.so.0.3100.6?

[archjun@latitude630 lib]$ sudo pacman -Qo libgdk_pixbuf-2.0.so.0.3100.6
[sudo] password for archjun: 
/usr/lib/libgdk_pixbuf-2.0.so.0.3100.6 is owned by gdk-pixbuf2 2.31.6-1

[archjun@latitude630 lib]$ sudo pacman -Ss gdk-pixbuf2
extra/gdk-pixbuf2 2.31.6-1 [installed]
    An image loading library

OK~ so gdk-pixbuf2 loads images.

According to gdb, the function that is causing trouble is called gdk_pixbuf_loader_write (). Looking at the Gnome GDK-PixBuf Reference Manual, we can find more detailed information on the C function gdk_pixbuf_loader_write (), but I'm not familiar with C so I am not sure where to start debugging this issue.

A google search for gdk-pixbuf2 gdk_pixbuf_loader_write () segfault returns an interesting (but dated) google code issues page on github that discusses problems with pyglet decoding images using gdk-pixbuf2. One proposed workaround is to try PIL, pyllow or pypng for image decoding instead of gdk-pixbuf2.

Opening /usr/share/brainworkshop/brainworkshop.pyw with a text editor such as vim or emacs quickly reveals that there are no explicit calls to gdk functions in the Python code. We know that pyglet can use gdk-pixbuf2 when it tries to decode images, so I searched for invocations of pyglet.image and found 6 results at lines 1031, 2304, 2327, 4622, 4626, and 4628, respectively:

if sys.platform == 'linux2':
    window.set_icon(pyglet.image.load(resourcepaths['misc']['brain'][0]))
...

self.spr_square = [pyglet.sprite.Sprite(pyglet.image.load(path))
...

self.image_set = [pyglet.sprite.Sprite(pyglet.image.load(path))
...

brain_icon = pyglet.sprite.Sprite(pyglet.image.load(random.choice(resourcepaths['misc']['brain'])))
...

if cfg.BLACK_BACKGROUND:
    brain_graphic = pyglet.sprite.Sprite(pyglet.image.load(random.choice(resourcepaths['misc']['splash-black'])))
else:
    brain_graphic = pyglet.sprite.Sprite(pyglet.image.load(random.choice(resourcepaths['misc']['splash'])))

I should take a look at the Python 2.7.9 to 2.7.10 changelog to see if there are any changes that might affect pyglet image loading...