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

Ingvar Stepanyan
Ingvar Stepanyan

Şu ana kadarki yol

Bir yıl önce Chrome, Geliştirici Araçları'nda yerel WebAssembly hata ayıklaması için ilk desteği duyurdu.

Temel adım desteğini gösterdik ve kaynak haritaları yerine DWARF bilgilerinin kullanımıyla ilgili fırsatların gelecekte açılacağından bahsettik:

  • Değişken adlarını çözümleme
  • Okunaklı baskı türleri
  • Kaynak dillerdeki ifadeleri değerlendirme
  • ...ve çok daha fazlası!

Bugün, vaat edilen özelliklerin hayata geçtiğini ve Emscripten ile Chrome DevTools ekiplerinin bu yıl, özellikle de C ve C++ uygulamalarında kaydettiği ilerlemeyi sergilemekten heyecan duyuyoruz.

Başlamadan önce, bu yeni deneyimin hâlâ beta sürümü olduğunu ve tüm araçların en son sürümünü kullanmanın doğurabileceği riskleri üstlenmeniz gerektiğini hatırlatmak isteriz. Sorunla karşılaşırsanız lütfen https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue adresine bildirin.

Sonuncudakiyle 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 eklemek için orijinal yayındaki gibi bir -g işareti iletiriz:

emcc -g temp.c -o temp.html

Artık oluşturulan sayfayı bir yerel ana makine HTTP sunucusundan sunabilir (örneğin, serve ile) ve en yeni Chrome Canary'de açabiliriz.

Bu kez ayrıca, Chrome Geliştirici Araçları ile entegre olan ve WebAssembly dosyasında kodlanan tüm hata ayıklama bilgilerini anlamlandırmasına yardımcı olan bir yardımcı uzantıya da ihtiyacımız olacak. Lütfen şu bağlantıya giderek uygulamayı yükleyin: goo.gle/wasm-debugging-extension

Geliştirici Araçları Denemeler bölümünde WebAssembly hata ayıklamasını da etkinleştirmek isteyebilirsiniz. Chrome Geliştirici Araçları'nı açın, Geliştirici Araçları bölmesinin sağ üst köşesindeki dişli simgesini () tıklayın, Denemeler paneline gidin ve WebAssembly Hata Ayıklaması: 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ın uygulanması için kendisini yeniden yüklemeyi önerir. Şimdi bu işlemi yapalım. Tek seferlik kurulum bu kadar.

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

&quot;Yakalanan istisnalarda duraklat&quot; özelliğ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ırıcı kodunda durur. Ancak sağ tarafta, hatanın yığın izini temsil eden bir Çağrı Grubu görünümü yer alır ve abort yöntemini ç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ğerleri gösteriliyor

Artık Kapsam görünümüne bakarsanız C/C++ kodunda değişkenlerin orijinal adlarını ve değerlerini görebilirsiniz. Böylece, $localN gibi karışık adların ne anlama geldiğini ve yazdığınız kaynak kodla nasıl bir ilişkisi olduğunu anlamanız gerekmez.

Bu, yalnızca tam sayı gibi temel değerler için değil, yapı, sınıf, dizi gibi bileşik türler için de geçerlidir.

Zengin tür desteği

Bunları göstermek için daha karmaşık bir örneğe göz atalı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();
}

Bu uygulamanın hâlâ oldukça küçük olduğunu görebilirsiniz. Uygulama, 50 satır kod içeren tek bir dosyadır. Ancak bu kez grafikler için SDL kitaplığı ve C++ standart kitaplığından karmaşık sayılar gibi bazı harici API'ler de kullanıyorum.

Hata ayıklama bilgilerini eklemek için yukarıdakiyle aynı -g işaretiyle derleyeceğim. Ayrıca Emscripten'dan SDL2 kitaplığını sağlayıp rastgele boyutlandırılmış belleğe izin vermesini isteyeceğim:

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

Tarayıcıda oluşturulan sayfayı ziyaret ettiğimde, bazı rastgele renklerle güzel kesirli şekli görebiliyorum:

Tanıtım sayfası

DevTools'u açtığımda tekrar orijinal C++ dosyasını görebiliyorum. Ancak bu kez kodda hata yok (süper!). Şimdi bunun yerine kodumuzun başına bir ayrılma noktası oluşturalım.

Sayfayı tekrar yeniden yüklediğimizde, hata ayıklayıcı doğrudan C++ kaynağımızda duraklar:

Geliştirici Araçları, &quot;SDL_Init&quot; çağrısında duraklatıldı

Tüm değişkenlerimizi sağda görebiliyoruz ancak şu anda yalnızca width ve height başlatıldığı için incelenecek pek bir şey yok.

Ana Mandelbrot döngümüzün içinde başka bir ayrılma noktası oluşturalım ve biraz ileri atlamak için yürütmeyi sürdürelim.

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

Bu noktada palette öğesi bazı rastgele renklerle dolduruldu ve hem dizinin kendisini hem de bağımsız SDL_Color yapılarını genişletip her şeyin iyi göründüğünü doğrulamak için bileşenlerini inceleyebiliriz (örneğin, "alfa" kanalı her zaman tam opaklık değerine ayarlanır). Benzer şekilde, center değişkeninde depolanan karmaşık sayının gerçek ve sanal kısımlarını da genişletip kontrol edebiliriz.

Kapsam görünümünden ulaşılması zor olan, derinlemesine 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; sonucunu gösteren konsol paneli

Yürütmeyi birkaç kez devam edelim ve içteki x öğesinin de nasıl değiştiğini Kapsam görünümüne tekrar bakarak, değişken adını izleme listesine ekleyerek, konsolda değerlendirerek veya fareyle kaynak koddaki değişkenin üzerine getirerek nasıl değiştiğini görebiliriz:

Kaynakta &quot;x&quot; değişkeninin üzerinde yer alan ve &quot;3&quot; değerini gösteren ipucu

Buradan, C++ ifadelerini adım adım izleyebilir ve diğer değişkenlerin de nasıl değiştiğini gözlemleyebiliriz:

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

Tamam. Hata ayıklama bilgileri mevcut olduğunda tüm bunlar harika çalışıyor. Peki hata ayıklama seçenekleriyle oluşturulmamış bir kodda hata ayıklamak istersek ne olacak?

Ham WebAssembly hata ayıklaması

Örneğin, Emscripten'dan bizim için kendimiz derlemek yerine önceden oluşturulmuş bir SDL kitaplığı sağlamasını istedik. Bu yüzden, en azından şu anda hata ayıklayıcının ilişkili kaynakları bulması mümkün değil. SDL_RenderDrawColor konusunun üzerinden tekrar geçelim:

&quot;mandelbrot.wasm&quot; sökme görünümünü gösteren Geliştirici Araçları

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

Şimdiyse bu biraz korkutucu görünüyor ve çoğu web geliştiricisinin hiç uğraşması gerekmeyecek ancak bazen hata ayıklama bilgileri olmadan oluşturulmuş bir kitaplıkta hata ayıklamak isteyebilirsiniz. Bunun nedeni, denetiminizin olmadığı üçüncü taraf bir kitaplık olması veya yalnızca üretimde oluşan hatalardan biriyle karşılaşmanız olabilir.

Bu durumlarda yardımcı olmak için temel hata ayıklama deneyiminde de bazı iyileştirmeler yaptık.

Her şeyden önce, daha önce ham WebAssembly hata ayıklamasını kullandıysanız tüm parçaların artık tek bir dosyada gösterildiğini fark edebilirsiniz. Artık wasm-53834e3e/ wasm-53834e3e-7 Kaynaklar girişinin hangi işleve karşılık gelebileceğini tahmin edebilirsiniz.

Yeni ad oluşturma şeması

Sökme görünümündeki adları da iyileştirdik. Önceden sadece sayısal dizinleri ya da işlevlerde hiç ad görmüyordunuz.

Artık WebAssembly ad bölümündeki ipuçlarını ve içe/dışa aktarma yollarını kullanarak ve diğer sökme araçlarına benzer şekilde adlar oluşturuyoruz. Ayrıca, başka hiçbir şey başarısız olursa bu adları, $func123 gibi öğenin türüne ve dizinine göre üretiyoruz. Yukarıdaki ekran görüntüsünde bunun biraz daha okunabilir yığın izleme ve sökmeye nasıl yardımcı olduğunu görebilirsiniz.

Kullanılabilir tür bilgisi olmadığında, temel öğeler dışındaki değerleri incelemek zor olabilir. Örneğin, işaretçiler normal tam sayılar olarak görünür ve bellekte ne depolandığını bilmenin bir yolu yoktur.

Bellek inceleme

Önceden, bağımsız baytları aramak için yalnızca Kapsam görünümünde env.memory ile gösterilen WebAssembly bellek nesnesini genişletebiliyordunuz. Bu, sıradan bazı senaryolarda işe yaradı, ancak genişlemeyi özellikle uygun bulmadı ve verilerin bayt değerleri dışındaki biçimlerde yeniden yorumlanmasını sağlamadı. Bu konuda yardımcı olması için yeni bir özellik ekledik: Doğrusal bellek denetleyici.

env.memory'yi sağ tıklarsanız Belleği incele adlı yeni bir seçenek görürsünüz:

Kapsam bölmesindeki &quot;env.memory&quot; alanında bulunan ve &quot;Belleği İnceleme&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 Memory Inspector açılır:

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

İleri düzey senaryolar ve uyarılar

WebAssembly kodunda profil oluşturma

Geliştirici Araçları'nı açtığınızda, hata ayıklamayı etkinleştirmek için WebAssembly kodu "katmanlara ayrılarak" optimize edilmemiş bir sürüme geçirilir. Bu sürüm çok daha yavaştır. Bu nedenle, Geliştirici Araçları açıkken kodunuzun hızını ölçmek için console.time, performance.now ve diğer yöntemlere güvenemezsiniz. Bunun nedeni, verilen rakamlar gerçek dünyadaki performansı hiçbir şekilde yansıtmayacak olmasıdır.

Bunun yerine, kodu en yüksek hızda çalıştıracak ve farklı işlevlerde harcanan sürenin ayrıntılı bir dökümünü sağlayacak olan Geliştirici Araçları Performans panelini kullanmanız gerekir:

Ç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 tamamladıktan sonra Konsolu incelemek için bu araçları açabilirsiniz.

Gelecekte profil oluşturma senaryolarını iyileştireceğiz, ancak bu, şu an için dikkat edilmesi gereken bir uyarıdır. WebAssembly katman oluşturma senaryoları hakkında daha fazla bilgi edinmek istiyorsanız WebAssembly derleme ardışık düzeni ile ilgili belgelerimize göz atın.

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

Docker, sanal makine veya uzak derleme sunucusu üzerinde derleme yaparken, derleme sırasında kullanılan kaynak dosyaların yollarının Chrome Geliştirici Araçları'nın çalıştığı kendi dosya sisteminizdeki yollarla eşleşmediği durumlarla karşılaşabilirsiniz. Bu durumda dosyalar Kaynaklar panelinde gösterilir ancak yüklenemez.

Bu sorunu düzeltmek için C/C++ uzantı seçeneklerine bir yol eşleme işlevi ekledik. Bunu rastgele yolları yeniden eşlemek ve Geliştirici Araçları'nın kaynakları bulmasına yardımcı olmak için kullanabilirsiniz.

Örneğin, ana makine makinenizdeki proje bir C:\src\my_project yolunun altındaysa ancak bu yolun /mnt/c/src/my_project olarak temsil edildiği bir Docker kapsayıcısının içinde oluşturulmuşsa 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ı

İlk eşleşen ön ek "kazanır". Diğer C++ hata ayıklayıcılarına aşinaysanız bu seçenek GDB'deki set substitute-path komutuna veya LLDB'deki target.source-map ayarına benzerdir.

Optimize edilmiş derlemelerde hata ayıklama

Diğer dillerde olduğu gibi, hata ayıklamada da optimizasyonlar devre dışı bırakıldığında daha iyi sonuç verir. Optimizasyonlar satır içinde işlevleri birbirlerine dönüştürebilir, kodu yeniden sıralayabilir veya kodun bazı bölümlerini tamamen kaldırabilir. Tüm bunların hata ayıklayıcıda ve sonuç olarak sizin de kullanıcı olarak kafa karışıklığına yol açma olasılığı vardır.

Daha sınırlı bir hata ayıklama deneyimi sizin için sorun değilse ve optimize edilmiş bir derlemede hata ayıklamak istiyorsanız işlev satır içine alma dışında optimizasyonların çoğu beklendiği gibi çalışır. İleride kalan sorunları çözmeyi planlıyoruz.Ancak şimdilik -O düzeyinde optimizasyonlarla derleme yaparken -fno-inline etiketini kullanarak bu özelliği devre dışı bırakabilirsiniz. Örneğin:

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

Hata ayıklama bilgilerini ayırma

Hata ayıklama bilgileri; kodunuz, tanımlı türler, değişkenler, işlevler, kapsamlar ve konumlar hakkında hata ayıklayıcının işine yarayabilecek her şey hakkında pek çok ayrıntıyı saklar. Bu nedenle, genellikle kodun kendisinden daha büyük olabilir.

WebAssembly modülünün yüklenmesini ve derlemesini hızlandırmak için bu hata ayıklama bilgilerini ayrı bir WebAssembly dosyasına bölmek isteyebilirsiniz. Bunu Emscripten'da yapmak için istediğiniz dosya adına bir -gseparate-dwarf=… işareti iletin:

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

Bu durumda, ana uygulama yalnızca temp.debug.wasm dosya adını depolar. Geliştirici Araçları'nı açtığınızda yardımcı uzantı ise dosyayı bulup yükleyebilir.

Bu özellik, yukarıda açıklanan optimizasyonlarla birleştirildiğinde uygulamanızın neredeyse optimize edilmiş üretim derlemelerini göndermek ve daha sonra yerel bir yan dosyayla bunların hatalarını ayıklamak için bile kullanılabilir. Bu durumda, uzantının yan dosyayı bulmasına yardımcı olmak için depolanan 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 vardı.

Tüm bu yeni entegrasyonlar sayesinde Chrome DevTools yalnızca JavaScript için değil, C ve C++ uygulamaları için de uygulanabilir, güçlü bir hata ayıklayıcı hâline geldi. Bu sayede, çeşitli teknolojilere sahip uygulamaları alıp platformlar arası, paylaşılan bir Web'e taşımayı her zamankinden daha kolay hale getiriyorlar.

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

  • Hata ayıklama deneyimindeki tüm sorunları giderme.
  • Özel tür biçimlendiriciler için destek eklendi.
  • WebAssembly uygulamaları için profil oluşturmayla ilgili iyileştirmeler üzerinde çalışıyoruz.
  • Kullanılmayan kodları bulmayı kolaylaştırmak için kod kapsamı desteği eklendi.
  • Konsol değerlendirmesinde ifadeler için desteği iyileştirme.
  • Daha fazla dil için destek eklendi.
  • …ve daha fazlası!

Bu sırada lütfen mevcut beta sürümünü kendi kodunuzda deneyerek ve bulunan sorunları https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue adresine bildirerek bize yardımcı olun.

Önizleme kanallarını indirme

Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Yeni geliştirilenler veya Beta'yı kullanmayı düşünün. Bu önizleme kanallarıyla Geliştirici Araçları'nın en yeni özelliklerine erişebilir, son teknoloji ürünü web platformu API'lerini test edebilir ve sitenizdeki sorunları kullanıcılarınızdan önce tespit edebilirsiniz!

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

Yayındaki yeni özellikler ve değişiklikler ya da Geliştirici Araçları ile ilgili diğer konular hakkında konuşmak için aşağıdaki seçenekleri kullanın.

  • crbug.com adresinden öneri veya geri bildirim gönderin.
  • Geliştirici Araçları'nda, Diğer seçenekler   Diğer > Yardım > Geliştirici Araçları sorunu bildir'i kullanarak Geliştirici Araçları sorunlarını bildirin.
  • @ChromeDevTools adresine tweet gönderin.
  • Geliştirici Araçları'ndaki YouTube videoları veya Geliştirici Araçları İpuçları YouTube videolarındaki yenilikler hakkındaki görüşlerinizi bizimle paylaşın.