... и скажем, я бы хотел отладить этот script, поставив точку останова в строке a=10
, а затем перейдя через script.
... и хотя весь Python script работает в пределах gdb
, точка останова просто не достигается.
Ответ 2
Извинения за длинный пост; Я снова вернулся к аналогичной проблеме с отладкой - случай, когда вы долго отправляетесь в отладчик, чтобы, наконец, обнаружить, что нет никакой ошибки - поэтому я просто хотел бы опубликовать свои заметки и некоторый код здесь (я все еще на Python 2.7, Ubuntu 11.04). Что касается вопроса OP - в более новых gdb
, его также можно разбить с помощью функции id(...)
в Python script и иметь gdb
break on builtin_id
; но здесь более подробно:
Опять же, у меня возникла проблема с библиотечным модулем C.so для Python; на этот раз это был svn.client
, который является модулем Swig (см. также здесь); в Debian/Ubuntu, доступный через sudo apt-get install python-subversion
(filelist). Проблема возникла при попытке запустить Пример 8.3. Сканер состояния Python - Использование API (svnbook) В этом примере должен выполняться то же самое, что и команда терминала svn status
; но когда я попробовал это на одной из моих рабочих копий, он разбился с "Ошибка (22): Ошибка преобразования записи в каталог" путь "к UTF-8", даже если svn status
обрабатывает ту же рабочую копию (WC) (в течение многих лет) - так что я хотел посмотреть, откуда это взялось. Моя версия теста script python-subversion-test.py; и мой полный журнал отладки находится в logsvnpy.gz (gzipped текстовый файл, ~ 188K несжатый, если кто-то захочет пробираться через бесконечные степпинга и backtraces) - это сокращенная версия. У меня установлены оба Python 2.7 и 3.2, но 2.7 по умолчанию на Ubuntu 11.04:
$ ls -la $(which python python-dbg)
lrwxrwxrwx 1 root root 9 2012-02-29 07:31 /usr/bin/python -> python2.7
lrwxrwxrwx 1 root root 13 2013-04-07 03:01 /usr/bin/python-dbg -> python2.7-dbg
$ apt-show-versions -r 'python[^-]+'
libpython2.7/natty uptodate 2.7.1-5ubuntu2.2
libpython3.2/natty uptodate 3.2-1ubuntu1.2
python2.7/natty uptodate 2.7.1-5ubuntu2.2
python2.7-dbg/natty uptodate 2.7.1-5ubuntu2.2
python2.7-dev/natty uptodate 2.7.1-5ubuntu2.2
python2.7-minimal/natty uptodate 2.7.1-5ubuntu2.2
python3/natty uptodate 3.2-1ubuntu1
python3-minimal/natty uptodate 3.2-1ubuntu1
python3.2/natty uptodate 3.2-1ubuntu1.2
python3.2-minimal/natty uptodate 3.2-1ubuntu1.2
В первую очередь следует отметить, как работает пример Python: там, чтобы получить статус всех файлов в каталоге, сначала svn.client.svn_client_status2
вызывается - кроме пути, также с _status_callback
в аргументах, так как функция обратного вызова в Python для регистрации - и затем блокирует. Пока status2
блокируется, базовый модуль выполняет итерацию через все файлы в пути каталога WC; и для каждой записи файла он вызывает зарегистрированный _status_callback
, который должен распечатывать информацию о записи. По завершении этой рекурсии завершается status2
. Таким образом, отказ UTF-8 должен исходить от базового модуля. Осмотрите этот модуль далее:
$ python -c 'import inspect,pprint,svn.client; pprint.pprint(inspect.getmembers(svn.client))' | grep status
('status', <function svn_client_status at 0xb7351f44>),
('status2', <function svn_client_status2 at 0xb7351f0c>),
('status3', <function svn_client_status3 at 0xb7351ed4>),
('status4', <function svn_client_status4 at 0xb7351e9c>),
('svn_client_status', <function svn_client_status at 0xb7351f44>),
# ...
... показывает, что существуют другие функции statusX
, однако status3
завершился с той же ошибкой UTF-8; в то время как status4
вызвал ошибку сегментации (что становится еще одной проблемой для отладки).
И снова, как и в моем комментарии к @EliBendersky, я хотел задать точку останова в Python, чтобы получить какой-то стек вызовов C позже будет функционировать, что выявит, где возникла проблема - без меня, чтобы я перестраивал модули C из источника; но это оказалось не так просто.
Python и gdb
Прежде всего, одна вещь, которая может быть очень запутанной, - это связь между gdb
и Python; здесь типичные ресурсы:
- http://wiki.python.org/moin/DebuggingWithGdb - упоминает a
gdbinit
в "Макросах GDB",
-
Это release27-maint/Misc/gdbinit находится в исходном дереве Python; определяет команды gdb
, такие как pylocals
и pyframe
, но также упоминает:
# ПРИМЕЧАНИЕ. Если у вас есть gdb 7 или более поздняя версия, он поддерживает отладку Python напрямую
# со встроенными макросами, которые вы можете найти выше того, что здесь. # См. Инструменты /gdb/libpython.py и http://bugs.python.org/issue8032.
-
Возможности /EasierPythonDebugging - FedoraProject - имеет пример, упоминает пакет Fedora python-debuginfo
и libpython
- Tools/gdb/libpython.py также находится в исходном дереве Python, и он упоминает:
Начиная с gdb 7, gdb build может быть сконфигурирован --with-python, что позволяет gdb для расширения с помощью кода Python, например. для визуализации данных, специфичных для библиотеки,
например, для типов STL С++.....
Этот модуль содержит знания о деталях реализации libpython, так что что мы можем испускать полезные визуализации, например. строка, список, dict, рамка
предоставление информации о файле/строке и состояние локальных переменных
- cpython/Lib/test/test_gdb.py - по-видимому, из cpython, похоже, тестирует
gdb
функциональность из Python
Это немного запутывает - кроме указателя, лучше получить gdb
v.7; Мне удалось получить для моей ОС:
$ apt-show-versions gdb
gdb 7.3-50.20110806-cvs newer than version in archive
Быстрый способ проверить, поддерживает ли gdb
Python следующее:
$ gdb --batch --eval-command="python print gdb"
<module 'gdb' (built-in)>
$ python -c 'import gdb; print gdb'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named gdb
... но gdb
, поддерживающий Python, не означает, что сам Python может получить доступ к функциональности gdb
(по-видимому, gdb
имеет собственный встроенный интерпретатор Python).
Оказывается, в Ubuntu 11.04 пакет python2.7-dbg
устанавливает файл libpython2.7.so.1.0-gdb.py
:
$ find / -xdev -name '*libpython*' 2>/dev/null | grep '\.py'
/usr/lib/debug/usr/lib/libpython2.7.so.1.0-gdb.py
$ sudo ln -s /usr/lib/debug/usr/lib/libpython2.7.so.1.0-gdb.py /usr/lib/debug/usr/lib/libpython.py
... и это соответствует названию Tools/gdb/libpython.py
; symlinking позволит нам ссылаться на него как libpython
и использовать import script, упомянутый в Features/EasierPythonDebugging.
test_gdb.py
script на самом деле для Python 3 - я изменил его на 2.7 и разместил в test_gdb2.7.py. Этот script вызывает gdb
через системный вызов ОС и проверяет его функциональность Python, распечатывает его на stdout; он также принимает параметр командной строки, -imp-lp
, который будет import libpython
в gdb
перед выполнением других команд. Так, например:
$ python-dbg test_gdb2.7.py
...
*** test_prettyprint ***
42 (self=0x0, v=0x8333fc8)
[] (self=0x0, v=0xb7f7506c)
('foo', 'bar', 'baz') (self=0x0, v=0xb7f7d234)
[0, 1, 2, 3, 4] (self=0x0, v=0xb7f7506c)
...
$ python-dbg test_gdb2.7.py -imp-lp
...
*** test_prettyprint ***
42 (self=0x0, v=42)
[] (self=0x0, v=[])
('foo', 'bar', 'baz') (self=0x0, v=('foo', 'bar', 'baz'))
[0, 1, 2, 3, 4] (self=0x0, v=[0, 1, 2, 3, 4])
...
Таким образом, libpython.py
предназначен специально для интерпретатора Python внутри gdb
, и помогает gdb
печатать представления Python (v=[]
) вместо адресов памяти (v=0xb7f7506c
) - что полезно, если gdb
происходит с отладкой Python script (вернее, он будет отлаживать исполняемый файл Python, который интерпретирует script).
test_gdb.py
script также дает указателю, что вы можете "запустить" python -c'id (DATA) "под gdb с точкой останова на builtin_id
"; для тестирования этого я опубликовал bash script, gdb_py_so_test.sh, который создает исполняемый файл с функцией потока подсчета и как простые distutils, так и модули swig (как в версиях отладки, так и в версии выпуска), которые взаимодействуют с одной и той же функцией. Он также создает .gdbinit
с gdb
и gdb
точками прерывания класса Python - и, наконец, он запускает gdb
на Python (загрузка одного из разделяемых модулей), где пользователь может надеяться увидеть, действительно ли точки останова запускаются.
segfault в gdb без восстановления источника
Сначала я сосредоточился на segfault status4
, и я хотел точно знать, из какого модуля работает эта функция. Я использовал функцию, которую можно найти в debug_funcs.py; который можно вызвать с помощью отдельного регулярного выражения для функций и модулей и может генерировать что-то вроде:
$ python python-subversion-test.py ./MyRepoWCDir
# ...
# example for debug_funcs.showLoadedModules(r'(?=.*\.(so|pyc))(?=.*svn)(?=.*client)')
#
svn.client 0xb74b83d4L <module 'svn.client' from '/usr/lib/pymodules/python2.7/svn/client.pyc'>
_client 0xb7415614L <module '_client' from '/usr/lib/pymodules/python2.7/libsvn/_client.so'>
libsvn.client 0xb74155b4L <module 'libsvn.client' from '/usr/lib/pymodules/python2.7/libsvn/client.pyc'>
#
# example for debug_funcs.showFunctionsInLoadedModules(r'status4', r'(?=.*\.(so|pyc))(?=.*svn)')
#
0xb738c4fcL libsvn.client svn_client_status4 libsvn/client.pyc
0xb74e9eecL _client svn_client_status4 libsvn/_client.so
0xb738c4fcL svn.client status4 svn/client.pyc
0xb738c4fcL svn.client svn_client_status4 svn/client.pyc
Однако обратите внимание, что:
$ python-dbg python-subversion-test.py ./MyRepoWCDir
# ...
0x90fc574 - _client /usr/lib/pymodules/python2.7/libsvn/_client_d.so
# ...
0x912b30c _client svn_client_status4 libsvn/_client_d.so
# ...
$ apt-show-versions -r python-subversion
python-subversion/natty uptodate 1.6.12dfsg-4ubuntu2.1
python-subversion-dbg/natty uptodate 1.6.12dfsg-4ubuntu2.1
... python-dbg
будет загружать разные версии (_d) .so
модулей libsvn
(или python-subversion
); и это потому, что у меня установлен пакет python-subversion-dbg
.
В любом случае мы можем думать, что мы знаем адреса, где модули и соответствующие функции загружаются при каждом вызове Python script, что позволит нам разместить точку останова gdb
на адрес программы; учитывая, что здесь мы работаем с "vanilla".so(которые не были восстановлены из источника). Однако сам Python не может видеть, что _client.so
фактически использует libsvn_client-1.so
:
$ ls -la $(locate '*2.7*/_client*.so') #check locations
$ ls -la $(locate 'libsvn_client') #check locations
$ ldd /usr/lib/pyshared/python2.7/libsvn/_client.so | grep client
libsvn_client-1.so.1 => /usr/lib/libsvn_client-1.so.1 (0x0037f000)
#
# instead of nm, also can use:
# objdump -dSlr file | grep '^[[:digit:]].*status4' | grep -v '^$\|^[[:space:]]'
#
$ nm -D /usr/lib/pyshared/python2.7/libsvn/_client.so | grep status4
U svn_client_status4
$ nm -a /usr/lib/pyshared/python2.7/libsvn/_client_d.so | grep status4
00029a50 t _wrap_svn_client_status4
U svn_client_status4
$ nm -D /usr/lib/libsvn_client-1.so.1 | grep status4 # -a: no symbols
00038c10 T svn_client_status4
Внутри Python мы можем сделать системный вызов, чтобы запросить /proc/pid/maps
для адреса, где загружен libsvn_client-1.so
, и добавить к нему адрес, сообщенный последней командой nm -D
для смещения svn_client_status4
; и получить адрес, в котором мы могли бы сломаться в gdb
(с синтаксисом b *0xAddress
), но это не обязательно, потому что если nm
может видеть символ, то можно gdb
- чтобы мы могли разбить непосредственно на имя функции. Другое дело, что в случае segfault gdb
останавливается сам по себе, и мы можем выдать backtrace (обратите внимание: используйте Ctrl-X A для выхода из режима TUI gdb после layout asm
):
$ gdb --args python python-subversion-test.py ./AudioFPGA/
(gdb) r
Starting program: /usr/bin/python python-subversion-test.py ./MyRepoWCDir
...
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) bt
#0 0x00000000 in ?? ()
#1 0x005a5bf3 in ?? () from /usr/lib/libsvn_client-1.so.1
#2 0x005dbf4a in ?? () from /usr/lib/libsvn_wc-1.so.1
#3 0x005dcea3 in ?? () from /usr/lib/libsvn_wc-1.so.1
#4 0x005dd240 in ?? () from /usr/lib/libsvn_wc-1.so.1
#5 0x005a5fe5 in svn_client_status4 () from /usr/lib/libsvn_client-1.so.1
#6 0x00d54dae in ?? () from /usr/lib/pymodules/python2.7/libsvn/_client.so
#7 0x080e0155 in PyEval_EvalFrameEx ()
...
(gdb) frame 1
#1 0x005a5bf3 in ?? () from /usr/lib/libsvn_client-1.so.1
(gdb) list
No symbol table is loaded. Use the "file" command.
(gdb) disas
No function contains program counter for selected frame.
(gdb) x/10i 0x005a5bf3
=> 0x5a5bf3: mov -0xc(%ebp),%ebx
0x5a5bf6: mov -0x8(%ebp),%esi
0x5a5bf9: mov -0x4(%ebp),%edi
0x5a5bfc: mov %ebp,%esp
(gdb) layout asm # No function contains program counter for selected frame (cannot show 0x5a5bf3)
(gdb) p svn_client_status4
$1 = {<text variable, no debug info>} 0x5a5c10 <svn_client_status4>
(gdb) frame 5
#5 0x005a5fe5 in svn_client_status4 () from /usr/lib/libsvn_client-1.so.1
(gdb) list
No symbol table is loaded. Use the "file" command.
(gdb) layout asm
│0x5a5fd8 <svn_client_status4+968> mov %esi,0x4(%esp) |
│0x5a5fdc <svn_client_status4+972> mov %eax,(%esp) |
│0x5a5fdf <svn_client_status4+975> mov -0x28(%ebp),%eax |
│0x5a5fe2 <svn_client_status4+978> call *0x38(%eax) |
>│0x5a5fe5 <svn_client_status4+981> test %eax,%eax |
│0x5a5fe7 <svn_client_status4+983> jne 0x5a5ce3 <svn_client_status4+211> |
│0x5a5fed <svn_client_status4+989> jmp 0x5a5ee3 <svn_client_status4+723> |
│0x5a5ff2 <svn_client_status4+994> lea -0x1fac(%ebx),%eax |
│0x5a5ff8 <svn_client_status4+1000> mov %eax,(%esp) |
Итак, наша ошибка происходит где-то в libsvn_client-1.so
, но в области памяти до начала svn_client_status4
запускается функция; и поскольку у нас нет отладочных символов, мы не можем сказать ничего другого. Использование python-dbg
может дать бит разные результаты:
Program received signal SIGSEGV, Segmentation fault.
0x005aebf0 in ?? () from /usr/lib/libsvn_client-1.so.1
(gdb) bt
#0 0x005aebf0 in ?? () from /usr/lib/libsvn_client-1.so.1
#1 0x005e4f4a in ?? () from /usr/lib/libsvn_wc-1.so.1
#2 0x005e5ea3 in ?? () from /usr/lib/libsvn_wc-1.so.1
#3 0x005e6240 in ?? () from /usr/lib/libsvn_wc-1.so.1
#4 0x005aefe5 in svn_client_status4 () from /usr/lib/libsvn_client-1.so.1
#5 0x00d61e9e in _wrap_svn_client_status4 (self=0x0, args=0x8471214)
at /build/buildd/subversion-1.6.12dfsg/subversion/bindings/swig/python/svn_client.c:10001
...
(gdb) frame 4
#4 0x005aefe5 in svn_client_status4 () from /usr/lib/libsvn_client-1.so.1
(gdb) list
9876 in /build/buildd/subversion-1.6.12dfsg/subversion/bindings/swig/python/svn_client.c
(gdb) p svn_client_status4
$1 = {<text variable, no debug info>} 0x5aec10 <svn_client_status4>
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
...
0x00497a20 0x004c8be8 Yes /usr/lib/pymodules/python2.7/libsvn/_core_d.so
0x004e9fe0 0x004f52c8 Yes /usr/lib/libsvn_swig_py2.7_d-1.so.1
0x004f9750 0x00501678 Yes (*) /usr/lib/libsvn_diff-1.so.1
0x0050f3e0 0x00539d08 Yes (*) /usr/lib/libsvn_subr-1.so.1
0x00552200 0x00572658 Yes (*) /usr/lib/libapr-1.so.0
0x0057ddb0 0x005b14b8 Yes (*) /usr/lib/libsvn_client-1.so.1
...
0x00c2a8f0 0x00d11cc8 Yes (*) /usr/lib/libxml2.so.2
0x00d3f860 0x00d6dc08 Yes /usr/lib/pymodules/python2.7/libsvn/_client_d.so
...
(*): Shared library is missing debugging information.
... но команда list
по-прежнему дает нам исходную строку, относящуюся к кадру 5 (не к кадру 4), и мы все еще не знаем больше о svn_client_status4
: в то время как модули python-subversion
загружаются в их версии отладки, информация об отладке отсутствует для libsvn_client-1.so
. Итак, время для восстановления из источника.
segfault в gdb с восстановлением источника
Это фактический subversion
, который нам нужно перестроить, или, скорее, часть библиотеки - поскольку у нас уже есть модули отладки от python-subversion
; пакет в моей системе называется libsvn1
:
$ apt-show-versions -r 'libsvn'
libsvn1/natty uptodate 1.6.12dfsg-4ubuntu2.1
$ apt-cache search 'libsvn' | grep 'dbg'
python-subversion-dbg - Python bindings for Subversion (debug extension)
... и для него нет отладочного пакета. Чтобы перестроить исходный код, я прошел через apt-get source libsvn1
, с зависимостями, найденными вручную через apt-rdepends --build-depends --follow=DEPENDS subversion
. Более подробные сведения содержатся в полном журнале, но здесь мы можем отметить, что исходный пакет может создавать как привязки SWIG Python (т.е. python-subversion
), так и библиотеку Subversion (libsvn1
). Кроме того, я запустил make install
с расположением из основного дерева ядра; это означает, что нужно было явно указывать исходные модули через переменные среды LD:
$ ELD=/path/to/src/subversion-1.6.12dfsg/tmpinst/usr/local/lib
$ LD_LIBRARY_PATH=$ELD:$ELD/svn-python/libsvn LD_PRELOAD="$ELD/libsvn_client-1.so $ELD/svn-python/libsvn/_core.so" gdb --args python python-subversion-test.py ./MyRepoWCDir
Одна сложная вещь заключается в том, что для создания модулей отладки SWIG требуется вызов с помощью python-dbg
; видимо, просто делать ./configure --enable-debug
не делает; и так, только _core.so
и т.д. создаются, хотя и с отладочной информацией. Если мы затем попытаемся выполнить его загрузку, как в приведенной выше команде, но с python-dbg
, мы получим undefined symbol: Py_InitModule4
, потому что:
$ objdump -d $(which python) | grep '^\w.*InitMod'
0813b770 <Py_InitModule4>:
$ objdump -d $(which python-dbg) | grep '^\w.*InitMod'
08124740 <Py_InitModule4TraceRefs>:
... python-dbg
имеет другую функцию Py_InitModule4
. Это, однако, не было проблемой, потому что использовался просто python
(как в приведенном выше вызове), а gdb
по-прежнему разрешал выполнять соответствующие функции во вновь созданной libsvn
(упомянутый bash script gdb_py_so_test.sh, в качестве примера строит базовый модуль Swig в обеих версиях отладки и выпуска, чтобы подтвердить правильную процедуру).
С отладочными символами для libsvn
, стек вызовов функций выглядит так (вставлен немного по-другому):
#5 0x0016e654 in svn_client_status4 (..., libsvn_client/status.c:369
#4 0x007fd209 in close_edit (..., libsvn_wc/status.c:2144
#3 0x007fafaa in get_dir_status (..., libsvn_wc/status.c:1033
#2 0x007fa4e7 in send_unversioned_item (..., libsvn_wc/status.c:722
#1 0x0016dd17 in tweak_status (..., libsvn_client/status.c:81
#0 0x00000000 in ?? ()
... и поскольку одни и те же функции библиотеки также используются в командной строке svn client
, мы можем сравнить, скажем, фрейм 5:
# `svn status`:
(gdb) p *(sb->real_status_func)
$3 = {svn_error_t *(void *, const char *, svn_wc_status2_t *, apr_pool_t *)} 0x805e199 <print_status>
...
# `python python-subversion-test.py`
(gdb) p *(svn_wc_status_func3_t*)sb->real_status_func
Cannot access memory at address 0x0
Итак, в случае вызова Python на status4
, sb->real_status_func
равен NULL, вызывая segfault. Причина этого может быть обнаружена после начала чтения источника: в ./subversion/libsvn_client/deprecated.c
определение для status3
имеет:
svn_client_status3(svn_revnum_t *result_rev,
const char *path,
const svn_opt_revision_t *revision,
svn_wc_status_func2_t status_func,
void *status_baton,
....
struct status3_wrapper_baton swb = { 0 };
swb.old_func = status_func;
swb.old_baton = status_baton;
return svn_client_status4(result_rev, path, revision, status3_wrapper_func,
&swb, depth, get_all, update, no_ignore,
ignore_externals, changelists, ctx, pool);
... то есть, когда status3
вызывается с функцией обратного вызова, он создает структуру и назначает функцию одному из свойств структуры, а затем использует структуру в дальнейшем вызове status4
! Поскольку status3
действительно работает с Python - вывод состоит в том, что мы не можем корректно вызывать status4
из Python (так как это связано с созданием C-структуры в Python); и это не имеет никакого значения, потому что мы можем вызывать status3
из Python, который затем сам вызывает status4
!
Тогда почему status4
адресуется из Python? Вероятно, потому что swig
просто автогенерировал для него интерфейс... В любом случае, вот пример, когда поездка в отладчик обнаруживает источник проблемы - но на самом деле не ошибка :)
Решение? Не используйте status4
.
Сбой C в модуле Python, в gdb с исходной перестройкой
Возвращаясь к ошибке UTF-8, которая произошла с status2
и status3
, было проще, учитывая, что теперь доступны исходные версии модулей. Проблема была очевидна в функции entry_name_to_utf8
, и, исследуя ее аргумент name
, можно было сначала понять, что имя файла, вызывающее проблему, действительно содержало не-ascii-но все еще законные символы UTF-8 (см. Программа для проверки/поиска символов UTF-8/Unicode в строке в командной строке? - Суперпользователь). Затем я использовал этот . Gdbinit, чтобы создать контрольную точку класса Python для gdb, которая будет печатать имена файлов и ломаться только по совпадению с проблематичный.
Тогда возникает вопрос: почему клиент командной строки svn status
не разбивается на одно имя файла? Посредством перехода через svn status
и python python-subversion-test.py
можно сравнить соответствующие стеки вызовов функций:
# call stack Python module:
#
_wrap_svn_client_status3 subversion/bindings/swig/python/svn_client.c * allocs:
(svn_swig_py_get_pool_arg(args, SWIGTYPE_p_apr_pool_t, &_global_py_pool, &_global_pool))
svn_client_status3 subversion/libsvn_client/deprecated.c
svn_client_status4 subversion/libsvn_client/status.c
close_edit subversion/libsvn_wc/status.c
get_dir_status subversion/libsvn_wc/status.c
# call stack svn client:
#
main subversion/svn/main.c
svn_cl__status subversion/svn/status-cmd.c * allocs
(subpool = svn_pool_create(pool))
svn_client_status4 subversion/libsvn_client/status.c
close_edit subversion/libsvn_delta/cancel.c
close_edit subversion/libsvn_wc/status.c
get_dir_status subversion/libsvn_wc/status.c
# svn call stack:
# ... svn_client_status4 - starts pool
#
get_dir_status subversion/libsvn_wc/status.c
handle_dir_entry subversion/libsvn_wc/status.c
get_dir_status subversion/libsvn_wc/status.c
svn_io_get_dirents2 subversion/libsvn_subr/io.c
entry_name_to_utf8 subversion/libsvn_subr/io.c
svn_path_cstring_to_utf8 subversion/libsvn_subr/path.c
svn_utf_cstring_to_utf8 subversion/libsvn_subr/utf.c * from here, bad node->handle
convert_cstring subversion/libsvn_subr/utf.c
convert_to_stringbuf subversion/libsvn_subr/utf.c * here, bad node => fail
В этот момент встречается тот факт, что Subversion использует libapr
(Apache Portable Runtime) для распределения памяти; и на самом деле эта часть вызывает отказ - в основном, функция apr_xlate_conv_buffer
ведет себя по-разному в двух случаях.
Но, может быть довольно сложно понять, что представляет собой настоящая проблема, потому что apr_xlate_conv_buffer
использует кодировку в node->frompage
, которая установлена в define APR_LOCALE_CHARSET 1
- и которая не изменяется между svn status
и Python. Чтобы дойти до этого, я скопировал все, что связано с строковым копированием и распределением APR по стеку вызовов, и восстановил простой пример, который создает модуль Swig, который должен просто скопировать строку с использованием времени выполнения APR; этот пример находится в каталоге aprtest, построенном с bash script build-aprtest.sh.
Благодаря этому примеру было обнаружено, что проблема сбоя UTF может быть исправлена путем вызова setlocale
в C перед любым распределением памяти строки APR - для получения дополнительной информации об этом тесте см. # 15977257 - Использование ввода utf-8 для модуля cmd Python. Соответственно, все, что нам нужно сделать из Python, выполняется:
import locale
locale.setlocale(locale.LC_ALL, '')
... перед любыми вызовами svn.client
(и, следовательно, до libsvn
и, следовательно, до libapr
). И здесь у нас есть еще один пример, для поездки в отладчик, без лишней ошибки :)