Linux vs. *BSD
del comment
前回、カーネルの統計って事で、ソース行数とオブジェクトファイルのサイズの関係とか調べた。その時ソースの行数はさらっと wc -l *.c してしまった。これでは、コメント込みの行数になってしまう。そこで、さらっとコメント除去の方法を調べてみた。
C言語のコードからコメントをまるっと削除する for gcc
gccに特化したやつはさておき、自作してる方が多い。手頃な例題なんですかね。オイラーもやってみるか。但し、なるべくコードを書かない方法で。。。
まずは、テストデータだな。
vbox$ cat -n t.c
1 /*
2 * comment 1
3 */
4
5 // commet 2
6 main(){
7 /* comment 3 */
8 printf("Hello\n"); // comment 4
9 }
こいつを cc の -E オプション付でコンパイル。結果を表示。
vbox$ cc -E t.c | cat -n
1 # 1 "t.c"
2 # 1 "<built-in>" 1
3 # 1 "<built-in>" 3
4 # 341 "<built-in>" 3
5 # 1 "<command line>" 1
6 # 1 "<built-in>" 2
7 # 1 "t.c" 2
8
9
10
11
12
13 main(){
14
15 printf("Hello\n");
16 }
どうやら、コメントが削除された時は、空行になるみたい。それから冒頭に # でコメントが入っている。ならば、これらも削除すればいいな。
vbox$ cat delcom.sh #! /bin/sh cc -E t.c | sed -e '/^$/d' | sed -e '/^# */d'
例によって、旧原人のスクリプトです。
vbox$ sh delcom.sh
main(){
printf("Hello\n");
}
空行はさっと削除してるけど、それで委員会。ええ、勿論、空行はコメントの一種ですから。
これで完成? と思ったら甘いぞ。ソースに #include を入れてみんしゃい。展開されたものが、ごそっと入ってしまって、行数が膨大になる。ならば、事前に #include は削除だな。
#! /bin/sh
# delete comment Usage: delcom.sh c-srcfile
cat $1 | sed -e '/#include */d' > /tmp/${$}.c
cc -E /tmp/${$}.c |
sed -e '/^$/d' |
sed -e '/^# */d'
rm -f /tmp/${$}.c
vbox$ wc kern_xxx.c
151 608 4348 kern_xxx.c
vbox$ /tmp/delcom.sh kern_xxx.c | wc
25 68 518
冒頭に33行のお約束のクレジット・コメントが入っている。それを除いたとしても、親切な解説が入っているのね。空行もコメントですって、よく分るよ。
余りに削られ過ぎているので、原本と見くらべてみたら、#ifdef xxx にコンパイラーは反応しちゃってる。これは、このコンパイラー任せには解決出来無いな。ショボンな気分ですよ。
待て、そもそも #include だけを特別扱いしてるっておかしいぞ。# は、コンパイラーに取って特別扱いする目印なんだから、それをやめさせてしまえば良いと思うよ。無害な _ にでも変更しちゃえ。
#! /bin/sh # delete comment, Usage: delcom.sh c-srcfile cat $1 | sed -e 's/#/_/' | cc -E - | sed -e '/^$/d' | sed -e '/^# */d'
どうやら、妥当そうな結果が得られた。
sakae@deb:/tmp$ ./delcom.sh kern_xxx.c | wc
94 293 2393
sakae@deb:/tmp$ ./delcom.sh kern_xxx.c | less
_include <sys/param.h>
_include <sys/systm.h>
_include <sys/kernel.h>
_include <sys/reboot.h>
_include <sys/sysctl.h>
_include <sys/mount.h>
_include <sys/syscallargs.h>
int rebooting = 0;
int
sys_reboot(struct proc *p, void *v, register_t *retval)
{
struct sys_reboot_args
*uap = v;
int error;
if ((error = suser(p)) != 0)
return (error);
_ifdef MULTIPROCESSOR
sched_stop_secondary_cpus();
KASSERT(CPU_IS_PRIMARY(curcpu()));
_endif
reboot(SCARG(uap, opt));
return (0);
}
:
今、Debianでセッションしてるんで、gcc方言も試してみる。
sakae@deb:/tmp$ gcc -fpreprocessed -dD -E kern_xxx.c | wc
120 296 2436
sakae@deb:/tmp$ gcc -fpreprocessed -dD -E kern_xxx.c | less
# 35 "kern_xxx.c"
#include <sys/param.h>
:
#ifdef MULTIPROCESSOR
sched_stop_secondary_cpus();
KASSERT(CPU_IS_PRIMARY(curcpu()));
#endif
reboot(SCARG(uap, opt));
return (0);
}
:
冒頭にごみが一行入っているな。ソースオリジナルな空行は、そのまま保存されてるって特性になった。空行はごみですって訴えておこう。まあ、目的によりさまざまだけど。。。
こやつを本当のノーコメント・アプリとして使うなら、このままでは駄目だ。コンパイラーのプリプロセスのターゲットから逃れる為、# を _ に変更した。ならば、最後にそれを戻しておかないとね。で、安易な選定 _ が問題。
ソースにスネーク・ケースの関数名やら変数名が潜んでいる。例えばこんなの、 days_in_month(i) 。これを安易に戻すと大変な事になる。そう、_ なんてのを使っちゃ駄目って事。
#! /bin/sh # delete comment, Usage: delcom.sh c-srcfile cat $1 | sed -e 's/#/@/' | cc -E - | sed -e '/^$/d' | sed -e '/^# */d' | sed -e 's/@/#/'
オイラー的には、永久保存版だと思うので、/usr/local/binに鎮座させておいたよ。
尚、空行は全部削除してるけど、それじゃあんまりだ。せめて、連続する空行だけは、一行に圧縮したいと言う要求が有るかもしれない。そんな場合は、空行を削除してるsedの行をuniq に変更すれば良い。
Linux vs. *BSD
折角ノーコメアプリを作ったんだから、少し利用してみる。
色々なOSのカーネルdirの中にあるCのソースで、ソースの行数、コメントを除いたいわゆるノーコメント行数、割合を求めてみる。リナの場合はサブdirが切ってあるので、その中のソースも含めた。
for f in *.c */*.c
do
all=`cat $f | wc -l`
nc=`delcom $f | wc -l`
ps=`echo "scale=2; 100 * $nc / $all" | bc`
printf "%7d%7d%7.1f %s\n" $nc $all $ps $f
done
こんな、一生に一度のスクリプトで採取。全体像ってか、ファイルの個数ってか母集団の状況。
sakae@deb:/tmp/work$ wc -l * 218 freebsd 347 linux 102 openbsd
OpenBSDは小粒で、追い掛けるにはうって付って分るな。 じゃ、小さい方から。
OpenBSD Rev 7.2
sakae@deb:/tmp/work$ sort -k 2 -n openbsd
nc lines % name
--------------------------------------
20 59 33.9 sysv_ipc.c
26 66 39.4 exec_conf.c
37 77 48.0 uipc_proto.c
49 81 60.5 dma_alloc.c
:
1880 2496 75.3 tty.c
2043 2540 80.4 kern_sysctl.c
1847 2598 71.1 subr_witness.c
2529 3453 73.2 vfs_syscalls.c
FreeBSD Rev 13.1
24 30 80.0 stack_protector.c 4 33 12.1 imgact_elf32.c 4 33 12.1 imgact_elf64.c 12 42 28.6 genoffset.c : 3565 6017 59.2 subr_bus.c 4327 6132 70.6 vfs_cache.c 5009 7104 70.5 vfs_subr.c 9849 11078 88.9 systrace_args.c
Linux linux-5.10.144/kernel
4 10 40.0 trace/trace_kprobe_selftest.c 10 15 66.7 trace/trace_selftest_dynamic.c 11 21 52.4 trace/power-traces.c 12 21 57.1 trace/rpm-traces.c : 6716 9697 69.2 trace/trace.c 6226 11432 54.5 sched/fair.c 9092 12491 72.8 bpf/verifier.c 8517 13245 64.3 events/core.c
次は、少し統計をば
gnuplot> stat 'openbsd' using 3:2 ;; % vs. lines Mean: 66.2882 838.7255 Std Dev: 10.6408 702.7683 Minimum: 33.9000 [ 69] 59.0000 [ 69] Maximum: 98.4000 [ 7] 3453.0000 [ 99] Quartile: 60.3000 316.0000 Median: 67.9000 684.0000 Quartile: 72.7000 1110.0000 Linear Model: y = 24.85 x - 808.7 Slope: 24.85 +- 6.119 Intercept: -808.7 +- 410.8 Correlation: r = 0.3763
gnuplot> stat 'freebsd' using 3:2 Mean: 67.2523 1216.2202 Std Dev: 11.4473 1410.1852 Minimum: 12.1000 [ 4] 30.0000 [ 96] Maximum: 98.8000 [174] 11078.0000 [175] Quartile: 62.6000 352.0000 Median: 68.5500 691.5000 Quartile: 74.4000 1624.0000 Linear Model: y = 43.68 x - 1722 Slope: 43.68 +- 7.837 Intercept: -1722 +- 534.7 Correlation: r = 0.3546
gnuplot> stat 'linux' using 3:2 Mean: 64.5942 1036.2997 Std Dev: 10.7153 1690.8837 Minimum: 32.7000 [234] 10.0000 [330] Maximum: 88.8000 [111] 13245.0000 [160] Quartile: 57.3000 223.0000 Median: 65.1000 511.0000 Quartile: 72.9000 1052.0000 Linear Model: y = 1.218 x + 957.6 Slope: 1.218 +- 8.495 Intercept: 957.6 +- 556.3 Correlation: r = 0.007719
リナの場合、行数とノーコメの行数には、有意な相関は無いと分った。*BSDの場合は微妙に正の相関が有るな。
リナはソースを文書として大事にしていない。対して*BSDは、それなりに監査されてるって事かな。
GENERIC.MP
前回relinkなんてのを調べていて、-g 付のカーネルが鎮座してる事を発見してしまった。 ならば、これを取出してきて、gdb出来るのではなかろうか? 検証してみる。
ホスト側のOpenBSDに下記を用意。
ob$ pwd /usr/obj/sys/arch/i386/compile/GENERIC.MP ob$ ls -l total 151072 -rwxr-x--- 1 root sakae 77267496 Nov 28 07:11 newbsd.gdb
問題なく動いたぞ。これでOpenBSDの開発マシンと同期出来た事になる。あの2時間もかけて、クライアント側でコンパイルした苦労は何だったんだ。
でも、GENERICは巨大な仕様、自前でコンパイルするとダイエット出来る特典がある。
この間、寒かったので近くの温泉に行ったのよ。そこに有った体重計に乘ったら、去年より5Kgも痩せていた。その理由は?
夕食でしっかり御飯を食べていたけど、胃が重いってんで、おかゆを常食するようにした。 そしたら、自然に痩せた、と思えるな。これが噂の炭水化物ダイエットか。何の苦労も無く痩せらたよ。
gettimeofday
ついでに、何回か前の疑問、riscvなシステムでdateした時、 sys_gettimeday が呼びだされないのを調べておく。dateコマンドをgdb出来るように設定。
riscv$ gdb -q date
Reading symbols from date...
(gdb) b gettimeofday
Breakpoint 1 at 0x15722: file /usr/src/lib/libc/sys/w_gettimeofday.c, line 24.
(gdb) r
Starting program: /tmp/date
(gdb) bt
#0 _libc_gettimeofday_wrap (tp=0x3ffffedd40, tzp=0x0)
at /usr/src/lib/libc/sys/w_gettimeofday.c:24
#1 0x0000000a2db6fc02 in _libc_time (t=0xa2db87ad0 <tval>)
at /usr/src/lib/libc/gen/time.c:39
#2 0x0000000a2db6d33a in main (argc=0, argv=0x3ffffee230) at date.c:98
(gdb) l 20,40
20 int
21 WRAP(gettimeofday)(struct timeval *tp, struct timezone *tzp)
22 {
23 int rc = 0;
24 struct timekeep *timekeep = _timekeep;
25 static struct timezone zerotz = { 0, 0 };
26
27 if (timekeep == NULL || timekeep->tk_user == 0)
28 return gettimeofday(tp, tzp);
29
30 if (tp)
31 rc = _microtime(tp, timekeep);
32 if (rc)
33 return gettimeofday(tp, tzp);
34
35 if (tzp)
36 *tzp = zerotz;
37
38 return 0;
39 }
gettimeofdayをラップしてる。
(gdb) b _microtime
Breakpoint 2 at 0xa2db7c926: file /usr/src/lib/libc/sys/microtime.c, line 101.
(gdb) c
Continuing.
Breakpoint 2, _microtime (tvp=0x3ffffedd40, tk=<optimized out>)
at /usr/src/lib/libc/sys/microtime.c:101
この中から最終的にbintimeが呼ばれていた。きっと、riscvの周辺の構造上、共通のシステムコールが使えないんだろうね。アーキテクチャの違いがユーザーランド側にも影響を及ぼすって例になるな。