python and gdb

ちょっと周回遅れかも知れないけれど、pythonブーム。

Python 3.6の新機能

機械学習の基礎の基礎、行列計算に欠かせないNumPyの基本的な使い方 (1/2)

これはもう、真面目にやっとくか。本家へ行ってみた。 python かっこいいページだな。表面を見ていてはいけません。注目は、Use Python for... と銘打った所だと思うぞ。

Web系に良し、グラフィック系も任せておけ。科学技術関係もお手の物。他の分野でも大活躍 しますよってね。これって一頃のPerlじゃん。何でも来い。

Pythonに咬まれるな : 注意すべきセキュリティリスクのリスト

但し、誰でも扱えるって事は、悪さをする連中にも公平である。注意めされ。 Status of Python branchesによると、2系は、 2020年までの命の予定。移行は計画的に。

python と gdb の癒着

前回思わぬ事からpythonをコンパイルしちゃった。コンパイルに要した時間は2分弱で、インストールには30秒ほどかかった。パイソンのコンパイルってこんなに速かったかな。 ああ、パソコン自体が速くなったからだな。

じゃ、コンパイルに長大な時間がかかったgdbをやっつけたら、何分かかるんだろう? うろ覚えの記憶では、7機で優に15分はかかっていたと思った。 gdb7.12と言う最新版を取り寄せて、fedoraでコンパイルしたら、6分強だった。時代は進歩 してますなあ。

で、折角なんで、gdbとpythonの関係がどうなってるか探ってみる事にした。なんでも、gdbで 上手くpythonを扱えるとかの記事を読んだ記憶があるからね。

DebuggingWithGdb

Extending gdb using Python (en)

Pythonを用いたGDBのスクリプティング 軽く、上記のような記事が引っかかってきた。面白そうなので実習。

[sakae@fedora tmp]$ gdb
GNU gdb (GDB) Fedora 7.11.1-86.fc24
  :
(gdb) pi
>>> dir()
['GdbRemoveReadlineFinder', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'gdb', 'glob', 'sys']
>>> import random
>>> random.random()
0.49227751963087696
>>> random.random()
0.0506881991869238
>>>   ;; exit with Ctl-D
(gdb)

fedora付属のgdbでは、pythonと癒着するような設定がなされていた。gdbコマンドが拡張され、pythonを起動出来る。モジュールにgdbが追加されてるのが、動かぬ証拠。 ずぶすぶに癒着してますなあ。

また、/gdb-7.12/gdb/testsuite/gdb.python には、面白そうなテストケースが載っててぞ。また、gdbは多国籍企業みたいで、色々な方面と関係を持っているな。たまには、こういう所にも目を光らせてみるものだ。

[sakae@fedora testsuite]$ ls -d gdb.*
gdb.ada     gdb.compile  gdb.gdb       gdb.modula2  gdb.perf     gdb.threads
gdb.arch    gdb.cp       gdb.go        gdb.multi    gdb.python   gdb.trace
gdb.asm     gdb.disasm   gdb.guile     gdb.objc     gdb.reverse  gdb.xml
gdb.base    gdb.dlang    gdb.java      gdb.opencl   gdb.rust
gdb.btrace  gdb.dwarf2   gdb.linespec  gdb.opt      gdb.server
gdb.cell    gdb.fortran  gdb.mi        gdb.pascal   gdb.stabs

他にも、gdb/python/ とかが有って、この中にgdbモジュールが置いてあった。これが、 gdb用語で言うdata-dirとして、

[sakae@fedora gdb]$ ls /usr/share/gdb
auto-load  guile  python  syscalls

こういう所に移動してるのね。

python 3.6 on OpenBSD

最新のpythonがOpenBSDに入るか、試してみる事にする。下調べとして、portsの下に あるpythonで、どんなpatchが当たっているか確認。 OpenBSD用に色々なパッチを当てているようなら、尻尾を巻いて逃げ出す予定。 結果は、皆軽微なものばかりで、本体にパッチを当てるというのは無かった。 非の打ち所がないって事だな。

喜びいさんで、gdbにかかるようにconfigure。gmakeしたら、

Python-3.6.0/Python/random.c で、sys/random.h が無いんでコンパイルを継続 出来ませんって言われたぞ。調べたらOpenBSDの標準の所にそんなヘッダーは無い。

多分、システム備え付けのrandomが欲しいのだろう。こういう時は、man -s 3 random だな。 やばいから使うなって警告が有って、OpenBSDでは、arc4randomをお勧めしますってなってた。 どうしても使いたいなら、stdlib.h を、includeするんだぞと思し召しされた。

その通りにしたら、コンパイル完了。インストールは、長大なテストスイーツが有るおかげで、 2分近くかかったぞ。

使い道

以前出てきたスクリプト。使いまわしですな。

[ob: tmp]$ cat pg.py
from string import ascii_lowercase, ascii_uppercase, digits
import random

random.seed( 1234 )
sel = random.sample(digits, 4) + \
      random.sample('@.,_#%+=;:~', 4) + \
      random.sample(ascii_uppercase, 4) + \
      random.sample(ascii_lowercase, 4)
random.shuffle(sel)
print(''.join(sel[:10]))

で、こいつを実行した時、モジュール内のC語のルーチン、random_seed で止めて、 そこに至る道筋を出したい。そんな時は、下記のようなgdbを操るpyスクリプトを用意 するそうな。(受け売り、スマソ)

[ob: tmp]$ cat cont.py
gdb.execute('b random_seed')
gdb.execute('run pg.py')
bt = gdb.execute('bt', to_string=True)
for line in bt.split('\n'):
  words = line.split(' ')
  print '%s %s' % (words[0], words[-1])

何となく、自動化の雰囲気。そして、gdbのスクリプトとして指定したpythonスクリプトを 指定して実行。

[ob: tmp]$ egdb -q -x cont.py python
Reading symbols from python...done.
Function "random_seed" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (random_seed) pending.

Breakpoint 1, random_seed (self=0x4ef4ecf35e8, args=0x4eef5c8a058)
    at /home/sakae/MINE/Python-3.6.0/Modules/_randommodule.c:236
236     {
#0 /home/sakae/MINE/Python-3.6.0/Modules/_randommodule.c:236
#1 /home/sakae/MINE/Python-3.6.0/Modules/_randommodule.c:442
#2 ../Objects/typeobject.c:895
#3 ../Objects/abstract.c:2316
#4 ../Objects/abstract.c:2480
#5 ../Python/ceval.c:4812
#6 ../Python/ceval.c:3275
#7 ../Python/ceval.c:718
#8 ../Python/ceval.c:4119
#9 ../Python/ceval.c:4140
#10 ../Python/ceval.c:695
#11 ../Python/bltinmodule.c:974
#12 ../Python/clinic/bltinmodule.c.h:281
#13 ../Objects/methodobject.c:114
#14 ../Python/ceval.c:5053
#15 ../Python/ceval.c:3357
#16 ../Python/ceval.c:718
#17 ../Python/ceval.c:4119
#18 ../Python/ceval.c:4929
#19 ../Python/ceval.c:4809
#20 ../Python/ceval.c:3275
#21 ../Python/ceval.c:718
#22 ../Python/ceval.c:4870
#23 ../Python/ceval.c:4905
#24 ../Python/ceval.c:4809
#25 ../Python/ceval.c:3275
#26 ../Python/ceval.c:718
#27 ../Python/ceval.c:4870
#28 ../Python/ceval.c:4905
#29 ../Python/ceval.c:4809
#30 ../Python/ceval.c:3275
#31 ../Python/ceval.c:718
---Type <return> to continue, or q <return> to quit---
#32 ../Python/ceval.c:4870
#33 ../Python/ceval.c:4905
#34 ../Python/ceval.c:4809
#35 ../Python/ceval.c:3275
#36 ../Python/ceval.c:718
#37 ../Python/ceval.c:4870
#38 ../Python/ceval.c:4972
#39 ../Objects/abstract.c:2295
#40 ../Objects/abstract.c:2780
#41 ../Python/import.c:1592
#42 ../Python/ceval.c:5166
#43 ../Python/ceval.c:2842
#44 ../Python/ceval.c:718
#45 ../Python/ceval.c:4119
#46 ../Python/ceval.c:4140
#47 ../Python/ceval.c:695
#48 ../Python/pythonrun.c:980
#49 ../Python/pythonrun.c:933
#50 ../Python/pythonrun.c:396
#51 ../Python/pythonrun.c:80
#52 ../Modules/main.c:320
#53 ../Modules/main.c:780
#54 ../Programs/python.c:69

(gdb) c
Continuing.

Breakpoint 1, random_seed (self=0x4ef4ecf35e8, args=0x4eec62acc88)
    at /home/sakae/MINE/Python-3.6.0/Modules/_randommodule.c:236
236     {
(gdb)
Continuing.

Breakpoint 1, random_seed (self=0x4ef4ecf35e8, args=0x4eebc2f0128)
    at /home/sakae/MINE/Python-3.6.0/Modules/_randommodule.c:236
236     {
(gdb)
Continuing.
.H@9:Ylz7A
[Inferior 1 (process 52498) exited normally]

自動と言いつつ、半自動になってしまったけど、その辺はもう少しコントロールスクリプトを いじれば、何とかなるだろう。

23. gdb Support

上ではcont.pyの中に実行するスクリプトを埋め込んだけど、下記のようにすると汎用的になる。

[ob: tmp]$ egdb -q -x cont.py --args python pg.py

debug対象はpythonに限った事ではない。C語で書いたハロワでもかまわない。ただ止めても 面白くないので、文字列を書き出しているはずの write にBPを置いて、辿ってきた道を 辿ってみる。cont.pyをちょっと修正。

gdb.execute('b write' )
gdb.execute('run')
bt = gdb.execute('bt',  to_string=True)
for line in bt.split('\n'):
  words = line.split(' ')
  print '%s %s' % (words[0], words[-1])
gdb.execute('c')
gdb.execute('q')
[ob: tmp]$ egdb -q -x cont.py a.out
Reading symbols from a.out...done.
Function "write" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (write) pending.

Breakpoint 1, *_libc_write_cancel (fd=1, buf=0x1eb901875000, nbytes=6)
    at /usr/src/lib/libc/sys/w_write.c:23
23      {
#0 /usr/src/lib/libc/sys/w_write.c:23
#1 /usr/src/lib/libc/stdio/stdio.c:66
#2 /usr/src/lib/libc/stdio/fflush.c:80
#3 /usr/src/lib/libc/stdio/fvwrite.c:191
#4 /usr/src/lib/libc/stdio/puts.c:59
#5 t.c:4

hello
[Inferior 1 (process 93798) exited with code 012]

cont.pyに引数として、止まって欲しい所を渡したかったんだけど、それは不可能だった。 ちょっと残念。

普通の人のためのpython debugger (pdb)

上のgdbとpythonの癒着は、はっきり言って、その筋の人用。普通の人は、パイソンのスクリプトレベルで、ソースデバッグしたいはず。

パイソンバッテリーにも、勿論debuggerが詰まっている。パイソン語で書かれて、libの下に、 pdb.pyって名前で置いてある。モジュールになってるんで、python -m pdb bug.py とか して、虫取りに励む事になる。

でも、これ虫取り中に対象ソースが常に見られないので、ちょっと不便。そこで、emacsの 登場になる。(あーあ、普通な人じゃ無くなったな。道を逸れてる)

使う前に、ちょっとおまじないのスクリプトをパスの通った所に置いておく。これを やっておくと、pdbの起動時にpdb.pyの在処を入力する手間を省ける。

[ob: ~]$ cat /usr/local/bin/pdb
#!/bin/sh
exec python -m pdb $1 $2 $3 $4 $5 $6 $7 $8 $9

emacsを起動したら、M-x pdb bug.py すれば良い。ソースを眺めながら、すいすいと 飛び回れるぞ。

26.2. pdb — Python デバッガ

Python で標準添付の Debuggerのpdb を利用してデバッグするメモ

Current directory is /tmp/
> /tmp/pg.py(1)<module>()
-> from string import ascii_lowercase, ascii_uppercase, digits
(Pdb) n
> /tmp/pg.py(2)<module>()
-> import random
(Pdb)
> /tmp/pg.py(4)<module>()
-> random.seed( 12345 )
(Pdb) b random.sample
Breakpoint 1 at /home/sakae/MINE/lib/python3.6/random.py:282
(Pdb) c
> /home/sakae/MINE/lib/python3.6/random.py(310)sample()
-> if isinstance(population, _Set):
(Pdb) n
> /home/sakae/MINE/lib/python3.6/random.py(312)sample()
-> if not isinstance(population, _Sequence):

こんな具合にやれば良い。

live debug

と言う、観察会を実施します。

ええ、anacondaがPython3.6になったのは、招致してますよ。3.5.2で3.6の機能を試したら どうなるか? まずは、文法崩れの場合をやってみる。

[debian tmp]$ python spam.py
  File "spam.py", line 2
    a = {:_x}'.format(0xFFFFFFFF)
         ^
SyntaxError: invalid syntax

次は、それを直したもの。

[debian tmp]$ python spam.py
Traceback (most recent call last):
  File "spam.py", line 1, in <module>
    a = '{:_x}'.format(0xFFFFFFFF)
ValueError: Invalid format specifier

やはり門前払いでした。(当然だろうに。微妙にエラーメッセージも変わっている。)番兵は何処にたむろしているんでしょう。軽く探索します。

[debian Python-3.5.2]$ find . -name '*.py' | xargs fgrep -l 'invalid syntax'
./Lib/test/test_import/__init__.py
./Lib/test/test_unpack_ex.py
./Lib/test/test_syntax.py
./Lib/test/test_grammar.py
./Lib/test/test_coroutines.py
./Lib/test/test_genexps.py
./Lib/configparser.py
./Lib/pydoc_data/topics.py

どうも、こちら方面では無いようなので、別の所を探します。

[debian Python-3.5.2]$ find . -name '*.[ch]' | xargs fgrep -l 'invalid syntax'
./Python/pythonrun.c

これを見ると、err_input()の中で出してるみたい。

[debian tmp]$ gdb -q python
Reading symbols from python...done.
(gdb) b err_input
Function "err_input" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (err_input) pending.
(gdb) r spam.py
Starting program: /home/sakae/anaconda3/bin/python spam.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, err_input (err=0x7fffffffdd60) at Python/pythonrun.c:1269
1269    Python/pythonrun.c: そのようなファイルやディレクトリはありません.
(gdb) bt
#0  err_input (err=0x7fffffffdd60) at Python/pythonrun.c:1269
#1  0x00007ffff7a55e0a in PyParser_ASTFromFileObject (fp=<optimized out>,
    filename=0x7ffff696ebc8, enc=<optimized out>, start=<optimized out>,
    ps1=<optimized out>, ps2=<optimized out>, flags=<optimized out>,
    errcode=0x0, arena=0x69acb0) at Python/pythonrun.c:1169
#2  0x00007ffff7a55fdd in PyRun_FileExFlags (fp=0x6f9070,
    filename_str=<optimized out>, start=257, globals=0x7ffff7e4a1c8,
    locals=0x7ffff7e4a1c8, closeit=1, flags=0x7fffffffdf90)
    at Python/pythonrun.c:922
#3  0x00007ffff7a57623 in PyRun_SimpleFileExFlags (fp=0x6f9070,
    filename=<optimized out>, closeit=1, flags=0x7fffffffdf90)
    at Python/pythonrun.c:396
#4  0x00007ffff7a728c7 in run_file (p_cf=0x7fffffffdf90,
    filename=0x603300 L"spam.py", fp=0x6f9070) at Modules/main.c:318
#5  Py_Main (argc=<optimized out>, argv=<optimized out>) at Modules/main.c:769
#6  0x0000000000400add in main (argc=2, argv=0x7fffffffe108)
    at ./Programs/python.c:65

あらら、ソースが無い。ああ、アナコンダのpythonだったわい。こやつは、どうコンパイル されてるのか、一応確認しとく。

[debian tmp]$ python3.5m-config --cflags
-I/home/sakae/anaconda3/include/python3.5m -I/home/sakae/anaconda3/include/python3.5m  -Wsign-compare  -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
[debian tmp]$ file /home/sakae/anaconda3/bin/python3.5m
/home/sakae/anaconda3/bin/python3.5m: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.9, not stripped

pg.pyで、random_seedにBPを置いたら

Breakpoint 1, random_seed (self=0x746af8, args=0x7ffff7e9d048)
    at /home/ilan/minonda/conda-bld/work/Python-3.5.2/Modules/_randommodule.c:208
208     /home/ilan/minonda/conda-bld/work/Python-3.5.2/Modules/_randommodule.c: そのようなファイルやディレクトリはありません.

へぇー、アナコンダの元はminicondaでそれを作った人の名前が割れたな。3.6でも、彼だか彼女が担当してくれてるのかな。プチ調べる楽しみが出来たな。

PyParser_ParseFileObject あたりを調べると、もう少し詳しい情報が得られそうだな。 どこかに、RHGならぬ、Python hacking Guide は無いものかしら。求む、青木二世。 (こう書いて、にやにやする人は、自他ともに認めるロートルです)

CentOS 7 の補足

気の迷いで入れたCentOSで、しこしことコンパイルに勤しんでいる。

問題発生。pythonを自前でコンパイルして入れたら、pipも付いてきたんだけど、こやつで pip install hoge とかやると、集積所がhttpsになってるんで、アクセス出来ないと 抜かす。そんな馬鹿な?

httpsを捌くには、sslライブラリーが多分必要になってくるだろう。そしてそれはダイナミックロードされるんだろうと当たりを付ける。で、該当箇所を見たら、そんなの無かった。

Debian様はどうかなと思って調べると、ちゃんと鎮座してる。と言う事は、またLinuxの 虐めにあったな。訴えてやるぞ。まったく、あほんだら。

感で、openssl-develを入れてから、pythonを作り直したら、httpsも扱えるpipが出来上がったぞ。configの時に、警告ぐらい出してくれたっていいと思うぞ。

それから、newlispを入れようとしたら、こちらは libffi.hが見つかりませんと、素直に エラーになってくれた。libffi-develを入れたのは言うまでもない。

こういう、試行錯誤を自homeでやっていると、ssdへの負担になる。(寿命が確実に縮まる) そんな訳で、diskの書き換えが頻繁に発生するコンパイル作業は、RAMDISKになってる /tmp を使うようにしてる。rebootすれば、以前の痕跡は跡形もなく無くなるから便利。

世の中のWindowsとか言うOSは、そういうtemp健忘症には罹患してないようです。 後生大事に、過去のものを保存するようです。

それを逆手に取って、tempを、ゴミ捨て場にしましょって提案してる記事に出くわした。

ファイルやノートを整理できない人はとにかく「temp」に保存せよ

DeskTopが汚い人は一度試してみたら。適当に肥大した時、更新日時でソートして、古い やつは棄てる。こういう整理法が昔は盛んに宣伝されてたな。

etc

Pythonで一部分のみ安全に?monkey patchしたい

26.6. unittest.mock — 入門

9.4. decimal — 十進固定及び浮動小数点数の算術演算

6.1. string — 一般的な文字列操作