5.5. hp2ps––ヒーププロファイルをPostScriptへ

使いかた:

hp2ps [flags] [<file>[.hp]]

hp2psは、RTSオプション-h<brak-down>を使って作られたヒーププロファイルをPostScriptのグラフに変換するプログラムである。hp2psが処理するファイルの拡張子は慣習に従って.hpとされる。PostScript出力は<file>@.psに書き込まれる。<file>が完全に省かれた場合は、このプログラムはフィルタとして動作する。

hp2psはGHCソース配布物中のghc/utils/hp2psにある。これはもともとHBC/LMLヒーププロファイラの一部としてDave Wakelingが開発したものである。

フラグは以下のものがある。

-d

グラフを見やすくするために、hp2psは項目の帯をソートする。デフォルトのソート順では、面積の大きい帯が小さい帯の上に置かれる。-dを使うと、よりギザギザの(元の値の標準偏差が大きい)帯が上に置かれるようになる。

-b

通常、hp2psはグラフの表題をページの上部の小さいボックスの中に配置する。しかし、JOB文字列が長すぎて小さいボックスに収まらないとき(35文字より長いとき)は、hp2psは代わりに大きいボックスを使う。-bは、hp2psが大きいボックスを使うように強制する。

-e<float>[in|mm|pt]

LaTex文書に含めるのに適したencapsulated PostScriptを生成する。通常、PostScriptのグラフは高さ6インチ幅9インチのランドスケープモードで描画され、hp2psはこの領域をa4用紙のほぼ中心に配置する。これはグラフを精査するには良いが、LaTex文書に取り込むのには不向きである。-eオプションが指定されるとグラフはポートレートモードで描画され、幅は<float>で指定される。単位はインチ、ミリメートル、ポイント(デフォルト)のいずれかである。結果としてできるPostScriptファイルはEncapsulated PostScript(EPS)としての基準を満たしており、Rokickiのdvi-PostScript変換器であるdvipsを使ってLaTex文書に含めることができる。

-g

PostScriptプレビューワgs(および類似品)に適した出力を作る。この場合グラフはスケーリングなしのポートレートモードで印刷される。この出力はレーザプリンタには向かない。

-l

通常プロファイル中の帯の数は20に制限され、それ以外の識別子は全てOTHERという帯にまとめられる。-lフラグを使うとこの20個の制限は取り除かれ、必要なだけ帯を作るようにする。項目一覧はどうせ収まりきらないので生成されない。たくさんの帯のある時間プロファイルを作るのに便利である。

-m<int>

通常プロファイル中の帯の数は20に制限され、それ以外の識別子は全てOTHERという帯にまとめられる。-mフラグを使うとこの個数制限を変更することができる。(最大20)

-m0とすると帯の数は無制限になり、必要なだけ帯を作るようにする。項目一覧はどうせ収まりきらないので生成されない。たくさんの帯のある時間プロファイルを作るのに便利である。

-p

前回のパラメタを使う。デフォルトではPostScriptグラフはページ全体を占めるように自動的に上下左右に伸縮される。しかし、一連のグラフを提示したいときには、新しいグラフを描くときに前回と同じ縮尺、色づけ、順序を使えると便利である。-pフラグは前回hp2psfileに対して使ったときのパラメタを使ってグラフを描くようにする。これらのパラメタはfile@.auxから得られる。

-s

表題用に小さいボックスを使う。

-t<float>

通常、合計してプロファイルの1%に満たないトレース要素はプロファイルに含められない。-tオプションを使うとこのパーセント値を変更することができる。(最大5%)

-t0とすると、全てのトレース要素がプロファイルに含められる。この結果、全てのデータが表示されることが確かになる。

-c

色付きの出力を生成する。

-y

マークを無視する。

-?

使いかたに関する情報を表示する。

5.5.1. hpファイルを操作する

(Jan-Willem Maessenが提供してくれた覚え書き)

プログラムFOOについてヒーププロファイルを行うとFOO.hpが生成されるが、これはとても単純な構造のテキストファイルである。以下に示すのは代表例だが、実際のデータは大部分省略されている。

JOB "FOO -hC"
DATE "Thu Dec 26 18:17 2002"
SAMPLE_UNIT "seconds"
VALUE_UNIT "bytes"
BEGIN_SAMPLE 0.00
END_SAMPLE 0.00
BEGIN_SAMPLE 15.07
  ... sample data ...
END_SAMPLE 15.07
BEGIN_SAMPLE 30.23
  ... sample data ...
END_SAMPLE 30.23
... etc.
BEGIN_SAMPLE 11695.47
END_SAMPLE 11695.47

最初の四行(JOBDATESAMPLE_UNITVALUE_UNIT)はヘッダである。BEGIN_SAMPLEではじまりEND_SAMPLEで終わる行の塊が一つの標本(sample)である。(ヒーププロファイルのグラフを縦方向に切った断面だと思うと良い)。正しい書式のヘッダのあとに*完全な*標本がいくつか続くという形式であれば、hp2psはどんなファイルでも受け付けるはずである。

5.5.2. プロファイルの特定の部分に注目する

プロファイルの特定の部分だけを見るには、.hpファイルのコピーをテキストエディタで編集して不要な標本を削除してしまうだけで良い。できた.hpファイルはhp2psに通せるので、後は閲覧したり印刷したりできる。

5.5.3. 実行中のプログラムのヒーププロファイルを見る

.hpファイルはプログラムの実行中に徐々に生成される。原理的には、この不完全なファイルに対してhp2psを実行することでプログラムのヒープ使用の現況報告を生成することができる。しかし、このファイルの最後の標本が不完全かも知れず、その場合にはhp2psが失敗する。UNIXユーティリティのある機械を使っているならこの問題を回避するのはそんなに難しくない。(コマンドがちょっと複雑怪奇ではあるが)

  head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
    | hp2ps > FOO.ps

fgrep -n END_SAMPLE FOO.hpというコマンドはFOO.hp中で完全な標本が終わるところを全て列挙し、行番号を付加する。これを元に、tailcutを使って最後の完全な標本の行番号を取得できる。これがheadへの引数として使わる。こうすることで、FOO.hpから末尾の不完全な標本を削除したのと同様の結果になる。できたものは正しい書式の.hpファイルであり、hp2psに直接入力できる。

5.5.4. ヒーププロファイルを実時間で閲覧する

gvghostviewといったプログラムには「ファイルを監視する」というオプションがあり、これを使ってプログラムの実行にあわせてその時点のヒーププロファイルを見ることができる。これには、徐々に伸長するプロファイルを前の節で解説した方法で作り、それに対してgvを次のように実行すれば良い。

  gv -watch -seascape FOO.ps

-watchフラグを付け忘れた場合でも「State」メニューから「Watch File」を選べば良い。これで、FOO.psが新しくなるごとに表示が自動的に更新される。

これを全部一つのスクリプトにまとめることができる。

  #!/bin/sh
  head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
    | hp2ps > FOO.ps
  gv -watch -seascape FOO.ps &
  while [ 1 ] ; do
    sleep 10 # We generate a new profile every 10 seconds.
    head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
      | hp2ps > FOO.ps
  done

不完全な状態のFOO.psを読み込もうとしてgvがフリーズすることがある。(これは、更新時でhp2psが走っている最中だったからである)。スクリプトはもう少し複雑になるが、gvがSIGHUPを受け取ると入力ファイルを再読み込みするという事実を使うと、次のようにしてこの問題を回避できる。

  #!/bin/sh
  head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
    | hp2ps > FOO.ps
  gv FOO.ps &
  gvpsnum=$!
  while [ 1 ] ; do
    sleep 10
    head -`fgrep -n END_SAMPLE FOO.hp | tail -1 | cut -d : -f 1` FOO.hp \
      | hp2ps > FOO.ps
    kill -HUP $gvpsnum
  done