Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ファームウェアの Panic を 自動で収集・解析・分類・集計して 市場品質の改善サイクルを回す

Hideaki Tai
September 29, 2022

ファームウェアの Panic を 自動で収集・解析・分類・集計して 市場品質の改善サイクルを回す

Bitkey x Nature / IoTぶっちゃけNight での発表資料です。
https://nature.connpass.com/event/257336/

Hideaki Tai

September 29, 2022
Tweet

More Decks by Hideaki Tai

Other Decks in Technology

Transcript

  1. 2022/09/28
    ファームウェアの Panic を
    自動で収集・解析・分類・集計して
    市場品質の改善サイクルを回す

    View full-size slide

  2. 自己紹介
    - 田井 秀昭
    - ハードウェア + メディアテクノロジーを楽しんでいる人
    - Arduino / 回路 -> 光・音・ロボティクス・ウェアラブル
    - Nature SWE (FW) <- Rhizomatiks HW <- 電機メーカー HW
    - HP / GitHub / Twitter

    View full-size slide

  3. Nature Remo では普段から
    様々なエラーログを収集しています
    🗒

    View full-size slide

  4. 市場には未知のバグが潜んでいるはず…!

    View full-size slide

  5. (天の声) Backtrace を収集したいですよね?
    💡

    View full-size slide

  6. ESP-IDF は Panic したら Backtrace を出してくれる
    Guru Meditation Error: Core 0 panic'ed (Cache disabled but cached memory region accessed).
    Exception was unhandled.
    Core 0 register dump:
    PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
    A2 : 0x00000000 A3 : 0x00000001 A4 : 0x00000001 A5 : 0x3ffb50dc
    A6 : 0x00000000 A7 : 0x00000001 A8 : 0x00000000 A9 : 0x3ffb5000
    A10 : 0x00000000 A11 : 0x3ffb2bac A12 : 0x40082d1c A13 : 0x06ff1ff8
    A14 : 0x3ffb7078 A15 : 0x00000000 SAR : 0x00000014 EXCCAUSE: 0x0000001d
    EXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0xffffffff
    Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050
    参考: Fatal Errors
    # ESP-IDF を Activate した状態で下記のようなツールで Backtrace を手動でデコードできる
    xtensa-esp32-elf-addr2line -pfiaC -e firmware.elf {Backtrace の文字列}

    View full-size slide

  7. IDF Monitor で見ていれば自動でデコードしてくれる
    Core 0 register dump:
    PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
    0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36
    A2 : 0x00000000 A3 : 0x00000001 A4 : 0x00000001 A5 : 0x3ffb50dc
    A6 : 0x00000000 A7 : 0x00000001 A8 : 0x00000000 A9 : 0x3ffb5000
    A10 : 0x00000000 A11 : 0x3ffb2bac A12 : 0x40082d1c A13 : 0x06ff1ff8
    0x40082d1c: _calloc_r at /Users/user/esp/esp-idf/components/newlib/syscalls.c:51
    A14 : 0x3ffb7078 A15 : 0x00000000 SAR : 0x00000014 EXCCAUSE: 0x0000001d
    EXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0xffffffff
    Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050
    0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36
    0x400d0802: main_task at /Users/user/esp/esp-idf/components/esp32/cpu_start.c:470

    View full-size slide

  8. ESP-IDF の Backtrace 出力をハックして
    収集できたら最高では?
    🤔

    View full-size slide

  9. ただし条件がある!

    View full-size slide

  10. Firmware OTA Update でのトラブルを起こさない!
    - 独自に ESP-IDF を Fork して変更したくない
    - 稼働中の数十万台の Remo の Flash の Partition は変えたくない
    - なるべく DRAM の容量を節約したい
    - Panic 中に Panic だけはしないように細心の注意を!!

    View full-size slide

  11. どうしたら良いか?
    🤔

    View full-size slide

  12. Backtrace を安全に収集するために解決すべきこと
    - 独自に ESP-IDF を Fork して変更したくない
    - 稼働中の数十万台の Remo の Flash の Partition は変えたくない
    - なるべく DRAM の容量を節約したい
    - Panic 中に Panic だけはしないように細心の注意を!!

    View full-size slide

  13. ESP-IDF で Backtrace を出力する流れはこんな感じらしい
    // ESP-IDF で実装されている Backtrace を表示する Public な関数
    void panic_print_backtrace(const void *f, int core) { /* … */ }
    参考: esp-idf/components/esp_system/port/arch/xtensa/panic_arch.c

    View full-size slide

  14. (天の声) リンカの wrap 機能はいかが?
    💡

    View full-size slide

  15. リンカの wrap 機能で既存の Public な関数を wrap する
    LDFLAGS=-Wl,--wrap=panic_print_backtrace
    // 元の panic_print_backtrace() は __real prefix を付ければ呼び出せる
    extern void __real_panic_print_backtrace(const void* f, int core);
    // 元の panic_print_backtrace() の呼び出しは __wrap prefix のものに置き換えられる
    void __wrap_panic_print_backtrace(const void* f, int core) {
    // ここで Backtrace を保存する
    // 必要であれば、元の実装を呼び出す
    __real_panic_print_backtrace(f, core);
    }
    // ESP-IDF で実装されている Backtrace を表示する Public な関数
    void panic_print_backtrace(const void *f, int core) { /* … */ }
    下記のように wrap 関数を定義し、LDFLAGS をつけてコンパイルすれば、
    ESP-IDF を Fork したりせずとも Public な関数をハックできる!

    View full-size slide

  16. - 独自に ESP-IDF を Fork して変更したくない
    - 稼働中の数十万台の Remo の Flash の Partition は変えたくない
    - なるべく DRAM の容量を節約したい
    - Panic 中に Panic だけはしないように細心の注意を!!
    Backtrace を安全に収集するために解決すべきこと

    View full-size slide

  17. どうやって再起動後まで Backtrace を保存するか?
    - 再起動後まで Backtrace のデータを保持するには、いくつかやり方がある
    - ESP-IDF の Core Dump 機能を使う (🆖 Flash に専用の Partition を作る必要がある)
    - リセットしても消えない DRAM領域を使う (🆖 DRAM 容量を節約したい)
    - リセットしても消えない RTC Slow Memory を使う (🆗 Flash を使用せずに 8KB 使える!)
    - 👉 リセットしても消えない RTC Slow Memory に Backtrace を保存する
    - Partition を変更する必要がない
    - DRAM 容量を節約できる
    // RTC Slow Memory 上に配置するデータ
    static RTC_NOINIT_ATTR int s_cached_panicked_core;
    static RTC_NOINIT_ATTR uint32_t s_pcs[BACKTRACE_CACHE_MAX_DEPTH];
    static RTC_NOINIT_ATTR uint32_t s_sps[BACKTRACE_CACHE_MAX_DEPTH];
    static RTC_NOINIT_ATTR size_t s_cached_backtrace_depth;
    static RTC_NOINIT_ATTR backtrace_status_t s_cached_backtrace_status;

    View full-size slide

  18. - 独自に ESP-IDF を Fork して変更したくない
    - 稼働中の数十万台の Remo の Flash の Partition は変えたくない
    - なるべく DRAM の容量を節約したい
    - Panic 中に Panic だけはしないように細心の注意を!!
    Backtrace を安全に収集するために解決すべきこと

    View full-size slide

  19. 実機テストで Panic 中に Panic したりしないか入念に確認
    // Backtrace が想定どおり取れるかテスト (restart, abort, assert, wdt, stack/buffer overflow)
    // 最後に余計な Panic なく想定どおりの回数だけ Panic していることを確認する
    typedef enum {
    TEST_STATE_INITIALIZE, TEST_STATE_RESTART, …,
    TEST_STATE_ABORT_CORE0, TEST_STATE_ABORT_CORE1, …,
    TEST_STATE_ASSERT_CORE0, TEST_STATE_ASSERT_CORE1, …,
    TEST_STATE_WATCHDOG_CORE0, TEST_STATE_WATCHDOG_CORE1, …,
    TEST_STATE_STACK_OVERFLOW_CORE0, TEST_STATE_STACK_OVERFLOW_CORE1, …,
    TEST_STATE_BUFFER_OVERFLOW_CORE0, TEST_STATE_BUFFER_OVERFLOW_CORE1, …,
    TEST_STATE_FINALIZE,
    } test_state_t;
    static RTC_NOINIT_ATTR test_state_t s_test_state; // RTC Memory に保存してテスト状況を追跡
    static RTC_NOINIT_ATTR size_t s_panic_count; // 想定外に Panic していないか監視
    const size_t MAX_PANIC_COUNT = 10; // このテストで Panic する回数

    View full-size slide

  20. [まとめ] Backtrace を保存してサーバへ送信する
    1. リンカの wrap 機能を使って Panic 時の Backtrace 表示関数を wrap
    ○ ESP-IDF を Fork したりせずにハックできる!
    2. RTC Slow Memory (.noinit 領域) を使って Backtrace 情報を保存
    ○ Partition を変更しない + DRAM 容量を節約して保存できる!
    3. RTC Slow Memory に Backtrace 情報が保存されていたらサーバへ送信
    ※ 実機テストで Panic 中に Panic するなどの致命的な不具合がないことを確認

    View full-size slide

  21. [まとめ] Backtrace を保存してサーバへ送信する

    View full-size slide

  22. ファームウェア側は完成!
    🙌

    View full-size slide

  23. サーバに送信した後は…
    - Backtrace を自動でデコードしてくれたらもっと良いのでは?
    - 自動でエラー解析をして原因箇所と経路を特定してもらえる
    - すぐに改善の検討を始められる
    - さらに自動でエラーを分類・集計・報告してくれたら最高では?
    - 市場の影響度 (改善の優先度) も見えるようになる

    View full-size slide

  24. Remo の Backtrace を自動で収集してくれる君の概要

    View full-size slide

  25. 不慣れな開発をする私に試練が襲いかかる…
    しかし

    View full-size slide

  26. 神 (1) が現れ 20 分で Go のバグを解決してくれる…

    View full-size slide

  27. 神 (2) が現れ sentry を紹介してくれる…

    View full-size slide

  28. sentry で Backtrace を自動で収集・分類・集計 (参考)

    View full-size slide

  29. 本日のまとめ
    - リンカの wrap 機能を使って IDF を変更せずに挙動をハックした
    - ESP の RTC Slow Memory を使って再起動後まで Backtrace を保存した
    - 市場の製品に影響を与えないよう入念に実機テストを行った
    - Nature の仲間の力を借りれば良い感じのシステムが構築できる!

    View full-size slide

  30. Nature では積極採用中です
    - Nature では一緒に開発をしてくれる仲間を募集しています
    - カジュアル面談も大歓迎です
    - Culture Deck : ミッション・サービス・組織・文化・福利厚生をご覧下さい

    View full-size slide

  31. ご清聴ありがとうございました
    🙌

    View full-size slide