Nodejs native С++ ошибка памяти модуля npm, обработка изображений cairo

Я искал TJ на node-canvas об ускорении кода, над которым я работаю в вилке node модуль, который он создал и поддерживает.

Я обнаружил, что Canvas.toBuffer() убивает наши ресурсы конвейера и создает альтернативу, которая просто преобразует из Canvas в изображение без перехода через буфер png/media url. Проблема в том, что каир - загадочный зверь, и существует дополнительный уровень озабоченности памятью, выделенной в модулях node, а не для получения GC'd матерью v8. Я добавил соответствующие дескрипторы HandleScopes ко всем требуемым функциям, которые получают доступ к данным V8.

Мне удалось проверить метод Canvas.loadImage(изображение) тысячи раз на моей настройке mac (6.18), а также автономные тесты на наших серверах ubuntu/production с той же версией node. Но когда код запускается как фоновый процесс/сервер и координируется Gearman, я получаю некоторые "интересные" памяти /segfaults.

Кроме того, мне не удается вызвать какой-либо из методов классов, определенных в node -canvas, которые не являются встроенными в файлы заголовков. В качестве побочного вопроса Каков наилучший способ создания общих пакетов исходных кодов, на которые могут рассчитывать другие модули node?

Я попытался воссоздать проблему и запустить ее с помощью gdb, node_g и всех модулей node, построенных с символами и флагами отладки. Но ошибка возникает в lib за пределами источника, я могу получить трассировку стека.

для справки здесь, где я вызываю loadImageData и в то время как он выполняется локально в различных условиях, в нашей производственной среде, когда он тщательно убирается в сервер кадров, он, похоже, вызывает segfaults (вчера провел день, пытаясь gdb node_g наш сервер кода, но серверы кадров запускаются с помощью редуктора... TL; DR не получил трассировку стека корневых причин)

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

и здесь, как выглядит текущий метод в Image (я удалил некоторую прокомментированную информацию о регистрации) 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;
}

Любая помощь, советы, помощь или слова поощрения будут оценены.

Первоначально из групп google

Ответы

Ответ 1

Получил это!

Я работал над другой библиотекой сегодня, которая использует cairomm, и обнаруженные поверхности, созданные из буферов данных, требуют, чтобы эти буферы жили так долго, как это делает поверхность.

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

"Создает поверхность изображения для предоставленных данных пикселя. Выходной буфер должен храниться до тех пор, пока не будет уничтожен cairo_surface_t, или на поверхности будет вызываться cairo_surface_finish(). Исходное содержимое данных будет использоваться в качестве исходного содержимого изображения; вы должны явно очистить буфер, используя, например, cairo_rectangle() и cairo_fill(), если вы хотите его очистить."

Я представил поверхность, созданную из временного буфера.


Простое решение в node -canvas fork:

Здесь есть переменная-член, называемая _data, для которой я могу назначить локально-распределенный буфер данных, который будет работать до тех пор, пока поверхность каира не будет выполнена.


Решение:

Общий способ копирования буфера на поверхность - создать временную поверхность из буфера, затем извлечь из временной поверхности на выделенную поверхность и позволить каиру управлять собственной памятью.

Он будет выглядеть примерно так, с помощью c api для каира.

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

Ответ 2

Кроме того, у меня возникли проблемы с вызовом любого из методов классов определенные в node -canvas, которые не являются встроенными в файлы заголовков. Как side question Какой лучший способ создать общий исходный код пакеты, на которые могут рассчитывать другие модули node?

Пока у меня нет ответа на проблему с памятью /seg, я столкнулся с ней в нашей промежуточной среде. У меня есть ответ на создание многоразовых библиотек с собственными модулями node.

Я использую подмодули git для всех независимых встроенных модулей node и добавил определение условного препроцессора для каждого из своих файлов wscript или binding.gyp, чтобы указать, следует ли создавать модуль общего доступа .node.

update. Альтернативно, уникальные имена функций инициализации или пространства имен могут окружать вызов инициализации модуля (перемещаются в эту настройку).

Кроме того, я буду использовать этот новый пакет, чтобы помочь в отладке или переделке разделов кода (я не могу тратить слишком много времени на отладку нескольких удаленных библиотек).

в wscript или binding.gyp

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

то в файле инициализации

#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

Таким образом, родной модуль node добавляется только тогда, когда установлен флаг. В противном случае его можно связать с обычным (как в другом модуле node).