├── .cargo └── config.github ├── .github ├── dependabot.yml └── workflows │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── bin.rs └── lib.rs └── web └── index.html /.cargo/config.github: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-gnu] 2 | linker = "aarch64-linux-gnu-gcc" 3 | 4 | [target.armv7-unknown-linux-gnueabihf] 5 | linker = "arm-linux-gnueabihf-gcc" -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | open-pull-requests-limit: 10 -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | publish-bin-to-github: 16 | name: Publish bin tool to Github 17 | runs-on: ${{matrix.os}} 18 | strategy: 19 | matrix: 20 | include: 21 | - build: linux 22 | os: ubuntu-latest 23 | rust: stable 24 | target: x86_64-unknown-linux-musl 25 | cross: false 26 | bin_suffix: x86_64-linux 27 | - build: arm-v7 28 | os: ubuntu-latest 29 | rust: stable 30 | target: armv7-unknown-linux-gnueabihf 31 | linker: gcc-arm-linux-gnueabihf 32 | cross: true 33 | bin_suffix: arm-v7-linux 34 | - build: aarch64 35 | os: ubuntu-latest 36 | rust: stable 37 | target: aarch64-unknown-linux-gnu 38 | linker: gcc-aarch64-linux-gnu 39 | cross: true 40 | bin_suffix: aarch64-linux 41 | - build: macos 42 | os: macos-latest 43 | rust: stable 44 | target: x86_64-apple-darwin 45 | cross: false 46 | bin_suffix: macos 47 | steps: 48 | - name: Checkout repository 49 | uses: actions/checkout@v4.2.2 50 | with: 51 | fetch-depth: 1 52 | 53 | - name: Cache 54 | uses: actions/cache@v4.2.3 55 | with: 56 | path: | 57 | ~/.cargo/registry 58 | ~/.cargo/git 59 | ~/.rustup 60 | target 61 | key: ${{ runner.os }}-${{ matrix.rust }} 62 | 63 | - name: Install Linker 64 | if: matrix.cross 65 | run: | 66 | sudo apt update 67 | sudo apt install ${{ matrix.linker }} 68 | cat .cargo/config.github >> .cargo/config 69 | - name: Install Rust 70 | run: | 71 | rustup install ${{ matrix.rust }} 72 | rustup target add ${{ matrix.target }} 73 | rustup show 74 | - name: Build 75 | run: cargo build --release --target ${{ matrix.target }} 76 | - name: Copy and rename binary 77 | run: cp target/${{ matrix.target }}/release/esp_exception_decoder esp_exception_decoder_${{ matrix.bin_suffix }} 78 | 79 | - name: Release 80 | uses: softprops/action-gh-release@v2.2.2 81 | with: 82 | files: esp_exception_decoder_${{ matrix.bin_suffix }} 83 | 84 | publish-exe-to-github: 85 | name: Publish exe tool to Github 86 | runs-on: ubuntu-latest 87 | steps: 88 | - name: Checkout repository 89 | uses: actions/checkout@v4.2.2 90 | with: 91 | fetch-depth: 1 92 | 93 | - name: Cache 94 | uses: actions/cache@v4.2.3 95 | with: 96 | path: | 97 | ~/.cargo/registry 98 | ~/.cargo/git 99 | ~/.rustup 100 | target 101 | key: ${{ runner.os }}-stable 102 | 103 | - name: Install Linker 104 | run: | 105 | sudo apt update 106 | sudo apt install mingw-w64 107 | - name: Install Rust 108 | run: | 109 | rustup install stable 110 | rustup target add x86_64-pc-windows-gnu 111 | rustup show 112 | - name: Build 113 | run: cargo build --release --target x86_64-pc-windows-gnu 114 | 115 | - name: Release 116 | uses: softprops/action-gh-release@v2.2.2 117 | with: 118 | files: target/x86_64-pc-windows-gnu/release/esp_exception_decoder.exe 119 | 120 | publish-web-to-github: 121 | name: Publish web tool to GitHub 122 | runs-on: ubuntu-latest 123 | permissions: 124 | pages: write 125 | id-token: write 126 | contents: write 127 | 128 | steps: 129 | - uses: actions/checkout@v4.2.2 130 | - uses: actions-rs/toolchain@v1 131 | with: 132 | toolchain: stable 133 | target: wasm32-unknown-unknown 134 | - uses: Swatinem/rust-cache@v2 135 | 136 | - uses: jetli/wasm-pack-action@v0.4.0 137 | - name: Run wasm-pack 138 | run: wasm-pack build --target web --out-dir web/ 139 | - name: Make an archive for web deployment 140 | run: tar czf esp_exception_decoder_wasm.tar.gz -C web index.html esp_exception_decoder_rs.js esp_exception_decoder_rs_bg.wasm 141 | 142 | - name: Release 143 | uses: softprops/action-gh-release@v2.2.2 144 | with: 145 | files: esp_exception_decoder_wasm.tar.gz 146 | 147 | - name: Copy files for GitHub Pages deployment 148 | run: mkdir -p public; cp web/index.html web/esp_exception_decoder_rs.js web/esp_exception_decoder_rs_bg.wasm public/ 149 | 150 | - uses: actions/upload-pages-artifact@v3.0.1 151 | with: 152 | path: public 153 | retention-days: 1 154 | - name: Setup Pages 155 | uses: actions/configure-pages@v5.0.0 156 | - name: Deploy to GitHub Pages 157 | id: deployment 158 | uses: actions/deploy-pages@v4.0.5 159 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.16.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" 10 | dependencies = [ 11 | "cpp_demangle", 12 | "fallible-iterator", 13 | "gimli", 14 | "object", 15 | "rustc-demangle", 16 | "smallvec", 17 | ] 18 | 19 | [[package]] 20 | name = "adler" 21 | version = "1.0.2" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 24 | 25 | [[package]] 26 | name = "aho-corasick" 27 | version = "0.7.18" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 30 | dependencies = [ 31 | "memchr", 32 | ] 33 | 34 | [[package]] 35 | name = "ansi_term" 36 | version = "0.11.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 39 | dependencies = [ 40 | "winapi", 41 | ] 42 | 43 | [[package]] 44 | name = "atty" 45 | version = "0.2.14" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 48 | dependencies = [ 49 | "hermit-abi", 50 | "libc", 51 | "winapi", 52 | ] 53 | 54 | [[package]] 55 | name = "autocfg" 56 | version = "1.0.1" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 59 | 60 | [[package]] 61 | name = "bitflags" 62 | version = "1.3.2" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 65 | 66 | [[package]] 67 | name = "bumpalo" 68 | version = "3.7.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" 71 | 72 | [[package]] 73 | name = "cfg-if" 74 | version = "1.0.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 77 | 78 | [[package]] 79 | name = "clap" 80 | version = "2.33.3" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 83 | dependencies = [ 84 | "ansi_term", 85 | "atty", 86 | "bitflags", 87 | "strsim", 88 | "textwrap", 89 | "unicode-width", 90 | "vec_map", 91 | ] 92 | 93 | [[package]] 94 | name = "colored" 95 | version = "2.0.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 98 | dependencies = [ 99 | "atty", 100 | "lazy_static", 101 | "winapi", 102 | ] 103 | 104 | [[package]] 105 | name = "cpp_demangle" 106 | version = "0.3.3" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "8ea47428dc9d2237f3c6bc134472edfd63ebba0af932e783506dcfd66f10d18a" 109 | dependencies = [ 110 | "cfg-if", 111 | ] 112 | 113 | [[package]] 114 | name = "crc32fast" 115 | version = "1.2.1" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 118 | dependencies = [ 119 | "cfg-if", 120 | ] 121 | 122 | [[package]] 123 | name = "esp-exception-decoder" 124 | version = "1.1.0" 125 | dependencies = [ 126 | "addr2line", 127 | "clap", 128 | "colored", 129 | "js-sys", 130 | "object", 131 | "regex", 132 | "typed-arena", 133 | "wasm-bindgen", 134 | ] 135 | 136 | [[package]] 137 | name = "fallible-iterator" 138 | version = "0.2.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 141 | 142 | [[package]] 143 | name = "flate2" 144 | version = "1.0.22" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 147 | dependencies = [ 148 | "cfg-if", 149 | "crc32fast", 150 | "libc", 151 | "miniz_oxide", 152 | ] 153 | 154 | [[package]] 155 | name = "gimli" 156 | version = "0.25.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" 159 | dependencies = [ 160 | "fallible-iterator", 161 | "stable_deref_trait", 162 | ] 163 | 164 | [[package]] 165 | name = "hermit-abi" 166 | version = "0.1.19" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 169 | dependencies = [ 170 | "libc", 171 | ] 172 | 173 | [[package]] 174 | name = "js-sys" 175 | version = "0.3.55" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 178 | dependencies = [ 179 | "wasm-bindgen", 180 | ] 181 | 182 | [[package]] 183 | name = "lazy_static" 184 | version = "1.4.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 187 | 188 | [[package]] 189 | name = "libc" 190 | version = "0.2.103" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" 193 | 194 | [[package]] 195 | name = "log" 196 | version = "0.4.14" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 199 | dependencies = [ 200 | "cfg-if", 201 | ] 202 | 203 | [[package]] 204 | name = "memchr" 205 | version = "2.4.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 208 | 209 | [[package]] 210 | name = "miniz_oxide" 211 | version = "0.4.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 214 | dependencies = [ 215 | "adler", 216 | "autocfg", 217 | ] 218 | 219 | [[package]] 220 | name = "object" 221 | version = "0.26.2" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" 224 | dependencies = [ 225 | "flate2", 226 | "memchr", 227 | ] 228 | 229 | [[package]] 230 | name = "proc-macro2" 231 | version = "1.0.29" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 234 | dependencies = [ 235 | "unicode-xid", 236 | ] 237 | 238 | [[package]] 239 | name = "quote" 240 | version = "1.0.10" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 243 | dependencies = [ 244 | "proc-macro2", 245 | ] 246 | 247 | [[package]] 248 | name = "regex" 249 | version = "1.5.4" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 252 | dependencies = [ 253 | "aho-corasick", 254 | "memchr", 255 | "regex-syntax", 256 | ] 257 | 258 | [[package]] 259 | name = "regex-syntax" 260 | version = "0.6.25" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 263 | 264 | [[package]] 265 | name = "rustc-demangle" 266 | version = "0.1.21" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 269 | 270 | [[package]] 271 | name = "smallvec" 272 | version = "1.7.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 275 | 276 | [[package]] 277 | name = "stable_deref_trait" 278 | version = "1.2.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 281 | 282 | [[package]] 283 | name = "strsim" 284 | version = "0.8.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 287 | 288 | [[package]] 289 | name = "syn" 290 | version = "1.0.80" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" 293 | dependencies = [ 294 | "proc-macro2", 295 | "quote", 296 | "unicode-xid", 297 | ] 298 | 299 | [[package]] 300 | name = "textwrap" 301 | version = "0.11.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 304 | dependencies = [ 305 | "unicode-width", 306 | ] 307 | 308 | [[package]] 309 | name = "typed-arena" 310 | version = "2.0.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" 313 | 314 | [[package]] 315 | name = "unicode-width" 316 | version = "0.1.9" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 319 | 320 | [[package]] 321 | name = "unicode-xid" 322 | version = "0.2.2" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 325 | 326 | [[package]] 327 | name = "vec_map" 328 | version = "0.8.2" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 331 | 332 | [[package]] 333 | name = "wasm-bindgen" 334 | version = "0.2.78" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 337 | dependencies = [ 338 | "cfg-if", 339 | "wasm-bindgen-macro", 340 | ] 341 | 342 | [[package]] 343 | name = "wasm-bindgen-backend" 344 | version = "0.2.78" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 347 | dependencies = [ 348 | "bumpalo", 349 | "lazy_static", 350 | "log", 351 | "proc-macro2", 352 | "quote", 353 | "syn", 354 | "wasm-bindgen-shared", 355 | ] 356 | 357 | [[package]] 358 | name = "wasm-bindgen-macro" 359 | version = "0.2.78" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 362 | dependencies = [ 363 | "quote", 364 | "wasm-bindgen-macro-support", 365 | ] 366 | 367 | [[package]] 368 | name = "wasm-bindgen-macro-support" 369 | version = "0.2.78" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 372 | dependencies = [ 373 | "proc-macro2", 374 | "quote", 375 | "syn", 376 | "wasm-bindgen-backend", 377 | "wasm-bindgen-shared", 378 | ] 379 | 380 | [[package]] 381 | name = "wasm-bindgen-shared" 382 | version = "0.2.78" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 385 | 386 | [[package]] 387 | name = "winapi" 388 | version = "0.3.9" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 391 | dependencies = [ 392 | "winapi-i686-pc-windows-gnu", 393 | "winapi-x86_64-pc-windows-gnu", 394 | ] 395 | 396 | [[package]] 397 | name = "winapi-i686-pc-windows-gnu" 398 | version = "0.4.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 401 | 402 | [[package]] 403 | name = "winapi-x86_64-pc-windows-gnu" 404 | version = "0.4.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 407 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "esp-exception-decoder" 3 | version = "1.1.0" 4 | edition = "2018" 5 | authors = ["Maxime Borges "] 6 | 7 | [lib] 8 | name = "esp_exception_decoder_rs" 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[bin]] 13 | name = "esp_exception_decoder" 14 | path = "src/bin.rs" 15 | 16 | [dependencies] 17 | clap = "2" 18 | addr2line = "0.16.0" 19 | object = { version = "0.26", default-features = false, features = ["read"], optional = true } 20 | typed-arena = "2" 21 | regex = "1.5.4" 22 | colored = "2" 23 | wasm-bindgen = { version = "0.2" } 24 | js-sys = "0.3" 25 | 26 | [profile.release] 27 | opt-level = "z" # Optimize for size. 28 | lto = true 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Maxime BORGES 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ESP Stack Trace Decoder 2 | 3 | A Rust ESP stack trace decoder that can also **runs in your browser** thanks to WebAssembly. 4 | 5 | It is composed of a ⌨️ Rust library, a 💻 Rust command line tool, and a 🌏 WebAssembly library with a HTML interface. 6 | 7 | ### Web tool 8 | 9 | The web tool is hosted on [Github Pages here](https://esphome.github.io/esp-stacktrace-decoder/). 10 | 11 | It is taking your `.elf` firmware and the stack trace, and outputs the list of functions and their locations, without uploading any of your data anywhere. 12 | 13 | Everything run in your browser, ✨just like that✨. 14 | 15 | ![screenshot](https://user-images.githubusercontent.com/159235/136428494-4fdb6c69-74ca-42ab-8bf7-e26d1d625a28.png) 16 | 17 | You can also deploy it yourself by hosting the content of the pre-compiled package `esp_exception_decoder_wasm.tar.gz` on the [release page](https://github.com/esphome/esp-stacktrace-decoder/releases), or by compiling the library in WebAssembly using `wasm-pack`: 18 | 19 | # Install the Rust toolchain by following the latest instructions from here: https://www.rust-lang.org/tools/install 20 | # Install wasm-pack by following the latest instructions from here: https://rustwasm.github.io/wasm-pack/installer 21 | # Build the WebAssembly library 22 | wasm-pack build --target web --out-dir web/ 23 | 24 | Note that only the `index.html`, `esp_exception_decoder_rs.js` and `esp_exception_decoder_rs_bg.wasm` from the `web/` directory are necessary. 25 | 26 | Then you can host the content of the `web/` directory on any HTTP server. 27 | 28 | Here you can find a lot of different ways of starting a simple HTTP server that can be used to serves the `web/` folder: http://gist.github.com/willurd/5720255 29 | 30 | Opening the `index.html` file from your filesystem in your browser will not work since we can't include JavaScript files without a HTTP server due to [default CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp). 31 | 32 | ### Command line tool 33 | 34 | A bit more boring command line tool is also available: 35 | 36 | ![esp_exception_decoder_rs_cli](https://user-images.githubusercontent.com/159235/136429806-48b82e04-cc55-4dda-84de-d143001165c3.png) 37 | 38 | Get the latest binary release here: [Releases](https://github.com/esphome/esp-stacktrace-decoder/releases) 39 | 40 | Or build it yourself: 41 | 42 | # Install the Rust toolchain by following the latest instructions here: https://www.rust-lang.org/tools/install 43 | # Build the command line binary 44 | cargo build --release 45 | 46 | To run the command line tool, make sure that the binary is executable: 47 | 48 | chmod +x esp_exception_decoder 49 | 50 | Then execute it like this, replacing `firmware.elf` with your `.elf` firmware and `stack_trace.txt` with the stack trace from your ESP: 51 | 52 | ./esp_exception_decoder firmware.elf stack_trace.txt 53 | 54 | You can also ommit the stack trace file and use the standard input instead: 55 | 56 | cat stack_trace.txt | ./esp_exception_decoder firmware.elf 57 | 58 | Or even use the tool semi-interactively by running the program without the stack trace file parameter, pasting the stack trace and pressing CTRL+D: 59 | 60 | ./esp_exception_decoder firmware.elf 61 | # The program is executing but not displaying anything 62 | # Paste the stack trace here 63 | # Then press CTRL+D 64 | 65 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{Read, Stdin}; 3 | 4 | use clap::{App, Arg}; 5 | use esp_exception_decoder_rs::decode; 6 | use colored::*; 7 | 8 | enum DumpSource<'a> { 9 | FilePath(&'a str), 10 | Stdin(Stdin), 11 | } 12 | 13 | fn main() { 14 | let matches = App::new("hardliner") 15 | .version("0.1") 16 | .about("ESP8266 exception decoder") 17 | .arg( 18 | Arg::with_name("elf") 19 | .value_name("binary_file") 20 | .help( 21 | "Specify the name of the .elf executable.", 22 | ) 23 | .required(true), 24 | ) 25 | .arg( 26 | Arg::with_name("dump") 27 | .value_name("stack_trace_file") 28 | .help("Stack trace file."), 29 | ) 30 | .get_matches(); 31 | 32 | let binary_file = matches.value_of("elf").unwrap(); 33 | let mut binary_file = File::open(binary_file).unwrap(); 34 | let mut binary_buf = Vec::::new(); 35 | let _ = binary_file.read_to_end(&mut binary_buf); 36 | 37 | let stdin = std::io::stdin(); 38 | let stack_trace_source = matches 39 | .value_of("dump") 40 | .map(DumpSource::FilePath) 41 | .unwrap_or_else(|| DumpSource::Stdin(stdin)); 42 | 43 | let stack_trace = match stack_trace_source { 44 | DumpSource::FilePath(file) => { 45 | let mut file = File::open(file).unwrap(); 46 | let mut stack_trace = String::new(); 47 | let _ = file.read_to_string(&mut stack_trace).unwrap(); 48 | stack_trace 49 | }, 50 | DumpSource::Stdin(stdin) => { 51 | let mut stdin_buffer = Vec::::new(); 52 | let _ = stdin.lock().read_to_end(&mut stdin_buffer).unwrap(); 53 | let stack_trace = String::from_utf8(stdin_buffer).unwrap(); 54 | stack_trace 55 | } 56 | }; 57 | 58 | let decoded_addresses = decode(&binary_buf, &stack_trace); 59 | for address in decoded_addresses { 60 | println!("0x{:04x}: {} at {}", 61 | address.address, 62 | address.function_name.bold(), 63 | address.location.blue() 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow}; 2 | use addr2line::{Context, fallible_iterator::FallibleIterator, gimli, object::{self, Endianness, Object, ObjectSection}}; 3 | 4 | use typed_arena::Arena; 5 | use regex::Regex; 6 | #[cfg(target_family = "wasm")] 7 | use wasm_bindgen::prelude::*; 8 | #[cfg(target_family = "wasm")] 9 | use js_sys::Array; 10 | 11 | fn load_file_section<'input, 'arena, Endian: gimli::Endianity>( 12 | id: gimli::SectionId, 13 | file: &object::File<'input>, 14 | endian: Endian, 15 | arena_data: &'arena Arena>, 16 | ) -> Result, object::Error> { 17 | let name = id.name(); 18 | match file.section_by_name(name) { 19 | Some(section) => match section.uncompressed_data()? { 20 | Cow::Borrowed(b) => Ok(gimli::EndianSlice::new(b, endian)), 21 | Cow::Owned(b) => Ok(gimli::EndianSlice::new(arena_data.alloc(b.into()), endian)), 22 | }, 23 | None => Ok(gimli::EndianSlice::new(&[][..], endian)), 24 | } 25 | } 26 | 27 | #[cfg(not(target_family = "wasm"))] 28 | pub struct DecodedAddress { 29 | pub address: u64, 30 | pub function_name: String, 31 | pub location: String, 32 | } 33 | #[cfg(target_family = "wasm")] 34 | #[wasm_bindgen] 35 | pub struct DecodedAddress { 36 | pub address: u64, 37 | function_name: String, 38 | location: String, 39 | } 40 | #[cfg(target_family = "wasm")] 41 | #[cfg_attr(target_family = "wasm", wasm_bindgen)] 42 | impl DecodedAddress { 43 | #[cfg_attr(target_family = "wasm", wasm_bindgen(getter))] 44 | pub fn function_name(&self) -> String { 45 | self.function_name.clone() 46 | } 47 | #[cfg_attr(target_family = "wasm", wasm_bindgen(setter))] 48 | pub fn set_function_name(&mut self, function_name: String) { 49 | self.function_name = function_name; 50 | } 51 | 52 | #[cfg_attr(target_family = "wasm", wasm_bindgen(getter))] 53 | pub fn location(&self) -> String { 54 | self.location.clone() 55 | } 56 | #[cfg_attr(target_family = "wasm", wasm_bindgen(setter))] 57 | pub fn set_location(&mut self, location: String) { 58 | self.location = location; 59 | } 60 | } 61 | 62 | #[cfg(target_family = "wasm")] 63 | type ReturnType = Array; 64 | #[cfg(not(target_family = "wasm"))] 65 | type ReturnType = Vec; 66 | 67 | #[cfg_attr(target_family = "wasm", wasm_bindgen)] 68 | pub fn decode(bin: &[u8], dump: &str) -> ReturnType { 69 | // Prepare vector for output data 70 | let mut decoded_data = Vec::::new(); 71 | #[cfg(target_family = "wasm")] 72 | let format_return = |decoded_data: Vec::| { 73 | decoded_data.into_iter().map(JsValue::from).collect() 74 | }; 75 | #[cfg(not(target_family = "wasm"))] 76 | let format_return = |decoded_data| { 77 | decoded_data 78 | }; 79 | 80 | // Used to keep slices from uncompressed section data 81 | let arena_data = Arena::new(); 82 | 83 | // Parse the binary as an object file 84 | let object = &object::File::parse(bin); 85 | let object = match object { 86 | Ok(object) => object, 87 | Err(_) => return format_return(decoded_data), 88 | }; 89 | let endianness = match object.endianness() { 90 | Endianness::Little => gimli::RunTimeEndian::Little, 91 | Endianness::Big => gimli::RunTimeEndian::Big 92 | }; 93 | 94 | // Wrapper for the Dwarf loader 95 | let mut load_section = |id: gimli::SectionId| -> Result<_, _> { 96 | load_file_section(id, object, endianness, &arena_data) 97 | }; 98 | 99 | // Load the Dwarf sections 100 | let dwarf = gimli::Dwarf::load(&mut load_section); 101 | let dwarf = match dwarf { 102 | Ok(dwarf) => dwarf, 103 | Err(_) => return format_return(decoded_data), 104 | }; 105 | let ctx = Context::from_dwarf(dwarf); 106 | let ctx = match ctx { 107 | Ok(ctx) => ctx, 108 | Err(_) => return format_return(decoded_data), 109 | }; 110 | 111 | // Match everything that looks like a program address 112 | let re = Regex::new(r"(4[0-9a-fA-F]{7})\b").unwrap(); 113 | for cap in re.captures_iter(dump) { 114 | let address = u64::from_str_radix(&cap[0], 16).unwrap(); 115 | // Look for frame that contains the address 116 | let mut frames = match ctx.find_frames(address) { 117 | Ok(frames) => frames.enumerate(), 118 | Err(_) => continue, 119 | }; 120 | while let Some((_, frame)) = frames.next().unwrap() { 121 | // Skip if it doesn't point to a function 122 | if frame.function.is_none() { 123 | continue; 124 | } 125 | // Extract function name 126 | let func = frame.function.unwrap(); 127 | let function_name = match func.raw_name() { 128 | Ok(function_name) => String::from(addr2line::demangle_auto(function_name, func.language)), 129 | Err(_) => "unknown_func".to_string(), 130 | }; 131 | // Extract location 132 | let location = match frame.location { 133 | Some(func_location) => { 134 | let file = func_location.file.unwrap_or("?"); 135 | let line = match func_location.line { 136 | Some(line) => line.to_string(), 137 | None => "?".to_string(), 138 | }; 139 | format!("{}:{}", file, line) 140 | }, 141 | None => "?:?".to_string(), 142 | }; 143 | 144 | // Append the decoded address 145 | let decoded = DecodedAddress { 146 | address, 147 | function_name, 148 | location, 149 | }; 150 | decoded_data.push(decoded); 151 | } 152 | } 153 | 154 | format_return(decoded_data) 155 | } -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ESP Stack trace decoder 7 | 8 | 51 | 52 | 53 | 54 |
55 |
56 |

ESP Stack Trace Decoder

57 |

An online ESP stack trace decoder that runs in your browser

58 |
59 |
60 |
61 |
62 |

63 | 64 |
65 |

Stack trace:

66 | 67 | 68 |
69 | 70 | 74 |
75 | 76 | 123 | 124 | --------------------------------------------------------------------------------