WebAssembly Hatalarını Daha Hızlı Ayıklama

Philip Pfaffe
Kim-Anh Tran
Kim-Anh Tran
Eric Leese
Sam Clegg

Chrome Geliştirici Zirvesi 2020'de, Chrome'un WebAssembly uygulamaları için hata ayıklama desteğini web'de ilk kez tanıttık. Ekip o zamandan beri, geliştirici deneyimini büyük ve hatta devasa uygulamalarda ölçeklendirmek için çok fazla enerji yatırımı yapmıştır. Bu gönderide, farklı araçlara eklediğimiz (veya çalıştırdığımız) düğmeleri ve bunları nasıl kullanacağınızı göstereceğiz.

Ölçeklenebilir hata ayıklama

2020 yayınımızda kaldığımız yerden devam edelim. O zamanlar karşılaştığımız örneği aşağıda bulabilirsiniz:

#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 hâlâ oldukça küçük bir örnek ve büyük bir uygulamada karşılaşacağınız gerçek sorunların hiçbirini büyük olasılıkla görmeyeceksiniz, ancak yine de size yeni özelliklerin neler olduğunu gösterebiliriz. Hızlı ve kolay bir şekilde ayarlayıp kendiniz deneyebilirsiniz!

Son gönderide, bu örneğin nasıl derlenip hata ayıklanacağını açıkladık. Bunu tekrar yapalım ancak //performance//.

$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH

Bu komut, 3 MB'lık wasm ikili programını üretir. Bu işlerin büyük kısmı da tahmin edebileceğiniz gibi hata ayıklama bilgileridir. Bunu llvm-objdump aracı [1] ile doğrulayabilirsiniz. Örneğin:

$ 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

Bu çıkış, oluşturulan wasm dosyasındaki tüm bölümleri gösterir. Bunların çoğu standart WebAssembly bölümleridir, ancak adı .debug_ ile başlayan birkaç özel bölüm de vardır. İkili program, hata ayıklama bilgilerimizi burada içerir. Tüm boyutları topladığımızda, hata ayıklama bilgilerinin 3 MB'lık dosyamızın yaklaşık 2,3 MB'ını oluşturduğunu görürüz. emcc komutunu da time yaparsak makinemizde çalışmasının yaklaşık 1, 5 saniye sürdüğünü görürüz. Bu rakamlar iyi bir başlangıç noktasıdır ancak o kadar küçüktür ki büyük olasılıkla kimse bunlara dikkat çekemez. Ancak gerçek uygulamalarda, hata ayıklama ikili programı kolayca GB cinsinden bir boyuta ulaşabilir ve derlemesi birkaç dakika sürer.

Binaryen atlanıyor

Emscripten ile bir wasm uygulaması oluştururken, uygulamanın son derleme adımlarından biri Binaryen optimize ediciyi çalıştırmaktır. Binaryen, WebAssembly (benzer) ikili programlarını optimize eden ve yasal hâle getiren bir derleyici araç setidir. Binaryen'i derlemenin bir parçası olarak çalıştırmak oldukça pahalıdır ancak yalnızca belirli koşullarda gereklidir. Hata ayıklama derlemelerinde, Binaryen geçişlerine ihtiyaç duymadığımız takdirde derleme süresini önemli ölçüde hızlandırabiliriz. En yaygın olarak gereken Binaryen geçişi, 64 bit tam sayı değerleri içeren işlev imzalarını yasallaştırmak içindir. -sWASM_BIGINT ile WebAssembly BigInt entegrasyonunu etkinleştirerek bunu önleyebiliriz.

$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK

İyi bir önlem olarak -sERROR_ON_WASM_CHANGES_AFTER_LINK işaretini belirledik. Binaryen'in ne zaman çalıştığını ve ikili programı beklenmedik bir şekilde yeniden yazdığını algılamaya yardımcı olur. Bu sayede hızlı yolda olduğumuzdan emin olabiliriz.

Örneğimiz oldukça küçük olsa da Binaryen'i atlamanın etkisini görebiliriz. time adlı kaynağa göre bu komut 1 saniyeden biraz daha kısa sürede çalışır. Yani öncesine göre yarım saniye daha hızlıdır.

Gelişmiş ince ayarlar

Giriş dosyası tarama atlanıyor

Normalde bir Emscripten projesini bağlarken emcc, tüm giriş nesnesi dosyalarını ve kitaplıklarını tarar. Bunu, programınızdaki JavaScript kitaplık işlevleri ile yerel simgeler arasında kesin bağımlılıklar uygulamak için yapar. Daha büyük projelerde giriş dosyalarının (llvm-nm kullanılarak) bu ek taraması, bağlantı süresini önemli ölçüde artırabilir.

Bunun yerine, emcc parametresine JavaScript işlevlerinin tüm olası yerel bağımlılıklarını dahil etmesini bildiren -sREVERSE_DEPS=all ile çalıştırılabilir. Bu, küçük bir kod boyutu ek yüküne sahiptir ancak bağlantı sürelerini hızlandırabilir ve hata ayıklama derlemelerinde yararlı olabilir.

Örneğimiz kadar küçük bir projede bu durum gerçek bir fark yaratmaz. Ancak projenizde yüzlerce, hatta binlerce nesne dosyası varsa bağlantı sürelerini anlamlı şekilde iyileştirebilirsiniz.

"Ad" bölümünü çıkarma

Büyük projelerde, özellikle de C++ şablonu çok fazla kullanılan projelerde, WebAssembly "name" bölümü çok büyük olabilir. Örneğimizde bu, toplam dosya boyutunun yalnızca küçük bir parçasıdır (yukarıdaki llvm-objdump çıkışına bakın) ancak bazı durumlarda çok önemli olabilir. Uygulamanızın "ad" bölümü çok büyükse ve cüce hata ayıklama bilgileri hata ayıklama ihtiyaçlarınız için yeterliyse "ad" bölümünü çıkarmak avantajlı olabilir:

$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm

Bu işlem, DWARF hata ayıklama bölümlerini korurken WebAssembly "name" bölümünü kaldırır.

Hata ayıklama fisyonu

Çok sayıda hata ayıklama verisi içeren ikili programlar hem derleme süresini hem de hata ayıklama süresini etkiler. Hata ayıklayıcının verileri yüklemesi ve "X yerel değişkeninin türü nedir?" gibi sorgulara hızlı bir şekilde yanıt verebilmek için bir dizin oluşturması gerekir.

Hata ayıklama fisyonu, bir ikili programın hata ayıklama bilgilerini iki bölüme ayırmamıza olanak tanır: biri ikili dosyada kalan, diğeri ise ayrı bir DWARF nesnesi (.dwo) dosyasında bulunan. -gsplit-dwarf işaretini Emscripten'e ileterek etkinleştirilebilir:

$ 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

Aşağıda, farklı komutları ve hata ayıklama verileri olmadan, hata ayıklama verileriyle derleyerek ve son olarak hem hata ayıklama verileri hem de hata ayıklama hatalarıyla derleyerek hangi dosyaların oluşturulduğunu gösteriyoruz.

farklı komutlar ve hangi dosyaların oluşturulduğu

DWARF verileri bölünürken hata ayıklama verilerinin bir kısmı ikili programla birlikte yer alır, büyük kısmı ise mandelbrot.dwo dosyasına yerleştirilir (yukarıda gösterildiği gibi).

mandelbrot için yalnızca bir kaynak dosyamız var, ancak genellikle projeler bundan daha büyük ve birden fazla dosya içeriyor. Hata ayıklama fizyonları, her biri için bir .dwo dosyası oluşturur. Hata ayıklayıcının mevcut beta sürümünün (0.1.6.1615) bu bölünmüş hata ayıklama bilgilerini yükleyebilmesi için bunların tümünü aşağıdaki gibi DWARF adı verilen bir pakette (.dwp) toplamamız gerekir:

$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp

dwo dosyalarını DWARF paketinde gruplandırma

Tek tek nesnelerden DWARF paketi oluşturmanın avantajı, fazladan bir dosya sunmanızdır. Şu anda ilerideki bir sürümde tüm nesneleri tek tek yüklemek için de çalışıyoruz.

DWARF 5 nedir?

Fark etmiş olabileceğiniz gibi, yukarıdaki emcc komutuna bir işaret daha, -gdwarf-5. Şu anda varsayılan olmayan DWARF sembollerinin 5. sürümünü etkinleştirmek, hata ayıklamayı daha hızlı başlatmamıza yardımcı olacak başka bir yöntemdir. Bu özellikle birlikte belirli bilgiler, varsayılan sürüm 4'ün dışarıda bıraktığı ana ikili programda depolanır. Özellikle, kaynak dosyalar grubunun tamamını yalnızca ana ikili programdan belirleyebiliriz. Bu, hata ayıklayıcının, tüm simge verilerini yükleyip ayrıştırmadan kaynak ağacının tamamını göstermek ve kesme noktalarını ayarlamak gibi temel işlemleri yapmasına olanak tanır. Bu da bölünmüş sembollerle hata ayıklamayı çok daha hızlı hale getirir. Bu sayede -gsplit-dwarf ve -gdwarf-5 komut satırı işaretlerini her zaman birlikte kullanırız.

DWARF5 hata ayıklama biçimi sayesinde başka bir faydalı özelliğe de erişebilirsiniz. -gpubnames işareti iletilirken oluşturulacak hata ayıklama verilerinde bir ad dizini bulunur:

$ 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

Hata ayıklama oturumu sırasında simge aramaları genellikle bir varlığı ada göre arayarak (ör. bir değişken veya tür ararken) gerçekleşir. Ad dizini, doğrudan söz konusu adı tanımlayan derleme birimini işaret ederek bu aramayı hızlandırır. Ad dizini olmadığında, aradığımız adlandırılmış öğeyi tanımlayan doğru derleme birimini bulmak için tüm hata ayıklama verilerinin kapsamlı bir şekilde aranması gerekir.

Meraklı kişiler için: Hata ayıklama verilerine göz atma

DWARF verilerine göz atmak için llvm-dwarfdump kullanabilirsiniz. Bir deneme yapalım:

llvm-dwarfdump mandelbrot.wasm

Bu sayede, hata ayıklama bilgilerine sahip olduğumuz "birimleri derle" (genel anlamda kaynak dosyalar) hakkında genel bilgi edinebilirsiniz. Bu örnekte, yalnızca mandelbrot.cc için hata ayıklama bilgileri mevcuttur. Genel bilgiler bize bir iskelet birimimiz olduğunu bildirir. Bu, söz konusu dosyada eksik veriler olduğu ve kalan hata ayıklama bilgilerini içeren ayrı bir .dwo dosyası olduğu anlamına gelir:

mandelbrot.wasm ve hata ayıklama bilgileri

Bu dosyadaki diğer tablolara da göz atabilirsiniz. Örneğin, yer alır.llvm-dwarfdump -debug-line

Ayrı .dwo dosyasında bulunan hata ayıklama bilgilerine de göz atabiliriz:

llvm-dwarfdump mandelbrot.dwo

mandelbrot.wasm ve hata ayıklama bilgileri

Özet: Hata ayıklama çiplerini kullanmanın avantajı nedir?

Büyük uygulamalarla çalışan hata ayıklama bilgilerini bölmenin çeşitli avantajları vardır:

  1. Daha hızlı bağlantı: Bağlayıcının artık hata ayıklama bilgilerinin tamamını ayrıştırması gerekmez. Bağlayıcıların genellikle ikili dosyadaki DWARF verilerinin tamamını ayrıştırması gerekir. Bağlayıcılar, hata ayıklama bilgilerinin büyük bir bölümünü ayrı dosyalara ayırarak daha küçük ikili dosyalarla çalışır. Bu da bağlantı sürelerinin daha hızlı olmasını sağlar (özellikle büyük uygulamalar için geçerlidir).

  2. Daha hızlı hata ayıklama: Hata ayıklayıcı, bazı simge aramaları için .dwo/.dwp dosyalarındaki ek simgeleri ayrıştırmayı atlayabilir. Bazı aramalarda (Wasm-C++ dosyalarının satır eşlemesi istekleri gibi) için ek hata ayıklama verilerine bakmamız gerekmez. Bu sayede, ek hata ayıklama verilerini yükleyip ayrıştırmak zorunda kalmadan zaman kazanabiliyoruz.

1: Sisteminizde llvm-objdump uygulamasının son sürümü yoksa ve emsdk kullanıyorsanız bunu emsdk/upstream/bin dizininde bulabilirsiniz.

Önizleme kanallarını indirme

Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Dev veya Beta'yı 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 özellikleri ve değişiklikleri ya da Geliştirici Araçları ile ilgili diğer her şeyi tartışmak için aşağıdaki seçenekleri kullanın.

  • Öneri veya geri bildirimlerinizi crbug.com adresinden bize iletebilirsiniz.
  • Geliştirici Araçları sorunlarını bildirmek için Diğer seçenekler'i Daha fazla > Yardım > Geliştirici Araçları'nda Geliştirici Araçları ile ilgili sorunları bildirin.
  • @ChromeDevTools adresinden tweet atabilirsiniz.
  • Geliştirici Araçları YouTube videoları veya Geliştirici Araçları ipuçları YouTube videolarına yorum yazın.