FreeBSD 12.2-RELESE, [ng]prolog

FreeBSD 12.2

FreeBSD 12.2が出た。マイナーなんで余り宣伝されていないみたい。この分だと来年3月に出産予定の13.0も宣伝されないだろうかしら? 何はともあれアップグレードしておくか。 どうやるんだっけかな? 1年に一回の事だから忘れてしまう。 FreeBSD ハンドブック の17章に説明があった。ドキュメントがきちんとしてるので助かるな。

root@fb:~ # freebsd-update -r 12.2-RELEASE upgrade
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 12.1-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata files... done.
Inspecting system... done.

The following components of FreeBSD seem to be installed:
kernel/generic world/base world/doc

The following components of FreeBSD do not seem to be installed:
kernel/generic-dbg world/base-dbg world/lib32 world/lib32-dbg

Does this look reasonable (y/n)? y

 -r で新しい版を指定するんだった。

Fetching metadata signature for 12.2-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata patches. done.
Applying metadata patches... done.
Fetching 1 metadata files... done.
Inspecting system... done.
Fetching files from 12.1-RELEASE for merging... done.
Preparing to download files...
  :
8920....8930....8940....8950....8960....8970....8980....8990....9000....9010...
done.
Applying patches...
Fetching 505 files... ....10....20....30...
 ======    marge file by hand ======
  :
/var/yp/Makefile.dist
To install the downloaded upgrades, run "/usr/sbin/freebsd-update install".
src component not installed, skipped
Installing updates...
Kernel updates have been installed.  Please reboot and run
"/usr/sbin/freebsd-update install" again to finish installing updates.

勝手にやってくれるかと思ったら、 /etc/ssh/sshd_conf に変更を加えていたんで、手動でのマージを要求された。viが立ち上がって来るので、古い部分を消したよ。何も知らないと右往左往させられてしまうだろう。それに続いて、変更されるファイルが永遠とリストアップされてきた。ちゃんと見せてあげてるんで、後は自己責任ねって態度。ちょい閲覧が負担になるな。

root@fb:~ #  freebsd-update install
src component not installed, skipped
Installing updates... done.
root@fb:~ # uname -a
FreeBSD fb 12.2-RELEASE FreeBSD 12.2-RELEASE r366954 GENERIC  amd64

そして、パッチ適用と言うインストール作業を執り行う。再起動してもう一度upgrade install。二回目のそれは、後始末なんだな。こうして無事に新しい版になった。12.2から13.0へみたいな、メジャーアップグレードの場合は、pkgもアップグレードが必要。ABIが変わるためである。

このあたりは、OpenBSDはちょっと方針が違う。メジャー/マイナーって考えは無い。upgradeは、バイナリーレベルでのインストール。設定ファイルは、RELESEの提供分はいじらないで、差分ファイルを追加して、それを保守する方針。だから、この方針に従っている限り、マージは全て自動で行われてしまう。

pkgな有無を言わせずに、アップグレードを要求される。なんたって、根幹のlibcが版を上げてしまうので、ユーザーランド側のパッケージも版を上げざるを得ない。古い物をずっと使い続ける手法は(アップグレードに手を出す限り)用意されていない。セキュリティー原理主義の面目が遺憾なく発揮されている。

root@fb:~ # df -h
Filesystem      Size    Used   Avail Capacity  Mounted on
/dev/ada0s1a     15G    3.5G    9.9G    26%    /
devfs           1.0K    1.0K      0B   100%    /dev
/dev/ada1e       15G    3.4G     11G    24%    /EXTd
tmpfs           1.0G    4.0K    1.0G     0%    /tmp

で、折角ハンドブックを開いたら暇時間にうろうろしてたら、DISK増設の説明が出てた。バーチャルボックス上で動いているので、OpenBSDでやった経験を元にDISK増設をしたよ。 local/ を、新DISKに逃がした。portsから気前よく入れると、こういう具合に膨らむのね。DISKの肥やしを増やさないで丁寧に使えよ。

nprolog

前回、nprologをOpenBSDに移植した。そのままでは、メモリー不足で下記のように起動を拒否された。で、ulimitを使って、(やみくもに)メモリーの使用制限を緩めた。本当はどれぐらい必要なの? アプリの外観検査をしてみる。

vbox$ ./npl
ksh: ./npl: Cannot allocate memory
vbox$ size npl
text    data    bss     dec     hex
200113  9292    804018248       804227653       2fef8a45

コードエリアは200113必要。規定値が入っている変数エリアは9292必要。場所だけ確保したい変数エリアは、とんでもなく大きい804018248も必要。これが災いしてるんだな。

多分、カーネルがアプリをロードしようとした時、制限オーバーで拒否したんだろう。 readelfで確認。

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [12] .text             PROGBITS        00005d70 004d70 02b8ac 00  AX  0   0 16
  [24] .data             PROGBITS        20001520 031520 001f2c 00  WA  0   0  4
  [25] .bss              NOBITS          2000344c 03344c 2fec5848 00  WA  0   0 4
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x004d70 0x00005d70 0x00005d70 0x2c200 0x2c200 R E 0x1000
  LOAD           0x031000 0x20000000 0x20000000 0x00520 0x00520 RW  0x1000
  LOAD           0x031520 0x20001520 0x20001520 0x01f2c 0x2fec7774 RW  0x1000

Program Header でも、該当する所を列挙。順番に.text,.data,.bssと思われる(Flgの状況より)。

上の予測が当たっているか、カーネルを調べてみる。 多分、ユーザーに知らされるエラーは、内部的には定数で定義されてるはず。

vbox$ cd /usr/src/sys/sys/
vbox$ egrep 'Cannot allocate memory' *.h
errno.h:#define ENOMEM          12      /* Cannot allocate memory */

ふむ、ENOMEMがその名前なんだな。今度はカーネルエリアにある exec_elf.c で、名前を頼りに 該当箇所を探す。

/*
 * Allocate the segment header array and setup to collect
 * the section sizes and offsets
 */
ws->psections = mallocarray(ws->npsections, sizeof(Elf_Phdr),
    M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO);
if (ws->psections == NULL)
        return ENOMEM;
ws->psectionslen = ws->npsections * sizeof(Elf_Phdr);

ENOMEMは、他にも数か所で使われていたけど、雰囲気的に、ここになるな。本当は、大好きなカーネルデバッグ観光をしたいのだけど、残念な事に6.7 -> 6.8 にしちゃったおかげで、環境が無い。

しょうがないので、execveのmanを見て億(ナイス誤変換)。

[ENOMEM]           The new process requires more virtual memory than is
                   allowed by the imposed maximum (getrlimit(2)).

ふとdebian(64Bit)の中に、qemuで動くカーネル観光用がある事を思い出した。起動したらOpenBSD 6.6と出て来た。こいつでちょいと実験。

int heap[256 * 1000 * 1000];

int main(){
        heap[12] = 34;
}

256Mのint配列を用意しただけ(mainの中はおまけ)。これで走らせてみる。

vm$ ./a.out
ksh: ./a.out: Cannot allocate memory
vm$ size a.out
text    data    bss     dec     hex
1240    196     1024000045      1024001481      3d0905c9

bssのサイズは256M個のintなんで、その4倍のバイト数になってる。湯水のごとく状態。 で、カーネル側は sys_execve にBPを置いて追っ手く。

        /* see if we can run it. */
        if ((error = check_exec(p, &pack)) != 0) {
=>              goto freehdr;
        }

kern_exec.c の299行目あたりで、チェックされてる。

(gdb) p error
$2 = 12

返ってきたエラーは、メモリーを確保出来ないってやつね。以上で確認終了。

npl.hの冒頭付近にメモリーマップが示されている。

memory map
address
0          - 17,000,000  heap area
17,000,001 - 20,000,000  working area
20,000,001 - 40,000,000  variant area
*/
#define CELLSIZE    20000000  // this is max on raspberryPI1B. If parsonal comp\
uter 30000000 is OK
 :
extern cell heap[CELLSIZE];
extern int variant[VARIANTSIZE];
extern int stack[STACKSIZE];

どうやらOpenBSDのデフォでは、ラズパイより厳しいメモリー利用をユーザーに課しているようだ。メモリーを湯水のごとく使うなとな。所で、variantって言う(巨大な)配列は、どんなデータを格納してるんだろう。intってなってるから、int == char と思って良いのかな。いわゆるatom、名前が入るか?

おいおい、ソースを見て行けば、秘密が解けるだろう。

前回あげた On Lispの中にある、prologの節。前にある複数の節を元に組み立てられている。 ポール・グレアムさんの渾身のマクロ本。歯が立たないと言うか、理解するに多大な時間が必要そう。ふと、グレアムさんの本がもう一冊(ANSI CommonLisp)あったので、調べてみたら、超簡易版のprologが解説されてた。こちらで概要を掴もう。

gprolog

nprologの勉強ついでに、一般系も入れてみる。swi-prologは巨大なので避ける事にした。すると残りはgprologって事になる。i386版のOpenBSDで入れようとしたら、そんなの無いと言う。 portsのMakefileを調べたら、

BROKEN-i386=            various errors from gplc during build (segv, "fatal error", etc at various different points)
BROKEN-powerpc=         ld.so: pl2wam: relocation failed

パッケージング出来ない理由が、こんな具合に説明されてた。しょうがないのでamd64版に入れてあげたよ。後は、資料収集だな。

The GNU Prolog web site

prolog tutorial(PDF)

Prolog入門

お気楽 Prolog プログラミング入門

debian(64bit)にも入れようとしたら、そんなaptは種々の理由で用意されていなかった。野良で入れたよ。make -j4 したら、わずか10秒で完了。あっけない。

sakae@pen:~$ gprolog
GNU Prolog 1.4.5 (64 bits)
Compiled Nov  1 2020, 16:31:17 with gcc
By Daniel Diaz
Copyright (C) 1999-2020 Daniel Diaz
| ?- cry(cat, mew).
uncaught exception: error(existence_error(procedure,cry/2),top_level/0)

猫はmewと鳴きますってのを登録しようとしたら、エラッタ。mewなんて言うメーラーは、今時流行らないから拒否されたんか。cry(dog, bow). でも駄目。ひょっしてトップレベルでは、宣言出来ないのか。

したらemacsから使うのが吉だろう。サフィックスが pl だとperlが優先されるので、下記を指定して、prologを優先させてあげる。

(setq auto-mode-alist
      (append '(("\\.pl" . prolog-mode))
       auto-mode-alist))

first light. no, first write.

| ?- ['/tmp/cry.pl'].
compiling /tmp/cry.pl for byte code...
/tmp/cry.pl compiled, 2 lines read - 298 bytes written, 14 ms

(2 ms) yes
| ?- listing.

% file: /tmp/cry.pl

cry(cat, mew).
cry(dog, bow).

yes

ファイルに書いておいて、それをC-c C-l でロードするのが、正しい道なのね。それじゃ、猫の鳴き声を追加。

| ?- cry(cat, What).

What = mew ? ;

What = mya

yes

猫は何と鳴く? mewとかmyaと鳴く。それじゃ、山寺の和尚さんのように、鞠はけりたし 鞠はなし 猫をかん袋に押し込んで、ポンとけりゃ、にゃんと鳴くとかも追加する。

そんな事より、かん袋って何よ。最近うるさいぐらいにTVに出て来る、東大のクイズ王に問いたいぞ。調べてみたら、紙袋が訛ったものらしい。古くは、太閤秀吉の時代、くるみ餅菓子舗の主人が太閤の前で瓦をばんばん投げると、紙の袋がヒラヒラと舞って行くようで、それを見た秀吉が「まるでかん袋が飛んで行くようじゃ」と褒めたところからと言うのが始まりらしい。

xmlの検算

しつこくe-StatがらみのRです。以前、収録統計表一覧 と言う、公開されてる統計をWebから確認出来るやつを上げた。これで、鉄板処理が正しく行われているか、確認してみる。

ページの真ん中辺にある体力・運動能力調査(00402102)の中の下から3番目 運動・スポーツの実施状況別体格測定・テストの結果 運動・スポーツの実施状況別体格測定・テストの結果 と言う表題のやつ。これを選んだのは全くの偶然、他意は無い。

統計データIDを元にxmlファイルをDL。

<VALUE tab="00100" cat01="100" cat02="100" cat03="100" cat04="100" \
time="2017100000">318</VALUE>

XMLのデータの部を確認。tabからtimeまでの6つのパラメータで、値が決まっているんで、そのとうりに設定してみる。

> head(df, n=2)
     tab           cat01        cat02 cat03 cat04     time value
1 標本数 体格_身長(cm) ほとんど毎日  男子   6歳 2017年度   318
2 標本数 体格_身長(cm) ほとんど毎日  男子   6歳 2016年度   313

自分用に絞り込んで、注目するデータだけを表示させる。結果は

> gd
                    me.cat01 me.value
1            体格_身長(cm)   166.14
2            体格_体重(kg)    63.41
3          テスト_握力(kg)    40.53
4    テスト_上体起こし(回)    17.40
5    テスト_長座体前屈(cm)    37.73
6  テスト_開眼片足立ち(秒)    90.47
7 テスト_10m障害物歩行(秒)     6.09
8      テスト_6分間歩行(m)   657.94
9              テスト_合計点    44.20

平均より太目、体はもっと柔らかいぞ、片足立ちは余裕で2分を越えてた。歩く速さは牛歩戦術だな。 書いたと言うか、調整したのは下記

library(XML)

foo = function(x, what="name") {
    n = getNodeSet(doc, paste0("//CLASS_OBJ[@id='", x, "']/CLASS"))
    tbl = sapply(n, xmlGetAttr, what)
    names(tbl) = sapply(n, xmlGetAttr, "code")
    tbl[sapply(items, xmlGetAttr, x)]
}

doc = xmlParse("0003293543.xml")
items = getNodeSet(doc, "//VALUE")

tab   = foo('tab')
cat01 = foo("cat01")
cat02 = foo("cat02")
cat03 = foo("cat03")
cat04 = foo("cat04")
time  = foo("time")
value = as.numeric(sapply(items, xmlValue))

df = data.frame(tab, cat01, cat02, cat03, cat04, time, value)
me = subset(df, tab=="平均値" & cat02=="ほとんど毎日" &
                cat03=="男子" & cat04=="65-69歳" & time=="2017年度")
gd = data.frame(me$cat01, me$value)

これをWebではCGIで、自由奔放に設定出来るのだな。ああ、検算は一致してたよ。

> me = subset(df, tab=="平均値" & cat02=="ほとんど毎日" & cat03=="男子" &
                  (cat04=="65-69歳" | cat04=="19歳") & time=="2017年度")
> gd = data.frame(me$cat01, me$cat04, me$value)
> gd
                               me.cat01 me.cat04 me.value
1                       体格_身長(cm)     19歳   171.96
2                       体格_身長(cm)  65-69歳   166.14
:
11              テスト_反復横とび(点)     19歳    59.99
12 テスト_20mシャトルラン(折り返し数)     19歳   100.04
13                テスト_*持久走(秒)     19歳   357.84
14                   テスト_50m走(秒)     19歳     7.16
15              テスト_立ち幅とび(cm)     19歳   234.27
16         テスト_ハンドボール投げ(m)     19歳    27.18
17            テスト_開眼片足立ち(秒)  65-69歳    90.47

CGIで思い付いた。これ手動でCGIです。meとそれに合わせてgdも少々変更してみた。年寄りの自分と19歳の若者との比較。若者は年寄りには無いテスト項目が追加されてますなあ。努々、若者とは張り合わないように。。。

それから、統計言語Rの人なら平均値だけの確認じゃなくて、tabに設定されてる、調査の標本数とか標準偏差も合わせて確認しておくように。平均値だけの確認じゃ、マスコミ並みですから。 あっ、マスコミは偏差値って言葉が大好きだったな。体の偏差値でも計算してみるか。

CGIなら、応答性を確認すべし。

> start <- Sys.time()
>   doc = xmlParse("0003293543.xml")
>   items = getNodeSet(doc, "//VALUE")
> Sys.time() - start
Time difference of 0.2985532 secs

Sys.time()は、時刻を返す関数なんだけど、分解能が1usなんで、かなり高精度で、実行時間を計測できる。何度かやると、ばらばらの時間になる。

Time difference of 0.3033557 secs
Time difference of 0.3422935 secs
Time difference of 0.3941607 secs
Time difference of 0.5930219 secs
Time difference of 0.6151147 secs
Time difference of 0.8335843 secs

ガベコレが走っている症状と思われる。gcinfo(TRUE)にして次の、foo関数とas.numericまでのブロックを実行すると、

 :
> value = as.numeric(sapply(items, xmlValue))
Garbage collection 106 = 88+6+12 (level 0) ...
43.7 Mbytes of cons cells used (29%)
20.8 Mbytes of vectors used (27%)
Garbage collection 107 = 89+6+12 (level 0) ...
44.8 Mbytes of cons cells used (30%)
20.9 Mbytes of vectors used (27%)
> Sys.time() - start
Time difference of 6.893388 secs

ガベコレが嵐のように襲って来て、実行に7秒近くもかかっている。 2万7千行、7項目の変換とは、荷が重いものだのう。 最後のframe作りと、まとめて検索する部分は、0.2秒程であった、

このままじゃ、CGIとして外部に公開出来ないわな。さあ、どうする?

swi-pl

FreeBSD upgrade記念にsw-plを入れてみる。

[sakae@fb ~]$ sudo pkg install swi-pl
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 6 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        libarchive: 3.4.3,1
        libyaml: 0.2.5
        lzo2: 2.10_1
        ossp-uuid: 1.6.2_9
        swi-pl: 8.2.0
        unixODBC: 2.3.7

データベースにも接続出来る大掛かりなやつ? 起動するか取り合えず確認。

[sakae@fb ~]$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.0)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?-

大丈夫そう。


This year's Index

Home