6

トレース関数内で関数呼び出しをデバッグしているときに、何らかの方法で呼び出し式を取得することは可能ですか?

traceback オブジェクトから呼び出し行番号を取得できますが、その行に複数の関数呼び出し (おそらく同じ関数への呼び出し) がある場合 (たとえば、より大きな式の部分式として)、この呼び出しがどこから来たのかをどのように知ることができますか? ソース行の先頭からのオフセットでも嬉しいです。

traceback.tb_lastiより詳細なコンテキスト(最後に試行されたバイトコードのインデックス)を提供しているようです-バイトコードを正確なソース範囲に接続することは何とか可能ですか?

編集: 明確にするために、呼び出し元の行から特定の (サブ) 式 (コールサイト) を抽出する必要があります。

4

4 に答える 4

6

トレースバック フレームにも行番号があります。

lineno = traceback.tb_lineno

名前とファイル名を持つコード オブジェクトに到達することもできます。

name = traceback.tb_frame.f_code.co_name
filename = traceback.tb_frame.f_code.co_filename

ファイル名と行番号に加えて、フレームグローバルとlinecacheモジュールを使用して、それを正しいソースコード行に効率的に変換できます。

linecache.checkcache(filename)
line = linecache.getline(filename, lineno, traceback.tb_frame.f_globals)

これは、tracebackモジュールがトレースバックを有用な情報に変換するために使用するものです。

バイトコードには行番号しか関連付けられていないため、バイトコードをソース コード行の正確な部分に直接戻すことはできません。その行を自分で解析して、各部分が発行するバイトコードを特定し、それをコード オブジェクトのバイトコードと照合する必要があります。

astmoduleを使用してそれを行うことはできますが、たとえば、ローカル、セル、グローバルの名前検索用に正しいバイトコードを生成するにはスコープ コンテキストが必要になるため、行単位で行うことはできません。

于 2012-12-19T12:09:50.817 に答える
3

残念ながら、コンパイルされたバイトコードは列オフセットを失いました。バイトコード インデックスから行番号へのマッピングは、co_lnotab行番号テーブルに含まれています。disモジュールは、バイトコードを見て解釈する良い方法ですco_lnotab:

>>> dis.dis(compile('a, b, c', '', 'eval'))
  1           0 LOAD_NAME                0 (a)
              3 LOAD_NAME                1 (b)
              6 LOAD_NAME                2 (c)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
  ^-- line number

ただし、行番号をいじるのを止めるものは何もありません。

>>> a = ast.parse('a, b, c', mode='eval')
>>> for n in ast.walk(a):
...     if hasattr(n, 'col_offset'):
...         n.lineno = n.lineno * 1000 + n.col_offset
>>> dis.dis(compile(a, '', 'eval'))
1000           0 LOAD_NAME                0 (a)

1003           3 LOAD_NAME                1 (b)

1006           6 LOAD_NAME                2 (c)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        

コードを直接コンパイルすることは、 を介してコンパイルすることと同じであるべきであり、行番号ast.parseいじっても、生成されたバイトコード ( .co_lnotab

  • ソースファイルを見つける
  • で解析しますast.parse
  • ast の行番号を変更して、列のオフセットを含めます
  • astをコンパイルする
  • を使用しtb_lastiてマンジドを検索しますco_lnotab
  • 変更された行番号を (行番号、列オフセット) に戻す
于 2012-12-19T14:17:27.287 に答える
1

私はそれがネクロマンシーであることを知っていますが、これを最初に見ずに昨日同様の質問を投稿しました. したがって、誰かが興味を持っている場合に備えて、Python3のモジュールinspectとモジュールを使用して、受け入れられた回答とは異なる方法で問題を解決しました。astそれはまだデバッグと教育目的のためですが、それはうまくいきます。

答えはかなり長いので、ここにリンクがあります

于 2015-02-01T08:22:56.440 に答える
0

それが私が最終的に問題を解決した方法です。元のプログラムの各関数呼び出しを、元の呼び出しのソースの場所に関する情報と共にヘルパー関数への呼び出しでラップすることにより、インストルメント化しました。実は、プログラム内の各部分式の評価を制御することに興味があったので、各部分式をラップしました。

より正確にeは、元のプログラムで式を使用すると、次のようになりました

_after(_before(location_info), e)

インストルメント化されたプログラムで。ヘルパーは次のように定義されました。

def _before(location_info):
    return location_info

def _after(location_info, value):
    return value

tracer が の呼び出しを報告したとき_before、 で表される場所で式を評価しようとしていることがわかりましたlocation_info(トレース システムにより、ローカル変数/パラメーターにアクセスできるようになりました。これにより、 の値を知ることができましたlocation_info)。tracer が への呼び出しを報告したとき_after、 によって示された expessionlocation_infoが評価され、値が にあることがわかりましたvalue

実行の「イベント処理」をこれらのヘルパー関数に直接記述して、トレース システムを完全にバイパスすることもできましたが、他の理由でもそれが必要だったので、これらのヘルパーはトレース システムで「呼び出し」イベントをトリガーするためだけに使用しました。

結果はここで見ることができます: http://thonny.org

于 2013-11-27T14:25:15.117 に答える