Breakpoints, actions, ו־watches#

פרק זה מכסה עצירה של מנפה השגיאות בנקודה ספציפית - שורה, תת־שגרה, תנאי, שינוי ערך - ואת מנגנון ה־prompt-hook המאפשר להריץ קוד אוטומטית בכל עצירה.

קוראים המגיעים לכאן יודעים כיצד להפעיל perl -d ולצעוד שורה אחת בכל פעם. המיומנות הבאה היא לדלג ישר למקום המעניין ולעצור רק כאשר הבעיה עומדת להופיע.

Breakpoints על שורות ו־subs#

פקודה

השפעה

b

breakpoint על השורה הנוכחית.

b LINE

breakpoint ב־LINE בקובץ הנוכחי.

b LINE COND

מותנה; עצירה כאשר COND אמת.

b FILE:LINE

חוצה־קבצים; FILE מתאים לרשומה ב־%INC.

b FILE:LINE COND

חוצה־קבצים מותנה.

b SUB

עצירה בשורה הראשונה של SUB (SUB חייב להיות מהודר).

b SUB COND

breakpoint sub מותנה.

b $coderef

עצירה ב־sub שאליו מפנה $coderef (COND אינו נתמך).

b compile SUB

עצירה לאחר ש־SUB סיים להתהדר, לפני שגופו רץ.

b postpone SUB

עצירה בפעם הראשונה ש־SUB נקרא, גם אם עדיין לא נטען.

b load FILE

עצירה כאשר FILE נטען עם require / do.

B LINE

מחיקת breakpoint ב־LINE.

B *

מחיקת כל ה־breakpoints.

disable [FILE:]LINE

השבתה ללא הסרה.

enable  [FILE:]LINE

הפעלה מחדש.

L

פירוט breakpoints, actions, ו־watches.

L b

פירוט breakpoints בלבד.

תנאים הם ביטויים גולמיים, לא if (…):

  DB<1> b 237 $count > 10
  DB<2> b 42 /unauthorized/i
  DB<3> b validate_payload ref($obj) eq 'HASH' && keys %$obj

Breakpoints נקבעים רק על שורות המכילות קוד הניתן לביצוע. שורה ריקה, הערה טהורה, או המשך של הוראה קודמת מסרבים:

  DB<1> b 3
Line 3 not breakable.

שורה נושאת breakpoint אחד ו־action אחד לכל היותר; הוספת שני דורסת את הראשון.

מטריצת תזמון#

היכן במחזור החיים של התוכנית breakpoint מופעל חשוב - במיוחד עבור קוד בבלוקי BEGIN או מודולים שנטענים בעצלות באמצעות require.

מטרה

פקודה

עצירה בהוראת זמן הריצה הראשונה

ברירת מחדל - מנפה השגיאות כבר עוצר שם.

עצירה בתוך בלוק BEGIN { }

הוסיפו $DB::single = 1; בתוך הבלוק.

עצירה לפני שגוף ה־sub רץ (אחרי שהוא מתהדר)

b compile SUB

עצירה בקריאה הראשונה ל־sub שעדיין לא נטען

b postpone SUB

עצירה כאשר קובץ נטען עם require או do

b load /full/path/from/%INC

עצירה בשורה ספציפית

b LINE

עצירה בשורה רק כאשר תנאי מתקיים

b LINE COND

$DB::single = 1; בתוך בלוק BEGIN הוא הטריק לניפוי שגיאות בזמן הידור: כתיבת b ב־prompt עובדת רק לאחר שמנפה השגיאות פועל, אך קוד BEGIN רץ לפני ה־prompt הראשון. שילבו את ההשמה לתוך הבלוק, הפעילו תחת -d, ומנפה השגיאות עוצר בשורה הניתנת לעצירה הבאה בתוך הבלוק.

גישה אל $DB::single כאשר אינכם תחת -d מבצעת autovivify של DB:: ללא נזק - התבנית בטוחה לקבע ב־commit.

פירוט וניהול breakpoints#

  DB<1> L
app.pl:
 42:   $user = User->load($id);
       break if (1)
 91:   dispatch($req);
       break if ($req->method eq 'POST')

  DB<2> disable 42
  DB<3> enable 42
  DB<4> B 42
  DB<5> B *

disable / enable מחליפים את מצב ה־breakpoint מבלי לשכוח את התנאי שלו - שימושי כאשר רוצים לדלג זמנית על breakpoint חם ולחמש אותו שוב מאוחר יותר.

Actions: הרצת קוד אוטומטית בשורה#

action מריץ קוד Perl לפני שהשורה המצוינת מבוצעת, ללא עצירה. השתמשו בו לתצפית פסיבית:

  DB<1> a 42 print "entering: user_id=$user_id\n"
  DB<2> a 91 warn "POST body:\n" . Dumper($body) if $req->method eq 'POST'
  DB<3> L a
app.pl:
 42: ...
       action: print "entering: user_id=$user_id\n"
  DB<4> A 42              # delete action at line 42
  DB<5> A *               # delete all actions

סדר בשורה הניתנת לעצירה:

  1. תנאי ה־breakpoint נבדק.

  2. פלט trace מודפס (אם tracing מופעל).

  3. action רץ.

  4. prompt מופיע (אם breakpoint נפגע או בצעידה).

  5. השורה עצמה מתבצעת.

actions ו־breakpoints חיים יחד על אותה שורה - אחד מכל סוג לכל היותר.

Watches: עצירה בשינוי ערך#

watch מדווח על הערך הישן והחדש כאשר ביטוי במעקב משתנה, ומעביר אתכם ל־prompt:

  DB<1> w $global_count
  DB<2> c
Watchpoint 0:   $global_count changed:
    old value:  42
    new value:  43
main::tick(app.pl:118):    ++$global_count;
  DB<3> W $global_count   # delete this watch
  DB<4> W *               # delete all watches

watches מוערכים בכל שורה הניתנת לעצירה. ביטויים חייבים להפנות לגלובלים או ל־aliases נגישים; משתני my לקסיקליים אינם ניתנים ל־watch ישירות. כאשר משתנה לקסיקלי חשוב, השתמשו ב־Tie::Watch בקוד כדי להתקין watcher בזמן ה־tie, או בצעו השמה דרך alias בתחום החבילה.

watches מוסיפים עלות לכל הוראה פרופורציונלית למספר ה־watches ועלות כל ביטוי. שמרו אותם ממוקדים והסירו אותם בסיום.

Prompt hooks: הרצת קוד בכל עצירה#

< / > מתקינים קוד Perl לריצה אוטומטית לפני / אחרי כל prompt. { / {{ עושים את אותו דבר עבור פקודות מנפה השגיאות:

פקודה

השפעה

< CMD

קוד Perl לפני כל prompt (= אחרי השורה שזה עתה בוצעה).

<< CMD

הוספה לרשימת ה־pre-prompt.

< ?

פירוט קוד pre-prompt.

< *

מחיקת כל קוד ה־pre-prompt.

> CMD / >> CMD

קוד Perl אחרי כל prompt (= לפני השורה הבאה).

{ DBCMD

פקודת מנפה שגיאות (לא Perl) pre-prompt.

{{ DBCMD

הוספה לרשימת פקודות־מנפה־השגיאות של pre-prompt.

שימוש טיפוסי: חותמת זמן בכל צעד:

  DB<1> < print "stop: ", scalar localtime, "\n"
  DB<2> > print "start: ", scalar localtime, "\n"

או הצגה תמידית של השורה הנוכחית ומשתנה במעקב:

  DB<1> { v
  DB<2> { p $important

רב־שורתי: בצעו escape לשורה החדשה עם \:

  DB<1> < for my $f (@important_files) {    \
cont:     print "  $f: ", (-s $f) // 'n/a', "\n"; \
cont: }

Aliases#

קיצורי פקודה מתמשכים באמצעות =:

  DB<1> = len s/^len(.*)/p length($1)/
  DB<2> = quit s/^quit(\s*)/exit/
  DB<3> =                      # list all aliases

aliases חיים ב־%DB::alias ומיושמים בזמן ה־dispatch. הגדירו פעם אחת ב־.perldb להתמדה חוצת־סשנים.

breakpoints חד־פעמיים#

c LINE קובע breakpoint ב־LINE, ממשיך, ומנקה את ה־breakpoint כאשר הוא נפגע:

  DB<1> c 42

הדרך הפשוטה ביותר לומר ״פשוט רוץ עד שורה 42 ועצור שם״. ללא זיווג ידני של b / B.

עצירה ב־import / קריאה ראשונה בעצלות#

כאשר sub חי במודול שייתכן שטרם נטען, b SUB נכשל:

  DB<1> b MyApp::Worker::dispatch
Subroutine not yet compiled.

b postpone ממתין ש־sub יסיים להתהדר:

  DB<1> b postpone MyApp::Worker::dispatch
  DB<2> c

עבור קבצים הנטענים על־ידי require / do, b load עוצר בטעינה הראשונה:

  DB<1> b load /usr/share/perl5/MyApp/Worker.pm

שם הקובץ חייב להתאים לנתיב המלא ב־%INC, לא רק לשם המודול.

למידע נוסף#

  • inspecting-state - במה לבחון לאחר ש־breakpoint מופעל.

  • tracing - כאשר רוצים לצפות במה שקורה מבלי לעצור.