TT Lab
はじめる
学ぶ 学習パス コース

見知らぬシステムの前で

200行と12手順を、2行と4手順に

TT Labで続きを見る

目標

顧客が送ってきた障害資料一式(処理プログラム1つ、フィード200行、再現手順12ステップ)を、最小再現例に縮めます。まず「まだ同じ失敗なのか」を機械に判定させる判定ツールを作り、その判定ツールだけで縮め、残ったもののどれか1つを抜くと再現が止まることまで証明します。

なぜ重要なのか

顧客が書いてくれた再現手順は、たいていその人がその日にしたことのすべてです。そのすべてを抱えて原因を探すと、見るべき組み合わせが爆発します。かといって、縮めることから始めると、もっと危険です。1行を抜いたらプログラムが別のエラーで落ち、それもエラーなので、まだ再現されていると勘違いしてしまいます。そうしてできた例は、私たちが追っていた欠陥とは無関係な別の欠陥を再現します。 そのため、順序があります。判定ツールを先に作り、その判定ツールだけで縮めます。判定ツールは、終了コードと、その失敗にだけ出るシグネチャ文字列を一緒に見ます。実行時刻や一時ファイル名のように実行ごとに変わるものは、判定に入れません。 縮める作業は機械が行います。1行ずつ抜いてみると、行数と同じ回数だけ判定ツールを呼びますが、候補を塊に分けて見るデルタデバッギング(ddmin)は、その回数を行数の対数に近くまで下げます。 採点ツールは、提出された数字を信じません。オラクルには採点ツールが作った複数の候補を入れてみて、縮小ツールには毎回違う場所に答えを隠した256行の入力を与えて、結果と判定ツールの呼び出し回数を一緒に測ります。

ステップ

  1. /root/mre/gen_case.pyを作成して実行し、/root/mre/app/ingest.py、/root/mre/input.txt(200行)、/root/mre/repro/steps.txt(12ステップ)を広げてください。
  2. /root/mre/oracle.pyを作成してください。--inputで受け取った候補が追っている失敗を起こせば終了コード0、そうでなければ1です。別のエラーは再現ではありません。
  3. オラクルでフィードを縮めて、/root/mre/min_input.txtを作成してください。残る行は原本の行で、原本の順序を守ります。
  4. /root/mre/minimality.jsonに、残った行ごとに、それを抜いたときに再現されるかどうかを書いて、1-最小であることを証明してください。
  5. /root/mre/shrink.pyにデルタデバッギング(ddmin)を実装してください。--input --oracle --outを受け取り、判定ツールを呼ぶ回数が行数に比例しないようにします。
  6. 再現手順12ステップを縮めて、/root/mre/min_steps.txtを作成してください。残るステップは原本の行そのままで、順序を守ります。
  7. 値を変えても再現されるかを確認し、/root/mre/generalize.jsonと変形ファイル3つを残してください。
  8. /root/mre/mre_report.mdに4つの節で報告してください。

参考

障害資料一式を広げる

/root/mre/gen_case.pyを作成して実行し、/root/mre/app/ingest.py、/root/mre/input.txt(200行)、/root/mre/repro/steps.txt(12ステップ)を作成してください。

現場で受け取るのは、プログラムとデータと手順の3つだけです。広げたら、手順を一度そのまま実行して、失敗を目で見てください。MRE_WORKをまだ存在しないディレクトリに設定して実行します。

同じ失敗かどうかを機械に判定させる

/root/mre/oracle.pyを作成してください。--inputで受け取った候補が追っている失敗を起こせば終了コード0、そうでなければ1です。設定ファイルはオラクルが自分で作り、終了コードとシグネチャ文字列を一緒に見ます。

0以外の終了コードをすべて再現とみなすと、縮めている途中でまったく別のエラーに移ったことに気づけません。このプログラムは、設定を読み込めなければ2、私たちが追っている欠陥なら3、qtyの行が1つもなければ4で終わります。標準エラー出力のシグネチャ文字列も一緒に見てください。

フィードを縮める

オラクルでフィードを縮めて、/root/mre/min_input.txtを作成してください。残る行はすべて原本のinput.txtの行で、原本の順序を守り、どれか1行を抜くと再現が止まる必要があります。

このステップは手作業でもかまいません。1行ずつ抜いてみて、再現が保たれる行だけを残す方法が最も単純です。判定ツールを200回ほど呼ぶことになりますが、その遅さが、ステップ5でddminを作る理由です。

残った行がなぜすべて必要なのかを証明する

/root/mre/minimality.jsonに、min_input.txtの行ごとに、その行を抜いたときに再現されるかどうかを書いてください。すべての行で再現が止まれば、minimalはtrueです。linesはmin_input.txtの順序そのままです。

1-最小とは、「残ったもののうちどれか1つを抜くと再現が止まる」という意味です。この証明がなければ、「なぜこの行が必要なのか」という質問に答えられず、受け取る側もただ信じるしかありません。判定はオラクルに任せ、結果だけを書いてください。

縮める作業を自動化する

/root/mre/shrink.pyにデルタデバッギング(ddmin)を実装してください。--input --oracle --outを受け取り、原本が再現されるかをまず確認してから縮め、判定ツールの呼び出し回数が行数に比例しないようにします。

1行ずつ抜く方法は、行数と同じ回数だけ判定ツールを呼びます。ddminは、候補をn個に分けて、1つの断片だけで再現されるかをまず見て、だめなら1つの断片を抜いた残りで再現されるかを見ます。どちらもだめなら、さらに細かく分けて繰り返します。採点ツールは、256行の入力で作成した縮小ツールを動かし、判定ツールを何回呼んだかを数えます。

再現手順も縮める

再現手順12ステップを縮めて、/root/mre/min_steps.txtを作成してください。残るステップはsteps.txtの行そのままで、原本の順序を守り、どれか1つのステップを抜くと再現が止まる必要があります。

手順を試すたびに、MRE_WORKをまだ存在しないディレクトリとして新しく用意してください。前の実行が残した設定ファイルがあると、「設定を作るステップ」を抜いても再現されてしまい、そのステップは不要だという誤った結論が出ます。再現の判定は、標準エラー出力にシグネチャ文字列が出るかどうかで行います。

値ではなく構造かを確認する

最小例から、値を変えたバージョンと構造を変えたバージョンを作り、/root/mre/generalize.jsonに書いてください。変形は3つで、値だけを変えたもの(/root/mre/min_input_generic.txt)は再現される必要があり、残りの2つは再現されない必要があります。3つのファイルをすべて、実際に残してください。

残った2行が偶然の値のせいなのか、構造のせいなのかを分けるステップです。数字を別の数字に変えても再現されるなら、原因はその数字ではありません。逆に、カンマをピリオドに変えたりロケールを変えたりすると止まることを示せば、原因が2つの条件の組み合わせだと証明できます。

何を再現と呼んだかを書く

/root/mre/mre_report.mdに、## 무엇을 재현이라고 불렀나、## 얼마나 줄였나、## 남은 줄이 왜 다 필요한가、## 남은 가정の4つの節で書いてください(韓国語の見出しは順に「何を再現と呼んだか」「どれだけ縮めたか」「残った行がなぜすべて必要なのか」「残る仮定」という意味です)。原本と最小例の行数とステップ数、オラクルが見る終了コードとシグネチャ文字列が、すべて出ている必要があります。

受け取る側は、入力2行だけでは「これがなぜ問題なのですか」とまず尋ねます。何を同じ失敗とみなしたかを先に書き、そのあとにどれだけ縮めたかと、なぜこれ以上縮められないかを書いてください。最後の節には、値を変えても再現されるという事実と、何を変えると止まるかを書きます。