Goブログ

Go 1.12でデプロイしたものをデバッグする

David Chase
2019年3月21日

はじめに

Go 1.11とGo 1.12では、開発者が本番環境にデプロイするのと同じ最適化されたバイナリをデバッグできるようにするための大きな進歩がありました。

Goコンパイラがより高速なバイナリを生成するようになるにつれて、デバッグ容易性は低下してきました。Go 1.10では、Delveのようなインタラクティブなツールで優れたデバッグエクスペリエンスを得るには、最適化を完全に無効にする必要がありました。しかし、特に本番サービスを実行している場合は、パフォーマンスとデバッグ容易性をトレードオフする必要はありません。問題が本番環境で発生している場合は、本番環境でデバッグする必要があり、そのためだけに最適化されていないバイナリをデプロイする必要はないはずです。

Go 1.11と1.12では、最適化されたバイナリ(Goコンパイラのデフォルト設定)でのデバッグエクスペリエンスの向上に重点を置きました。改善点には以下が含まれます。

  • より正確な値の検査、特に関数エントリでの引数。
  • ステートメントの境界をより正確に識別し、ステップ実行がジャンプしにくくなり、ブレークポイントがプログラマーが期待する場所に止まるようになる。
  • DelveがGo関数を呼び出すための予備的なサポート(ゴルーチンとガベージコレクションにより、これはCおよびC++よりも複雑になります)。

Delveを使用した最適化されたコードのデバッグ

Delveは、LinuxとmacOSの両方をサポートするx86上のGoのデバッガです。Delveはゴルーチンやその他のGoの機能を認識しており、最高のGoデバッグエクスペリエンスを提供します。Delveは、GoLandVS Code、およびVimの背後にあるデバッグエンジンでもあります。

通常、Delveは-gcflags "all=-N -l"でデバッグしているコードをリビルドし、インライン化とほとんどの最適化を無効にします。Delveで最適化されたコードをデバッグするには、最初に最適化されたバイナリをビルドし、次にdlv exec your_programを使用してデバッグします。または、クラッシュからのコアファイルがある場合は、dlv core your_program your_coreで調べることができます。1.12と最新のDelveリリースでは、最適化されたバイナリでも多くの変数を調べることができるはずです。

改善された値の検査

Go 1.10で生成された最適化されたバイナリをデバッグする場合、通常、変数の値はまったく利用できませんでした。対照的に、Go 1.11以降では、完全に最適化されていない限り、最適化されたバイナリでも変数を調べることができます。Go 1.11では、コンパイラがDWARFロケーションリストの出力を開始したため、デバッガは変数がレジスタに出入りする際に追跡し、異なるレジスタとスタックスロットに分割されている複雑なオブジェクトを再構築できます。

改善されたステップ実行

これは、1.10のデバッガで単純な関数をステップ実行する例を示しています。欠陥(スキップされた行と繰り返された行)は赤い矢印で強調表示されています。

このような欠陥があると、プログラムをステップ実行するときに自分の位置を見失いやすく、ブレークポイントに到達するのを妨げます。

Go 1.11と1.12はステートメント境界情報を記録し、最適化とインライン化を通じてソース行番号をより適切に追跡します。その結果、Go 1.12では、このコードをステップ実行するとすべての行で停止し、期待どおりの順序で停止します。

関数呼び出し

Delveでの関数呼び出しのサポートはまだ開発中ですが、単純なケースは機能します。例えば

(dlv) call fib(6)
> main.main() ./hello.go:15 (PC: 0x49d648)
Values returned:
    ~r1: 8

今後の展望

Go 1.12は最適化されたバイナリのデバッグエクスペリエンスを向上させるための一歩であり、さらに改善する計画があります。

デバッグ容易性とパフォーマンスの間には根本的なトレードオフがあるため、最も優先度の高いデバッグの欠陥に焦点を当て、進捗状況を監視し、回帰をキャッチするための自動化されたメトリックを収集するよう努めています。

変数の場所に関する正しい情報をデバッガ用に生成することに重点を置いているため、変数を印刷できる場合は正しく印刷されます。また、特にコールサイトなどのキーポイントで、変数値をより多く利用できるようにすることも検討していますが、多くの場合、これを改善するにはプログラムの実行速度を遅くする必要があります。最後に、ステップ実行の改善に取り組んでいます。パニック時のステップ実行の順序、ループ周辺のステップ実行の順序、および可能な限りソース順序に従うように努めています。

macOSサポートに関する注意

Go 1.11では、バイナリサイズを縮小するためにデバッグ情報が圧縮されるようになりました。これはDelveでネイティブにサポートされていますが、LLDBもGDBもmacOSでの圧縮されたデバッグ情報をサポートしていません。LLDBまたはGDBを使用している場合は、2つの回避策があります。-ldflags=-compressdwarf=falseを使用してバイナリをビルドするか、splitdwarfgo get golang.org/x/tools/cmd/splitdwarf)を使用して、既存のバイナリのデバッグ情報を解凍します。

次の記事: Go 2018年調査結果
前の記事: Goモジュールの使用
ブログのインデックス