अब तक की राह
एक साल पहले, Chrome ने Chrome DevTools में नेटिव WebAssembly की डीबगिंग के लिए, शुरुआती सहायता का एलान किया था.
हमने बुनियादी चरणों की सहायता के बारे में बताया. साथ ही, आने वाले समय में सोर्स मैप के बजाय DWARF की जानकारी का इस्तेमाल करने के अवसरों के बारे में भी बताया:
- वैरिएबल के नामों को हल करना
- प्रिटी-प्रिंट के टाइप
- सोर्स भाषाओं में एक्सप्रेशन का आकलन करना
- …और ऐसी ही अन्य सुविधाएं!
आज, हमें यह बताते हुए खुशी हो रही है कि हमने जो सुविधाएं देने का वादा किया था वे अब उपलब्ध हैं. साथ ही, हम Emscripten और Chrome DevTools की टीमों की इस साल की उपलब्धियों के बारे में भी बताना चाहते हैं. खास तौर पर, C और C++ ऐप्लिकेशन के लिए की गई उपलब्धियों के बारे में.
शुरू करने से पहले, कृपया ध्यान रखें कि यह नए वर्शन का बीटा वर्शन है. आपको सभी टूल के नए वर्शन का इस्तेमाल, अपनी जोखिम पर करना होगा. अगर आपको कोई समस्या आती है, तो कृपया https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 पर जाकर शिकायत करें.
आइए, पिछले लेख में दिए गए C के उसी आसान उदाहरण से शुरू करते हैं:
#include <stdlib.h>
void assert_less(int x, int y) {
if (x >= y) {
abort();
}
}
int main() {
assert_less(10, 20);
assert_less(30, 20);
}
इसे कंपाइल करने के लिए, हम नए Emscripten का इस्तेमाल करते हैं. साथ ही, डिबग करने से जुड़ी जानकारी शाम��ल करने के लिए, ओरिजनल पोस्ट की तरह ही -g
फ़्लैग पास करते हैं:
emcc -g temp.c -o temp.html
अब जनरेट किए गए पेज को localhost एचटीटीपी सर्वर से दिखाया जा सकता है. उदाहरण के लिए, serve की मदद से. साथ ही, इसे Chrome Canary के नए वर्शन में खोला जा सकता है.
इस बार, हमें एक हेल्पर एक्सटेंशन की भी ज़रूरत होगी, जो Chrome DevTools के साथ इंटिग्रेट हो. साथ ही, WebAssembly फ़ाइल में एन्कोड की गई, डीबग करने से जुड़ी सारी जानकारी को समझने में मदद करता हो. कृपया इस लिंक पर जाकर इसे इंस्टॉल करें: goo.gle/wasm-debugging-extension
आपको DevTools के एक्सपेरिमेंट में, WebAssembly को डीबग करने की सुविधा भी चालू करनी होगी. Chrome DevTools खोलें. इसके बाद, DevTools पैनल के सबसे ऊपर दाएं कोने में मौजूद गियर (⚙) आइकॉन पर क्लिक करें. इसके बाद, प्रयोग पैनल पर जाएं और WebAssembly डीबगिंग: DWARF सपोर्ट चालू करें पर सही का निशान लगाएं.
सेटिंग बंद करने पर, DevTools खुद को फिर से लोड करने का सुझाव देगा, ताकि सेटिंग लागू की जा सकें. चलिए, अब ऐसा ही करें. एक बार के लिए सेटअप करने के लिए बस इतना ही.
अब हम सोर्स पैनल पर वापस जा सकते हैं और अपवाद मिलने पर रोकें (⏸ आइकॉन) को चालू कर सकते हैं. इसके बाद, पकड़े गए अपवादों पर रोकें को चुनें और पेज को फिर से लोड करें. आपको DevTools में, अपवाद पर रोके जाने की जानकारी दिखेगी:
डिफ़ॉल्ट रूप से, यह Emscripten से जनरेट किए गए ग्लू कोड पर रुक जाता है, लेकिन दाईं ओर आपको कॉल स्टैक व्यू दिखता है, जो गड़बड़ी के स्टैकट्रेस को दिखाता है. साथ ही, आपको उस ओरिजनल C लाइन पर नेविगेट करने की सुविधा मिलती है जो abort
को शुरू करती है:
अब स्कोप व्यू में जाकर, C/C++ कोड में वैरिएबल के ओरिजनल नाम और वैल्यू देखी जा सकती हैं. साथ ही, अब आपको यह पता लगाने की ज़रूरत नहीं है कि $localN
जैसे गलत नामों का क्या मतलब है और वे आपके लिखे गए सोर्स कोड से कैसे जुड़े हैं.
यह सिर्फ़ पूर्णांक जैसी प्राइमिटिव वैल्यू पर ही नहीं, बल्कि स्ट्रक्चर, क्लास, ऐरे वगैरह जैसे कंपाउंड टाइप पर भी लागू होता है!
रिच टाइप के लिए सहायता
आइए, इनके बारे में जानने के लिए एक ज़्यादा मुश्किल उदाहरण देखते हैं. इस बार, हम C++ के इस कोड की मदद से मंडेलब्रॉट फ़्रैक्टल बनाएंगे:
#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();
}
यह देखा जा सकता है कि यह ऐप्लिकेशन अब भी काफ़ी छोटा है-एक ऐसी फ़ाइल है जिसमें 50 लाइनों के कोड होते हैं, लेकिन इस बार मैं कुछ बाहरी एपीआई का भी इस्तेमाल कर रहा/रही हूं, जैसे कि ग्राफ़िक के लिए SDL लाइब्रेरी और C++ स्टैंडर्ड लाइब्रेरी के जटिल नंबर.
मैं इसे ऊपर दिए गए -g
फ़्लैग के साथ ही कंपाइल करूंगा, ताकि डीबग करने से जुड़ी जानकारी शामिल की जा सके. साथ ही, मैं Emscripten से SDL2 लाइब्रेरी उपलब्ध कराने और मनमुताबिक साइज़ की मेमोरी इस्तेमाल करने की अनुमति देने के लिए कहूंगा:
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
ब्राउज़र में जनरेट किए गए पेज पर जाने पर, मुझे कुछ रंगों के साथ खूबसूरत फ़्रैक्टल आकार दिखता है:
DevTools खोलने पर, मुझे फिर से ओरिजनल C++ फ़ाइल दिख रही है. हालांकि, इस बार कोड में कोई गड़बड़ी नहीं है (वाह!), इसलिए अ��ने कोड की शुरुआत में कुछ ब्रेकपॉइंट सेट करें.
जब हम पेज को फिर से लोड करेंगे, तो डीबगर हमारे C++ सोर्स में ही रुक जाएगा:
हमें दाईं ओर अपने सभी वैरिएबल पहले से दिख रहे हैं. हालांकि, फ़िलहाल सिर्फ़ width
और height
को शुरू किया गया है. इसलिए, जांचने के लिए ज़्यादा कुछ नहीं है.
आइए, अपने मुख्य मैंडलब्रॉट लूप में एक और ब्रेकपॉइंट सेट करें और थोड़ा आगे जाने के लिए, प्रोग्राम को फिर से शुरू करें.
इस समय, हमारे palette
में कुछ रंग भरे गए हैं. साथ ही, हम ऐरे और अलग-अलग SDL_Color
स्ट्रक्चर, दोनों को बड़ा कर सकते हैं. साथ ही, उनके कॉम्पोनेंट की जांच करके यह पुष्टि कर सकते हैं कि सब कुछ ठीक है या नहीं. उदाहरण के लिए, "अल्फा" चैनल हमेशा पूरी ओपैसिटी पर सेट है. इसी तरह, center
वैरिएबल में सेव की गई कॉम्प्लेक्स संख्या के रीयल और इमेजरी पार्ट को बड़ा करके देखा जा सकता है.
अगर आपको किसी ऐसी प्रॉपर्टी को ऐक्सेस करना है जो स्कोप व्यू से ऐक्सेस करना मुश्किल है, तो कंसोल के आकलन का इस्तेमाल किया जा सकता है! हालांकि, ध्यान दें कि फ़िलहाल C++ के ज़्यादा जटिल एक्सप्रेशन काम नहीं करते.
आइए, कुछ समय के लिए फ़ंक्शन को फिर से शुरू करें. इससे हमें यह पता चलेगा कि ��ंदर मौजूद x
भी कैसे बदल रहा है. इसके लिए, स्कोप व्यू में फिर से देखें, वॉच लिस्ट में वैरिएबल का नाम जोड़ें, कंसोल में उसका आकलन करें या सोर्स कोड में वैरिएबल पर कर्सर घुमाएं:
यहां से, हम C++ स्टेटमेंट में स्टैप-इन या स्टैप-ओवर कर सकते हैं. साथ ही, यह भी देख सकते हैं कि अन्य वैरिएबल कैसे बदल रहे हैं:
ठीक है, डीबग करने की जानकारी उपलब्ध होने पर यह तरीका बहुत अच्छा काम करता है. हालांकि, अगर हमें ऐसे कोड को डीबग करना हो जिसे डिबग करने के विकल्पों के साथ नहीं बनाया गया है, तो क्या होगा?
रॉ WebAssembly डीबगिंग
उदाहरण के लिए, हमने Emscripten से कहा है कि वह हमें पहले से बनी SDL लाइब्रेरी उपलब्ध कराए. हम खुद सोर्स से इसे कंपाइल नहीं करना चाहते. इसलिए, कम से कम फ़िलहाल, डीबगर के पास इससे जुड़े सोर्स ढूंढने का कोई तरीका नहीं है.
SDL_RenderDrawColor
में जाने के लिए, फिर से शुरू करें:
हम WebAssembly के रॉ डीबग करने के अनुभव पर वापस आ गए हैं.
यह थोड़ा डरावना लगता है और यह ऐसा नहीं है कि ज़्यादातर वेब डेवलपर को समस्या हल करने की ज़रूरत न पड़े. हालांकि, कभी-कभी ऐसा हो सकता है कि आप डीबग क��� ��ान�����री के बिना बनाई गई लाइब्रेरी को डीबग करना चाहें. भले ही, यह तीसरे पक्ष की ऐसी लाइब्रेरी हो जिस पर आपका कोई कंट्रोल न हो या फिर कोई ऐसी गड़बड़ी हो रही हो जो सिर्फ़ प्रोडक्शन में होती है.
ऐसे मामलों में मदद करने के लिए, हमने डिबग करने के बुनियादी तरीके में भी कुछ सुधार किए हैं.
पहली बात, अगर आपने पहले कभी रॉ WebAssembly डीबगिंग का इस्तेमाल किया है, तो
आपने देखा होगा कि पूरा डिसअसेंबली अब एक ही फ़ाइल में दिख रहा है.
इससे यह अंदाज़ा नहीं लगाया जा सकेगा कि सोर्स एंट्री wasm-53834e3e/
wasm-53834e3e-7
किस फ़ंक्शन से मेल खाती है.
नाम जनरेट करने की नई स्कीम
हमने डिसअसेंबली व्यू में भी नामों में सुधार किया है. पहले आपको सिर्फ़ संख्या वाले इंडेक्स दिखते थे या फ़ंक्शन के मामले में, कोई नाम नहीं दिखता था.
अब हम अलग करने वाले दूसरे टूल की तरह ही नाम जनरेट कर रहे हैं. इसके लिए, हम WebAssembly के नाम वाले सेक्शन से मिले संकेतों का इस्तेमाल करके, इंपोर्ट/एक्सपोर्ट के पाथ इस्तेमाल करते हैं. अगर बाकी सब कुछ काम नहीं करता, तो $func123
जैसे आइटम के टाइप और इंडेक्स के हिसाब से उन्हें जनरेट किया जा सकता है. ऊपर दिए गए स्क्रीनशॉट में देखा जा सकता है कि इससे पहले से ही, थोड़ा बेहतर स्टैक ट्रेस और डिसअसेम्बली पाने में मदद मिलती है.
जब टाइप की जानकारी उपलब्ध नहीं होती है, तो प्राइमिटिव के अलावा किसी भी वैल्यू की जांच करना मुश्किल हो सकता है. उदाहरण के लिए, पॉइंटर सामान्य पूर्णांक के तौर पर दिखेंगे. साथ ही, यह पता नहीं चलेगा कि मेमोरी में उनके पीछे क्या स्टोर किया गया है.
मेमोरी की जांच
पहले, अलग-अलग ब��इट देखने के लिए, सिर्फ़ WebAssembly मेमोरी ऑब्जेक्ट को बड़ा किया जा सकता था. यह ऑब्जेक्ट, स्कोप व्यू में env.memory
से दिखाया जाता है. यह कुछ सामान्य स्थितियों में काम करता था, लेकिन इसे बड़ा करना खास तौर पर आसान नहीं था. साथ ही, इससे बाइट वैल्यू के अलावा किसी दूसरे फ़ॉर्मैट में डेटा को फिर से समझने की अनुमति नहीं मिलती थी. हमने इसमें मदद करने के लिए एक नई सुविधा भी जोड़ी है: एक लीनियर मेमोरी इंस्पेक्टर.
env.memory
पर राइट क्लिक करने पर, आपको मेमोरी की जांच करें नाम का एक नया विकल्प दिखेगा:
क्लिक करने के बाद, आपको एक मेमोरी इंस्पेक्टर दिखेगा. इसमें, हेक्साडेसिमल और ASCII व्यू में WebAssembly मेमोरी की जांच की जा सकती है. साथ ही, खास पतों पर नेविगेट किया जा सकता है और डेटा को अलग-अलग फ़ॉर्मैट में देखा जा सकता है:
बेहतर स्थितियां और सावधानियां
WebAssembly कोड की प्रोफ़ाइलिंग करना
DevTools खोलने पर, WebAssembly कोड को डीबग करने की सुविधा चालू करने के लिए, "कम परफ़ॉर्म करने वाले वर्शन" में "कम किया जाता है". यह वर्शन काफ़ी धीमा है. इसका मतलब है कि DevTools खुले होने पर, console.time
, performance.now
, और कोड की स्पीड को मेज़र करने के अन्य तरीकों पर भरोसा नहीं किया जा सकता. ऐसा इसलिए, क्योंकि आपको जो संख्याएं मिलेंगी वे असल परफ़ॉर्मेंस को बिलकुल नहीं दिखाएंगी.
इसके बजाय, आपको DevTools के परफ़ॉर्मेंस पैनल का इस्तेमाल करना ��ाहिए. इससे कोड पूरी स्पीड से चलेगा और आपको अलग-अलग फ़ंक्शन में बिताए गए समय की पूरी जानकारी मिलेगी:
इसके अलावा, DevTools बंद करके भी ऐप्लिकेशन को चलाया जा सकता है. इसके बाद, कंसोल की जांच करने के लिए, DevTools खोलें.
हम आने वाले समय में, प्रोफ़ाइल बनाने की सुविधा को बेहतर बनाएंगे. हालांकि, फ़िलहाल इस बात का ध्यान रखें. अगर आपको WebAssembly में अलग-अलग कैटगरी बनाने की स्थितियों के बारे में ज़्यादा जानना है, तो WebAssembly कंपाइलेशन पाइपलाइन पर बने हमारे दस्तावेज़ देखें.
अलग-अलग मशीनों (Docker / होस्ट सहित) पर बिल्ड करना और डीबग करना
Docker, वर्चुअल मशीन या रिमोट बिल्ड सर्वर में बिल्ड करते समय, आपको ऐसी स्थितियों का सामना करना पड़ सकता है जहां बिल्ड के दौरान इस्तेमाल की गई सोर्स फ़ाइलों के पाथ, आपके फ़ाइल सिस्टम के उन पाथ से मेल नहीं खाते जहां Chrome DevTools चल रहे हैं. इस मामले में, फ़ाइलें सोर्स पैनल में दिखेंगी, लेकिन वे लोड नहीं हो पाएंगी.
इस समस्या को ठीक करने के लिए, हमने C/C++ एक्सटेंशन के विकल्पों में पाथ मैपिंग की सुविधा लागू की है. इसका इस्तेमाल करके, आर्बिट्रेरी पाथ को फिर से मैप किया जा सकता है. साथ ही, DevTools को सोर्स ढूंढने में मदद की जा सकती है.
उदाहरण के लिए, अगर आपकी होस्ट मशीन पर प्रोजेक्ट, पाथ C:\src\my_project
में है, लेकिन उसे Docker कंटेनर में बनाया गया था, जहां उस पाथ को /mnt/c/src/my_project
के तौर पर दिखाया गया था, तो डीबग करने के दौरान उसे फिर से मैप किया जा सकता है. इसके लिए, उन पाथ को प्रीफ़िक्स के तौर पर बताएं:
मैच होने वाला पहला प्रीफ़िक्स "ज�������ा है". ��गर आपको अन्य C++ डीबगर के बारे में पता है, तो यह विकल्प GDB में set substitute-path
कमांड या LLDB में target.source-map
सेटिंग की तरह है.
ऑप्टिमाइज़ किए गए बिल्ड को डीबग करना
अन्य भाषाओं की तरह ही, डीबग करने की सुविधा सबसे अच्छी तरह तब काम करती है, जब ऑप्टिमाइज़ेशन बंद हों. ऑप्टिमाइज़ेशन की वजह से, फ़ंक्शन एक-दूसरे में इनलाइन हो सकते हैं, कोड का क्रम बदल सकता है या कोड के कुछ हिस्से हट सकते हैं. इन सभी वजहों से, डीबगर और उपयोगकर्ता को भ्रम हो सकता है.
अगर आपको डीबग करने का सीमित अनुभव चाहिए और आपको ऑप्टिमाइज़ किए गए बिल्ड को डीबग करना है, तो ज़्यादातर ऑप्टिमाइज़ेशन उम्मीद के मुताबिक काम करेंगे. हालांकि, फ़ंक्शन इनलाइनिंग को छोड़कर. हम आने वाले समय में बाकी समस्याओं को हल करेंगे.हालांकि, फ़िलहाल -O
लेवल के ऑप्टिमाइज़ेशन के साथ कंपाइल करते समय, इसे बंद करने के लिए -fno-inline
का इस्तेमाल करें. उदाहरण के लिए:
emcc -g temp.c -o temp.html \ -O3 -fno-inline
डीबग की जानकारी को अलग करना
डीबग करने की जानकारी में आपके कोड, तय किए गए टाइप, वैरिएबल, फ़ंक्शन, स्कोप, और जगह की जानकारी के बारे में ऐसी बहुत सारी जानकारी सुरक्षित रहती है जो डीबगर के लिए काम की हो सकती है. इस वजह से, यह अक्सर कोड से बड़ा हो सकता है.
WebAssembly मॉड्यूल को तेज़ी से लोड और कंपाइल करने के लिए, हो सकता है कि आप इस डीबग जानकारी को अलग WebAssembly फ़ाइल में बांटना चाहें. Emscripten में ऐसा करने के लिए, मनचाहे फ़ाइल नाम के साथ -gseparate-dwarf=…
फ़्लै�� पास करें:
emcc -g temp.c -o temp.html \ -gseparate-dwarf=temp.debug.wasm
इस मामले में, मुख्य ऐप्लिकेशन सिर्फ़ फ़ाइल का नाम temp.debug.wasm
सेव करेगा. साथ ही, DevTools खोलने पर, हेल्पर एक्सटेंशन उसे ढूंढकर लोड कर पाएगा.
ऊपर बताए गए ऑप्टिमाइज़ेशन के साथ इस सुविधा का इस्तेमाल, अपने ऐप्लिकेशन के ऑप्टिमाइज़ किए गए प्रोडक्शन बिल्ड को शिप करने के लिए भी किया जा सकता है. इसके बाद, स्थानीय साइड फ़ाइल की मदद से उन्हें डीबग किया जा सकता है. इस मामले में, हमें एक्सटेंशन को साइड फ़ाइल ढूंढने में मदद करने के लिए, सेव किए गए यूआरएल को बदलना होगा. उदाहरण के लिए:
emcc -g temp.c -o temp.html \ -O3 -fno-inline \ -gseparate-dwarf=temp.debug.wasm \ -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]
जारी रहेगा…
वाह, बहुत सारी नई सुविधाएं!
इन सभी नए इंटिग्रेशन के साथ, Chrome DevTools सिर्फ़ JavaScript के लिए ही नहीं, बल्कि C और C++ ऐप्लिकेशन के लिए भी एक बेहतर, कारगर और डीबगर बन गया है. इसकी वजह से, कई तरह क��� टेक्नोलॉजी में बनाए गए और शेयर किए गए क्रॉस-प्लैटफ़ॉर्म वेब पर ऐप्लिकेशन इस्तेमाल करना पहले से ज़्यादा आसान हो गया है.
हालांकि, हमारी यात्रा अभी पूरी नहीं हुई है. हम आगे इन चीज़ों पर काम करेंगे:
- डीबग करने की प्रोसेस को आसान बनाया जा रहा है.
- कस्टम टाइप फ़ॉर्मैटर के लिए सहायता जोड़ी गई है.
- WebAssembly ऐप्लिकेशन की प्रोफ़ाइलिंग को बेहतर बनाने के लिए, काम चल रहा है.
- कोड कवरेज के लिए सहायता जोड़ी गई है, ताकि इस्तेमाल न किए गए कोड को आसानी से ढूंढा जा सके.
- कंसोल के आकलन में एक्सप्रेशन के लिए सहायता को बेहतर बनाना.
- ज़्यादा भाषाओं के लिए सहायता जोड़ी जा रही है.
- …और ऐसे ही अन्य ट्रेंड!
इस बीच, अपने कोड पर मौजूदा बीटा वर्शन आज़माकर, हमें मदद करें. साथ ही, अगर आपको कोई समस्या मिलती है, तो https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 पर जाकर हमें इसकी शिकायत करें.
झलक दिखाने वाले चैनलों को डाउनलोड करें
Chrome कैनरी, डेवलपर या बीटा को अपने डिफ़ॉल्ट डेवलपमेंट ब्राउज़र के तौर पर इस्तेमाल करें. इन झलक वाले चैनलों की मदद से, आपको DevTools की नई सुविधाओं का ऐक्सेस मिलता है. साथ ही, इनसे आपको वेब प्लैटफ़ॉर्म के सबसे नए एपीआई की जांच करने में मदद मिलती है. इसके अलावा, इनकी मदद से उपयोगकर्ताओं से पहले ही अपनी साइट पर समस्याओं का पता लगाया जा सकता है!
Chrome DevTools की टीम से संपर्क करें
DevTools से जुड़ी नई सुविधाओं, अपडेट या किसी भी अन्य चीज़ के बारे में चर्चा करने के लिए, यहां दिए गए विकल्पों का इस्तेमाल करें.
- crbug.com पर जाकर हमें फ़ीडबैक और सुविधा के अनुरोध सबमिट करें.
- DevTools में ज़्यादा विकल्प > सहायता > DevTools से जुड़ी समस्या की शिकायत करें का इस्तेमाल करके, DevTools से जुड़ी समस्या की शिकायत करें.
- @ChromeDevTools पर ट्वीट करें.
- DevTools के बारे में YouTube वीडियो में क्या नया है या DevTools के बारे में YouTube वीडियो में सलाह पर टिप्पणियां करें.