日比野
啓
![]() |
2008 年 11 月の LaTeX を使ったハンズオンで、 wizzytex-mode から使われている advi がときどき固まってしまう問題につ いて調べてみました。
advi は一見普通の DVI viewer なのですが、 なぜか OCaml という変わった言語で実装されています。 今回 は advi から呼ばれる ghostscript が止まっているらしい、 ということまで分かっている状態から調べ始めま した。
とりあえず、 問題が起きているソースを取ってきて展開してみます。
% apt-get source advi
... dpkg-source: extracting advi in advi-1.6.0 dpkg-source: info: unpacking advi_1.6.0.orig.tar.gz dpkg-source: info: applying advi_1.6.0-13.diff.gz % cd advi-1.6.0 % ls *.ml addons.ml drawimage.ml font.ml gs.ml main.ml search.ml transimpl.ml ageometry.ml driver.ml global_options.ml gterm.ml misc.ml shot.ml ttfont.ml ... |
*.ml というのが OCaml のソースファイルです。 なんか、 gs.ml とかいうそのものズバリっぽいものが見えます。 gs.ml の中 をまず gs で検索していってみると、
...
let command = Config.gs_path in let command_args = [| command; "-dNOPLATFONTS"; "-dNOPAUSE"; "-sDEVICE=" ^ (if !antialias then x11alpha else x11); "-q"; "-dSAFER"; "-"; |] in let _ = debugs command; ... |
おお、 それっぽい。 あと、 デバッグ用っぽい機能 - debugs を発見。 さらにこんどは command で探してい くと、
...
let lpd_in, lpd_out = Unix.pipe () in ... let leftout = Unix.out_channel_of_descr lpd_out in ... let pid = Unix.create_process command command_args lpd_in rpd_out (* Unix.stdout *) Unix.stderr ... method line l = try showps l; output_string leftout l; output_char leftout ’\n’; ... |
どうやら gs にパイプで PS を書きこんでいるようです。 showps とかいうので PS の中身を見ることができるんじゃないか なーとか。
もう一度、 こんどは gs.ml の最初の方からデバッグ用の機能だけ見ていきます。
...
let debugs = Misc.debug_endline;; ... let showps_ref = ref false;; let showps s = if !showps_ref then (print_endline (Printf.sprintf "%s" s));; ... Options.add "--showps" (Arg.Set showps_ref) " ask advi to print to stdout a copy\ \n\t of the PostScript program sent to gs.";; ... |
Misc.というのは Misc という別のモジュールへの参照です。 ここでは単に misc.ml の中を見ればよさそうです。 showps_refは書き換え可能なフラグのようです。 と思ったらすぐ下にコマンドライン引数からフラグをセットできるように なっているようです。 misc.ml の中も見てみると、
...
(* Debugging. *) let forward_debug_endline = ref (function (_ : string) -> failwith "undefined forward debug_endline");; let debug_endline s = (!forward_debug_endline s : unit);; let set_forward_debug_endline f = forward_debug_endline := f;; ... |
さらにset_forward_debug_endlineで grep すると、 global_options.mlが引っかかるので、 その中も見てみ ると
...
(* To print debugging messages. *) let debug_endline = Options.debug "--debug" " General debug";; (* Setting the forward in Misc. *) Misc.set_forward_debug_endline debug_endline;; ... |
結局、 どっちもコマンドラインから設定できるようですね。 さっそく試してみると、
% platex debianmeetingresume200812-presentation.tex
... % advi debianmeetingresume200812-presentation.dvi ... /usr/bin/gs -dNOPLATFONTS -dNOPAUSE -sDEVICE=x11 -q -dDELAYSAFER - ... %!PS-Adobe-2.0 %%Creator: Active-DVI %! [1 0 0 -1 0 0] concat (/usr/share/texmf-texlive/dvips/base/texc.pro) run (/usr/share/texmf-texlive/dvips/base/special.pro) run ... %% Newpage grestore 0 0 moveto TeXDict begin 12769384 12769384 div dup /Resolution X /VResolution X end TeXDict begin /DVImag 194.845342 def end gsave flushpage (... ) print flush |
たしかに gs のコマンドラインらしきものと、 それから書きこんだ PS の内容らしいものが見えてます。 PS で目印となる文字 列を出力される命令flushpage (...) print flushを gs に書きこんで、 その出力を待っているようなのですが、 戻ってき ていないようです。
gs が止まってしまう場合とそうでない場合も比べてみたのですが、 止まってしまう場合の PS の最小セットを割り出すのが 難しく、 よくわかりませんでした。
なにか別の方法で止まってしまうのを回避できないか、 と gs の出力を待っている部分も見てみます。
...
let rec select fd_in fd_out fd_exn timeout = (* dirty hack: Graphics uses itimer internally! *) let start = Unix.gettimeofday () in try Unix.select fd_in fd_out fd_exn timeout with Unix.Unix_error (Unix.EINTR, _, _) as exn -> let now = Unix.gettimeofday () in let remaining = start +. timeout -. now in if remaining > 0.0 then select fd_in fd_out fd_exn timeout else [], [], [] ... match select [ rpd_in ] [] [] 1.0 with | [], _, _ -> begin match Unix.waitpid [ Unix.WNOHANG ] pid with | x, Unix.WEXITED y when x > 0 -> raise (Killed "gs exited") | 0, _ -> raise (Killed "gs alive but not responding") | _, _ -> raise (Killed "gs in strange state") end ... |
gs の出力を select で待っているようです。 タイムアウトも仕込んであるようです。 なぜうまくいっていないので しょう。
ここでは前半で定義されている select に注目です。 せっかくタイムアウトの残り時間を計算しているのに、 渡しているのは もとの値です。 どうりでいつまでたってもタイムアウトしないわけです。
...
if remaining > 0.0 then select fd_in fd_out fd_exn timeout else [], [], [] ... |
これを
...
if remaining > 0.0 then select fd_in fd_out fd_exn remaining else [], [], [] ... |
と直すと、 gs を待ってもタイムアウトするようになります。 gs が固まる原因を取り除くような根本的な解決はできませんで したが、 とりあえずは advi が止まらないようにはなりそうです。
第
50 回東京エリア Debian 勉強会 2009 年 3 月
____________________________________________________________________________________________