בכנס מפתחי Chrome 2020, הדגמה של התמיכה בניפוי באגים של Chrome באפליקציות WebAssembly באינטרנט בפעם הראשונה. מאז, הצוות השקיע הרבה אנרגיה כדי לשפר את חוויית המפתחים עבור אפליקציות גדולות ואפילו ענקיות. בפוסט הזה נראה לכם את הידיעות שהוספנו (או שעבדנו עליהן) בכלים השונים ואיך להשתמש בהן!
ניפוי באגים גמיש
נמשיך מהמקום שבו הפסקנו בפוסט שפרסמנו ב-2020. זו הדוגמה שבדקנו אז:
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
זו עדיין דוגמה קטנה יחסית, וכנראה שלא תראו אף אחת מהבעיות האמיתיות שיופיעו באפליקציה גדולה מאוד, אבל עדיין נוכל להראות לכם מהן התכונות החדשות. ההגדרה והניסיון של התכונה הזו קלים ומהירים.
בפוסט הקודם הוסבר איך לקמפל ולפתור באגים בדוגמה הזו. ננסה שוב, אבל נעיף גם מבט על //performance//:
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
הפקודה הזו יוצרת קובץ בינארי של 3MB Wam. החלק העיקרי של המידע הזה, כפי שניתן לצפות, הוא מידע על תוצאות ניפוי הבאגים. אפשר לאמת זאת באמצעות הכלי llvm-objdump
[1], לדוגמה:
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
בפלט הזה מוצגים כל הקטעים בקובץ Wam שנוצר. רובם הם קטעי WebAssembly רגילים, אבל יש גם כמה מקטעים בהתאמה אישית ששמם מתחיל ב-.debug_
. זה המקום שבו הקובץ הבינארי מכיל את המידע על תוצאות ניפוי הבאגים! אם נוסיף את כל הגדלים, נראה שפרטי ניפוי הבאגים מהווים כ-2.3MB מתוך הקובץ בנפח 3MB. אם מריצים גם את הפקודה time
emcc
, רואים שבמכונה שלנו חלפו כ-1.5 שניות עד שהיא הושלמה. המספרים האלה יוצרים בסיס מצוין, אבל הם קטנים כל כך שכנראה אף אחד לא יתרגש מהם. עם זאת, באפליקציות אמיתיות, קובץ הבינארי של ניפוי הבאגים יכול להגיע בקלות לגודל של כמה ג'יגה-בייטים, ויצירתו עשויה להימשך כמה דקות.
דילוג על Binaryen
כשמפתחים אפליקציה ב-Wasm באמצעות Emscripten, אחד משלבי ה-build הסופיים הוא הפעלת כלי האופטימיזציה של Binaryen. Binaryen הוא ערכת כלים של מהדר שמבצעת אופטימיזציה של קובצי בינארי מסוג WebAssembly (או דומים) ומכשירה אותם לשימוש. הפעלת Binaryen כחלק מה-build היא יקרה למדי, אבל היא נדרשת רק בתנאים מסוימים. לגבי גרסאות build לצורך ניפוי באגים, אפשר לזרז את זמן ה-build באופן משמעותי אם נימנע מהצורך בפעולות של Binaryen. הבדיקה הנפוצה ביותר שצריך לבצע ב-Binaryen היא כדי להכשיר חתימות של פונקציות שכוללות ערכים שלמים של 64 ביט. כדי למנוע זאת, אפשר להביע הסכמה לשילוב של WebAssembly BigInt באמצעות -sWASM_BIGINT
.
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
הוספנו את הדגל -sERROR_ON_WASM_CHANGES_AFTER_LINK
למקרה הצורך. הוא עוזר לזהות מתי Binaryen פועל ומשכתב את הקובץ הבינארי באופן בלתי צפוי. כך נוכל לוודא שנמשיך בדרך המהירה.
למרות שהדוגמה שלנו קטנה למדי, עדיין אפשר לראות את ההשפעה של דילוג על Binaryen. לפי time
, הפקודה הזו פועלת תוך פחות משנייה, כלומר חצי שנייה מהר יותר מבעבר!
שיפורים מתקדמים
דילוג על סריקת קובץ הקלט
בדרך כלל, כשמקשרים פרויקט Emscripten, emcc
יסרוק את כל הקבצים והספריות של אובייקטי הקלט. הפעולה הזו מאפשרת להטמיע יחסי תלות מדויקים בין פונקציות של ספריית JavaScript לבין סמלים מקוריים בתוכנית שלכם. בפרויקטים גדולים יותר, הסריקה הנוספת של קובצי הקלט (באמצעות llvm-nm
) יכולה להאריך משמעותית את זמן הקישור.
אפשר להריץ את הקוד עם -sREVERSE_DEPS=all
במקום זאת, כדי להורות ל-emcc
לכלול את כל יחסי התלות האפשריים של פונקציות JavaScript. השיטה הזו גורמת לעומס קטן על קוד, אבל היא יכולה לזרז את זמני הקישורים, והיא יכולה להיות שימושית לגרסאות build של ניפוי באגים.
בפרויקט קטן כמו הדוגמה שלנו, אין הבדל ממשי, אבל אם יש לכם מאות או אפילו אלפי קובצי אובייקטים בפרויקט, הדבר יכול לשפר משמעותית את זמני הקישורים.
הסרת הקטע 'שם'
בפרויקטים גדולים, במיוחד בפרויקטים שבהם נעשה שימוש רב בתבניות C++, הקטע 'name' ב-WebAssembly יכול להיות גדול מאוד. בדוגמה שלנו, הוא רק חלק זעיר מגודל הקובץ הכולל (ראו את הפלט של llvm-objdump
למעלה), אבל במקרים מסוימים הוא יכול להיות משמעותי מאוד. אם הקטע 'name' של האפליקציה גדול מאוד, ומידע ניפוי הבאגים של dwarf מספיק לצורכי ניפוי הבאגים, כדאי להסיר את הקטע 'name':
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
הפעולה הזו תסיר את הקטע 'name' ב-WebAssembly ותשמור על קטעי ניפוי הבאגים של DWARF.
ניפוי באגים ב-Fission
קבצים בינאריים עם הרבה נתונים של ניפוי באגים לא רק מפעילים לחץ על זמן ה-build, אלא גם על זמן ניפוי הבאגים. מנתח הבאגים צריך לטעון את הנתונים וליצור עבורם אינדקס, כדי שיוכל להגיב במהירות לשאילתות כמו "מהו הסוג של המשתנה המקומי x?".
פיצול ניפוי באגים מאפשר לנו לפצל את פרטי ניפוי הבאגים של קובץ בינארי לשני חלקים: אחד שנשאר בקובץ הבינארי, והשני שמכיל בקובץ נפרד שנקרא אובייקט DWARF (.dwo
). ניתן להפעיל אותה על ידי העברת הדגל -gsplit-dwarf
ל-Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
בהמשך מפורטות הפקודות השונות והקבצים שנוצרים על ידי הידור ללא נתוני ניפוי באגים, עם נתוני ניפוי באגים ולבסוף עם נתוני ניפוי באגים ועם פיסוק של ניפוי באגים.
כשמפצלים את נתוני DWARF, חלק מנתוני ניפוי הבאגים נמצאים יחד עם הנתונים הבינאריים, ואילו חלקם מועבר לקובץ mandelbrot.dwo
(כפי שמתואר למעלה).
ב-mandelbrot
יש לנו רק קובץ מקור אחד, אבל בדרך כלל פרויקטים גדולים יותר וכוללים יותר מקובץ אחד. פיצול ניפוי הבאגים יוצר קובץ .dwo
לכל אחד מהם. כדי שגרסת הבטא הנוכחית של הכלי לניפוי באגים (0.1.6.1615) תוכל לטעון את המידע המפוצל על תוצאות ניפוי הבאגים, עלינו לאגד את כל הנתונים לחבילת DWARF שנקראת (.dwp
) באופן הבא:
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
היתרון של יצירת חבילת DWARF מהאובייקטים הנפרדים הוא שצריך להציג רק קובץ אחד נוסף. אנחנו עובדים כרגע על האפשרות לטעון גם את כל האובייקטים הנפרדים בגרסה עתידית.
מה קורה עם DWARF 5?
אולי שמת לב, הצבנו דגל נוסף לפקודה emcc
שלמעלה, -gdwarf-5
. הפעלת גרסה 5 של הסמלים של DWARF, שכרגע היא לא ברירת המחדל, היא טריק נוסף שיעזור לנו להתחיל לנפות באגים מהר יותר. בעזרתו, מידע מסוים מאוחסן בקובץ הבינארי הראשי שלא נכלל בגרסה 4 שמוגדרת כברירת מחדל. באופן ספציפי, אנחנו יכולים לקבוע את הקבוצה המלאה של קובצי המקור רק מהקובץ הבינארי הראשי. כך אפשר לבצע פעולות בסיסיות כמו הצגת עץ המקור המלא והגדרת נקודות עצירה ללא טעינה וניתוח של נתוני הסמל המלאים. כך ניקוי הבאגים באמצעות סמלים מפוצלים מהיר הרבה יותר, ולכן אנחנו תמיד משתמשים בדגלים -gsplit-dwarf
ו--gdwarf-5
בשורת הפקודה ביחד.
פורמט ניפוי הבאגים DWARF5 מאפשר גם גישה לתכונה שימושית נוספת. הוא מוסיף אינדקס שמות לנתוני ניפוי הבאגים שייווצרו כשמעבירים את הדגל -gpubnames
:
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
במהלך סשן ניפוי באגים, חיפושים של סמלים מתבצעים לעיתים קרובות על ידי חיפוש ישות לפי שם, למשל כשמחפשים משתנה או טיפוס. אינדקס השמות מאיץ את החיפוש על ידי הפניה ישירה ליחידת ההידור שמגדירה את השם הזה. בלי אינדקס שמות, יידרש חיפוש מקיף של כל נתוני ניפוי הבאגים כדי למצוא את יחידת ההידור הנכונה שמגדירה את הישות בעלת השם שאנחנו מחפשים.
לסקרנים: בחינת נתוני ניפוי הבאגים
אפשר להשתמש ב-llvm-dwarfdump
כדי להציץ בנתוני DWARF. ננסה את זה:
llvm-dwarfdump mandelbrot.wasm
כך אנחנו מקבלים סקירה כללית על 'יחידות ה-Compile' (כלומר, קובצי המקור) שיש לנו מידע על תוצאות ניפוי הבאגים שלהם. בדוגמה הזו, יש לנו רק את פרטי ניפוי הבאגים של mandelbrot.cc
. המידע הכללי יעזור לנו לדעת שיש לנו יחידה שלד, כלומר שיש לנו נתונים חלקיים בקובץ הזה, ויש קובץ .dwo
נפרד שמכיל את שאר פרטי ניפוי הבאגים:
אפשר גם לעיין בטבלאות אחרות בקובץ הזה, למשל בטבלת השורות שבה מוצגת המיפוי של קוד באקיט של wasm לשורות ב-C++ (אפשר לנסות להשתמש ב-llvm-dwarfdump -debug-line
).
אפשר גם לעיין במידע על תוצאות ניפוי הבאגים שכלול בקובץ .dwo
הנפרד:
llvm-dwarfdump mandelbrot.dwo
אמ;לק: מה היתרונות של שימוש בבקע לניפוי באגים?
כשעובדים עם אפליקציות גדולות, יש כמה יתרונות לפיצול של נתוני ניפוי הבאגים:
קישור מהיר יותר: ה-linker כבר לא צריך לנתח את כל פרטי ניפוי הבאגים. בדרך כלל, מקשרים צריכים לנתח את כל נתוני ה-DWARF שנמצאים בקובץ הבינארי. כשמסירים חלקים גדולים של נתוני ניפוי הבאגים לקובצי נפרד, הקישור מתבצע בקובצי בינארי קטנים יותר, וכתוצאה מכך זמני הקישור קצרים יותר (במיוחד באפליקציות גדולות).
ניפוי באגים מהיר יותר: מנתח הבאגים יכול לדלג על ניתוח הסמלים הנוספים בקבצים
.dwo
/.dwp
עבור חלק מחיפושי הסמלים. בחיפושים מסוימים (כמו בקשות לגבי מיפוי שורות של קבצים מסוג wasm ל-C++), אנחנו לא צריכים לבדוק את נתוני ניפוי הבאגים הנוספים. כך אנחנו יכולים לחסוך זמן, בלי שתצטרכו לטעון ולנתח את נתוני ניפוי הבאגים הנוספים.
1: אם אין לכם בגרסה עדכנית של llvm-objdump
במערכת ואתם משתמשים ב-emsdk
, תוכלו למצוא אותו בספרייה emsdk/upstream/bin
.
הורדת הערוצים לתצוגה מקדימה
מומלץ להשתמש ב-Chrome Canary, ב-Dev או ב-Beta כדפדפן הפיתוח שמוגדר כברירת מחדל. הערוצים לתצוגה מקדימה אלה מעניקים לך גישה לתכונות החדשות של כלי הפיתוח, מאפשרים לך לבדוק ממשקי API מתקדמים של פלטפורמות אינטרנט ולעזור לך לגלות בעיות באתר שלך לפני שהמשתמשים עושים זאת.
יצירת קשר עם צוות כלי הפיתוח ל-Chrome
אפשר לבחור מבין האפשרויות הבאות כדי לדון בתכונות החדשות, בעדכונים או בכל נושא אחר שקשור לכלי פיתוח.
- אתם יכולים לשלוח לנו משוב ובקשות להוספת תכונות בכתובת crbug.com.
- מדווחים על בעיה בכלי הפיתוח באמצעות הסמל אפשרויות נוספות > עזרה > דיווח על בעיה בכלי הפיתוח ב-DevTools.
- שולחים ציוץ אל @ChromeDevTools.
- נשמח לשמוע מה חדש: מה חדש בסרטונים של כלי הפיתוח ב-YouTube או טיפים בנושא כלי פיתוח ב-YouTube.