crashcatcher

CrashCatcher

https://github.com/adamgreen/CrashCatcher
HardFaultに入ったときに素早く原因を突き止める

バックスラッシュを使うとだめになるのでパスはスラッシュで指定する

なんとか動かせたのでやったことメモしておく

サブモジュールにCrashCatcherをクローンする
ソースファイルにCrashCatcher.cCrashCatcher_armv7m.Sを追加する。拡張子.s.Sが別物扱いされたので.S用のコンパイル設定を追加しておく。ヘッダファイルにフォルダを追加する

# C sources
C_SOURCES =  \
[...] \ # いろんなCソースファイル
CrashCatcher/Core/src/CrashCatcher.c
 
ASM_UPPER_SOURCES = \
CrashCatcher/Core/src/CrashCatcher_armv7m.S
 
# C includes
C_INCLUDES =  \
[...] \ # いろんなインクルードディレクトリ
-ICrashCatcher/include \
-ICrashCatcher/Core/src
 
# .Sファイルのオブジェクトファイルを作るためのコマンドを追加しておく
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_UPPER_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASM_UPPER_SOURCES)))

https://github.com/fouge/nrf_utils/blob/master/debug/log/crash_hexdump.c を参考にクラッシュ時にダンプする出力関数の定義を行う。
以下の4つの関数はCrashCatcher.cから呼び出されるので関数を定義する

/*
送信するためのペリフェラルの初期化などはここで実行する
ダンプの送信開始をシリアルクライアントに知らせるため、###CRASH### を送信する
*/
void CrashCatcher_DumpStart(const CrashCatcherInfo* pInfo)
 
/*
ダンプするRAMの領域を定義する。RAMの出力をしない場合はNULLポインタを返すようにしておく。
*/
const CrashCatcherMemoryRegion* CrashCatcher_GetMemoryRegions(void)
 
/*
実際のダンプデータを1行ずつ出力する
*/
void CrashCatcher_DumpMemory(const void* pvMemory, CrashCatcherElementSizes elementSize, size_t elementCount)
 
/*
ダンプの送信終了をシリアルクライアントに知らせるため、###END### を送信する
*/
CrashCatcherReturnCodes CrashCatcher_DumpEnd(void)

自分のリポジトリにコピペして、1byteずつ送る関数の定義とかしておく。
CrashCatcher_GetMemoryRegions()関数には注意が必要で、RAMの出力を行わないときはNULLポインタを返すようにコードを変更しておく。
CrashCatcherの中でHardFault_Handler()が定義されているので、関数の重複でビルドエラーになる事があるから注意

Pythonでpyserialを追加しておく
https://github.com/adamgreen/CrashDebugをクローンして実行ファイルを入手する
記事をもとにPythonのスクリプトを作る

表示クリック ⇲

非表示クリック ⇱

dump.py

from serial import Serial
import sys
import platform
import subprocess
 
port = sys.argv[1]
path_to_elf = sys.argv[2]
ser = Serial(port, 115200) # Configure speed depending on your config
 
while 1:
    # Read line by line (waiting for '\n')
    line = ser.readline()
    if not line:
        break
 
    # When crash is detected
    # Crash dump is added into a temporary file
    # GDB is used to back trace the crash
    if b"###CRASH###" in line.strip():
        print("Crash detected, retrieving crash info...")
        dump_filename = "last_crash_dump.txt"
        dump_file = open(dump_filename, 'wb+')
 
        # Get the CrashDebug executable right into the local directory
        crashdebug_exe = "../CrashDebug/bins/lin64/CrashDebug"
        if platform.system() == "Darwin":
            crashdebug_exe = "../CrashDebug/osx64/CrashDebug"
        if platform.system() == "Windows":
            crashdebug_exe = "../CrashDebug/bins/win32/CrashDebug"
        cmd = "arm-none-eabi-gdb -nx --batch --quiet " + path_to_elf + "  -ex \"set target-charset ASCII\" -ex \"target remote | " + crashdebug_exe + " --elf " + path_to_elf + " --dump " + dump_filename + "\" -ex \"set print pretty on\" -ex \"bt full\" -ex \"quit\""
        # For debugging purpose, we may want to print the command
        print(cmd)
 
        # We are now storing the stack dump into the file
        line = ser.readline()
        dumping = True
        while b"###END###" not in line.strip():
            dump_file.write(line)
            line = ser.readline()
 
        print("Crash info retrieved.\n")
 
        dump_file.close()
 
        # We can call GDB and CrashDebug using the command and print the results
        process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
        output, error = process.communicate()
 
        print(output.decode("utf-8"))
        print("---------\n")
        line = b""
 
    # Print out the line
    print(line.decode('utf-8').rstrip())

今回はこんな感じのディレクトリ構成で進める

WorkSpace
├─CrashDebug
│  ├─bins
│  │  ├─lin64
│  │  ├─osx64
│  │  └─win32
│  ︙
└─CrashTest # <- ここで下のコマンドを実行する想定
   ├─build
   │  ├─CrashTest.elf
   ︙ ︙
   └─dump.py

COM6と通信するものとする

path/to/WorkSpace$ python dump.py COM6 build/CrashTest.elf

こんな感じの出力が出た

Crash detected, retrieving crash info...
arm-none-eabi-gdb -nx --batch --quiet build/CrashTest.elf  -ex "set target-charset ASCII" -ex "target remote | ../CrashDebug/bins/win32/CrashDebug --elf build/CrashTest.elf --dump last_crash_dump.txt" -ex "set print pretty on" -ex "bt full" -ex "quit"
Crash info retrieved.
 
F:\gcc-arm-none-eabi-10.3-2021.10\bin\arm-none-eabi-gdb.exe: warning: Couldn't determine a path for the index cache directory.
GotoCrash () at Core/Src/main.c:70
70        __builtin_trap();
#0  GotoCrash () at Core/Src/main.c:70
No locals.
#1  0x080006d4 in main () at Core/Src/main.c:126
No locals.
Backtrace stopped: Cannot access memory at address 0x2001fffc
A debugging session is active.
 
        Inferior 1 [Remote target] will be killed.
 
Quit anyway? (y or n) [answered Y; input not from terminal]
 
---------

これでHardFaultが発生したとき原因究明のスピードアップが期待できる

参考文献

  • crashcatcher.txt
  • 最終更新: 2023/06/25
  • by yuqlid