【PL/SQL】DBMS_TRACEで処理の実行経路を追跡する方法

【PL/SQL】DBMS_TRACEで処理の実行経路を追跡する方法 PL/SQL

PL/SQLの実行経路(どのプロシージャや関数がどの順で呼ばれ、どの行で例外が起きたか)を可視化したいときは、DBMS_TRACEを使うとSQLトレースでは見えにくい「PL/SQL呼び出しの足跡」を取得できます。DBMS_PROFILERやDBMS_HPROFが「時間」に強いのに対し、DBMS_TRACEは「経路(コール/ステートメント/例外の発生位置)」の追跡に向きます。ここではセットアップから有効化・無効化、結果の読み方、実務での活用ポイントまでを解説します。

前提準備:トレース表の作成と権限

DBMS_TRACEは結果を専用の表に書き出します。管理者に依頼してサーバ上の管理スクリプト(例:$ORACLE_HOME/rdbms/admin/tracetab.sql)を一度だけ実行し、PLSQL_TRACE_RUNSPLSQL_TRACE_UNITSPLSQL_TRACE_EVENTSなどの表を作成します。アプリ実行スキーマからそれら表へのINSERT/SELECT権限と、DBMS_TRACEパッケージのEXECUTE権限を付与してもらえば準備完了です。

最小の使い方:有効化→対象処理→無効化

基本は「有効化(どの種類のイベントを追うか指定)→処理実行→無効化→結果参照」という流れです。対象処理の直前で有効化し、終わったら必ず無効化します。

-- 追跡したい処理(例)
CREATE OR REPLACE PROCEDURE demo_calc(p_n IN PLS_INTEGER) IS
  v_sum PLS_INTEGER := 0;
BEGIN
  FOR i IN 1..p_n LOOP
    v_sum := v_sum + i;
  END LOOP;
  IF v_sum < 0 THEN
    RAISE_APPLICATION_ERROR(-20001, 'unexpected');
  END IF;
END;
/

-- トレースの有効化 → 処理実行 → 無効化
DECLARE
  v_run_comment VARCHAR2(200) := 'trace-demo ' || TO_CHAR(SYSTIMESTAMP,'YYYY-MM-DD HH24:MI:SS.FF3');
BEGIN
  -- どのイベントを記録するかをビット和で指定
  DBMS_TRACE.SET_PLSQL_TRACE(
    DBMS_TRACE.TRACE_ENABLED_CALLS       +  -- サブルーチン呼び出し
    DBMS_TRACE.TRACE_ENABLED_STATEMENTS  +  -- ステートメント(行)到達
    DBMS_TRACE.TRACE_ENABLED_EXCEPTIONS     -- 例外発生
  , v_run_comment);

  -- 対象処理
  demo_calc(1000);

  -- 無効化(バッファを表へフラッシュ)
  DBMS_TRACE.CLEAR_PLSQL_TRACE;
END;
/

SET_PLSQL_TRACEの第1引数はフラグのビット和で、必要なものだけを選びます。処理後はCLEAR_PLSQL_TRACEで必ず無効化してください。

直近ランの特定と概要確認

ひとつのセッションで複数回トレースを走らせることを想定し、まずはランを特定してから掘り下げます。

-- ラン一覧(新しい順)
SELECT runid, run_owner, run_comment, run_timestamp
  FROM plsql_trace_runs
 ORDER BY runid DESC;

-- 特定runidのユニット(パッケージ/プロシージャ/関数)別出現数
SELECT u.runid, u.unit_number, u.unit_owner, u.unit_name, COUNT(*) AS events
  FROM plsql_trace_units u
  JOIN plsql_trace_events e
    ON e.runid = u.runid AND e.unit_number = u.unit_number
 WHERE u.runid = :runid
 GROUP BY u.runid, u.unit_number, u.unit_owner, u.unit_name
 ORDER BY events DESC;

実行経路を読む:行番号・イベント種別の追跡

イベント表には「どのユニットの何行目で、どんなイベントが起きたか」が順序付きで記録されます。ユニット表と結合して読みやすい形に整えます。

-- 代表的なevent#の例:
--   1.. 呼び出し開始/終了(CALL/RETURN)
-- 100.. ステートメント到達(LINE)
-- 200.. 例外発生(EXCEPTION)
SELECT
  e.seq#,
  u.unit_owner||'.'||u.unit_name AS unit,
  e.line#,
  e.event#,
  CASE
    WHEN e.event# BETWEEN 1 AND 9   THEN 'CALL/RETURN'
    WHEN e.event# BETWEEN 100 AND 199 THEN 'LINE'
    WHEN e.event# BETWEEN 200 AND 299 THEN 'EXCEPTION'
    ELSE 'OTHER'
  END AS event_kind
FROM plsql_trace_events e
JOIN plsql_trace_units  u
  ON u.runid = e.runid AND u.unit_number = e.unit_number
WHERE e.runid = :runid
ORDER BY e.seq#;

seq#でソートすると呼び出し順にたどれます。例外を起点に前後のイベントを追うことで、どの行で何が起きてどこへ伝播したかを素早く把握できます。

関数/プロシージャごとの到達行をハイライトする

ユニット単位で「どの行が実行されたか」を集計すると、未到達分岐の洗い出しやテスト観点の補強に役立ちます。

-- 指定ユニットの到達行一覧
SELECT e.line#, COUNT(*) AS hits
  FROM plsql_trace_events e
  JOIN plsql_trace_units  u
    ON u.runid=e.runid AND u.unit_number=e.unit_number
 WHERE e.runid = :runid
   AND u.unit_name = 'DEMO_CALC'   -- 例:対象プロシージャ名(大文字)
   AND e.event# BETWEEN 100 AND 199 -- LINEイベント
 GROUP BY e.line#
 ORDER BY e.line#;

よくある使い所と運用Tips

テストでは「例外が上がった箇所の直前に何が実行されていたか」を追跡し、想定外の分岐やトリガ経由の呼び出しを発見するのに有効です。性能分析が目的ならDBMS_PROFILER/DBMS_HPROFを併用し、DBMS_APPLICATION_INFOでモジュール/アクション名を出しておくとAWR/ASHとも突き合わせやすくなります。長時間の連続トレースは表が大きくなるため、PLSQL_TRACE_RUNSを起点に一定期間で古いランを削除する保守ジョブを用意しておくと安全です。

トラブルシュート:記録されない/権限エラー

記録が残らない場合は、(1) トレース表が未作成、(2) INSERT権限不足、(3) CLEAR_PLSQL_TRACE忘れ、(4) ユニットのコンパイル時オプション/無効化 などを確認します。共有環境ではスキーマを跨ぐときの権限(EXECUTE/INSERT/SELECT)に注意し、テストはまず自分のスキーマ内ユニットで行うのが確実です。

まとめ

DBMS_TRACEは「PL/SQLがどの順でどの行を通ったか」を細かく記録し、例外の前後関係や複雑なコールスタックを可視化します。SET_PLSQL_TRACEで必要なイベントだけを有効化し、処理後にCLEAR_PLSQL_TRACEで確実にフラッシュ。ラン→ユニット→イベントの順に読み解けば、再現しづらい障害の道筋も追いやすくなります。時間分析はプロファイラ、経路分析はDBMS_TRACE——目的に応じて使い分けるのが効果的です。