رفتن به محتوا

اشکال‌زدایی تست ناموفق با یک عامل

این راهنما نشان می‌دهد چگونه از یک عامل بخواهید روی شکستی که دستورهای print آن را توضیح نمی‌دهند، از یک اشکال‌زدا استفاده کند.

ابزار debug یک نشست واقعی Debug Adapter Protocol را هدایت می‌کند. یک فریم متوقف‌شده هم‌زمان به همهٔ پرسش‌ها دربارهٔ آن لحظه پاسخ می‌دهد — هر متغیر محلی، هر فراخوان، و عبارتی که در حوزهٔ خودِ فریم ارزیابی می‌شود — در حالی که یک دستور print تنها به پرسشی پاسخ می‌دهد که پیش از اجرا به ذهنتان رسیده بود.

این ابزار روی سطح باقی می‌ماند تا عامل بتواند بپرسد چرا هیچ‌چیز قابل‌استفاده نیست، به‌جای اینکه حدس بزند. یک آداپتور تنها زمانی واقعاً می‌تواند آغاز به کار کند که هر دو مورد زیر برقرار باشند:

  • یک نشانگر پروژه در چک‌اوت، و
  • پیدا شدن باینری آداپتور.

از عامل بخواهید بررسی کند:

debug(op: "adapters")

با آنچه یافته پاسخ می‌دهد، یا توضیح می‌دهد که یک آداپتور به هر دو نیمه نیاز دارد.

زبان آداپتور نشانگر نصب
Dart / Flutter dart debug_adapter pubspec.yaml همراه SDK عرضه می‌شود — کاری لازم نیست
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt همراه LLVM عرضه می‌شود
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

نخست نقطهٔ توقف را بگذارید، سپس اجرا کنید

بخش با عنوان “نخست نقطهٔ توقف را بگذارید، سپس اجرا کنید”

ترتیب مهم است و ابزار آن را تحمیل می‌کند: نقطه‌های توقف را می‌توان پیش از وجود یک نشست گذاشت، چون پروتکل همین توالی را می‌طلبد.

چیزی شبیه این از عامل بخواهید:

تابع compute در bin/main.dart مقدار اشتباهی برمی‌گرداند. روی خطی که product را مقداردهی می‌کند نقطهٔ توقف بگذار، آن را زیر اشکال‌زدا اجرا کن و بگو a و b در واقع چه هستند.

تقریباً این‌ها را اجرا می‌کند:

debug(op: "breakpoints", file: "bin/main.dart", lines: [7])
debug(op: "launch", program: "bin/main.dart")

اجرا گزارش می‌دهد کجا متوقف شده است:

Launched bin/main.dart on dart (session dart-1). Stopped: breakpoint. Use `stack` to see where.
debug(op: "stack")
→ #1 compute (bin/main.dart:7:3)
#2 main (bin/main.dart:2:18)

سپس داخل آن قدم بگذارید:

debug(op: "scopes", frame_id: 1)
→ Locals (reference 12)
debug(op: "variables", reference: 12)
→ a (int) = 6
b (int) = 7
product (int) = 42

شیئی که فرزند دارد [expand: reference N] را گزارش می‌کند — اما تنها زمانی که واقعاً چیزی پشت آن باشد؛ پس بسط‌دادنی که عامل نتواند از آن استفاده کند، هرگز ارائه نمی‌شود.

برای هر چیزی که به‌صورت متغیر محلی دیده نمی‌شود:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
آنچه می‌خواهید عملیات
اجرا تا توقف بعدی continue
خط بعدی، همان فریم step_over
ورود به فراخوان step_in
خروج به فراخوان‌کننده step_out
متوقف‌کردن برنامه‌ای در حال اجرا pause

اتصال به چیزی که همین حالا در حال اجراست

بخش با عنوان “اتصال به چیزی که همین حالا در حال اجراست”

برای فرایندی گیرکرده، نه فرایندی که خودتان در حال راه‌اندازی آن هستید:

debug(op: "attach", port: 5858)
debug(op: "terminate")

همیشه نشست را پایان دهید. یک آداپتور اشکال‌زدا مالک یک فرایند متوقف‌شده است که هر آنچه آن فرایند نگه داشته را نگه می‌دارد — یک پورت، یک قفل، یک اتصال پایگاه داده.

بسط‌دادن variables روی یک گراف شیء عمیق طبیعتاً کران‌دار نیست — یک ویجت Flutter به کل درخت عناصر می‌رسد. فرزندان هر حوزه حداکثر ۵۰ مورد، مقادیر حداکثر ۴۰۰ نویسه و فریم‌های پشته حداکثر ۳۰ مورد محدود شده‌اند. نتیجهٔ ابزاری که پنجرهٔ بافت را پر کند، ابزاری است که همان نوبتی را که داشت کمکش می‌کرد پایان می‌دهد.