クラッシュレポートの出力(Windows)

概要

Choreonoidには、アプリケーションが異常終了(クラッシュ)したときに、その原因を特定するための情報をファイルに書き出す機能があります。この機能は現在のところWindowsでのみ利用できます。

この機能を有効にしておくと、クラッシュした際に以下の2つのファイルが出力されます。

  • クラッシュレポート(.txt)

テキストファイルです。クラッシュした位置の呼出履歴(コールスタック)、例外の種類、読み込まれていたモジュールの一覧が記録されます。このファイル単体で、プログラムのどこで落ちたかが分かります。

  • ダンプファイル(.dmp)

クラッシュ時のプロセスの状態を記録したバイナリファイルです。Visual Studioなどのデバッガで開いて、より詳しく調べるために使います。

この機能は、以下のような状況で利用することを想定しています。

  • 開発環境のない利用者のPCでのみ再現するクラッシュについて、利用者にレポートを送ってもらって原因を調べる

  • Choreonoidをベースとするアプリケーションを納品した先で発生したクラッシュの情報を受け取る

クラッシュが発生すると、以下のような通知ダイアログが表示され、レポートファイルの保存先が示されます。

../_images/crash-dialog-ja.png

Windowsでは、この機能がない場合、クラッシュ時にウィンドウが何も表示されないまま静かに終了することがあり、利用者には何が起きたのか分からないことがあります。この機能を有効にしておけば、クラッシュしたことがダイアログで伝わり、開発者に送るべきファイルも明確になります。

Windowsには同種の機能として、Windows Error Reporting(WER)のローカルダンプ機能がありますが、そちらは管理者権限でのレジストリの編集が必要で、得られるのもダンプファイルのみです。本機能は管理者権限を必要とせず、テキストのレポートが直接得られる点が異なります。

Choreonoidの利用者の方は、クラッシュに遭遇した場合、 機能の有効化 から 出力されるファイル までの内容を参考にして、出力されたファイルを開発者に送ってください。レポートの内容を自分で確認したい場合は レポートの読み方 を参照してください。 関数名と行番号を出力するには 以降は、主にChoreonoid本体やChoreonoidをベースとするアプリケーションの開発者を対象とした内容です。

機能の有効化

この機能はデフォルトでは無効になっています。レポートにはファイルのパスや読み込まれているモジュールの一覧が、ダンプファイルにはプロセスのメモリの内容が含まれるため、利用者が意図しないところでこうしたファイルが残ることのないようにしています。無効の場合は、関連するライブラリの読み込みも出力先ディレクトリの作成も行われません。

機能の有効化は、Choreonoidを起動する際の環境変数で行います。

環境変数

動作

CNOID_CRASH_DUMP

未設定または空

無効(デフォルト)

CNOID_CRASH_DUMP

0

無効

CNOID_CRASH_DUMP

full

有効にし、ダンプファイルにプロセスのメモリ全体を含める

CNOID_CRASH_DUMP

上記以外の値(1 など)

有効にする

CNOID_CRASH_DUMP_DIR

任意のパス

出力先ディレクトリを変更する

Choreonoidをベースとするアプリケーション(Choreonoidの App クラスを使用して構築されたもの)でも、同じ環境変数で有効になります。アプリケーション側でのコードの追加は不要です。

利用者に有効化してもらう場合は、以下のように環境変数を設定してから起動するバッチファイルを渡すのが確実です。

@echo off
set CNOID_CRASH_DUMP=1
start "" "C:\Program Files\Choreonoid\bin\choreonoid.exe"

"C:\Program Files\Choreonoid\bin\choreonoid.exe" の部分は、実際のインストール先に合わせて置き換えてください。

出力されるファイル

出力先

ファイルは以下のディレクトリに出力されます。

%LOCALAPPDATA%\<組織名>\<アプリケーション名>\CrashDumps

<組織名> と <アプリケーション名> はアプリケーションによって決まる名前で、Choreonoid本体ではどちらも "Choreonoid" です。したがってChoreonoid本体の場合、出力先は以下になります。

C:\Users\<ユーザ名>\AppData\Local\Choreonoid\Choreonoid\CrashDumps

出力先のディレクトリは自動的に作成されます。 CNOID_CRASH_DUMP_DIR を設定している場合は、そのディレクトリに出力されます。

AppData は隠しフォルダなので、エクスプローラで開くには、アドレスバーに

%LOCALAPPDATA%\Choreonoid\Choreonoid\CrashDumps

と入力するのが手軽です。

../_images/crash-files-explorer.png

古いファイルは自動的に削除され、最大で10回分のクラッシュのファイルが保持されます。

注釈

出力先は、Choreonoidの設定ファイルが置かれる %APPDATA%(AppData\Roaming)ではなく、AppData\Local の側になります。ダンプファイルは大きく、またそのPCで起きた事象の記録であるため、移動ユーザプロファイルによって他のPCに同期される場所には置かないようにしています。これはWERのローカルダンプ機能のデフォルトの出力先(%LOCALAPPDATA%\CrashDumps)と同じ階層です。

ファイル名

ファイル名は以下の形式になります。

<実行ファイル名>_<日付>_<時刻>_<プロセスID>.txt
<実行ファイル名>_<日付>_<時刻>_<プロセスID>.dmp

例えば以下のようになります。

choreonoid_20260826_021646_2428.txt
choreonoid_20260826_021646_2428.dmp

同じクラッシュのレポートとダンプファイルは同じ名前になります。

ファイルのサイズ

ファイルのサイズは、Choreonoidの起動処理中にクラッシュさせた場合の実測で以下のようになります。

ファイル

サイズ

クラッシュレポート(.txt)

約10KB

ダンプファイル(デフォルト)

約240KB

ダンプファイル(CNOID_CRASH_DUMP=full の場合)

約450MB

レポートとデフォルトのダンプファイルは合わせて250KB程度なので、メールに添付して送ることができます。 full を指定した場合のダンプファイルは、ファイル転送サービスなどを使って送る必要があります。

レポートの読み方

クラッシュレポートは以下のような内容のテキストファイルです(モジュール一覧は途中を省略しています)。レポートの内容は、Choreonoidの表示言語によらず英語で出力されます。

Crash report of an application based on Choreonoid

Application     : Choreonoid
Choreonoid ver. : 2.6.0
Executable      : C:\Program Files\Choreonoid\bin\choreonoid.exe
Date            : 2026-08-26 02:16:46
Process ID      : 2428
Thread ID       : 22692
Reason          : Unhandled exception
Exception code  : 0xC0000005 (EXCEPTION_ACCESS_VIOLATION)
Exception addr  : 0x00007FFEE8DA6A4D
Faulting module : C:\Program Files\Choreonoid\bin\CnoidBase.dll
Fault offset    : 0x0000000000076A4D
Access type     : write to 0x0000000000000000
Dump file       : C:\Users\<user>\AppData\Local\Choreonoid\Choreonoid\CrashDumps\choreonoid_20260826_021646_2428.dmp
Dump type       : minidump

Call stack of the crashed thread (the most recent call first):

  00  CnoidBase.dll             + 0x00076A4D  cnoid::MainWindow::Impl::Impl + 0x9D  [MainWindow.cpp:149]
  01  CnoidBase.dll             + 0x00076F9A  cnoid::MainWindow::MainWindow + 0xAA  [MainWindow.cpp:138]
  02  CnoidBase.dll             + 0x000776AE  cnoid::MainWindow::initialize + 0x3E  [MainWindow.cpp:119]
  03  CnoidBase.dll             + 0x00035287  cnoid::App::Impl::initialize + 0xB77  [App.cpp:713]
  04  CnoidBase.dll             + 0x0002ECA2  cnoid::App::Impl::exec + 0x42  [App.cpp:881]
  05  choreonoid.exe            + 0x0000149E  main + 0x20E  [choreonoid.cpp:7]
  06  choreonoid.exe            + 0x000022FD  qtEntryPoint + 0x15D  [qtentrypoint_win.cpp:50]
  07  choreonoid.exe            + 0x0000171E  __scrt_common_main_seh + 0x106  [exe_common.inl:288]
  08  KERNEL32.DLL              + 0x0001259D  BaseThreadInitThunk + 0x1D  (*)
  09  ntdll.dll                 + 0x0005AF78  RtlUserThreadStart + 0x28  (*)

  The debug information (PDB file) of the modules of the frames without a
  source line is not available on this computer. A name marked with (*) is
  just the exported symbol nearest to the address and is often a function
  other than the one that actually contains it. The module name and the
  offset in it are always exact, and the developer can identify the exact
  position from them with the same binary and its debug information.

Loaded modules (100):

  base address        size      time stamp  file
  0x00007FF7AA300000  0x00011000  0x6A8DCA30  C:\Program Files\Choreonoid\bin\choreonoid.exe
  0x00007FFF9F5D0000  0x00218000  0xAC55DAA3  C:\WINDOWS\SYSTEM32\ntdll.dll
  0x00007FFF9DE80000  0x000C4000  0xE4CACA22  C:\WINDOWS\System32\KERNEL32.DLL
  ...

Please send this file to the developer of the application. The dump file
listed above is also useful to investigate the details of the problem.

この例は、動作確認のために意図的にクラッシュを起こして得たものです。レポートは、クラッシュの概要、呼出履歴、モジュール一覧の3つの部分から構成されます。以下では各部分の読み方を説明します。

クラッシュの概要

レポートの冒頭には、クラッシュの概要が以下の項目で記録されます。

項目

意味

Application

アプリケーション名

Choreonoid ver.

ベースとなっているChoreonoidのバージョン

Executable

実行ファイルのパス

Date

クラッシュした日時

Process ID / Thread ID

プロセスID / クラッシュしたスレッドのID

Reason

異常終了の種別(下記参照)

Exception code

例外コードとその名前(下記参照)

Exception addr

例外が発生したアドレス

Faulting module

例外が発生したモジュール(DLL/EXE)のパス

Fault offset

そのモジュール内でのオフセット

Access type

アクセス違反の場合、読み書きの別と対象アドレス

Dump file

対になるダンプファイルのパス

Dump type

minidump または full dump

Faulting module と Fault offset は、Windowsのイベントビューアで「アプリケーションエラー」(イベントID 1000)として記録される「障害が発生しているモジュール名」「フォールト オフセット」と同じ意味の値です。イベントビューアを開かなくても、レポートから同じ情報が得られます。

Reason には以下のいずれかの値が出力されます。

意味

Unhandled exception

処理されない例外が発生した(最も一般的なケース)

Abnormal termination by the abort function

abort関数が呼ばれた(assertの失敗など)

An exception that is not caught by any handler

どこでもcatchされないC++例外が発生した

Call of a pure virtual function

純粋仮想関数が呼ばれた

An invalid parameter given to a runtime library function

ランタイムライブラリが不正な引数を検出した

Exception code としてよく出力されるのは以下のものです。

コード

名前

意味

0xC0000005

EXCEPTION_ACCESS_VIOLATION

アクセス違反。無効なポインタの読み書き。最も多いケース

0xC0000094

EXCEPTION_INT_DIVIDE_BY_ZERO

整数のゼロ除算

0xC00000FD

EXCEPTION_STACK_OVERFLOW

スタックオーバーフロー。無限再帰など

0xC0000374

STATUS_HEAP_CORRUPTION

ヒープの破壊が検出された

0xE06D7363

C++ exception

処理されないC++例外

呼出履歴

"Call stack of the crashed thread" 以下には、クラッシュしたスレッドの呼出履歴が記録されます。各行の形式は以下のとおりです。

番号  モジュール名 + モジュール内オフセット  関数名 + 関数内オフセット  [ソースファイル:行番号]
  • 上にある行ほど新しい呼び出しで、番号 00 の行がクラッシュした位置そのものです。

  • モジュール名とモジュール内オフセットは常に正確に出力されます。

  • 関数名と行番号は、そのPCにモジュールのデバッグ情報(PDBファイル)がある場合にのみ出力されます。

  • 関数名に (*) が付いているものは、PDBファイルがなかったために、そのアドレスの手前にある最も近いエクスポート関数の名前を表示したものです。実際にそのアドレスを含む関数とは限りません。上の例では、Windowsのシステムライブラリ(KERNEL32.DLL、ntdll.dll)がこれにあたります。

  • 記録されるのはクラッシュしたスレッドのみです。他のスレッドの状態が必要な場合は、ダンプファイルを解析してください。

デバッグ情報がない場合でも、呼出履歴の並びとモジュール名・オフセットは正確なので、レポートから「どのモジュールのどの位置で落ちたか」は必ず分かります。開発者側で同じビルドのバイナリとデバッグ情報を用意しておけば、この情報から正確な位置を特定できます。デバッグ情報の準備については 関数名と行番号を出力するには を参照してください。

モジュール一覧

"Loaded modules" 以下には、クラッシュ時に読み込まれていたモジュールが、ベースアドレス、サイズ、タイムスタンプ、ファイルパスとともに列挙されます。これは以下のような用途に使えます。

  • プロセスに注入された第三者のDLLの有無を確認する。セキュリティソフト、画面オーバーレイ、フック型の常駐ソフトなどがクラッシュの原因になっていることがあり、その確認に使えます。

  • 各モジュールがどのビルドのものであるかを、タイムスタンプとサイズから特定する。

関数名と行番号を出力するには

ここからは主に開発者向けの内容です。

上で述べたように、呼出履歴の並びとモジュール名・オフセットはデバッグ情報がなくても正確に出力されます。デバッグ情報が必要なのは、関数名と行番号を出力するためだけです。

Windowsでは、デバッグ情報は実行ファイル(EXE/DLL)とは別の「PDBファイル」に格納されます。関数名と行番号を出力するには、デバッグ情報付きでビルドを行い、PDBファイルを適切に配置する必要があります。

デバッグ情報付きのビルド

Visual C++のReleaseビルドでは、デフォルトではPDBファイルが生成されません。これを生成するには、Choreonoidのビルド時にCMakeの ENABLE_MSVC_DEBUG_INFO_FOR_RELEASE オプションをONにします。

cmake -B build -G "Visual Studio 17 2022" -DENABLE_MSVC_DEBUG_INFO_FOR_RELEASE=ON

CMakeのGUIツールで設定する場合は、通常の設定一覧に表示されるこのオプションのチェックをONにしてください。CMakeでの設定操作については オプション機能のビルド を参照してください。

このオプションはコンパイラに /Zi を、リンカに /DEBUG /OPT:REF /OPT:ICF を追加します。最適化のオプションは変わらないため、生成される機械語は同一であり、実行性能には影響しません。増えるのはビルド時間とPDBファイルのディスク容量(Choreonoid本体で1GB強)だけです。

注釈

PDB(Program Database)は、リンカが実行ファイルとは別に生成するデバッグ情報のファイルです。関数名や変数名とアドレスの対応、型情報、アドレスとソースファイル・行番号の対応表が格納されています。ソースコード自体は含まれません。Linuxではデバッグ情報がデフォルトで実行ファイルの中に含まれるのに対して、Windowsでは常に別ファイルとなるため、デバッグ情報を有効にしてもEXE/DLLのサイズはほとんど変わらず、PDBファイルを配布しなければデバッグ情報のないビルドと同等になります。

PDBファイルの配置

生成されたPDBファイルは、 cmake --install を実行すると、対応するEXE/DLLと同じディレクトリ(bin、lib\choreonoid-<バージョン>、およびその下の bodyhandler、customizer)にインストールされます。ビルドディレクトリでは bin\Release と lib\choreonoid-<バージョン>\Release 以下にあります。

なお、QtやVisual C++のランタイムなど、サードパーティのDLLにはPDBファイルはありません。

PDBファイルは、対応するEXE/DLLと同じディレクトリにあれば、レポートの出力時に自動的に見つけられます。したがって、以下の2通りの運用が可能です。

  1. PDBファイルを配布物に同梱する

レポートに関数名と行番号が直接出力されるので、レポートを送ってもらうだけで済みます。配布物のサイズは増えます(Choreonoid本体で数百MB規模)。

  1. PDBファイルを開発側で保管する

レポートにはモジュール名、オフセット、タイムスタンプが正確に出力されるので、同じビルドのPDBファイルと突き合わせれば位置を特定できます。この場合、リリースごとにPDBファイルを確実に保管しておく必要があります。

PDBファイルとバイナリの対応

PDBファイルには、リンク時に生成される固有のIDが記録されており、対応するバイナリにも同じIDが埋め込まれています。IDが一致しないPDBファイルは読み込まれません。したがって、以下の点に注意してください。

  • ビルドし直したバイナリに、以前のビルドのPDBファイルを使うことはできません。

  • 「まずレポートだけを受け取り、後からPDB付きでビルドし直す」という段取りは成立しません。関数名と行番号まで得るには、最初からデバッグ情報付きでビルドしたバイナリでクラッシュを再現してもらう必要があります。

この仕組みによって、誤ったPDBファイルによる間違った解析結果が出ることが防がれています。

ダンプファイルの解析

レポートの情報だけでは足りない場合、例えば他のスレッドの状態や、変数・オブジェクトの中身を確認したい場合は、ダンプファイルをデバッガで開いて解析します。ここではVisual Studio 2022を使う場合の手順を示します。

  1. Visual Studioを起動します。

  2. .dmp ファイルをVisual Studioのウィンドウにドラッグ&ドロップします。メニューの「ファイル」-「開く」-「ファイル」から開いても構いません。

  3. 「ミニダンプ ファイルの概要」の画面が表示されるので、例外コードとモジュール一覧を確認します。

  1. 画面右の「アクション」から「ネイティブのみでデバッグ」をクリックします。

  2. メニューの「デバッグ」-「ウィンドウ」-「呼び出し履歴」(Ctrl+Alt+C)で、呼出履歴を確認します。PDBファイルがあれば、関数名と行番号まで表示されます。

  1. メニューの「デバッグ」-「ウィンドウ」-「モジュール」(Ctrl+Alt+U)で、読み込まれていたモジュールを確認できます。

シンボルが解決されず関数名が表示されない場合は、「ツール」-「オプション」-「デバッグ」-「シンボル」で、PDBファイルのあるフォルダをシンボルファイルの場所として追加してください。Windowsのシステムライブラリの関数名も表示したい場合は、同じ設定で「Microsoft シンボル サーバー」を有効にします。

別のPCで生成されたダンプファイルを解析する場合は、以下の点に注意してください。

  • デフォルトのダンプファイル(ミニダンプ)にはモジュールのコード本体が含まれません。そのため、解析にはPDBファイルだけでなく、対になるEXE/DLLも必要です。x64でスタックの巻き戻しに使われる情報はバイナリ側に格納されているため、バイナリがないと呼出履歴自体が正しく得られません。

  • Visual Studioはシンボルファイルの場所をPDBファイルだけでなくバイナリの探索にも使用するので、EXE/DLLとPDBファイルを含むフォルダをシンボルファイルの場所に追加すれば解析できます。ダンプが生成されたPCのディレクトリ構成を再現する必要はありません。

制約と注意事項

  • この機能はWindows専用です。他のプラットフォームでは、環境変数を設定しても何も起きません。

  • 呼出履歴に記録されるのは、クラッシュしたスレッドのみです。

  • クラッシュしたプロセス自身がレポートの出力を行うため、ヒープが激しく破壊されている場合には、関数名の解決に失敗することがあります。その場合もモジュール名とオフセットは出力されます。ヒープの破壊が検出されている場合(STATUS_HEAP_CORRUPTION)は、はじめから関数名の解決を行いません。

  • スタックオーバーフローに備えてメインスレッドのスタックには余裕を確保していますが、ワーカースレッドでスタックオーバーフローが発生した場合は、レポートを出力できないことがあります。

  • レポートにはファイルパスとモジュール一覧が、ダンプファイルにはプロセスのメモリの内容が含まれます。第三者に送る際は内容を確認してください。

  • この機能はWindows Error Reporting(WER)の動作を妨げません。イベントログのアプリケーションエラー(ID 1000)は従来どおり記録され、WERのローカルダンプ機能を設定している環境ではそちらのダンプも生成されます。

  • --headless--batch--non-interactive の各オプションで起動した場合は、自動実行がモーダルダイアログで停止しないように、通知ダイアログは表示されません。その場合は同じ内容が標準エラー出力に出力されます。

  • クラッシュに備える処理は起動時のごく早い段階で組み込まれます。その時点ではまだメッセージの翻訳データが読み込まれていないため、起動直後にクラッシュした場合のダイアログは英語で表示されます。

性能への影響

この機能を有効にしても、通常の実行中に行われる処理はありません。クラッシュしたときにOSから呼び出される仕組みであり、常駐するスレッドや定期的な処理はなく、関数呼び出しや例外処理が遅くなることもありません。

有効にした場合のコストは起動時の初期化のみで、起動時間の差は測定誤差の範囲内、メモリ使用量の増加は約0.2MB(デバッグ用のライブラリ dbghelp.dll が読み込まれるため)です。またスタックオーバーフローの検出用の余裕として、メインスレッドで使用可能なスタックが64KB分狭くなります。

Linuxの場合

Linuxでは、OSの標準的な仕組みであるCoreファイル(Coreダンプ)を使って、同様の情報を得ることができます。詳細は Coreファイルを用いて落ちた箇所を探る方法 を参照してください。Linuxではデバッグ情報が実行ファイルの中に含まれるため、PDBファイルのような別ファイルを用意する必要はありません。