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

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

Chrome Dev Summit 2020'de web'deki Chrome'un WebAssembly uygulamaları için hata ayıklama desteğini ilk kez tanıttık. O zamandan beri ekip, geliştirici deneyimini büyük ve hatta çok büyük uygulamalara ölçeklendirmek için büyük miktarda enerji harcadı. Bu yayında, farklı araçlara eklediğimiz (veya yaptığımız) düğmeleri ve bunları nasıl kullanacağınızı göstereceğiz!

Ölçeklenebilir hata ayıklama

2020 gönderimizde kaldığımız yerden devam edelim. İşte o zaman incelediğimiz örnek:

#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 örnektir ve büyük bir uygulamada karşılaşacağınız gerçek sorunların hiçbirini büyük olasılıkla görmezsiniz. Yine de size yeni özelliklerin neler olduğunu gösterebiliriz. Kurulumu hızlı ve kolaydır. Üstelik kendiniz de deneyebilirsiniz.

Son yayında, bu örneğin nasıl derleneceği ve hata ayıklanacağı anlatılmıştır. Bunu tekrar yapalım, ancak //performance// bölümüne de göz atalım:

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

Bu komut, 3 MB'lık bir wasm ikili programı oluşturur. Tahmin edebileceğiniz gibi, bunların büyük kısmı hata ayıklama bilgileridir. Bunu llvm-objdump aracını [1] kullanarak 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 mevcuttur. İkili programın hata ayıklama bilgilerimizi barındırdığı yer burasıdır. 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 işlemini gerçekleştirirsek makinemizde çalışmasının yaklaşık 1, 5 saniye sürdüğünü görürüz. Bu sayılar küçük bir referans noktası olabilir, ancak o kadar küçüktür ki kimse bu rakamlara çarpmayacaktır. Ancak gerçek uygulamalarda, hata ayıklama ikili programı GB cinsinden bir boyuta kolayca ulaşabilir ve birkaç dakika içinde oluşturabilir.

Binaryen'i atlama

Emscripten ile bir wasm uygulaması oluştururken, uygulamanın son derleme adımlarından biri Binaryen optimize ediciyi çalıştırmaktır. Binaryen, WebAssembly gibi ikili programları hem optimize eden hem de yasal hâle getiren bir derleyici araç setidir. Binaryen'in derlemenin bir parçası olarak çalıştırılması oldukça pahalıdır, ancak bunun yalnızca belirli koşullarda yapılması gerekir. Hata ayıklama derlemelerinde Binaryen kartlarına gerek kalmazsa derleme süresini önemli ölçüde kısaltabiliriz. En yaygın olarak kullanılan İkili Geçiş geçişi, 64 bit tam sayı değerleri içeren işlev imzalarını yasallaştırmak içindir. -sWASM_BIGINT kullanarak 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 attık. Binaryen'in çalıştığı ve ikili dosyayı beklenmedik bir şekilde yeniden yazdığı durumların tespit edilmesine yardımcı olur. Bu şekilde, hızlı yoldan ilerleyeceğimizden 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 saniyenin biraz altında çalıştığından yarım saniye daha hızlıdır!

Gelişmiş ince ayarlar

Giriş dosyası taraması atlanıyor

Normalde bir Emscripten projesi bağlanırken 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 şekilde ek tarama yapılması bağlantı süresini önemli ölçüde uzatabilir.

Bunun yerine, emcc öğesine JavaScript işlevlerinin tüm olası yerel bağımlılıklarını dahil etmesini bildiren -sREVERSE_DEPS=all ile çalıştırılabilir. Bunun küçük bir kod boyutu ek yüküne neden olur ancak bağlantı sürelerini kısaltabilir ve hata ayıklama derlemelerinde faydalı olabilir.

Örneğimiz kadar küçük bir proje için bu durum hiçbir fark yaratmaz ancak projenizde yüzlerce, hatta binlerce nesne dosyası varsa bağlantı süreleri anlamlı bir şekilde kısaltılabilir.

“Ad” bölümünü kaldırma

Büyük projelerde, özellikle de çok fazla C++ şablonu kullananlar için WebAssembly “adı” bölümü çok büyük olabilir. Örneğimizde bu, toplam dosya boyutunun küçük bir kısmı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ün kaldırılması 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.

Fisyonda hata ayıklama

Çok sayıda hata ayıklama verisi içeren ikili programlar, yalnızca derleme süresini değil, hata ayıklama süresini de 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 verebilmesi için, verilere yönelik bir dizin oluşturması gerekir.

Hata ayıklama fisyonu, bir ikili programın hata ayıklama bilgilerini iki bölüme ayırmamızı sağlar: biri ikili programda kalan, diğeri ise DWARF nesnesi (.dwo) adlı ayrı bir dosyada bulunan parça. -gsplit-dwarf işareti Emscripten'a geçirilerek 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 ve son olarak hem hata ayıklama verileri hem de hata ayıklama fisyonuyla derlenerek hangi dosyaların oluşturulduğu gösterilmektedir.

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

DWARF verileri bölünürken hata ayıklama verilerinin bir kısmı ikili programla birlikte tutulurken büyük bölümü 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 büyüktür ve birden fazla dosya içerir. Hata ayıklama fisyonu, 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ölme hata ayıklama bilgilerini yükleyebilmesi için hepsini aşağıdaki gibi DWARF paketi (.dwp) adı verilen bir pakette toplamamız gerekir:

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

dwo dosyalarını DWARF paketinde gruplandırma

DWARF paketini tek tek nesnelerden oluşturmak, yalnızca bir ekstra dosya sunmanızın gerekeceği avantajıdır! Şu anda, gelecekteki bir sürümde tüm nesneleri tek tek yüklemek için çalışıyoruz.

DWARF 5'in içeriği nedir?

Yukarıdaki emcc komutuna başka bir bayrak yerleştirdiğimizi fark etmiş olabilirsiniz: -gdwarf-5. DWARF simgelerinin şu anda varsayılan olmayan sürüm 5'ini etkinleştirmek, hata ayıklama işlemini daha hızlı başlatmamıza yardımcı olacak başka bir ipucudur. Bu algoritma ile belirli bilgiler, varsayılan sürüm 4'ün dışarıda bıraktığı ana ikili programda depolanır. Özellikle, kaynak dosyaların tamamını yalnızca ana ikili programdan belirleyebiliriz. Bu, hata ayıklayıcının, tam simge verilerini yüklemeden ve ayrıştırmadan, kaynak ağacının tamamını göstermek ve ayrılma noktalarını ayarlamak gibi temel işlemleri yapmasına olanak tanır. Bu, bölme simgeleriyle hata ayıklamayı çok daha hızlı hale getirir. Böylece, her zaman -gsplit-dwarf ve -gdwarf-5 komut satırı işaretlerini birlikte kullanırız!

DWARF5 hata ayıklama biçimiyle başka bir faydalı özelliğe de erişim elde ederiz. Hata ayıklama verilerinde, -gpubnames işareti iletildiğinde oluşturulacak bir ad dizini sunar:

$ 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 sembol aramaları genellikle bir varlığı ada göre arayarak (ör. değişken veya tür ararken) gerçekleşir. Ad dizini, doğrudan söz konusu adı tanımlayan derleme birimine işaret ederek bu aramayı hızlandırır. Ad dizini olmadan, aradığımız adlandırılmış varlığı tanımlayan doğru derleme birimini bulmak için hata ayıklama verilerinin tamamında kapsamlı bir arama yapılması gerekir.

Meraklılar 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, hata ayıklama bilgilerine sahip olduğumuz "Birimleri derle" (kabaca kaynak dosyalar) ile ilgili bir genel bakış sağlar. Bu örnekte yalnızca mandelbrot.cc için hata ayıklama bilgileri mevcuttur. Verilen genel bilgiler bize bir iskelet birimimiz olduğunu bildirecektir. Bu, 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 bakabilirsiniz. Örneğin, wasm bayt kodunun C++ satırlarıyla eşlenmesini gösteren satır tablosu (llvm-dwarfdump -debug-line kullanmayı deneyin).

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 fisyonu kullanmanın avantajı nedir?

Büyük uygulamalarla çalışan bir hata ayıklama bilgilerini bölmenin birçok avantajı vardır:

  1. Daha hızlı bağlama: Bağlayıcının artık hata ayıklama bilgilerinin tamamını ayrıştırmasına gerek yoktur. Bağlayıcıların genellikle ikili programdaki DWARF verilerinin tamamını ayrıştırması gerekir. Bağlayıcılar, hata ayıklama bilgilerinin büyük bölümlerini ayrı dosyalar halinde çıkararak daha küçük ikili programlarla ilgilenir. Bu da daha kısa bağlantı sürelerine neden olur (ö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 (ör. wasm-to-C++ dosyalarının satır eşlemesiyle ilgili istekler) ek hata ayıklama verilerini incelememiz gerekmez. Bu bize zaman kazandırır ve ek hata ayıklama verilerini yükleyip ayrıştırmamıza gerek kalmaz.

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

Ö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.