Breakpoints, actions, ו־watches#
פרק זה מכסה עצירה של מנפה השגיאות בנקודה ספציפית - שורה, תת־שגרה, תנאי, שינוי ערך - ואת מנגנון ה־prompt-hook המאפשר להריץ קוד אוטומטית בכל עצירה.
קוראים המגיעים לכאן יודעים כיצד להפעיל perl -d ולצעוד שורה אחת בכל פעם. המיומנות הבאה היא לדלג ישר למקום המעניין ולעצור רק כאשר הבעיה עומדת להופיע.
Breakpoints על שורות ו־subs#
פקודה | השפעה |
|---|---|
| breakpoint על השורה הנוכחית. |
| breakpoint ב־LINE בקובץ הנוכחי. |
| מותנה; עצירה כאשר COND אמת. |
| חוצה־קבצים; FILE מתאים לרשומה ב־ |
| חוצה־קבצים מותנה. |
| עצירה בשורה הראשונה של SUB (SUB חייב להיות מהודר). |
| breakpoint sub מותנה. |
| עצירה ב־sub שאליו מפנה |
| עצירה לאחר ש־SUB סיים להתהדר, לפני שגופו רץ. |
| עצירה בפעם הראשונה ש־SUB נקרא, גם אם עדיין לא נטען. |
| עצירה כאשר FILE נטען עם |
| מחיקת breakpoint ב־LINE. |
| מחיקת כל ה־breakpoints. |
| השבתה ללא הסרה. |
| הפעלה מחדש. |
| פירוט breakpoints, actions, ו־watches. |
| פירוט 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.
מטרה | פקודה |
|---|---|
עצירה בהוראת זמן הריצה הראשונה | ברירת מחדל - מנפה השגיאות כבר עוצר שם. |
עצירה בתוך בלוק | הוסיפו |
עצירה לפני שגוף ה־sub רץ (אחרי שהוא מתהדר) |
|
עצירה בקריאה הראשונה ל־sub שעדיין לא נטען |
|
עצירה כאשר קובץ נטען עם |
|
עצירה בשורה ספציפית |
|
עצירה בשורה רק כאשר תנאי מתקיים |
|
$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
סדר בשורה הניתנת לעצירה:
תנאי ה־breakpoint נבדק.
פלט trace מודפס (אם tracing מופעל).
action רץ.
prompt מופיע (אם breakpoint נפגע או בצעידה).
השורה עצמה מתבצעת.
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. { / {{ עושים את אותו דבר עבור פקודות מנפה השגיאות:
פקודה | השפעה |
|---|---|
| קוד Perl לפני כל prompt (= אחרי השורה שזה עתה בוצעה). |
| הוספה לרשימת ה־pre-prompt. |
| פירוט קוד pre-prompt. |
| מחיקת כל קוד ה־pre-prompt. |
| קוד Perl אחרי כל prompt (= לפני השורה הבאה). |
| פקודת מנפה שגיאות (לא Perl) pre-prompt. |
| הוספה לרשימת פקודות־מנפה־השגיאות של 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 - כאשר רוצים לצפות במה שקורה מבלי לעצור.