素朴な疑問、$0を書き換えてApacheが処理してるURLを可視化するテクについて
いろいろ調べて個人的には勉強になったけど、あまりに雑多なメモなので、大抵の人には参考にならないと思います。
PragDave: See Rails request paths in 'top'の技なんだけど、Rubyで書かれてるmongrel限定で、multi-threadの環境だったらダメなんだろうな。
そもそも、rubyが$0を書き換えるってのは、どうやって実現しているのだろう?
ちょっとそれを調べる前に、、、
いやいや、ローテクで行こう。ENVを書き換えればいいんだ。
Apacheのconfigで、requestのuriをENVの中に紛れ込ませれば、それを/proc/プロセス番号/environで参照できる。そして、プロセスのメモリサイズがぴょこんと変化したときのuriをログしておけば、、、いけるんじゃないかな。
Apacheの設定
SetEnvIfを利用
SetEnvIf Request_URI ".php$" _MONITOR_URI=Request_URI
ps ax | awk '$5 ~ /httpd/ {print $1}' | while read pid do cat /proc/$pid/environ done | awk '
ところが、うまく行かない
- SetEnvIfで設定される値は、文字列リテラルであってRequest_URIという変数の値ではない
- Rubyで使える$0の書き換えの技も、/proc/プロセス番号/environには影響しない。おそらく、プロセスを起動するときに引き渡す値のリストが格納されている模様
そもそもRubyで$0を書き換えるのはどうやってるの?again
Ruyb-1.8.7-p72のソースを見た。
ruby.cにそれらしい部分がある。
void ruby_prog_init() { init_ids(); ruby_sourcefile = rb_source_filename("ruby"); rb_define_hooked_variable("$VERBOSE", &ruby_verbose, 0, verbose_setter); rb_define_hooked_variable("$-v", &ruby_verbose, 0, verbose_setter); rb_define_hooked_variable("$-w", &ruby_verbose, 0, verbose_setter); rb_define_variable("$DEBUG", &ruby_debug); rb_define_variable("$-d", &ruby_debug); rb_define_readonly_variable("$-p", &do_print); rb_define_readonly_variable("$-l", &do_line); rb_define_hooked_variable("$0", &rb_progname, 0, set_arg0); rb_define_hooked_variable("$PROGRAM_NAME", &rb_progname, 0, set_arg0); rb_define_readonly_variable("$*", &rb_argv); rb_argv = rb_ary_new(); rb_define_global_const("ARGV", rb_argv); rb_define_readonly_variable("$-a", &do_split); rb_global_variable(&rb_argv0); #ifdef MSDOS /* * There is no way we can refer to them from ruby, so close them to save * space. */ (void)fclose(stdaux); (void)fclose(stdprn); #endif }
どうも、set_arg0で書き換えられそうな感じ
static void set_arg0(val, id) VALUE val; ID id; { VALUE progname; char *s; long i; int j; #if !defined(PSTAT_SETCMD) && !defined(HAVE_SETPROCTITLE) static int len = 0; #endif if (origargv == 0) rb_raise(rb_eRuntimeError, "$0 not initialized"); StringValue(val); s = RSTRING(val)->ptr; i = RSTRING(val)->len; #if defined(PSTAT_SETCMD) if (i >= PST_CLEN) { union pstun j; j.pst_command = s; i = PST_CLEN; RSTRING(val)->len = i; *(s + i) = '\0'; pstat(PSTAT_SETCMD, j, PST_CLEN, 0, 0); } else { union pstun j; j.pst_command = s; pstat(PSTAT_SETCMD, j, i, 0, 0); } progname = rb_tainted_str_new(s, i); #elif defined(HAVE_SETPROCTITLE) setproctitle("%.*s", (int)i, s); progname = rb_tainted_str_new(s, i); #else if (len == 0) { len = get_arglen(origargc, origargv); } if (i >= len) { i = len; } memcpy(origargv[0], s, i); s = origargv[0] + i; *s = '\0'; if (++i < len) memset(s + 1, ' ', len - i); for (i = len-1, j = origargc-1; j > 0 && i >= 0; --i, --j) { origargv[j] = origargv[0] + i; *origargv[j] = '\0'; } progname = rb_tainted_str_new2(origargv[0]); #endif rb_progname = rb_obj_freeze(progname); }
PSTATもSETPROCTITLEもBSD系の機能らしい。えー、そうするとorigargvをいきなり書き変えて終わりってわけか。それってどこ?
$0関連のruby-devの古いメッセージとかが参考になる
- 404 Not Found
- ruby-dev 28287
- 1.13 どうすれば(psで見えるような)プログラムの名前を変更できますか?-Unix programming FAQ:Linuxにはsetproctitleという関数は無い。
- ruby-dev:28282 Re: PATCH solaris 10 isinf and ruby_setenv fixesによれば、SETPROCTITLEはHP/UX向けに想定されていたらしい
- ruby-dev 28287
ちなみにsetenvとかenviron(7)とか
CentOS5.2のmanpageのenviron(7)にはmulti-threadの環境ではenvironをいじるなとある。
extern char **environ; is initialized as a pointer to an array of character pointers to the environment strings. The argv and environ arrays are each terminated by a null pointer. The null pointer terminating the argv array is not counted in argc. Conforming multi-threaded applications shall not use the environ vari- able to access or modify any environment variable while any other thread is concurrently modifying any environment variable. A call to any function dependent on any environment variable shall be considered a use of the environ variable to access that environment variable.
ではログにpid,メモリサイズを出すか?
それは出来るような気がする。直近のログとpsコマンドを組み合わせれば、かなりのことはわかるのではないかな。
mod_log_config フォーマット文字列 - Apache httpdによれば、以下の値が役に立ちそう。
- %P リクエストを扱った子プロセスのプロセス ID
- %{format}P リクエストを扱ったワーカーのプロセス ID かスレッド ID。 format として有効な値は pid, tid, hextid です。hextid を使うには APR 1.2.0 以降が必要です。
- %D リクエストを処理するのにかかった時間、マイクロ秒単位
- %X 応答が完了したときの接続ステータス
- %T リクエストを扱うのにかかった時間、秒単位
%{FOOBAR}e 環境変数 FOOBAR の内容 ここに当該プロセスのメモリサイズが突っ込めると最高だが、それは難しそうだ。