python VM

実践はSmall Basicで!「インドの小学校で教えるプログラミングの授業」

こういう本が有ったので、読んでみた。インドはIT大国。どんな事を教えているかと思ったら basicでやってるのね。日本でも小学生からパソコンを教えるとか英語を教えるとか、世界に 打って出ていけるように必死になってるな。

どこかのママが調べてくれたのがあった。 世界のプログラミング教育事情

こちらは、その筋の先生。

プログラミング教育では、試行錯誤が論理的思考力よりも重要

オイラーも入れてみた。

Microsoft Small Basic 1.2 - 日本語

インターナショナルって謳っているので、マイクロソフトの考える世界標準かな。

International Small Basic Getting Started Guide

これを卒業したら、下記を見ればよい。

Small Basic API Reference

灯台下暗しで、

C:\Program Files (x86)\Microsoft\Small Basic\Samples

に、サンプルが置いてあるから、適当な所に取り出して、いじくり倒せばよい。エラーを 出した回数だけ、知力が増すぞ。あーだこーだが勉強さ。

いくつかのエラーが発見されました。
アクセスが拒否されました。(HSRESULTからの例外:0x80070005(E_ACCESSDENIED))

オリジナルな場所にあるサンプルで実行すると上記のエラー。で、きっと書き込み権限が 無いんだろうと思って、DeskTopに持ってきたら、実行出来た。オリジナルのソースである、*.sb に対して、*.pdb ってのと、*.exe と、SmallBasicLibrary.dllが作られていた。*.exeを ダブルクリックすると、そのままアプリが実行出来たよ。ちょいとアプリを作るには 便利なやつだな。起動するたびに、OneDriveを設定しろと言ってくるのはうざいけどね。

python VM

Python VM

a = 1 とだけ書いたt.pyを実行。

#0  run_file (fp=fp@entry=0x955780, filename=filename@entry=0x916280 L"t.py", p\
_cf=p_cf@entry=0x7fffffffe360) at ../Modules/main.c:323
#1  0x0000000000435443 in Py_Main (argc=argc@entry=2, argv=argv@entry=0x915010)\
 at ../Modules/main.c:780
#2  0x000000000041d14a in main (argc=2, argv=0x7fffffffe4d8) at ../Programs/pyt\
hon.c:69

python t.pyのように実行した場合は、run_file関数の中で処理が行われる。t.pyにimportが 含まれていると、importの膨大な処理が入ってきてめげるので、簡単なスクリプトがお得。 以下、処理の追跡結果。

(gdb) bt 5
#0  _PyEval_EvalCodeWithName (_co=_co@entry=0x7ffff7ea41c0, globals=globals@ent\
ry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30, args=args@entry=0x0, arg\
count=argcount@entry=0, kwnames=kwnames@entry=0x0, kwargs=kwargs@entry=0x8, kwc\
ount=kwcount@entry=0, kwstep=kwstep@entry=2, defs=defs@entry=0x0, defcount=defc\
ount@entry=0, kwdefs=kwdefs@entry=0x0, closure=closure@entry=0x0, name=name@ent\
ry=0x0, qualname=qualname@entry=0x0) at ../Python/ceval.c:3862
#1  0x0000000000530b58 in PyEval_EvalCodeEx (_co=_co@entry=0x7ffff7ea41c0, glob\
als=globals@entry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30, args=args\
@entry=0x0, argcount=argcount@entry=0, kws=kws@entry=0x0, kwcount=kwcount@entry\
=0, defs=defs@entry=0x0, defcount=defcount@entry=0, kwdefs=kwdefs@entry=0x0, cl\
osure=closure@entry=0x0) at ../Python/ceval.c:4140
#2  0x0000000000530ba2 in PyEval_EvalCode (co=co@entry=0x7ffff7ea41c0, globals=\
globals@entry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30) at ../Python/\
ceval.c:695
#3  0x00000000004230f4 in run_mod (mod=mod@entry=0x955aa8, filename=filename@en\
try=0x7ffff0b6f860, globals=globals@entry=0x7ffff7eb2a30, locals=locals@entry=0\
x7ffff7eb2a30, flags=flags@entry=0x7fffffffe360, arena=arena@entry=0x7ffff7e885\
20) at ../Python/pythonrun.c:980
#4  0x0000000000425974 in PyRun_FileExFlags (fp=fp@entry=0x955780, filename_str\
=filename_str@entry=0x7ffff7e599b8 "t.py", start=start@entry=257, globals=globa\
ls@entry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30, closeit=closeit@en\
try=1, flags=flags@entry=0x7fffffffe360) at ../Python/pythonrun.c:933
(More stack frames follow...)

ソースを参照しながら追って行くと気づくけど、returnの所に置いてある関数が主力の処理と なる。それ以前のものは、準備をしてるだけ。こういう書き方は、OSのシステムコールとか でも煩雑に行われているので、迷う事なく、深く潜って行けるぞ。

#0  _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at \
../Python/ceval.c:1220
#1  0x000000000052fb14 in PyEval_EvalFrameEx (f=f@entry=0x976e18, throwflag=thr\
owflag@entry=0) at ../Python/ceval.c:718
#2  0x000000000053053b in _PyEval_EvalCodeWithName (_co=_co@entry=0x7ffff7ea41c\
0, globals=globals@entry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30, ar\
gs=args@entry=0x0, argcount=argcount@entry=0, kwnames=kwnames@entry=0x0, kwargs\
=kwargs@entry=0x8, kwcount=kwcount@entry=0, kwstep=kwstep@entry=2, defs=defs@en\
try=0x0, defcount=defcount@entry=0, kwdefs=kwdefs@entry=0x0, closure=closure@en\
try=0x0, name=name@entry=0x0, qualname=qualname@entry=0x0) at ../Python/ceval.c\
:4119
#3  0x0000000000530b58 in PyEval_EvalCodeEx (_co=_co@entry=0x7ffff7ea41c0, glob\
als=globals@entry=0x7ffff7eb2a30, locals=locals@entry=0x7ffff7eb2a30, args=args\
@entry=0x0, argcount=argcount@entry=0, kws=kws@entry=0x0, kwcount=kwcount@entry\
=0, defs=defs@entry=0x0, defcount=defcount@entry=0, kwdefs=kwdefs@entry=0x0, cl\
osure=closure@entry=0x0) at ../Python/ceval.c:4140
(More stack frames follow...)

こんな風に、そろりそろりと潜っていくのが、観光と言うか探検のセオリーです。

解説書によると、三つの基本要素:CodeObject, FunctionObject, FrameObject を理解する必要があるそうだ。

CodeObjectってlispで言うatomの事かな。FunctionObjectはlambdaか。そしてFrameObjectは、実行環境と、今の所思っておこう。(後で、修正されるかも知れないので、真にうけないで)

また、FrameObjectは、モジュール、クラス、functionと別々にデータスタックを持つとな。 C語みたいにマシンべったりではなくて、整理されてるな。

ちょいと復習で、dis用コード

[cent py]$ cat a.py
def hoge(x):
    return x * x

y = hoge(321)
# import dis;import inspect as ins;dis.dis(ins.stack()[0][0].f_code)

普通に、disる。

[cent py]$ python3 -m dis a.py
  1           0 LOAD_CONST               0 (<code object hoge at 0x7fde75012f40, file "a.py", line 1>)
              2 LOAD_CONST               1 ('hoge')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (hoge)

  4           8 LOAD_NAME                0 (hoge)
             10 LOAD_CONST               2 (321)
             12 CALL_FUNCTION            1
             14 STORE_NAME               1 (y)
             16 LOAD_CONST               3 (None)
             18 RETURN_VALUE

肝心のdef内部が見えない。

[cent py]$ python3 -i a.py
>>> import dis
>>> dis.dis(hoge)
  2           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                0 (x)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE

こういう時は、codeを実行後もreplが回っているように、-iを 付けて起動。そして、disで関数をdisる。

gdb for python debug

前回ちらっと探し当てていた、gdb実行中にpythonのスタックを見られたりする拡張方法。 試してみたけど、py-btとかは見つからないと拒否されていた。

試しに、gdbの起動時に、gdb -x python-gdb.py python とか、gdbの起動後に、source python-gdb.py すると、使えるようになる。

何でかな? ~/.gdbinitに書いたやつが無視されてるっぽい。穴のあくほど、説明書を 読みましたよ。そして裏読みです。pythonをコンパイルしたdirで実行ってなってるな。 オイラーは、残骸を全部消して、python-gdb.pyだけを残していたんだ。 ひょっとして、gdbを起動したdir内に、python本体が必要かも知れんぞ。

ものは試しと、

[cent ~]$ tree py
py
├── a.py
├── pg.py
├── python
└── python-gdb.py

のようにしてみた。そしたら、説明書のごとく、普通に動いた。これって、gdbはwork-dirに pythonが有ると、お供のpython-gdb.pyって言う初期化ファイルを特別に読み取って くれるのではなかろうか。

もし、そうなら、今使ってる(CentOS様ご提供の)gdbに仕掛けが施されているに違いない。 痕跡を探してみる。

[cent ~]$ strings /usr/bin/gdb | grep python
libpython2.7.so.1.0
  :
set auto-load python-scripts
show auto-load python-scripts
info auto-load python-scripts
  :

こういうのは見つかったけど、ずばりのpython-gdb.pyは出てこないですね。

しゃーない、ソース嫁。

[cent tmp]$ tar Jxf gdb-7.12.1.tar.xz
[cent gdb-7.12.1]$ find . -name '*.[ch]' | xargs grep -l -- -gdb.py
./gdb/extension-priv.h
./gdb/python/python.c
./gdb/auto-load.c
./gdb/extension.c

なにか、それっぽいのが引っかかってきたぞ。

その前に、新しいgdbを入れておくか。インストールしようとしたら、infoを作れないと言われてSTOPした。makeinfoが必要らしいんだけど、入っていない。 texinfoを入れると付いて くるそうだ。それから、pythonって名前のやつしか認識しないようなので、もし、python3系 を使いたかったら、pythonにリンクしておけ。

python.cに名前の定義が、auto-load.cに、pythonと言う拡張言語用のスクリプト読み込み ルーチンが登録されてた。で、ソースを見てる時、何が読み込まれた知る方法が示されて いたので、試してみる。

[cent py]$ gdb -q python
Reading symbols from python...done.
(gdb) info auto-load python-scripts
Loaded  Script
Yes     /home/sakae/py/python-gdb.py
(gdb) q
[cent py]$ mv python python3
[cent py]$ gdb -q python3
Reading symbols from python3...done.
(gdb) info auto-load python-scripts
No auto-load scripts.
[cent py]$ gdb -q /my/bin/python
Reading symbols from /my/bin/python...done.
(gdb) info auto-load python-scripts
No auto-load scripts.

このように、debug対象のプログラム名は、work-dirにあるpython(3)、そいつに-gdb.pyをくっつけたファイルが有る場合のみ、auto-loadされる。python開発者の便を図る特殊 仕様なんだな。(installしなくても、lib等が使えるように、sys.pathがセットされてるぞ)

早速、gdbとpythonの癒着の成果を使ってみる。

Breakpoint 2, PyEval_EvalFrameEx (
    f=f@entry=Frame 0x97c698, for file a.py, line 1, in hoge (x=321),
    throwflag=throwflag@entry=0) at ../Python/ceval.c:716
716     {
(gdb) py-list
  >1    def hoge(x):
   2        return x * x
   3
   4    y = hoge(321)
(gdb) py-bt
Traceback (most recent call first):
  File "a.py", line 1, in hoge
    def hoge(x):
  File "a.py", line 4, in <module>
    y = hoge(321)

いよいよVMの始動。実行を進めていくと

(gdb) s
717         PyThreadState *tstate = PyThreadState_GET();
(gdb)
718         return tstate->interp->eval_frame(f, throwflag);
(gdb)
_PyEval_EvalFrameDefault (
    f=Frame 0x97c698, for file a.py, line 1, in hoge (x=321), throwflag=0)
    at ../Python/ceval.c:723
723     {

大分進んだ所

(gdb) py-bt
Traceback (most recent call first):
  File "a.py", line 2, in hoge
    return x * x
  File "a.py", line 4, in <module>
    y = hoge(321)
(gdb) py-list
   1    def hoge(x):
  >2        return x * x
   3
   4    y = hoge(321)

これに対する、C語の部分も分かるので、プチ嬉しいぞ。と、いささか猫に小判状態であります。

virtualBox Disk compress

Vboxも長く使っていると、Diskが肥大化してくる。VMWARE Playerだと、open-vm-tools相当を入れておけば

[fb11: sakae]# vmware-toolbox-cmd disk shrink /
Please disregard any warnings about disk space for the duration of shrink process.
Progress: 54 [======>    ]

こんな風にして圧縮出来るんだけど、vboxの場合は如何に?

VirtualBoxの仮想ディスク容量を圧縮する方法

VirtualBoxの仮想ディスクを圧縮する

仕組みは同じだけど、一手間余計にかかる。まず、Windows上のdiskファイルサイズ

2017/03/28  05:38     9,895,411,712 Debian.vdi

続いて、Debianでの使用状態

[debian ~]$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda1       19620732 4304940  14296044  24% /

約5Gの無駄が有るな。ゴミをzeroで埋めて、gcの目印を作る。

root@debian:/home/sakae# dd if=/dev/zero of=/zero bs=4k
dd: error writing '/zero': No space left on device
3813833+0 records in
3813832+0 records out
15621455872 bytes (16 GB) copied, 29.0207 s, 538 MB/s

続いて、ごみの /zeroを削除しとく。そしてdebianを落とす。

C:\Users\sakae\VirtualBox VMs>"c:\Program Files\Oracle\VirtualBox\VBoxManage.exe" list hdds
UUID:           72f6c0b3-9639-4676-9dab-4b16a507e756
Parent UUID:    base
State:          created
Type:           normal (base)
Location:       C:\Users\sakae\VirtualBox VMs\Debian\Debian.vdi
Storage format: VDI
Capacity:       20480 MBytes
Encryption:     disabled

idが分かったら、それを指定して、圧縮。

C:\Users\sakae\VirtualBox VMs>"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyhd 72f6c0b3-9639-4676-9dab-4b16a507e756 --compact
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

2017/03/14  14:20     5,313,134,592 Debian.vdi

diskを取り戻せたぞ。半年に一度ぐらいは実行しよう。

VMWAREでゲストしてる、OpenBSDはどうする? 移植しようとしたら、作りが異端児なんで 拒否されたぞ。

etc

やっぱり VM となれば、Forthは外せません。

Forthを作るのじゃ

Forth関連の情報サイトのまとめ

SmallBasicは、Windowsでしか動かんからなあ。その点、こちらはブラウザーが有れば、 どこでもおk。

p5.js

p5.min.jsを落としてきて、index.htmlをクリック。後は、マウスでぐりぐり。

[debian tmp]$ cat index.html
<html>
  <head>
    <script src="p5.min.js"></script>
    <script src="testme.js"></script>
  </head>
  <body>
  </body>
</html>
[debian tmp]$ cat testme.js
function setup() {
  createCanvas(640, 480);
}

function draw() {
  if (mouseIsPressed) {
    fill(0);
  } else {
    fill(255);
  }
  ellipse(mouseX, mouseY, 80, 80);
}

なんか、プロセッシングに源流が有るそうですよ。javascript恐るべし。