compat_xxx

去年、ipadのネットアクセスが遅くなるって事象が発生し、その時は使っているWiFiのチャンネル 妨害が原因と突き止めた。

以来、ネットのアクセスがどのぐらい出てるか監視しよってんで、相応のアプリを探して 入れておいた。Webへのアクセスが遅くなるんで、ブラウザー上で計測出来るのがいいな。

大方はフラッシュやJAVAを使ったやつ。生憎と言うかサファリではフラッシュが使えない(ので、安心安全) から、代わりのやつって事で探すのに苦労した。回線速度測定

それから、デフォっぽい、専用の計測ツール(speedtest)も入れておいた。 気が向いた時に計測して、普段の状況を把握。

先日の事。パソコンのネットアクセスが非常に遅い現象が発現。すわとばかり、ipadで 計測してみると、

Web版        Down: 1.1 Mbps              Up:  4.9  Mbps
speedtest    Down: 1.35 Megabits         Up: 13.18 Megabits

普段は、

Web版        Down: 13.5 - 24.4 Mbps      Up:  5.1 - 10.9 Mbps
speedtest    Down: 16.0 - 40.0 Megabits  Up: 13.0 - 40.0 Megabits

こうして比べてみると、現象発現時には下りスピードが普段の1/10ぐらいに低下してる 事が分かる。

ここでの単位は、Bit par secondってなってるけど、ちとピントこない。ソフト屋が 使う単位は、Byte par secondだからだ。変換するには、8で割れ。

NICとか扱う人なら、10で割れかな。ビット転送時の変調方式はNRZ。送られてきたビット列 からストローブクロックを再生して、そのクロックでデータをサンプリング。

クロックの発生にはPLLを使うんだけど、PLLの同期にはある程度の助走期間が必要。 そんな事で、イーサフレームの頭に、SYNCフレームが付く。これでPLLの回路を ビットレートに引きこむとな。

ここまではいいんだけど、送るデータがずっとZEROとか1とかだと、NRZ変調では波形が 変化しない。これじゃ同期クロックを発生出来ない。で、8ビットを送るのに、実際は 10ビット分を使い、その中から10 to 8 decoder って方式で、都合のよい (同一パターンが連なってもNRZ波形が変化するように) ビットパターンを 選んでる。

こんな事がTCP/IPを勉強した時に書いてあったな。生きる世界によって、同じbpsでも 理解のしかたが違うとな。

あれ? 10ビットうんぬんは昔の話かな。 MACフレームを運ぶイーサネット物理層で、 知識の棚卸しをしましょ。

progressbar

ネトワークからisoファイル等をDLする時は、大抵firefoxを使っている。こやつには、DLの 回線速度とあと何分で終了するかを表示するパネルが用意されてる。回線スピードって どうやって計算してるのだろう? そんなのソース嫁。

ソース読むには大きすぎますわな。もっとコンパクトなやつ、例えばwgetにも表示機が 付いていたな。えっ、わざわざソースを落としてくる? なんか無駄っぽい。手元にある ソースで何とかするんだ。えーとね、えーとね。そうだ、NetBSDのftpにそんな機能が 付いていたような。。。 試して見れ。

nb7$ ftp ftp://ftp4.jp.freebsd.org/pub/.../FreeBSD-10.2-RELEASE-i386-disc1.iso
Connected to ftp1.mex.ad.jp.
220 ftp1.mex.ad.jp FTP server (Version wu-2.6.2(1) Tue Dec 27 23:47:22 JST 2011) ready.
 :
229 Entering Extended Passive Mode (|||10855|)
150 Opening BINARY mode data connection for FreeBSD-10.2-RELEASE-i386-disc1.iso (645224448 bytes).
  4% |*                                  | 29666 KiB  647.82 KiB/s    15:26 ETA

ビンゴ。回線スピードをちゃんと表示してくれている。後ETAも。この時間がどんどん減って 行くんで、残り時間だわな。ETAって何の略? まあいい。ソースと御対面。

nb7$ cd /usr/src/usr.bin/ftp
nb7$ ls
CVS             complete.c      ftp.1           progressbar.c   ssl.h
Makefile        domacro.c       ftp.c           progressbar.h   util.c
cmds.c          extern.h        ftp_var.h       ruserpass.c     version.h
cmdtab.c        fetch.c         main.c          ssl.c

もろにバーの表示機ってのが有ったぞ。

/*
 * List of order of magnitude suffixes, per IEC 60027-2.
 */
static const char * const suffixes[] = {
        "",     /* 2^0  (byte) */
        "KiB",  /* 2^10 Kibibyte */
        "MiB",  /* 2^20 Mebibyte */
        "GiB",  /* 2^30 Gibibyte */
        "TiB",  /* 2^40 Tebibyte */
        "PiB",  /* 2^50 Pebibyte */
        "EiB",  /* 2^60 Exbibyte */
#if 0
                /* The following are not necessary for signed 64-bit off_t */
        "ZiB",  /* 2^70 Zebibyte */
        "YiB",  /* 2^80 Yobibyte */
#endif
};

まず目に付いたのがこれ。intが64Bitを超えるようなサイズを誰が落としてくるのだろうか? ぐぐる様がアーカイブしてるのを全部足したって、EiBぐらいで収まるだろうに。超オーバー スペックさに、呆れておきましょう。

バー表示はftpのセッションと並行して行われる。どういう仕組み? そんな疑問が、簡潔に コメントで述べられていた。

 * Display a transfer progress bar if progress is non-zero.
 * SIGALRM is hijacked for use by this function.
 * set alarm timer for 1 Hz

一秒毎にアラームタイマーで割り込みを起こして、表示するとな。スレッドとかじゃないんだ。

        timersub(&now, &start, &td);
        elapsed = td.tv_sec + (td.tv_usec / 1000000.0);

        bytespersec = 0;
        if (bytes > 0) {
                bytespersec = bytes;
                if (elapsed > 0.0)
                        bytespersec /= elapsed;
        }

回線スピードの計算。ftpのセッションが始まってからの総ダウンロードバイト数がbytesに 入っていて、現在時刻から開始時刻を引いたものelapsedで除してるとな。

        len += snprintf(buf + len, BUFLEFT,
            " " LLFP("3") ".%02d %.2sB/s ",
            (LLT)(bytespersec / 1024),
            (int)((bytespersec % 1024) * 100 / 1024),
            suffixes[i]);

後は、こういう形で、表示形式を整えているとな。続けて、ETAの計算表示になるんだけど、 5秒間の間にパケットのやり取りが無いと、stalled(失速) を表示するとな。 ソースが手元に有ると、いろいろ分かって楽しいな。

ftpコマンドでは、ファイル名の補間が出来るようになってる。サービス精神旺盛。 こういうお宝ソースは是非読んで見れ。

FreeBSDの困ったちゃん

何が困ったって、pkgから入れたgdbの最新版が、哀れな事にお亡くなりになるんだ。

$ gdb
i386fbsd-kern.c:482: internal-error: void _initialize_i386_kgdb_tdep(): Assertion `offsetof(struct pcb, pcb_ebx) == i386fbsd_pcb_offset[I386_EBX_REGNUM]' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Abort trap (core dumped)

以前はこういう事が無かったんだけど。。。内蔵のgdbと衝突してるって、そりゃないでしょ。 デフォで入ってるgdbは版が古くてemacsとの相性が悪いんで、新しいのを入れたのに。 gdbserverと連携出来ないじゃん。諦めろ、ですかね?

NetBSDにFreeBSDのふりをさせる

まてまて、FreeBSDで動いているgdbserverをNetBSDにでも持ってきて、動かしたらいいじゃん。 NetBSD バイナリーエミュレーション がサポートされてるはずですから。

早速、man compat_freebsdしましたよ。

DESCRIPTION
     NetBSD supports running FreeBSD binaries.  Most binaries should work,
     except programs that use FreeBSD-specific features.  These include
     i386-specific calls, such as syscons utilities.  The FreeBSD
     compatibility feature is active for kernels compiled with the
     COMPAT_FREEBSD option enabled.

第一要件として、NetBSDのカーネルが、FREEBSDとの互換性を許すようにコンパイルして ある事ですって。GENEICのconfをみたら、哀れ、コメントアウトされてた。 しょうがないので、イネーブルにして、コンパイルとインストールを実施。 configがテキストになってて良かったね。

次は、/emul/freebsdってdirを作り、そこをFreeBSDの仮想root-dirとして、必要な ライブラリー類を放り込めとな。

libc.so.7とld-elf.so.1を配置。本体のgdbserverは何処に置く? そりゃ、/usr/local/binの 下あたりでしょ。OSがアプリの素性を調べて、必要なら/emul/freebsd以下のライブラリィーを 先に見つけて使ってくれるって説明が有りましたから。

取り合えず、隔離政策で、

nb7$ file /emul/freebsd/bin/gdbserver
/emul/freebsd/bin/gdbserver: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), for FreeBSD 10.2, stripped

じゃ、早速起動

nb7$ /emul/freebsd/bin/gdbserver
/bin/ksh: /emul/freebsd/bin/gdbserver: Inappropriate file type or format

不適当なファイルタイプだってさ。なんで? ちゃんとFreeBSDのそれって認識してるじゃん。 不当な扱いに抗議して、判定者を割り出してみますかね。

nb7$ cd /usr/src/sys
nb7$ grep Inappropriate -r *
  :
sys/errno.h:#define     EFTYPE          79              /* Inappropriate file type or format */

どうやら、マクロ名で扱われているっぽい。で、検索してみると、compat/freebsdの下 あたりでヒットすると予想してたんだけど該当無し。深入りは止そう。

次なる手。ダイナミックリンクを止めてstaticリンクにしたgdbserverを使ったらどう? こういうアイデアは、リンクがちゃんと出来てるか、FreeBSD側から、lddをfreebsd-lddとかの 名前で輸入して、確認ってmanに書いてあったから。

仰せの通りにやってみたけど、そもそもfreebsd-lddが、上記と同じエラーで落ちてしまったのだ。 だったら、staticでいいじゃんとなるわな。-static オプションを付けてコンパイルした のを用意した。

nb7$ gdbserver
Usage:  gdbserver COMM PROG [ARGS ...]
        gdbserver COMM --attach PID

COMM may either be a tty device (for serial debugging), or
HOST:PORT to listen for a TCP connection.

Exiting

おお調子いいぞ。いざ実戦投入

nb7$ gdbserver localhost:1234 mg
Memory fault (core dumped)

なんじゃーー。見かけ倒しじゃん。coreが出来ているので臨場します。

nb7$ gdb gdbserver gdbserver.core
GNU gdb (GDB) 7.7.1
 :
Core was generated by `gdbserver'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x08050ad7 in malloc ()
(gdb) bt
#0  0x08050ad7 in malloc ()
#1  0x0804ba75 in set_target_ops ()
#2  0x0804bf2c in initialize_low ()
#3  0x0804ab92 in main ()

OpenBSDはどうよ

上記のごとく、NetBSDにFreeBSDの真似をさせるのは盛大に失敗した。NetBSDにLinuxの真似を させるって手もあるけど、いまいち信用ならん。

親分の監視の目が光っているOpenBSDはどうか? compat_linuxだけがサポートされてるっぽい。 やりかたはNetBSDとほぼ同じなんだけど、一点異なる事がある。

それは、管理者が意志を持ってLinuxのエミュレーションを許可しないと、どこの馬の骨か 知れないLinuxバイナリーは実行出来ないって事。

[ob: ulinux]$ sudo sysctl -w kern.emul.linux=1
kern.emul.linux: 0 -> 1
[ob: ulinux]$ ./gdbserver
Segmentation fault (core dumped)

sysctlを使って、エミュレーションを許可する。そして実行。哀れセグフォ。 やっぱり臨場するか。

[ob: ulinux]$ gdb gdbserver gdbserver.core
GNU gdb (GDB) 7.8.1
 :
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB.  Attempting to continue with the default i386 settings.

Reading symbols from gdbserver...(no debugging symbols found)...done.
Segmentation fault (core dumped)

何処の馬の骨とも知れないものに触ってはいけません。って、もう触ってしまったので、 感染の恐れ有りって事で、親分から刺客。哀れ、検査人も殺されましたとさ。

gdbserverを覗く

このままで終ってしまっては、あれなんで、FreeBSDに立ち返り gdbserverの動きを 追ってみる。まずは、gdbにかけられるようにコンパイルだな。

$ cd /usr/src/gnu/usr.bin/gdb/gdbserver/
$ vi Makefile
   :
CFLAGS+=        -DNO_MMALLOC -DGDBSERVER -static -g
$ sudo make

出来上がったgdbserverを適当な所へ持って行って、走らせる。

$ gdb gdbserver
GNU gdb 6.1.1 [FreeBSD]
 :
(gdb) b main
Breakpoint 1 at 0x804ab19: file /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/server.c, line 333.
(gdb) r localhost:1234 ./a.out
Starting program: /usr/home/sakae/z/gdbserver localhost:1234 ./a.out

Breakpoint 1, main (argc=3, argv=0x0)
    at /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/server.c:333
349       initialize_low ();
(gdb) s
initialize_low () at fbsd-low.c:1260
1260      set_target_ops (&fbsd_target_ops);
(gdb) s
set_target_ops (target=0x80aac64)
    at /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/target.c:110
110       the_target = (struct target_ops *) malloc (sizeof (*the_target));
(gdb) p (sizeof (*the_target))
$1 = 56
(gdb) n
111       memcpy (the_target, target, sizeof (*the_target));
(gdb) p the_target
$2 = (struct target_ops *) 0x28805040

mallocが失敗する事は考えていないんだな。もっとも、この文脈じゃ失敗する事は考えつかない よな。やっぱり、NetBSD側のエミュレーションに齟齬があるんだろうな。

core解析

で、gdb用のインフォメーションを付けたgdbserverを再びNetBSDに持ち込んで、走らせてみると 当然の事ながらゲロを吐いて終わり。

ここに臨場用ファイルが有るなら、精密に分析したいな。名医は一体どんな解析をしてるの だろう? ちょいと探ってみると、 コアの解析に詳しくない人がコアファイルを見付けたらどうしたら良いか が見つかった。TVでよくやってる研修医が見立てて、ベテランが突っ込みを入れる雰囲気かな。 健康病の事をやっとくと、視聴率が取れますからね。

nb7$ file gdbserver.core
gdbserver.core: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), NetBSD-style, from 'gdbserver' (signal 11)

へぇー。coreファイルってELFファイルなんだ。知らなかったぞ。正に、人間は猿の仲間って 事を知らない脳天気な人ですなあ。

続いてピーなんとかをやってみると、ことごとく not found。NetBSDはsunと違って 田舎の診療所のようです。諦めてgdb一本に頼りましょ。

nb7$ gdb gdbserver gdbserver.core
GNU gdb (GDB) 7.7.1
  :
Core was generated by `gdbserver'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x08050ad7 in malloc ()
(gdb) bt
#0  0x08050ad7 in malloc ()
#1  0x0804ba75 in set_target_ops (target=0x80aac64 <fbsd_target_ops>)
    at /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/target.c:110

gdb情報が付いているんで、少しは詳しく表示したな。普通はここで打ち切りなんだけど、 もう少ししつこく追ってみる。まずは、

(gdb) info registers
eax            0x3fd000 4182016
ecx            0x80c5e2e        135028270
edx            0x38     56
ebx            0x38     56
esp            0xbfbfdb08       0xbfbfdb08
ebp            0xbfbfdf74       0xbfbfdf74
esi            0x80aac64        134917220
edi            0x40     64
eip            0x8050ad7        0x8050ad7 <malloc+4951>
eflags         0x10246  [ PF ZF IF RF ]
cs             0x17     23
ss             0x1f     31
ds             0x1f     31
es             0x1f     31
fs             0xab     171
gs             0xb3     179

続いて

(gdb) disassemble *$pc
  :
   0x08050ab8 <+4920>:  cmp    $0x125,%eax
   0x08050abd <+4925>:  mov    %eax,0x14(%edx)
   0x08050ac0 <+4928>:  jne    0x8050b63 <malloc+5091>
   0x08050ac6 <+4934>:  mov    %edx,(%esp)
   0x08050ac9 <+4937>:  call   0x807bbb0 <__jemalloc_tcache_event_hard>
   0x08050ace <+4942>:  jmp    0x8050b63 <malloc+5091>
   0x08050ad3 <+4947>:  test   %edx,%edx
   0x08050ad5 <+4949>:  je     0x8050b03 <malloc+4995>
=> 0x08050ad7 <+4951>:  mov    %gs:0x0,%eax
   0x08050ade <+4958>:  mov    -0x40(%eax),%eax
   0x08050ae4 <+4964>:  test   %eax,%eax
   0x08050ae6 <+4966>:  jne    0x8050aed <malloc+4973>
   0x08050ae8 <+4968>:  call   0x804f5e0 <__jemalloc_choose_arena_hard>
   0x08050aed <+4973>:  mov    %ebx,0x4(%esp)
   0x08050af1 <+4977>:  mov    %eax,(%esp)
   0x08050af4 <+4980>:  movl   $0x0,0x8(%esp)
   0x08050afc <+4988>:  call   0x80916a0 <__jemalloc_arena_malloc_small>

ぼんくらな研修医には、mallocって大きいんすねぐらいしか、分からないぞ。 一度、FreeBSDに立ち返って、mallocに潜ってみろ。

まてまて、FreeBSDなら古い6.4が年代物のパソコンで動いているぞ。そこからgdbserverを 採取してきてやってみれ。

Core was generated by `gdbserver'.
Program terminated with signal SIGSYS, Bad system call.
#0  0x080577b3 in sigaction ()
(gdb) bt
#0  0x080577b3 in sigaction ()
#1  0x0804eb18 in signal ()
#2  0x08049c01 in start_inferior (argv=0xbfbfe834, statusptr=0xbfbfdfeb "")
    at /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/server.c:48
#3  0x0804a7b5 in main (argc=3, argv=0xbfbfe82c)
    at /usr/src/gnu/usr.bin/gdb/gdbserver/../../../../contrib/gdb/gdb/gdbserver/server.c:356
(gdb) info registers
eax            0x4e     78
ecx            0x15     21
edx            0x0      0
ebx            0x16     22
esp            0xbfbfdf2c       0xbfbfdf2c
ebp            0xbfbfdf88       0xbfbfdf88
esi            0xbfbfdf60       -1077944480
edi            0x806164b        134616651
eip            0x80577b3        0x80577b3 <sigaction+7>
eflags         0x203    [ CF IF ]
cs             0x17     23
ss             0x1f     31
ds             0x1f     31
es             0x1f     31
fs             0xab     171
gs             0xb3     179
(gdb) disassemble *$pc
Dump of assembler code for function sigaction:
   0x080577ac <+0>:     mov    $0x1a0,%eax
   0x080577b1 <+5>:     int    $0x80
=> 0x080577b3 <+7>:     jb     0x80577a4
   0x080577b5 <+9>:     ret
   0x080577b6 <+10>:    nop
   0x080577b7 <+11>:    nop
   0x080577b8 <+12>:    jmp    0x8059318 <.cerror>
   0x080577bd <+17>:    lea    0x0(%esi),%esi
End of assembler dump.

落ちる所が変わったね。少しは先に進んだようだ。6.4側の該当ソースを参照すると

     38 /* The PID of the originally created or attached inferior.  Used to
     39    send signals to the process when GDB sends us an asynchronous interrupt
     40    (user hitting Control-C in the client), and to wait for the child toexit
     41    when no longer debugging it.  */
     42
     43 int signal_pid;
     44
     45 static unsigned char
     46 start_inferior (char *argv[], char *statusptr)
     47 {
     48   signal (SIGTTOU, SIG_DFL);
     49   signal (SIGTTIN, SIG_DFL);
     50

何が気に入らないのだろう? 基本的なシステムコールと思うのだけど。制御端末について試みられたバック グラウンドの読み書きは、標準処理(プロセスの停止)をお願いって意味。普通のアプリでは 使わない?だろうから、実装漏れだったりして。

jemalloc

FreeBSD 10.2 からのgdbserverは、mallocの中で落ちた。落ちた所は、mov %gs:0x0,%eax なぜ これがSEGVなんだろう? gsって、例の腐った石の暗部をいじろうとしたから? スタックセグメントを見に行っただけじゃん。

NetBSDのcompat_freebsdは、最新のFreeBSDをサポートしていないんだろうね。manの例を 見てもfreebsd2.xの時代での説明だよ。freebsd6.4で少し先へ進んだのは、mallocが古かったから だろうね。

最新のFreeBSDでは、jemallocとかいうのが採用 されてる。並行性重視って、現代風だな。一杯有る石がメモリー欲しい、メモリー欲しいと 競合するのを旨く捌く、目的に合わせて性能を調整出来る事に重点を置いて開発 したんだな。ソースを覗いてみたけど、超複雑。

malloc

jemalloc()

jemallocとかLD_PRELOADについて調べてみた

mod_mrubyのメモリ問題をvalgrindで調査の上jemallocで改善

readme

linux_emu

OpenBSDの下でDebian GNU/Linuxを飼う

以下はFreeBSDによる新式のエミュレーションです。石が豪華じゃないと実行出来ないのが 玉に傷だな。

bhyve

bhyve all

AlphaGo

AlphaGo: マシンラーニングで囲碁を

囲碁ソフトがプロ棋士に勝利したようです

イ・セドル、100万ドルかけて'コンピュータ'と対決

厚みと模様とAlphaGo