ข้ามไปยังเนื้อหา

การตรวจสอบ WebAssembly Binaries

ทุกไฟล์ .wasm คือลำดับของ sections ที่มีหมายเลขกำกับ การทำความเข้าใจว่า section ใด contribute อะไรทำให้สามารถกำหนดเป้าหมายความพยายามในการ optimise ได้อย่างแม่นยำ บทเรียนนี้แสดงเครื่องมือที่นำข้อมูลนั้นขึ้นมา

WebAssembly binary แบ่งออกเป็น sections มาตรฐานสูงสุดสิบสาม sections แต่ละ section มี ID เฉพาะ แผนภาพด้านล่างแสดง sections ที่พบบ่อยที่สุดและความสัมพันธ์กัน

flowchart TD
  A[".wasm binary"] --> B["Type section\nfunction signatures"]
  A --> C["Import section\nJS / host imports"]
  A --> D["Function section\ntype index per function"]
  A --> E["Export section\npublic API names"]
  A --> F["Code section\nactual instructions"]
  A --> G["Data section\nmemory initialisers"]
  A --> H["Custom sections\nname debug DWARF producers"]
.wasm section layout

วิธีที่เร็วที่สุดในการอ่าน binary ใดก็ตามคือ wasm2wat ซึ่งสร้าง WAT text representation ขึ้นมาใหม่ รวมถึงชื่อฟังก์ชันหาก name section มีอยู่

Terminal window
# Dump the full WAT to stdout
wasm2wat add.wasm
# Save to a file for editing or diffing
wasm2wat add.wasm -o add.wat
# Generate synthetic names when none are present
wasm2wat add.wasm --generate-names

wasm-objdump -x แสดงสรุปที่มีโครงสร้างของทุก section: type signatures, imports, exports และ function indices เร็วกว่า full disassembly เมื่อคุณต้องการแค่ public API ของ module

Terminal window
# Full section summary
wasm-objdump -x my.wasm
# Disassemble code section only (shows raw instructions)
wasm-objdump -d my.wasm
# Show a single section
wasm-objdump -j export my.wasm
wasm-objdump -j import my.wasm

twiggy คือ code-size profiler ที่กำหนดทุก byte ใน binary .wasm ให้กับฟังก์ชันหรือ section เป็นวิธีที่เร็วที่สุดในการตอบคำถาม “ฟังก์ชันใดทำให้ binary ใหญ่?”

Terminal window
# Install
cargo install twiggy
# Print the top contributors to binary size
twiggy top my.wasm
# Show the retaining paths for a specific function
twiggy paths my.wasm --function "some_large_function"
# Output as JSON for further processing
twiggy top my.wasm --output-format json

รายงาน twiggy top มีลักษณะประมาณนี้:

Terminal window
Shallow Bytes | Shallow % | Item
----------------+-----------+------------------------------
14512 | 42.38% | "function names" subsection
8032 | 23.46% | code[42]: some_large_function
3200 | 9.35% | code[17]: decode_utf8

browser สมัยใหม่มาพร้อมกับการสนับสนุน WebAssembly debugging โดยตรงใน DevTools ใน Chrome และ Firefox คุณสามารถเปิด Sources → wasm เพื่อดู disassembly ของทุก module ที่ instantiate แล้ว ตั้ง breakpoints บนคำสั่งแต่ละตัว และตรวจสอบ local variables และ value stack ที่แต่ละ breakpoint

เพื่อให้ได้ชื่อฟังก์ชันที่อ่านได้ใน DevTools คุณต้องการ:

  • name section ใน binary (ผลิตโดย wat2wasm --debug-names หรือ wasm-opt --debuginfo) หรือ
  • DWARF section ที่ emit โดย Emscripten หรือ clang ด้วย -g
ตัวเลือกBenefitCost
wasm-opt -Oz (การ optimise เชิงรุกสุด)ได้ size และ speed ที่ดีที่สุดหลัง optimisebuild time นานขึ้น และผลลัพธ์ที่เห็นใน wasm2wat/wasm-objdump ห่างไกลจาก source เดิมจนอ่านยากขึ้น
เก็บ name section (--debug-names) ไว้DevTools และ wasm2wat แสดงชื่อฟังก์ชันที่อ่านได้เพิ่มขนาด binary ที่ต้อง ship จริง
  • ข้าม inspection step แล้วเดาว่าอะไรทำให้ binary ใหญ่ แทนที่จะใช้ twiggy top หาตัวการจริงๆ
  • เปิด wasm-opt เชิงรุกไปก่อนที่จะรู้จริงๆ ว่า section หรือฟังก์ชันไหนใหญ่หรือช้า
  • ลบ name section ทิ้งตั้งแต่แรก ทำให้ wasm-objdump, wasm2wat และ DevTools อ่าน stack trace ไม่ออกเลย

💡 ตัวอย่างจากของจริง

Emscripten เรียก wasm-opt ของ Binaryen อัตโนมัติเมื่อ build ด้วย -O2/-O3 (เอกสาร Emscripten) ส่วนเครื่องมือในบทนี้ (wasm-objdump, twiggy, wasm2wat) คือสิ่งที่หยิบมาใช้ทีหลัง เมื่อต้องรู้ให้แน่ชัดว่า binary ที่ optimize แล้วมีอะไรอยู่ข้างในบ้าง

tool ไหนที่ระบุได้ว่าแต่ละ byte ของไฟล์ .wasm เป็นของ function ไหน?
wasm-objdump -x แสดงอะไรออกมา?
section ไหนที่เก็บ instructions จริงของแต่ละ function?
ต้องมีอะไรใน binary ถึงจะเห็นชื่อ function ที่อ่านได้ใน DevTools?