GDB による C API 拡張と CPython 内部のデバッグ
**********************************************

このドキュメントでは、Python の GDB 拡張 "python-gdb.py" を GDB デバッ
ガと組み合わせて、CPython 拡張や CPython インタープリタ自体をデバッグ
する方法を説明します。

クラッシュやデッドロックなどの低レベルの問題を調べて修正するには、GDB
などの低レベルのデバッガが役立ちます。 デフォルトでは GDB (およびその
フロントエンド) は CPython インタープリタ固有の高レベルの情報に対応し
ていません。

"python-gdb.py" 拡張は CPython インタープリタの情報を GDB に追加します
。 実行中の Python 関数のスタックを調べるのに役立ちます。 PyObject* ポ
インタで表された Python オブジェクトについて、型と値を表示できます。

このドキュメントは、CPython 拡張の開発者や CPython の C で書かれた部分
を扱う開発者が、GDB で "python-gdb.py" 拡張を使う方法を学ぶためのもの
です。

注釈:

  このドキュメントは GDB と CPython C API の基礎を理解していることを前
  提としています。 開発者ガイド と Python wiki の説明をまとめたもので
  す。


前提条件
========

次のものが必要です:

* GDB 7 以降。(それ以前の GDB については、Python 3.11 以前のソースにあ
  る "Misc/gdbinit" を参照してください。)

* Python およびデバッグ対象の拡張の、GDB に対応したデバッグ情報。

* "python-gdb.py" 拡張。

この拡張は Python と一緒にビルドされますが、別途配布されていることも、
配布されていないこともあります。 以下では一般的なシステムをいくつか例
に挙げて説明します。 お使いのシステムに該当する手順でも、情報が古くな
っている可能性に注意してください。


ソースからビルドした Python での設定
------------------------------------

CPython をソースからビルドすると、通常はデバッグ情報が利用でき、リポジ
トリのルートディレクトリに "python-gdb.py" ファイルが追加されます。

サポートを有効にするには、"python-gdb.py" のあるディレクトリを GDB の
"auto-load-safe-path" に追加する必要があります。 追加していない場合、
最近の GDB は追加方法を示す警告を表示します。

注釈:

  お使いの GDB で手順が表示されない場合は、設定ファイル ("~/.gdbinit"
  または "~/.config/gdb/gdbinit") に次を記述してください。

     add-auto-load-safe-path /path/to/cpython

  ":" で区切って複数のパスを追加することもできます。


Linux ディストリビューションの Python での設定
----------------------------------------------

多くの Linux システムでは、システムの Python のデバッグ情報を "python-
debuginfo" や "python-dbg" などの名前のパッケージで提供しています。 例
えば:

* Fedora:

        sudo dnf install gdb
        sudo dnf debuginfo-install python3

* Ubuntu:

        sudo apt install gdb python3-dbg

最近の Linux システムの一部では、GDB が *debuginfod* を使ってデバッグ
シンボルを自動的にダウンロードできます。 ただし、これでは "python-
gdb.py" 拡張はインストールされません。 通常はデバッグ情報のパッケージ
を別途インストールする必要があります。


デバッグビルドと開発モードの利用
================================

デバッグを容易にするため、次の方法も検討してください:

* Python の デバッグビルド を使う。(ソースからビルドする場合は
  "configure --with-pydebug" を使います。Linux ディストリビューション
  では、"python-debug" や "python-dbg" などのパッケージがあればインス
  トールして実行します。)

* 実行時の 開発モード ("-X dev") を使う。

Both enable extra assertions and disable some optimizations. Sometimes
this hides the bug you are trying to find, but in most cases they make
the process easier.


"python-gdb" 拡張の利用
=======================

この拡張を読み込むと、主に二つの機能が利用できます。 Python の値を整形
して表示するプリティプリンタと、追加のコマンドです。


プリティプリンタ
----------------

この拡張を有効にすると、GDB のバックトレースは次のようになります (一部
省略)。

   #0  0x000000000041a6b1 in PyObject_Malloc (nbytes=Cannot access memory at address 0x7fffff7fefe8
   ) at Objects/obmalloc.c:748
   #1  0x000000000041b7c0 in _PyObject_DebugMallocApi (id=111 'o', nbytes=24) at Objects/obmalloc.c:1445
   #2  0x000000000041b717 in _PyObject_DebugMalloc (nbytes=24) at Objects/obmalloc.c:1412
   #3  0x000000000044060a in _PyUnicode_New (length=11) at Objects/unicodeobject.c:346
   #4  0x00000000004466aa in PyUnicodeUCS2_DecodeUTF8Stateful (s=0x5c2b8d "__lltrace__", size=11, errors=0x0, consumed=
       0x0) at Objects/unicodeobject.c:2531
   #5  0x0000000000446647 in PyUnicodeUCS2_DecodeUTF8 (s=0x5c2b8d "__lltrace__", size=11, errors=0x0)
       at Objects/unicodeobject.c:2495
   #6  0x0000000000440d1b in PyUnicodeUCS2_FromStringAndSize (u=0x5c2b8d "__lltrace__", size=11)
       at Objects/unicodeobject.c:551
   #7  0x0000000000440d94 in PyUnicodeUCS2_FromString (u=0x5c2b8d "__lltrace__") at Objects/unicodeobject.c:569
   #8  0x0000000000584abd in PyDict_GetItemString (v=
       {'Yuck': <type at remote 0xad4730>, '__builtins__': <module at remote 0x7ffff7fd5ee8>, '__file__': 'Lib/test/crashers/nasty_eq_vs_dict.py', '__package__': None, 'y': <Yuck(i=0) at remote 0xaacd80>, 'dict': {0: 0, 1: 1, 2: 2, 3: 3}, '__name__': '__main__', 'z': <Yuck(i=0) at remote 0xaace60>, '__doc__': None}, key=
       0x5c2b8d "__lltrace__") at Objects/dictobject.c:2171

"PyDict_GetItemString" の引数の辞書が、内部の分からない "PyObject *"
ポインタではなく "repr()" の結果として表示されている点に注目してくださ
い。

この拡張は "PyObject *" 型の値に専用の表示処理を提供します。 オブジェ
クトの低レベルの詳細を調べるには、適切な型のポインタにキャストしてくだ
さい。 例えば次のようにします。

   (gdb) p globals
   $1 = {'__builtins__': <module at remote 0x7ffff7fb1868>, '__name__':
   '__main__', 'ctypes': <module at remote 0x7ffff7f14360>, '__doc__': None,
   '__package__': None}

   (gdb) p *(PyDictObject*)globals
   $2 = {ob_refcnt = 3, ob_type = 0x3dbdf85820, ma_fill = 5, ma_used = 5,
   ma_mask = 7, ma_table = 0x63d0f8, ma_lookup = 0x3dbdc7ea70
   <lookdict_string>, ma_smalltable = {{me_hash = 7065186196740147912,
   me_key = '__builtins__', me_value = <module at remote 0x7ffff7fb1868>},
   {me_hash = -368181376027291943, me_key = '__name__',
   me_value ='__main__'}, {me_hash = 0, me_key = 0x0, me_value = 0x0},
   {me_hash = 0, me_key = 0x0, me_value = 0x0},
   {me_hash = -9177857982131165996, me_key = 'ctypes',
   me_value = <module at remote 0x7ffff7f14360>},
   {me_hash = -8518757509529533123, me_key = '__doc__', me_value = None},
   {me_hash = 0, me_key = 0x0, me_value = 0x0}, {
     me_hash = 6614918939584953775, me_key = '__package__', me_value = None}}}

プリティプリンタは実際に "repr()" を呼び出すわけではありません。 基本
的な型では、その結果にできるだけ近い表示を行います。

一部の型の専用の表示処理は、標準の型に対する GDB 組み込みの表示処理と
よく似ているため、混同しやすいことがあります。 例えば Python の "int"
(PyLongObject*) のプリティプリンタによる表示は、通常の機械レベルの整数
の表示と区別がつきません。

   (gdb) p some_machine_integer
   $3 = 42

   (gdb) p some_python_integer
   $4 = 42

PyLongObject* にキャストすると内部構造を確認できます。

   (gdb) p *(PyLongObject*)some_python_integer
   $5 = {ob_base = {ob_base = {ob_refcnt = 8, ob_type = 0x3dad39f5e0}, ob_size = 1},
   ob_digit = {42}}

"str" 型でも、GDB 組み込みの "char *" の表示とよく似ているため、同様に
混同しやすいことがあります。

   (gdb) p ptr_to_python_str
   $6 = '__builtins__'

"str" インスタンスのプリティプリンタは、デフォルトでシングルクォートを
使います (Python の文字列の "repr" と同様です)。 一方、"char *" の標準
の表示はダブルクォートを使い、16 進数のアドレスも含みます。

   (gdb) p ptr_to_char_star
   $7 = 0x6d72c0 "hello world"

この場合も PyUnicodeObject* にキャストすると実装の詳細を確認できます。

   (gdb) p *(PyUnicodeObject*)$6
   $8 = {ob_base = {ob_refcnt = 33, ob_type = 0x3dad3a95a0}, length = 12,
   str = 0x7ffff2128500, hash = 7065186196740147912, state = 1, defenc = 0x0}


"py-list"
---------

   この拡張は "py-list" コマンドを追加します。 このコマンドは選択中の
   スレッドの現在のフレームに対応する Python ソースコードがあれば、そ
   れを表示します。 現在の行には ">" が付きます。

      (gdb) py-list
       901        if options.profile:
       902            options.profile = False
       903            profile_me()
       904            return
       905
      >906        u = UI()
       907        if not u.quit:
       908            try:
       909                gtk.main()
       910            except KeyboardInterrupt:
       911                # properly quit on a keyboard interrupt...

   Python ソース内の別の行から表示するには "py-list START" を、特定の
   行範囲を表示するには "py-list START,END" を使います。


"py-up" と "py-down"
--------------------

   "py-up" と "py-down" は GDB の通常の "up" と "down" に相当するコマ
   ンドですが、C のフレームではなく CPython のフレーム単位で移動しよう
   とします。

   GDB is not always able to read the relevant frame information,
   depending on the optimization level with which CPython was
   compiled. Internally, the commands look for C frames that are
   executing the default frame evaluation function (that is, the core
   bytecode interpreter loop within CPython) and look up the value of
   the related "PyFrameObject *".

   これらのコマンドはスレッド内の (C レベルの) フレーム番号を表示しま
   す。

   例えば:

      (gdb) py-up
      #37 Frame 0x9420b04, for file /usr/lib/python2.6/site-packages/
      gnome_sudoku/main.py, line 906, in start_game ()
          u = UI()
      (gdb) py-up
      #40 Frame 0x948e82c, for file /usr/lib/python2.6/site-packages/
      gnome_sudoku/gnome_sudoku.py, line 22, in start_game(main=<module at remote 0xb771b7f4>)
          main.start_game()
      (gdb) py-up
      Unable to find an older python frame

   これで Python スタックの先頭に到達しました。

   フレーム番号は GDB の標準の "backtrace" コマンドで表示されるものと
   対応します。 Python コードを実行していない C のフレームはスキップさ
   れます。

   逆向きに戻ると次のようになります。

      (gdb) py-down
      #37 Frame 0x9420b04, for file /usr/lib/python2.6/site-packages/gnome_sudoku/main.py, line 906, in start_game ()
          u = UI()
      (gdb) py-down
      #34 (unable to read python frame information)
      (gdb) py-down
      #23 (unable to read python frame information)
      (gdb) py-down
      #19 (unable to read python frame information)
      (gdb) py-down
      #14 Frame 0x99262ac, for file /usr/lib/python2.6/site-packages/gnome_sudoku/game_selector.py, line 201, in run_swallowed_dialog (self=<NewOrSavedGameSelector(new_game_model=<gtk.ListStore at remote 0x98fab44>, puzzle=None, saved_games=[{'gsd.auto_fills': 0, 'tracking': {}, 'trackers': {}, 'notes': [], 'saved_at': 1270084485, 'game': '7 8 0 0 0 0 0 5 6 0 0 9 0 8 0 1 0 0 0 4 6 0 0 0 0 7 0 6 5 0 0 0 4 7 9 2 0 0 0 9 0 1 0 0 0 3 9 7 6 0 0 0 1 8 0 6 0 0 0 0 2 8 0 0 0 5 0 4 0 6 0 0 2 1 0 0 0 0 0 4 5\n7 8 0 0 0 0 0 5 6 0 0 9 0 8 0 1 0 0 0 4 6 0 0 0 0 7 0 6 5 1 8 3 4 7 9 2 0 0 0 9 0 1 0 0 0 3 9 7 6 0 0 0 1 8 0 6 0 0 0 0 2 8 0 0 0 5 0 4 0 6 0 0 2 1 0 0 0 0 0 4 5', 'gsd.impossible_hints': 0, 'timer.__absolute_start_time__': <float at remote 0x984b474>, 'gsd.hints': 0, 'timer.active_time': <float at remote 0x984b494>, 'timer.total_time': <float at remote 0x984b464>}], dialog=<gtk.Dialog at remote 0x98faaa4>, saved_game_model=<gtk.ListStore at remote 0x98fad24>, sudoku_maker=<SudokuMaker(terminated=False, played=[], batch_siz...(truncated)
                  swallower.run_dialog(self.dialog)
      (gdb) py-down
      #11 Frame 0x9aead74, for file /usr/lib/python2.6/site-packages/gnome_sudoku/dialog_swallower.py, line 48, in run_dialog (self=<SwappableArea(running=<gtk.Dialog at remote 0x98faaa4>, main_page=0) at remote 0x98fa6e4>, d=<gtk.Dialog at remote 0x98faaa4>)
                  gtk.main()
      (gdb) py-down
      #8 (unable to read python frame information)
      (gdb) py-down
      Unable to find a newer python frame

   これで Python スタックの末尾に到達しました。

   Python 3.12 以降では、複数の Python スタックフレームが同じ C スタッ
   クフレームを使うことがあります。 このため "py-up" と "py-down" は複
   数の Python フレームを一度に移動する場合があります。 例えば次のよう
   になります。

      (gdb) py-up
      #6 Frame 0x7ffff7fb62b0, for file /tmp/rec.py, line 5, in recursive_function (n=0)
         time.sleep(5)
      #6 Frame 0x7ffff7fb6240, for file /tmp/rec.py, line 7, in recursive_function (n=1)
         recursive_function(n-1)
      #6 Frame 0x7ffff7fb61d0, for file /tmp/rec.py, line 7, in recursive_function (n=2)
         recursive_function(n-1)
      #6 Frame 0x7ffff7fb6160, for file /tmp/rec.py, line 7, in recursive_function (n=3)
         recursive_function(n-1)
      #6 Frame 0x7ffff7fb60f0, for file /tmp/rec.py, line 7, in recursive_function (n=4)
         recursive_function(n-1)
      #6 Frame 0x7ffff7fb6080, for file /tmp/rec.py, line 7, in recursive_function (n=5)
         recursive_function(n-1)
      #6 Frame 0x7ffff7fb6020, for file /tmp/rec.py, line 9, in <module> ()
         recursive_function(5)
      (gdb) py-up
      Unable to find an older python frame


"py-bt"
-------

   "py-bt" コマンドは現在のスレッドの Python レベルのバックトレースを
   表示しようとします。

   例えば:

      (gdb) py-bt
      #8 (unable to read python frame information)
      #11 Frame 0x9aead74, for file /usr/lib/python2.6/site-packages/gnome_sudoku/dialog_swallower.py, line 48, in run_dialog (self=<SwappableArea(running=<gtk.Dialog at remote 0x98faaa4>, main_page=0) at remote 0x98fa6e4>, d=<gtk.Dialog at remote 0x98faaa4>)
                  gtk.main()
      #14 Frame 0x99262ac, for file /usr/lib/python2.6/site-packages/gnome_sudoku/game_selector.py, line 201, in run_swallowed_dialog (self=<NewOrSavedGameSelector(new_game_model=<gtk.ListStore at remote 0x98fab44>, puzzle=None, saved_games=[{'gsd.auto_fills': 0, 'tracking': {}, 'trackers': {}, 'notes': [], 'saved_at': 1270084485, 'game': '7 8 0 0 0 0 0 5 6 0 0 9 0 8 0 1 0 0 0 4 6 0 0 0 0 7 0 6 5 0 0 0 4 7 9 2 0 0 0 9 0 1 0 0 0 3 9 7 6 0 0 0 1 8 0 6 0 0 0 0 2 8 0 0 0 5 0 4 0 6 0 0 2 1 0 0 0 0 0 4 5\n7 8 0 0 0 0 0 5 6 0 0 9 0 8 0 1 0 0 0 4 6 0 0 0 0 7 0 6 5 1 8 3 4 7 9 2 0 0 0 9 0 1 0 0 0 3 9 7 6 0 0 0 1 8 0 6 0 0 0 0 2 8 0 0 0 5 0 4 0 6 0 0 2 1 0 0 0 0 0 4 5', 'gsd.impossible_hints': 0, 'timer.__absolute_start_time__': <float at remote 0x984b474>, 'gsd.hints': 0, 'timer.active_time': <float at remote 0x984b494>, 'timer.total_time': <float at remote 0x984b464>}], dialog=<gtk.Dialog at remote 0x98faaa4>, saved_game_model=<gtk.ListStore at remote 0x98fad24>, sudoku_maker=<SudokuMaker(terminated=False, played=[], batch_siz...(truncated)
                  swallower.run_dialog(self.dialog)
      #19 (unable to read python frame information)
      #23 (unable to read python frame information)
      #34 (unable to read python frame information)
      #37 Frame 0x9420b04, for file /usr/lib/python2.6/site-packages/gnome_sudoku/main.py, line 906, in start_game ()
          u = UI()
      #40 Frame 0x948e82c, for file /usr/lib/python2.6/site-packages/gnome_sudoku/gnome_sudoku.py, line 22, in start_game (main=<module at remote 0xb771b7f4>)
          main.start_game()

   フレーム番号は GDB の標準の "backtrace" コマンドで表示されるものと
   対応します。


"py-print"
----------

   "py-print" コマンドは Python の名前を検索し、その値を表示しようとし
   ます。 現在のスレッドのローカル変数、グローバル変数、組み込みの名前
   の順に検索します。

      (gdb) py-print self
      local 'self' = <SwappableArea(running=<gtk.Dialog at remote 0x98faaa4>,
      main_page=0) at remote 0x98fa6e4>
      (gdb) py-print __name__
      global '__name__' = 'gnome_sudoku.dialog_swallower'
      (gdb) py-print len
      builtin 'len' = <built-in function len>
      (gdb) py-print scarlet_pimpernel
      'scarlet_pimpernel' not found

   現在の C フレームが複数の Python フレームに対応する場合、"py-print"
   は最初のものだけを対象にします。


"py-locals"
-----------

   "py-locals" コマンドは、選択中のスレッドの現在の Python フレームに
   あるすべてのローカル変数を調べ、それぞれの値の表現を表示します。

      (gdb) py-locals
      self = <SwappableArea(running=<gtk.Dialog at remote 0x98faaa4>,
      main_page=0) at remote 0x98fa6e4>
      d = <gtk.Dialog at remote 0x98faaa4>

   現在の C フレームが複数の Python フレームに対応する場合、すべてのフ
   レームのローカル変数が表示されます。

      (gdb) py-locals
      Locals for recursive_function
      n = 0
      Locals for recursive_function
      n = 1
      Locals for recursive_function
      n = 2
      Locals for recursive_function
      n = 3
      Locals for recursive_function
      n = 4
      Locals for recursive_function
      n = 5
      Locals for <module>


GDB コマンドとの併用
====================

拡張のコマンドは GDB 組み込みのコマンドを補完します。 例えば、"py-bt"
が表示するフレーム番号を "frame" コマンドに渡すと、選択中のスレッドの
特定のフレームに移動できます。

   (gdb) py-bt
   (output snipped)
   #68 Frame 0xaa4560, for file Lib/test/regrtest.py, line 1548, in <module> ()
           main()
   (gdb) frame 68
   #68 0x00000000004cd1e6 in PyEval_EvalFrameEx (f=Frame 0xaa4560, for file Lib/test/regrtest.py, line 1548, in <module> (), throwflag=0) at Python/ceval.c:2665
   2665                            x = call_function(&sp, oparg);
   (gdb) py-list
   1543        # Run the tests in a context manager that temporary changes the CWD to a
   1544        # temporary and writable directory. If it's not possible to create or
   1545        # change the CWD, the original CWD will be used. The original CWD is
   1546        # available from test_support.SAVEDCWD.
   1547        with test_support.temp_cwd(TESTCWD, quiet=True):
   >1548            main()

"info threads" コマンドはプロセス内のスレッドの一覧を表示します。
"thread" コマンドを使うと別のスレッドを選択できます。

   (gdb) info threads
     105 Thread 0x7fffefa18710 (LWP 10260)  sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:86
     104 Thread 0x7fffdf5fe710 (LWP 10259)  sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:86
   * 1 Thread 0x7ffff7fe2700 (LWP 10145)  0x00000038e46d73e3 in select () at ../sysdeps/unix/syscall-template.S:82

"thread apply all COMMAND" (省略形は "t a a COMMAND") を使うと、すべて
のスレッドでコマンドを実行できます。 "py-bt" と組み合わせると、各スレ
ッドが Python レベルで何を実行しているか確認できます。

   (gdb) t a a py-bt

   Thread 105 (Thread 0x7fffefa18710 (LWP 10260)):
   #5 Frame 0x7fffd00019d0, for file /home/david/coding/python-svn/Lib/threading.py, line 155, in _acquire_restore (self=<_RLock(_Verbose__verbose=False, _RLock__owner=140737354016512, _RLock__block=<thread.lock at remote 0x858770>, _RLock__count=1) at remote 0xd7ff40>, count_owner=(1, 140737213728528), count=1, owner=140737213728528)
           self.__block.acquire()
   #8 Frame 0x7fffac001640, for file /home/david/coding/python-svn/Lib/threading.py, line 269, in wait (self=<_Condition(_Condition__lock=<_RLock(_Verbose__verbose=False, _RLock__owner=140737354016512, _RLock__block=<thread.lock at remote 0x858770>, _RLock__count=1) at remote 0xd7ff40>, acquire=<instancemethod at remote 0xd80260>, _is_owned=<instancemethod at remote 0xd80160>, _release_save=<instancemethod at remote 0xd803e0>, release=<instancemethod at remote 0xd802e0>, _acquire_restore=<instancemethod at remote 0xd7ee60>, _Verbose__verbose=False, _Condition__waiters=[]) at remote 0xd7fd10>, timeout=None, waiter=<thread.lock at remote 0x858a90>, saved_state=(1, 140737213728528))
               self._acquire_restore(saved_state)
   #12 Frame 0x7fffb8001a10, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 348, in f ()
               cond.wait()
   #16 Frame 0x7fffb8001c40, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 37, in task (tid=140737213728528)
                   f()

   Thread 104 (Thread 0x7fffdf5fe710 (LWP 10259)):
   #5 Frame 0x7fffe4001580, for file /home/david/coding/python-svn/Lib/threading.py, line 155, in _acquire_restore (self=<_RLock(_Verbose__verbose=False, _RLock__owner=140737354016512, _RLock__block=<thread.lock at remote 0x858770>, _RLock__count=1) at remote 0xd7ff40>, count_owner=(1, 140736940992272), count=1, owner=140736940992272)
           self.__block.acquire()
   #8 Frame 0x7fffc8002090, for file /home/david/coding/python-svn/Lib/threading.py, line 269, in wait (self=<_Condition(_Condition__lock=<_RLock(_Verbose__verbose=False, _RLock__owner=140737354016512, _RLock__block=<thread.lock at remote 0x858770>, _RLock__count=1) at remote 0xd7ff40>, acquire=<instancemethod at remote 0xd80260>, _is_owned=<instancemethod at remote 0xd80160>, _release_save=<instancemethod at remote 0xd803e0>, release=<instancemethod at remote 0xd802e0>, _acquire_restore=<instancemethod at remote 0xd7ee60>, _Verbose__verbose=False, _Condition__waiters=[]) at remote 0xd7fd10>, timeout=None, waiter=<thread.lock at remote 0x858860>, saved_state=(1, 140736940992272))
               self._acquire_restore(saved_state)
   #12 Frame 0x7fffac001c90, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 348, in f ()
               cond.wait()
   #16 Frame 0x7fffac0011c0, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 37, in task (tid=140736940992272)
                   f()

   Thread 1 (Thread 0x7ffff7fe2700 (LWP 10145)):
   #5 Frame 0xcb5380, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 16, in _wait ()
       time.sleep(0.01)
   #8 Frame 0x7fffd00024a0, for file /home/david/coding/python-svn/Lib/test/lock_tests.py, line 378, in _check_notify (self=<ConditionTests(_testMethodName='test_notify', _resultForDoCleanups=<TestResult(_original_stdout=<cStringIO.StringO at remote 0xc191e0>, skipped=[], _mirrorOutput=False, testsRun=39, buffer=False, _original_stderr=<file at remote 0x7ffff7fc6340>, _stdout_buffer=<cStringIO.StringO at remote 0xc9c7f8>, _stderr_buffer=<cStringIO.StringO at remote 0xc9c790>, _moduleSetUpFailed=False, expectedFailures=[], errors=[], _previousTestClass=<type at remote 0x928310>, unexpectedSuccesses=[], failures=[], shouldStop=False, failfast=False) at remote 0xc185a0>, _threads=(0,), _後片付けs=[], _type_equality_funcs={<type at remote 0x7eba00>: <instancemethod at remote 0xd750e0>, <type at remote 0x7e7820>: <instancemethod at remote 0xd75160>, <type at remote 0x7e30e0>: <instancemethod at remote 0xd75060>, <type at remote 0x7e7d20>: <instancemethod at remote 0xd751e0>, <type at remote 0x7f19e0...(truncated)
           _wait()
