Skip to content

Commit

Permalink
gdb: fix the extension to be loaded with Python 2
Browse files Browse the repository at this point in the history
There was a mystic error when the extension was loaded by the old gdb
versions built against Python 2:
| (gdb) source luajit-gdb.py
| Traceback (most recent call last):
|   File "luajit-gdb.py", line 702, in <module>
|     load(None)
|   File "luajit-gdb.py", line 699, in load
|     'lj-gc': LJGC,
|   File "luajit-gdb.py", line 687, in init
|     command(name)
|   File "luajit-gdb.py", line 468, in __init__
|     gdb.write('{} command initialized\n'.format(name))
| ValueError: sequence.index(x): x not in sequence

I made a little investigation (for more info see the mentioned issue)
and found the next fun fact: the exception was raised much earlier to
<str.format>, more precisely in <gdb.events.new_objfile.disconnect>.
However, the handled exception is preserved until <str.format> call and
hits the condition underneath leading to the extension load failure.

As a result to avoid the exception, the special global variable is
introduced for legacy (i.e. Python 2) environment. It checks whether any
callback is associated with new_objfile event prior to disconnecting it.
This variable usage is encapsulated within two introduced routines:
<connect> and <disconnect> which are wrappers for ones provided by gdb.

Furthermore, after diving to gdb sources related to Python embedding, I
found that callbacks are grouped into an internal list. Previous
implementation appended the <load> function to this callback list on
each its unsuccessful call, but only the successful one is removes it
from the list. Thereby disconnect action is moved prior to connect one
so there is no more than one <load> instance kept in callback list.

Fixes tarantool/tarantool#4828

Reported-by: Oleg Babin <olegrok@tarantool.org>
Reviewed-by: Oleg Babin <olegrok@tarantool.org>
Reviewed-by: Sergey Ostanevich <sergos@tarantool.org>
Signed-off-by: Igor Munkin <imun@tarantool.org>
  • Loading branch information
igormunkin authored and kyukhin committed Jul 22, 2020
1 parent 377198b commit ad1d444
Showing 1 changed file with 33 additions and 7 deletions.
40 changes: 33 additions & 7 deletions src/luajit-gdb.py
Expand Up @@ -7,7 +7,10 @@

# make script compatible with the ancient Python {{{

if re.match(r'^2\.', sys.version):
LEGACY = re.match(r'^2\.', sys.version)

if LEGACY:
CONNECTED = False
int = long
range = xrange

Expand Down Expand Up @@ -662,19 +665,42 @@ def invoke(self, arg, from_tty):
def init(commands):
global LJ_64, LJ_GC64, LJ_FR2

# XXX Fragile: though connecting the callback looks like a crap but it
# respects both Python 2 and Python 3 (see #4828).
def connect(callback):
if LEGACY:
global CONNECTED
CONNECTED = True
gdb.events.new_objfile.connect(callback)

# XXX Fragile: though disconnecting the callback looks like a crap but it
# respects both Python 2 and Python 3 (see #4828).
def disconnect(callback):
if LEGACY:
global CONNECTED
if not CONNECTED:
return
CONNECTED = False
gdb.events.new_objfile.disconnect(callback)

try:
# Try to remove the callback at first to not append duplicates to
# gdb.events.new_objfile internal list.
disconnect(load)
except:
# Callback is not connected.
pass

try:
# Detect whether libluajit objfile is loaded.
gdb.parse_and_eval('luaJIT_setmode')
except:
gdb.write('luajit-gdb.py initialization is postponed '
'until libluajit objfile is loaded\n')
gdb.events.new_objfile.connect(load)
# Add a callback to be executed when the next objfile is loaded.
connect(load)
return

try:
gdb.events.new_objfile.disconnect(load)
except:
pass # was not connected

try:
LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64'
LJ_FR2 = LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64'
Expand Down

0 comments on commit ad1d444

Please sign in to comment.