Modern araçlarla WebAssembly hatalarını ayıklama

Ingvar Stepanyan
Ingvar Stepanyan

Geldiğimiz nokta

Chrome, bir yıl önce Chrome Geliştirici Araçları'nda yerel WebAssembly hata ayıklama için ilk desteği duyurmuştu.

Temel adım desteğini gösterdik ve kaynak haritalar yerine DWARF bilgilerinin kullanımının gelecekte bize sunacağı fırsatlardan bahsettik:

  • Değişken adlarını çözme
  • Okunaklı hale getirme türleri
  • Kaynak dillerdeki ifadeleri değerlendirme
  • …ve daha fazlası!

Bugün, vadettiğimiz özelliklerin hayata geçtiğini göstermekten heyecan duyuyoruz. Emscripten ve Chrome Geliştirici Araçları ekiplerinin C ve C++ uygulamaları için büyük bir fırsat oluşturuyor.

Başlamadan önce, bunun yeni deneyimin beta sürümünün hâlâ olduğunu, tüm araçların en son sürümünü kendi sorumluluğunuzda kullanmanız gerektiğini ve herhangi bir sorunla karşılaşırsanız lütfen https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 adresinden bildirin.

Geçen seferkiyle aynı basit C örneğiyle başlayalım:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Derlemek için en son Emscripten'i kullanırız ve hata ayıklama bilgilerini dahil etmek üzere orijinal gönderide olduğu gibi bir -g işareti iletiriz:

emcc -g temp.c -o temp.html

Artık oluşturulan sayfayı bir localhost HTTP sunucusundan ( örneğin, serve) ve en son Chrome Canary'de açmanız gerekir.

Bu kez, Chrome DevTools ile entegre olan ve WebAssembly dosyasında kodlanmış tüm hata ayıklama bilgilerini anlamasına yardımcı olan bir yardımcı uzantıya da ihtiyacımız var. Lütfen şu bağlantıya giderek yükleyin: goo.gle/wasm-debugging-extension

DevTools Denemeler bölümünde WebAssembly hata ayıklama özelliğini de etkinleştirmeniz gerekir. Chrome Geliştirici Araçları'nı açın, DevTools bölmesinin sağ üst köşesindeki dişli () simgesini tıklayın, Deneysel paneline gidin ve WebAssembly Hata Ayıklama: DWARF desteğini etkinleştir'i işaretleyin.

Geliştirici Araçları ayarlarının Denemeler bölmesi

Ayarlar'ı kapattığınızda Geliştirici Araçları, ayarları uygulamak için kendisini yeniden yüklemeyi önerir. Biz de bunu yapalım. Tek seferlik kurulum bu kadar.

Şimdi Kaynaklar paneline geri dönebilir, İstisnalarda duraklat'ı (⏸ simgesi) etkinleştirebilir, ardından Tespit edilen istisnalarda duraklat'ı işaretleyip sayfayı yeniden yükleyebiliriz. Geliştirici Araçları'nın bir istisnada duraklatıldığını göreceksiniz:

&quot;Yakalanan istisnalarda duraklat&quot; seçeneğinin nasıl etkinleştirileceğini gösteren Kaynaklar panelinin ekran görüntüsü

Varsayılan olarak, Emscripten tarafından oluşturulan bir yapıştırma kodunda durur ancak sağ tarafta hatanın yığın izlemesini temsil eden bir Çağrı Yığını görünümü görebilirsiniz ve abort'yi çağıran orijinal C satırına gidebilirsiniz:

Geliştirici Araçları &quot;assert_less&quot; işlevinde duraklatıldı ve kapsam görünümünde &quot;x&quot; ile &quot;y&quot; değerlerini gösteriyor

Artık Kapsam görünümüne baktığınızda orijinal adların ve değerlerini C/C++ kodundaki değişkenlerin değerleri ile ilişkilendirir ve artık $localN gibi karışık isimlerin ne anlama geldiğini ve kaynak kodundan kaynaklanabilir.

Bu durum yalnızca tam sayılar gibi ilkel değerler için değil, yapı, sınıf, dizi vb. gibi karmaşık türler için de geçerlidir.

Zengin tür desteği

Bunları göstermek için daha karmaşık bir örneğe bakalım. Bu kez aşağıdaki C++ koduyla bir Mandelbrot fraktalı çizeceğiz:

#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();
}

Gördüğünüz gibi bu uygulama da oldukça küçük. Uygulama, 50 satır kod içeren bir dosya ama bu defa ben de için SDL kitaplığı gibi harici API'ler karmaşık sayılar ve bunların C++ standart kitaplığı.

Dahil etmek için yukarıdakiyle aynı -g işaretiyle derleyeceğim. hata ayıklama bilgilerini aktaracağım. Ayrıca, Emscripten'den SDL2 kitaplığını açabilir ve rastgele boyutlandırılmış belleğe izin verebilirsiniz:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Oluşturulan sayfayı tarayıcıda ziyaret ettiğimde, rastgele renklerle güzel bir fraktalı şekil görüyorum:

Demo sayfası

Geliştirici Araçları'nı açtığımda tekrar orijinal C++ dosyasını görüyorum. Bu ancak kodda herhangi bir hata yok (üe!). Bu nedenle, kodumuzun başına koyabileceğimiz yeni bir

Sayfayı tekrar yüklediğimizde hata ayıklayıcı, C++ kaynağımızın tam içinde duraklar:

DevTools, &quot;SDL_Init&quot; çağrısında duraklatıldı

Tüm değişkenlerimizi sağ tarafta görebiliriz ancak şu anda yalnızca width ve height başlatılmıştır. Bu nedenle, incelenecek çok fazla şey yoktur.

Ana Mandelbrot döngümüze başka bir ayrılma noktası ekleyip ileri sarabilirim.

Geliştirici Araçları, iç içe yerleştirilmiş döngüler içinde duraklatıldı

Bu noktada palette aracımız rastgele renklerle dolduruldu. hem dizinin kendisini hem de şunları doğrulamak için SDL_Color yapıyı ve bileşenlerini inceleyin her şey yolunda görünüyor (örneğin, "alfa" kanalı tam opaklığa). Benzer şekilde, mevcut müşteri tabanının center değişkeninde depolanan karmaşık sayının sanal bölümleri.

Kapsam görünümü aracılığıyla erişmesi zor olan, derin iç içe yerleştirilmiş bir mülke erişmek istiyorsanız Console değerlendirmesini de kullanabilirsiniz. Ancak daha karmaşık C++ ifadelerinin henüz desteklenmediğini unutmayın.

&quot;palette[10].r&quot; ifadesinin sonucunu gösteren konsol paneli

Yürütmeye birkaç kez devam edelim ve iç x öğesinin nasıl olduğunu Kapsam görünümüne tekrar bakarak, yeni bir kitle eklemek için bunu izleme listesine ekleyerek, konsolda değerlendirerek veya kaynak koddaki değişkenin üzerine gelin:

Kaynaktaki &quot;x&quot; değişkeni üzerine, &quot;3&quot; değerini gösteren ipucu

Buradan, C++ ifadelerini devreye alabilir veya bunların nasıl diğer değişkenler de değişiyor:

&quot;color&quot;, &quot;point&quot; ve diğer değişkenlerin değerlerini gösteren ipuçları ve kapsam görünümü

Hata ayıklama bilgileri mevcut olduğunda bu yöntemler mükemmel şekilde çalışır. Peki hata ayıklama seçenekleriyle derlenmemiş bir kodda hata ayıklama yapmak istersek ne olur?

Ham WebAssembly hata ayıklama

Örneğin, Emscripten'den her örnek için önceden oluşturulmuş bir SDL kitaplığı kaynağından kendimiz derlemek yerine, en azından Şu anda hata ayıklayıcının ilişkili kaynakları bulması mümkün değildir. SDL_RenderDrawColor devreye girelim:

&quot;mandelbrot.wasm&quot; dosyasının kaynak kodunun ayrıştırılmış görünümünü gösteren Geliştirici Araçları

Ham WebAssembly hata ayıklama deneyimine geri döndük.

Çok korkutucu görünüyor ve çoğu Web geliştiricisinin isteyeceği bir şey değil, zaman zaman bir hata ayıklamanız gerekebilir, ancak veya hata ayıklama bilgileri içermeyen bir kitaplık Kontrolün sizde olmadığı 3. taraf kitaplığı veya yalnızca üretimde oluşan hatalardan biriyle karşılaşabilirsiniz.

Bu durumlarda yardımcı olmak için, Okuyucu Gelirleri Yöneticisi'nin hata ayıklama deneyiminden yararlanabilirsiniz.

Öncelikle, daha önce ham WebAssembly hata ayıklama özelliğini kullandıysanız artık tüm kod ayrıştırmasının tek bir dosyada gösterildiğini fark edebilirsiniz. Artık wasm-53834e3e/ wasm-53834e3e-7 Kaynaklar girişinin hangi işleve karşılık geldiğini tahmin etmeniz gerekmez.

Yeni ad oluşturma şeması

Demonte görünümündeki adları da iyileştirdik. Daha önce yalnızca sayısal dizinler veya işlevler söz konusu olduğunda hiç ad gösterilmiyordu.

Artık WebAssembly ad bölümündeki ipuçlarını, içe/dışa aktarma yollarını kullanarak ve diğer tüm yöntemler başarısız olursa $func123 gibi öğenin türüne ve dizine göre adları diğer disassemblör araçlarına benzer şekilde oluşturuyoruz. Şunları yapabilirsiniz: yukarıdaki ekran görüntüsünde, bu yöntemin biraz da daha okunabilir yığın izlemeleri ve parçalarına ayırmaya yardımcı olur.

Tür bilgisi olmadığında, ilkellerin dışındaki değerleri incelemek zor olabilir. Örneğin, işaretçiler normal tam sayılar olarak gösterilir ve bellekte arkalarında nelerin depolandığını bilmek mümkün olmaz.

Bellek denetimi

Daha önce, yalnızca Kapsam görünümünde env.memory ile gösterilen WebAssembly bellek nesnesini genişletebiliyordunuz. kullanılabilir. Bu yöntem bazı basit senaryolarda işe yaradı ancak ve verilerin yeniden yorumlanmasına izin vermemek, özellikle farklı biçimlerde olabilir. Bu konuda size yardımcı olacak yeni bir özellik de ekledik: doğrusal bellek denetleyicisi.

env.memory öğesini sağ tıklarsanız artık yeni bir Belleği incele adlı seçeneği görebilirsiniz:

Kapsam bölmesinde &quot;env.memory&quot; üzerinde &quot;Hafızayı İncele&quot; öğesini gösteren içerik menüsü

Tıklandığında, WebAssembly belleğini onaltılık ve ASCII görünümlerinde inceleyebileceğiniz, belirli adreslere gidebileceğiniz ve verileri farklı biçimlerde yorumlayabileceğiniz bir Bellek Denetleyicisi açılır:

Geliştirici Araçları&#39;ndaki, belleğin onaltılık ve ASCII görünümlerini gösteren Bellek Denetleyicisi bölmesi

Gelişmiş senaryolar ve uyarılar

WebAssembly kodunda profil oluşturma

Geliştirici Araçları'nı açtığınızda WebAssembly kodu, hata ayıklama özelliğini etkinleştirmek için optimize edilmemiş bir sürüme "indirilir". Bu sürüm çok daha yavaş. Bu da console.time, performance.now özelliklerine güvenemeyeceğiniz anlamına gelir. ve diğer yöntemler de kodsuz olduğunda elde ettiğiniz sayılar gerçek dünya performansını temsil etmediğinden, mümkün değil.

Bunun yerine Geliştirici Araçları Performans panelini kullanmalısınız. Bu da kodu tam hızda çalıştırır ve size bir farklı fonksiyonlarda harcanan zamanın ayrıntılı dökümü:

Çeşitli Wasm işlevlerini gösteren profil oluşturma paneli

Alternatif olarak, uygulamanızı Geliştirici Araçları kapalıyken çalıştırabilir ve Konsol'u incelemek için işlem tamamlandıktan sonra Geliştirici Araçları'nı açabilirsiniz.

Gelecekte profil çıkarma senaryolarını geliştireceğiz, ancak şimdilik bir dikkat etmeniz gerekir. WebAssembly hakkında daha fazla bilgi edinmek isterseniz WebAssembly derleme ardışık düzeni ile ilgili dokümanlarımıza göz atın.

Farklı makinelerde (Docker/ana makine dahil) derleme ve hata ayıklama

Docker, sanal makine veya uzak derleme sunucusunda derleme yaparken kaynak dosyalara giden yolların belirli bir süre içinde oluşturma sırasında kullanılan yolların, kendi dosya sisteminizdeki çalıştığını fark edebilirsiniz. Bu durumda, dosyalar Kaynaklar panelinde gösterilir ancak yüklenemez.

Bu sorunu düzeltmek için C/C++ uzantı seçenekleri vardır. Bunu rastgele yolları ve hedefleri yeniden eşlemek için Geliştirici Araçları'nın kaynakları bulmasına yardımcı olabilirsiniz.

Örneğin, ana makinenizdeki proje C:\src\my_project yolunun altındaysa ancak bu yolun /mnt/c/src/my_project olarak temsil edildiği bir Docker kapsayıcısında derlendiyse bu yolları ön ek olarak belirterek hata ayıklama sırasında yeniden eşleyebilirsiniz:

C/C++ hata ayıklama uzantısının Seçenekler sayfası

Eşleşen ilk ön ek "kazanır". Diğer C++ öğelerine aşinaysanız bu seçenek set substitute-path komutuna benzerdir veya LLDB'de target.source-map ayarı yapın.

Optimize edilmiş derlemelerde hata ayıklama

Diğer tüm dillerde olduğu gibi, optimizasyonlar devre dışı bırakılırsa hata ayıklama en iyi şekilde çalışır. Optimizasyonlar, işlevleri birbirine satır içi olarak yerleştirebilir, kodu yeniden sıralayabilir veya kod parçalarını tamamen kaldırabilir. Tüm bunların hata ayıklayıcıyı ve dolayısıyla kullanıcı olarak sizi şaşırtma olasılığı vardır.

Daha sınırlı bir hata ayıklama deneyiminin sizin için sakıncası yoksa ve yine de hata ayıklamak istiyorsanız optimizasyonların çoğu ( satır içi işlevin hariç tutulması dışında) beklenenler. Kalan sorunları gelecekte ele almayı planlıyoruz ancak şimdilik -O düzeyinde optimizasyonlarla derleme yaparken devre dışı bırakmak için lütfen -fno-inline kullanın. Örneğin:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Hata ayıklama bilgilerini ayırma

Hata ayıklama bilgileri, kodunuz, tanımlanmış türler, değişkenler, işlevler, kapsamlar ve konumlar (hata ayıklayıcı için yararlı olabilecek her şey) hakkında birçok ayrıntıyı korur. Sonuç olarak, genellikle daha fazla bilgi edineceksiniz.

WebAssembly modülünün yüklenmesini ve derlenmesini hızlandırmak için bu hata ayıklama bilgilerini farklı bir WebAssembly dosyası olarak kaydedebilirsiniz. Bunu Emscripten'de yapmak için-gseparate-dwarf=… istediğiniz bir dosya adına sahip olmanız gerekir:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

Bu durumda, ana uygulama yalnızca bir dosya adı saklar temp.debug.wasm ve yardımcı uzantı, Geliştirici Araçları'nı açtığınızda da yükleyebilirsiniz.

Yukarıda açıklanan optimizasyonlarla birlikte kullanıldığında bu özellik, uygulamanızın neredeyse optimize edilmiş üretim derlemelerini göndermek ve daha sonra yerel taraftaki bir dosyayla bu derlemelerde hata ayıklama yapmak için bile kullanılabilir. Bu durumda, uzantının yan dosyayı bulmasına yardımcı olmak için saklanan URL'yi de geçersiz kılmamız gerekir. Örneğin:

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]

Devam etmek için...

Vay canına, bir sürü yeni özellik sunduk.

Tüm bu yeni entegrasyonlarla Chrome Geliştirici Araçları, yalnızca JavaScript için değil, C ve C++ uygulamaları için de geçerli, güçlü ve kullanışlı bir hata ayıklayıcı haline gelir. Böylece, çeşitli teknolojilerde geliştirilen uygulamaları paylaşılan, platformlar arası bir web'e taşımak hiç olmadığı kadar kolay hale gelir.

Ancak yolculuğumuz henüz sona ermedi. Bundan sonra üzerinde çalışacağımız bazı konular:

  • Hata ayıklama deneyimindeki sorunlu kısımları temizleme.
  • Özel tür biçimlendiriciler için destek eklendi.
  • WebAssembly uygulamaları için profil oluşturma özelliğinde iyileştirmeler üzerinde çalışıyoruz.
  • Kullanılmayan kodun bulunmasını kolaylaştırmak için kod kapsamı desteği eklendi.
  • Konsol değerlendirmesinde ifadeler için sağlanan desteği iyileştirme.
  • Daha fazla dil için destek eklendi.
  • …ve daha fazlası!

Bu sırada, mevcut beta sürümünü kendi kodunuzda deneyerek ve karşılaştığınız sorunları https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 adresinden bildirerek bize yardımcı olabilirsiniz.

Önizleme kanallarını indirme

Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Geliştirici veya Beta sürümünü kullanabilirsiniz. Bu önizleme kanalları en yeni Geliştirici Araçları özelliklerine erişmenizi, son teknoloji ürünü web platformu API'lerini test etmenizi ve kullanıcılarınızdan önce sitenizdeki sorunları bulmanızı sağlar.

Chrome Geliştirici Araçları ekibiyle iletişim kurma

Yayındaki yeni özellikler ve değişiklikler ya da DevTools ile ilgili başka bir konu hakkında konuşmak için aşağıdaki seçenekleri kullanın.