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

การตรวจสอบ 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 WAT ทั้งหมดไปยัง stdout
wasm2wat add.wasm
# บันทึกลงไฟล์สำหรับแก้ไขหรือ diff
wasm2wat add.wasm -o add.wat
# สร้างชื่อ synthetic เมื่อไม่มีชื่อ
wasm2wat add.wasm --generate-names

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

Terminal window
# สรุป section ทั้งหมด
wasm-objdump -x my.wasm
# Disassemble code section เท่านั้น (แสดงคำสั่งดิบ)
wasm-objdump -d my.wasm
# แสดง section เดียว
wasm-objdump -j export my.wasm
wasm-objdump -j import my.wasm

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

Terminal window
# ติดตั้ง
cargo install twiggy
# แสดง contributors หลักต่อขนาด binary
twiggy top my.wasm
# แสดง retaining paths สำหรับฟังก์ชันเฉพาะ
twiggy paths my.wasm --function "some_large_function"
# Output เป็น JSON สำหรับการประมวลผลต่อ
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 ส่วนทีมที่ ship ไปที่ Cloudflare Workers/CDN มักตรวจ binary ด้วยเครื่องมือในบทนี้ก่อนเสมอ เพื่อคุม budget ของขนาด binary ให้อยู่ในเป้าที่ตั้งไว้

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