Chrome Dev Summit 2020 में, हमने पहली बार वेब पर WebAssembly ऐप्लिकेशन के लिए, Chrome की डीबगिंग सहायता का डेमो दिखाया था. तब से, टीम ने बड़े और बहुत बड़े ऐप्लिकेशन के लिए, डेवलपर अनुभव को स्केल करने में काफ़ी मेहनत की है. इस पोस्ट में, हम आपको अलग-अलग टूल में जोड़े गए (या काम करने वाले) बटन और उनका इस्तेमाल करने का तरीका दिखाएंगे!
स्केलेबल डीबगिंग
आइए, 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
इस निर्देश से 3 एमबी की wasm बाइनरी बनती है. इसमें ज़्यादातर जानकारी, डीबग करने से जुड़ी होती है. 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
इस आउटपुट में, जनरेट की गई wasm फ़ाइल के सभी सेक्शन दिखते हैं. इनमें से ज़्यादातर वेब असेंबली के स्टैंडर्ड सेक्शन होते हैं. हालांकि, इसमें कई कस्टम सेक्शन भी होते हैं, जिनका नाम .debug_
से शुरू होता है. बाइनरी में डीबग की जानकारी यहीं होती है! सभी साइज़ जोड़ने पर, हमें पता चलता है कि डीबग की जानकारी, हमारी 3 एमबी की फ़ाइल का करीब 2.3 एमबी हिस्सा है. अगर हम emcc
कमांड को भी time
करते हैं, तो हमें पता चलता है कि हमारी मशीन पर इसे चलने में करीब 1.5 सेकंड लगे. ये संख्याएं एक अच्छा बेसलाइन बनाती हैं, लेकिन ये इतनी कम हैं कि शायद कोई भी इन पर ध्यान न दे. हालांकि, असल ऐप्लिकेशन में डीबग बाइनरी का साइज़ आसानी से जीबी में पहुंच सकता है और इसे बनाने में मिनट लग सकते हैं!
Binaryen को स्किप करना
Emscripten की मदद से wasm ऐप्लिकेशन बनाते समय, Binaryen ऑप्टिमाइज़र को चलाना, बिल्ड करने का आखिरी चरण होता है. Binaryen एक कंपाइलर टूलकिट है, जो WebAssembly जैसी बाइनरी को ऑप्टिमाइज़ करता है और उन्हें कानूनी बनाता है. Binaryen को बिल्ड के हिस्से के तौर पर चलाना काफ़ी महंगा है. हालांकि, इसे सिर्फ़ कुछ खास स्थितियों में चलाना ज़रूरी है. डीबग बिल्ड के लिए, Binaryen पास की ज़रूरत को पूरा किए बिना, बिल्ड में लगने वाले समय को काफ़ी कम किया जा सकता है. Binaryen का सबसे ज़्यादा इस्तेमाल किया जाने वाला पास, 64 बिट की पूर्णांक वैल्यू वाले फ़ंक्शन सिग्नेचर को कानूनी बनाने के लिए होता है. -sWASM_BIGINT
का इस्तेमाल करके, WebAssembly 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 फ़ंक्शन की सभी संभावित नेटिव डिपेंडेंसी शामिल करने के लिए कहता है. इसमें कोड का साइज़ थोड़ा ज़्यादा होता है, लेकिन इससे लिंक करने में लगने वाला समय कम हो सकता है. साथ ही, यह डीबग बिल्ड के लिए भी फ़ायदेमंद हो सकता है.
हमारे उदाहरण जैसे छोटे प्रोजेक्ट के लिए, इससे कोई फ़र्क़ नहीं पड़ता. हालांकि, अगर आपके प्रोजेक्ट में सैकड़ों या हज़ारों ऑब्जेक्ट फ़ाइलें हैं, तो इससे लिंक करने में लगने वाले समय में काफ़ी सुधार हो सकता है.
“name” सेक्शन को हटाना
बड़े प्रोजेक्ट में, खास तौर पर उन प्रोजेक्ट में जिनमें C++ टेंप्लेट का ज़्यादा इस्तेमाल किया गया है, WebAssembly का “name” सेक्शन बहुत बड़ा हो सकता है. हमारे उदाहरण में, यह फ़ाइल के कुल साइज़ का सिर्फ़ एक छोटा हिस्सा है (ऊपर llvm-objdump
का आउटपुट देखें). हालांकि, कुछ मामलों में यह काफ़ी अहम हो सकता है. अगर आपके ऐप्लिकेशन का “name” सेक्शन बहुत बड़ा है और डीबग करने के लिए, dwarf डीबग की जानकारी काफ़ी है, तो “name” सेक्शन को हटाने से फ़ायदे मिल सकते हैं:
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
इससे, DWARF डीबग सेक्शन को बरकरार रखते हुए, WebAssembly के “name” सेक्शन को हटा दिया जाएगा.
डीबग फ़िज़न
ज़्यादा डीबग डेटा वाली बाइनरी, न सिर्फ़ बिल्ड के समय पर असर डालती हैं, बल्कि डीबग करने में भी ज़्यादा समय लेती हैं. डीबगर को डेटा लोड करना होता है और उसके लिए इंडेक्स बनाना होता है, ताकि वह "लोकल वैरिएबल x का टाइप क्या है?" जैसी क्वेरी का तुरंत जवाब दे सके.
डीबग फ़िज़न की मदद से, किसी बाइनरी के लिए डीबग की जानकारी को दो हिस्सों में बांटा जा सकता है: पहला हिस्सा बाइनरी में रहता है और दूसरा हिस्सा, DWARF ऑब्जेक्ट (.dwo
) फ़ाइल में होता है. Emscripten में -gsplit-dwarf
फ़्लैग पास करके, इसे चालू किया जा सकता है:
$ 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
एक और फ़्लैग जोड़ा है. DWARF सिंबल के वर्शन 5 को चालू करना, डीबगिंग को तेज़ी से शुरू करने का एक और तरीका है. फ़िलहाल, यह वर्शन डिफ़ॉल्ट तौर पर उपलब्ध नहीं है. इसकी मदद से, मुख्य बाइनरी में कुछ ऐसी जानकारी सेव की जाती है जो डिफ़ॉल्ट वर्शन 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
डीबगिंग सेशन के दौरान, किसी इकाई को नाम से खोजने पर, अक्सर सिंबल लुकअप होता है. उदाहरण के लिए, वैरिएबल या टाइप खोजते समय. नेम इंडेक्स, सीधे उस कंपाइलेशन यूनिट पर ले जाकर इस खोज को तेज़ करता है जो उस नाम को तय करता है. नाम सूची के बिना, पूरे डीबग डेटा को खोजना होगा. इससे, हमें वह कंपाइलेशन यूनिट मिल पाएगी जो नाम वाली उस इकाई के बारे में बताती है जिसे हम ढूंढ रहे हैं.
दिलचस्पी रखने वालों के लिए: डीबग डेटा देखना
DWARF डेटा को देखने के लिए, llvm-dwarfdump
का इस्तेमाल किया जा सकता है. आइए, इसे आज़माते हैं:
llvm-dwarfdump mandelbrot.wasm
इससे हमें उन “कंपाइल यूनिट” (सोर्स फ़ाइलों) के बारे में खास जानकारी मिलती है जिनके लिए हमारे पास डीबग करने की जानकारी होती है. इस उदाहरण में, हमारे पास सिर्फ़ mandelbrot.cc
के लिए डीबग की जानकारी है. सामान्य जानकारी से हमें पता चलेगा कि हमारे पास स्केलेटन यूनिट है. इसका मतलब है कि हमारे पास इस फ़ाइल का अधूरा डेटा है. साथ ही, एक अलग .dwo
फ़ाइल है जिसमें बचे हुए डीबग की जानकारी मौजूद है:
इस फ़ाइल में मौजूद अन्य टेबल भी देखी जा सकती हैं.जैसे, लाइन टेबल, जो wasm बाइटकोड को C++ लाइनों से मैप करती है. इसके लिए, llvm-dwarfdump -debug-line
का इस्तेमाल करें.
हम अलग .dwo
फ़ाइल में मौजूद डीबग की जानकारी भी देख सकते हैं:
llvm-dwarfdump mandelbrot.dwo
कम शब्दों में: डीबग फ़िज़न का इस्तेमाल करने का क्या फ़ायदा है?
अगर बड़े ऐप्लिकेशन के साथ काम किया जा रहा है, तो डीबग की जानकारी को अलग-अलग हिस्सों में बांटने के कई फ़ायदे हैं:
ज़्यादा तेज़ी से लिंक करना: अब लिंकर को पूरी डीबग जानकारी को पार्स करने की ज़रूरत नहीं है. आम तौर पर, लिंकर को बाइनरी में मौजूद पूरे DWARF डेटा को पार्स करना पड़ता है. डीबग की जानकारी के बड़े हिस्सों को अलग-अलग फ़ाइलों में बांटकर, लिंकर छोटी बाइनरी के साथ काम करते हैं. इससे लिंक करने में कम समय लगता है. यह बात खास तौर पर बड़े ऐप्लिकेशन के लिए सही है.
ज़्यादा तेज़ी से डीबग करना: डीबगर, कुछ सिंबल लुकअप के लिए
.dwo
/.dwp
फ़ाइलों में मौजूद अतिरिक्त सिंबल को पार्स करने की प्रोसेस को छोड़ सकता है. कुछ लुकअप के लिए, हमें अतिरिक्त डीबग डेटा देखने की ज़रूरत नहीं होती. जैसे, wasm से C++ फ़ाइलों की लाइन मैपिंग के अनुरोध. इससे हमें समय की बचत होती है, क्योंकि हमें अतिरिक्त डीबग डेटा को लोड और पार्स करने की ज़रूरत नहीं होती.
पहला: अगर आपके सिस्टम पर llvm-objdump
का नया वर्शन नहीं है और emsdk
का इस्तेमाल किया जा रहा है, तो आपको यह emsdk/upstream/bin
डायरेक्ट्री में मिल सकता है.
झलक वाले चैनल डाउनलोड करना
Chrome कैनरी, डेवलपर या बीटा को अपने डिफ़ॉल्ट डेवलपमेंट ब्राउज़र के तौर पर इस्तेमाल करें. इन झलक वाले चैनलों की मदद से, आपको DevTools की नई सुविधाओं का ऐक्सेस मिलता है. साथ ही, इनसे आपको वेब प्लैटफ़ॉर्म के सबसे नए एपीआई की जांच करने में मदद मिलती है. इसके अलावा, इनकी मदद से उपयोगकर्ताओं से पहले ही अपनी साइट पर समस्याओं का पता लगाया जा सकता है!
Chrome DevTools की टीम से संपर्क करना
DevTools से जुड़ी नई सुविधाओं, अपडेट या किसी भी अन्य चीज़ के बारे में चर्चा करने के लिए, यहां दिए गए विकल्पों का इस्तेमाल करें.
- crbug.com पर जाकर, हमें सुझाव/राय दें या शिकायत करें. साथ ही, किसी सुविधा का अनुरोध करें.
- DevTools में ज़्यादा विकल्प > सहायता > DevTools से जुड़ी समस्या की शिकायत करें का इस्तेमाल करके, DevTools से जुड़ी समस्या की शिकायत करें.
- @ChromeDevTools पर ट्वीट करें.
- DevTools के YouTube वीडियो में नया क्या है या DevTools के बारे में सलाह देने वाले YouTube वीडियो पर टिप्पणियां करें.