2012-06-11 6 views
15

Trafiłem na TJ na node-canvas o przyspieszenie kodu pracuję nad rozwidleniem modułu węzła, który jest autorem i utrzymuje.nodejs natywny C++ błąd pamięci modułu NMM, przetwarzanie obrazu w kairu

Znalazłem Canvas.toBuffer(), aby zabić zasoby naszego potoku i stworzyłem alternatywę, która po prostu zamieniłaby obraz z Canvas na obraz bez przechodzenia przez bufor png/URL mediów. Problem polega na tym, że cairo jest tajemniczą bestią i istnieje dodatkowy poziom troski o pamięć przydzieloną w modułach węzła, aby nie uzyskać GCd od matki v8. Dodałem odpowiednie HandleScopes do wszystkich wymaganych funkcji, które uzyskują dostęp do danych V8.

Byłem w stanie przetestować metodę Canvas.loadImage (obraz) tysięcy razy na mojej konfiguracji mac (6.18), a także samodzielne testy na naszych serwerach ubuntu/produkcyjnych z tą samą wersją węzła. Ale kiedy kod jest uruchamiany jako proces w tle/serwer i koordynowany przez Gearmana, otrzymuję kilka "interesujących" pamięci/segfaultów.

Ponadto mam problemy z wywołaniem dowolnej z metod klas zdefiniowanych w kanale węzłowym, które nie są wstawiane wewnątrz plików nagłówkowych. Jako pytanie boczne: Jaki jest najlepszy sposób tworzenia popularnych pakietów kodu źródłowego, na których mogą polegać inne moduły węzłów?

Próbowałem odtworzyć problem i uruchomić go za pomocą gdb, node_g i wszystkich modułów węzła zbudowanych z symboli i flag debugowania. Ale błąd pojawia się w bibliotece poza źródłem, z którego mogę pobrać ślad stosu.

dla odniesienia tutaj, gdzie wywołuję loadImageData i podczas gdy działa lokalnie w różnych warunkach, w naszym środowisku produkcyjnym, gdy ostrożnie schowany w serwerze ramek wydaje się, że powoduje on segfaults (spędził dzień wczoraj próbując gdb node_g nasz Kod serwer ale serwery ramy są skopany przez gearman ... TL; DR nie dostać ślad stosu przyczyna)

https://github.com/victusfate/node-canvas/blob/master/src/Canvas.cc#L497

Handle<Value> 
Canvas::LoadImage(const Arguments &args) { 
    HandleScope scope; 
    LogStream mout(LOG_DEBUG,"node-canvas.paint.ccode.Canvas.LoadImage");  
    mout << "Canvas::LoadImage top " << LogStream::endl; 

    Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This()); 
    if (args.Length() < 1) { 
    mout << "Canvas::LoadImage Error requires one argument of Image type " << LogStream::endl; 
    return ThrowException(Exception::TypeError(String::New("Canvas::LoadImage requires one argument of Image type"))); 
    } 

    Local<Object> obj = args[0]->ToObject(); 
    Image *img = ObjectWrap::Unwrap<Image>(obj); 
    canvas->loadImageData(img); 
    return Undefined(); 
} 

void Canvas::loadImageData(Image *img) { 
    LogStream mout(LOG_DEBUG,"node-canvas.paint.ccode.Canvas.loadImageData");  
    if (this->isPDF()) { 
    mout << "Canvas::loadImageData pdf canvas type " << LogStream::endl; 
    cairo_surface_finish(this->surface()); 
    closure_t *closure = (closure_t *) this->closure(); 

    int w = cairo_image_surface_get_width(this->surface()); 
    int h = cairo_image_surface_get_height(this->surface()); 

    img->loadFromDataBuffer(closure->data,w,h); 
    mout << "Canvas::loadImageData pdf type, finished loading image" << LogStream::endl; 
    } 
    else { 
    mout << "Canvas::loadImageData data canvas type " << LogStream::endl; 
    cairo_surface_flush(this->surface()); 
    int w = cairo_image_surface_get_width(this->surface()); 
    int h = cairo_image_surface_get_height(this->surface()); 

    img->loadFromDataBuffer(cairo_image_surface_get_data(this->surface()),w,h); 
    mout << "Canvas::loadImageData image type, finished loading image" << LogStream::endl; 
    } 
} 

i oto co obecna metoda wygląda Image (Usunąłem niektóre skomentowane informacje dotyczące logowania) https://github.com/victusfate/node-canvas/blob/master/src/Image.cc#L240

/* 
* load from data buffer width*height*4 bytes 
*/ 
cairo_status_t 
Image::loadFromDataBuffer(uint8_t *buf, int width, int height) { 
    this->clearData(); 
    int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); // 4*width + ? 
    this->_surface = cairo_image_surface_create_for_data(buf,CAIRO_FORMAT_ARGB32,width,height,stride); 
    this->data_mode = DATA_IMAGE; 
    this->loaded(); 
    cairo_status_t status = cairo_surface_status(_surface); 
    if (status) return status; 
    return CAIRO_STATUS_SUCCESS; 
} 

Każda pomoc, porady, pomoc lub słowa zachęty byłyby mile widziane.

Pochodzi z google groups

+3

Używamy również płótna węzłowego w produkcji i mamy procesy autonomiczne, które są zabijane i restartowane po wykonaniu ~ 200 obrazów (z powodu wycieków pamięci i fragmentacji). Sprawdź swoje natywne rozszerzenia z Valgrind dla przekroczenia pamięci: http://valgrind.org/docs/manual/QuickStart.html To narzędzie powinno ujawnić powód naruszenia segmentacji już w testowaniu env. –

+0

dzięki teemu, zbieramy i budujemy między partiami obrazów ~ 24-> 100 lub więcej. –

+0

@TeemuIkonen Miałem problemy z wcześniejszym korzystaniem z valgrindu zarówno na stronie osx jak i linuxie, wszelkie wskazówki dotyczące sensownego wyniku (zamrożone natychmiast na os x, wykonałem kilka połączeń, by wspomnieć o błędzie po stronie Linux) –

Odpowiedz

1

Gotowy!

Pracowałem nad inną biblioteką dzisiaj, która wykorzystuje cairomm, a odkryte powierzchnie utworzone z buforów danych wymagają, aby te bufory żyły tak długo, jak powierzchnia.

http://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create-for-data

„tworzy powierzchnię obrazu dla określonych danych pikselowych. Bufor wyjściowy musi być około aż cairo_surface_t jest zniszczony lub cairo_surface_finish() jest wywoływana na powierzchni. Stosowane będą początkowa zawartość danych jako początkową zawartość obrazu, musisz jawnie wyczyścić bufor, używając, na przykład, cairo_rectangle() i cairo_fill(), jeśli chcesz go wyczyścić. "

Wprowadziłem powierzchnię, która została utworzona z tymczasowego bufora.


proste rozwiązanie w węzeł płótnie widelcem:

Jest zmienna członek nazywa _data który mogę przypisać lokalnie malloced bufor danych do pociągów, które będą żyć tak długo, jak powierzchnia Kair robi.


Rozwiązanie:

ogólny sposób skopiować bufor na powierzchni jest stworzenie tymczasowego powierzchnię z bufora, a następnie wyciągnąć z tymczasowej nawierzchni na wydzielonej powierzchni i niech Kair zarządzać to własna pamięć.

Wyglądałoby to mniej więcej tak, jak przy pomocy api do cairo.

cairo_surface_t *pTmp = cairo_image_surface_create_for_data (
    data 
, CAIRO_FORMAT_ARGB32 
, width 
, height 
, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width)); 

_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32 
, width 
, height); 

cairo_t *cr = cairo_create (_surface); 
cairo_set_source_surface (cr, pTmp, x, y); 
cairo_paint (cr); 
1

Dodatkowo mam problemy wzywającą którejkolwiek z metod określonych w klasach płótnie węzłów, które nie są inline w plikach nagłówkowych. Jako boczne pytanie Jaki jest najlepszy sposób tworzenia popularnych pakietów macierzystych o kodzie , na których mogą polegać inne moduły węzłów?

Chociaż nie mam odpowiedzi na pytanie o problem z pamięcią/skasowaniem, na które natrafiłem w naszym środowisku pomostowym. Mam odpowiedź na budowę bibliotek wielokrotnego użytku z natywnymi modułami węzłów.

Używam submodułów git dla wszystkich niezależnych modułów natywnego węzła i dodano warunkową definicję preprocesora dla każdego z ich plików wscript lub binding.gyp w celu określenia, czy wygenerować wspólny obiekt .node.

aktualizacjiAlternatywnie unikalne nazwy funkcji init lub nazw może otoczyć wywołanie inicjalizacji modułu (przeniesiony do tej konfiguracji).

Ponadto będę używać tego nowego pakietu do pomocy w debugowaniu lub przepisywaniu sekcji kodu (nie mogę spędzać zbyt wiele czasu na debugowaniu wykorzystania kilku zdalnych bibliotek).

w wscript lub binding.gyp

flags = ['-D_NAME_NODE_MODULE', '-O3', '-Wall', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE_SOURCE', '-msse2'] 

następnie w pliku inicjującym

#ifdef _NAME_NODE_MODULE 

extern "C" { 
    static void init(Handle<Object> target) { 
    HandleScope scope; 
    NODE_SET_METHOD(target, "someFunction", someFunction); 
    } 

    NODE_MODULE(moduleName, init); 
} 

#endif 

ten sposób węzeł moduł rodzimy dodaje tylko, gdy flaga jest ustawiona. W przeciwnym razie może być podłączony do normalnie (jak w innym module węzła).

Powiązane problemy