2012-06-11 14 views
15

He estado molestando a TJ en node-canvas sobre un código de velocidad en el que estoy trabajando en una bifurcación de un módulo de nodo que él creó y mantiene.nodejs nativo C++ npm error de memoria del módulo, cairo procesamiento de imágenes

Encontré que Canvas.toBuffer() estaba matando nuestros recursos de canalización y creamos una alternativa que simplemente convertiría Canvas en una Imagen sin pasar por un búfer png/media url. El problema es que El Cairo es una bestia misteriosa, y hay un nivel adicional de preocupación acerca de la memoria asignada dentro de los módulos de nodos, ya que no se puede obtener por GC v madre. He agregado los HandleScopes adecuados a todas las funciones requeridas que acceden a los datos de V8.

Pude probar el método Canvas.loadImage (imagen) miles de veces en la configuración de mac (6.18), así como pruebas independientes en nuestros servidores ubuntu/producción que ejecutan la misma versión de nodo. Pero cuando el código se ejecuta como un proceso/servidor en segundo plano y coordinado por Gearman, estoy obteniendo algunos "segmentos" de memoria/segmentos "interesantes".

Además, tengo problemas para llamar a cualquiera de los métodos de clases definidos en node-canvas que no están en línea dentro de los archivos de encabezado. Como pregunta complementaria ¿Cuál es la mejor manera de crear paquetes de código fuente nativos comunes en los que otros módulos de nodo puedan contar?

He intentado volver a crear el problema y ejecutarlo con gdb, node_g y todos los módulos de nodo construidos con símbolos y marcadores de depuración. Pero el error aparece en una lib fuera del origen para el que puedo obtener un seguimiento de la pila.

como referencia aquí es donde llamo loadImageData y mientras se ejecuta localmente bajo una variedad de condiciones, en nuestro entorno de producción cuando cuidadosamente escondido dentro de un servidor de marcos parece estar causando segfaults (pasó el día de ayer intentando gdb node_g nuestro código del servidor, pero los servidores de bastidor se iniciaron por gearman ... TL; DR no conseguir una traza de la raíz causa pila)

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; 
    } 
} 

y esto es lo que el método actual en imagen se ve como (Eliminé alguna información de registro comentada) 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; 
} 

Cualquier ayuda, sería apreciado pro consejos, ayuda o palabras de aliento.

Originalmente desde google groups

+3

Estamos utilizando también node-canvas en producción y tenemos procesos independientes que son eliminados y reiniciados después de renderizar ~ 200 imágenes (debido a fugas de memoria y fragmentación). Verifique sus extensiones nativas con Valgrind para sobrepasar la memoria: http://valgrind.org/docs/manual/QuickStart.html Esta herramienta debería revelar el motivo de las violaciones de segmentación que ya están en la prueba env. –

+0

gracias teemu, derribamos y construimos entre lotes de imágenes ~ 24-> 100 más o menos. –

+0

@TeemuIkonen Tuve problemas para usar valgrind el día de hoy tanto en osx side como en linux, cualquier consejo para dar sentido a los resultados (congelado inmediatamente en os x, tomé algunas llamadas para mencionar un error en linux) –

Respuesta

1

¡Lo tengo!

Estaba trabajando en otra biblioteca hoy en día que usa cairomm y las superficies descubiertas creadas a partir de los almacenamientos intermedios de datos requieren que esos almacenamientos intermedios vivan mientras la superficie lo haga.

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

"crea una superficie de imagen para los datos de píxeles proporcionados. El búfer de salida debe mantenerse alrededor hasta el cairo_surface_t se destruye o cairo_surface_finish() se llama en la superficie. Se utilizarán los contenidos iniciales de datos como contenido de la imagen inicial, debe borrar explícitamente el búfer, utilizando, por ejemplo, cairo_rectangle() y cairo_fill() si desea borrarlo. "

Introduje una superficie que se creó a partir de un búfer temporal.


solución Fácil en tenedor nodo-lienzo:

Hay una variable miembro llamada _data que puedo asignar un búfer de datos a nivel local malloced a, que vivirá en el tiempo que la superficie Cairo hace.


Solución:

de manera general a copiar una memoria intermedia en una superficie es crear una superficie temporal de la memoria intermedia, a continuación, extraer de la superficie temporal sobre una superficie asignada y dejar Cairo manejarlo es propia memoria.

Se vería algo así con la c api to cairo para implementar.

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

Además estoy teniendo problemas para llamar a cualquiera de los métodos de las clases definidas en el nodo -lona que no están dentro del texto en los archivos de cabecera. Como una pregunta complementaria de ¿Cuál es la mejor manera de crear paquetes comunes de código fuente nativo con los que otros módulos de nodo puedan contar?

Si bien no tengo una respuesta para la falla de problema de memoria/seg que me encontré en nuestro entorno de ensayo. Tengo una respuesta para construir bibliotecas reutilizables con módulos de nodo nativos.

Estoy usando submódulos de git para todos los módulos de nodo nativos independientes, y agregué una definición de preprocesador condicional para cada uno de sus archivos wscript o binding.gyp para especificar si generar o no un objeto compartido módulo .node.

actualizaciónnombres o espacios de nombres de función init Alternativamente únicos pueden rodear la llamada módulo de inicialización (trasladado a esta configuración).

Además, voy a utilizar este nuevo paquete para ayudar en la depuración o reescritura de secciones de código (no puedo pasar demasiado tiempo depurando la utilización de varias bibliotecas remotas).

en wscript o binding.gyp

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

entonces en un archivo de inicialización

#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 

esta manera un módulo nativo nodo sólo se añade a cuando se establece el indicador. De lo contrario, puede vincularse normalmente (como en otro módulo de nodo).