nodejs native c ++ npm模块内存错误,cairoimage processing

我一直在node-canvas上对TJ进行窃听,关于我正在编写和维护的一个节点模块的分支上加速的代码。

我发现Canvas.toBuffer()是要杀死我们的pipe道资源,并创build了一个替代scheme,只需从Canvas转换为图像,而无需通过PNG缓冲区/媒体url。 问题是cairo是一个神秘的野兽,并且在节点模块内部分配的内存还有一个额外的关注点,因为不能被v8版本获得GC'd。 我已经将正确的HandleScopes添加到访问V8数据的所有必需的函数中。

我能够在我的Mac安装程序(6.18)上testingCanvas.loadImage(image)方法数千次,以及在运行相同版本节点的ubuntu / production服务器上进行独立testing。 但是,当代码作为后台进程/服务器运行,并由Gearman协调时,我得到一些“有趣”的内存/段错误。

另外,我无法调用在node-canvas中定义的类中任何不在头文件中内联的方法 。 作为一个侧面问题创build其他节点模块可以依赖的常见本地源代码包的最佳方式是什么?

我已经尝试重新创build问题并使用gdb,node_g和所有使用符号和debugging标志构build的节点模块来运行它。 但是这个错误在源代码之外的一个lib中可以得到一个堆栈跟踪。

这里是我调用loadImageData的地方,虽然它在各种条件下在本地运行,但在我们的生产环境中,当仔细地将其放在一个框架服务器中时,它似乎是导致段错误(昨天花了一天的时间尝试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; } 

任何帮助,专业提示,援助或鼓励的话,将不胜感激。

最初来自谷歌组

得到它了!

我今天正在研究另一个使用cairomm的库,并且发现从数据缓冲区创build的曲面需要这些缓冲区只要表面存在。

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

“为所提供的像素数据创build一个图像表面,输出缓冲区必须一直保留,直到cairo_surface_t被破坏,或者在表面上调用cairo_surface_finish(),数据的初始内容将被用作初始图像内容;清除缓冲区,使用例如cairo_rectangle()和cairo_fill()(如果你想清除)。

我介绍了一个从临时缓冲区创build的表面。


简单的解决scheme在节点帆布叉

有一个名为_data的成员variables,我可以分配一个本地malloced数据缓冲区,只要cairo表面一样,它将一直存在。


解答

将缓冲区复制到表面的一般方法是从缓冲区创build一个临时表面,然后从临时表面绘制到分配的表面上,让开罗pipe理它自己的内存。

这看起来像是与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); 

另外,我无法调用在node-canvas中定义的类中任何不在头文件中内联的方法。 作为一个侧面问题创build其他节点模块可以依赖的常见本地源代码包的最佳方式是什么?

虽然我没有解决我在暂存环境中遇到的内存问题/ seg问题。 我有一个答案来构build与本地节点模块可重用的库。

我为所有独立的本地节点模块使用git子模块,并为其每个wscript或binding.gyp文件添加了一个条件预处理器定义,以指定是否生成共享对象.node模块。

更新 或者,唯一的init函数名称或名称空间可以围绕模块初始化调用(移至此设置)。

此外,我将使用这个新的包来帮助debugging或重写代码段(我不能花太多时间debugging几个远程库的使用)。

在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 

这样一个节点本地模块只能在标志被设置的时候被添加。 否则,它可以正常链接(如在另一个节点模块中)。