Matlab Engine - passing strings giving invalid characters
Ältere Kommentare anzeigen
Hi,
I am writing an Engine application in C, which will be standalone eventually, but I would also like to call the routines via a mex interface if possible. So, at the moment I have a rather convoluted process of a mex file creating a separate instance of the Matlab engine, which I want to talk to (eventually this will be multiple engines via MPI). I'm doing this using Matlab coder.
So, I have the test code below which is trying to open a new engine from a mex file and talk to it...
function outp = engTest()
%Build with.... codegen engTest -o engTest_mex
params = [1, 2, 3, 4, 5, 6, 7, 8];
paramsLen = 8;
funName = 'total = debugMfile(params,bulk_in,bulk_out,contrast);';
pathCall = 'cd(''/home/arwel/Documents/coding/cevalTests/mlEng'');';
bulkIn = 2.073e-6;
bulkOut = 6.35e-6;
contrast = 1.0;
path = '/home/arwel/Documents/coding/cevalTests/mlEng';
incPath1 = '/usr/local/MATLAB/R2015b/extern/include';
incPath2 = '/usr/include/openmpi';
linkPath1 = '/usr/local/MATLAB/R2015b/bin/glnxa64';
linkFile1 = 'libeng.so';
linkFile2 = 'libmx.so';
source1 = 'matlabCallFun.c';
source2 = 'matlabEngine_demo.h';
libPriority = '';
libPreCompiled = true;
libLinkOnly = true;
%libName = 'LinkObj.lib';
%libPath = 'c:\Link_Objects';
%coder.updateBuildInfo('addLinkObjects', libName, libPath, ...
%libPriority, libPreCompiled, libLinkOnly);
%coder.updateBuildInfo('addSourceFiles',filename)
coder.cinclude(source2);
coder.updateBuildInfo('addSourceFiles',source1);
coder.updateBuildInfo('addSourcePaths',path);
coder.updateBuildInfo('addIncludePaths',incPath1);
coder.updateBuildInfo('addIncludePaths',incPath2);
coder.updateBuildInfo('addLinkObjects',linkFile1,linkPath1,libPriority,libPreCompiled,libLinkOnly);
coder.updateBuildInfo('addLinkObjects',linkFile2,linkPath1,libPriority,libPreCompiled,libLinkOnly);
outp = zeros(2,3);
%matlabCallFun(params, paramsLen, funName, pathCall, bulkIn, bulkOut, contrast, s);
coder.ceval('matlabCallFun', params, paramsLen, funName, pathCall, bulkIn, bulkOut, contrast, coder.wref(outp));
end
and matlabCallFun.c as follows.....
/*
* matlabCallFun.c
*
* Created on: 21 Jul 2017
* Author: arwel
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "engine.h"
void matlabCallFun(double params[], int arrayLen, char *funName, char *pathCall, double bulkIn, double bulkOut, double contrast, double *sum) {
static Engine *ep;
static double engStatus = 0;
mxArray *result = NULL;
mxArray *PARAMS = NULL;
mxArray *BULKIN = NULL;
mxArray *BULKOUT = NULL;
mxArray *CONTRAST = NULL;
mxArray *FNAME = mxCreateString(funName);
double *s;
if(engStatus == 0) {
ep = engOpen("");
if(ep==0) {
printf("Connecton to Matlab Engine failed\n");
}
else {
printf("Connecton to Matlab Engine succeeded!\n");
engEvalString(ep,(void *)pathCall);
engStatus = 1;
}
}
PARAMS = mxCreateDoubleMatrix(1,arrayLen,mxREAL);
memcpy((void *)mxGetPr(PARAMS), (void *)params, arrayLen*sizeof(double));
engPutVariable(ep,"params",PARAMS);
BULKIN = mxCreateDoubleMatrix(1,1,mxREAL);
memcpy((void *)mxGetPr(BULKIN), &bulkIn, 1*sizeof(double));
engPutVariable(ep,"bulk_in",BULKIN);
BULKOUT = mxCreateDoubleMatrix(1,1,mxREAL);
memcpy((void *)mxGetPr(BULKOUT), &bulkOut, 1*sizeof(double));
engPutVariable(ep,"bulk_out",BULKOUT);
CONTRAST = mxCreateDoubleMatrix(1,1,mxREAL);
memcpy((void *)mxGetPr(CONTRAST), &contrast, 1*sizeof(double));
engPutVariable(ep,"contrast",BULKOUT);
engEvalString(ep,(void *)funName);
result = engGetVariable(ep,"total");
s = (double *)mxGetData(result);
memcpy(sum, s, 8*sizeof(double));
engClose(ep);
}
When I compile this, and try to run
codegen engTest -o engTest_mex
y = engTest_mex
I get a seg fault, and the following in the relevant terminal window....
Connecton to Matlab Engine succeeded!
cd('/home/arwel/Documents/coding/cevalTests/mlEng');7
|
Error: The input character is not valid in MATLAB statements or expressions.
total = debugMfile(params,bulk_in,bulk_out,contrast);,YǂZ
|
Error: The input character is not valid in MATLAB statements or expressions.
What am I missing here?
Cheers,
Arwel
p.s.
The fun being called is....
function total = debugMfile(params,bulk_in,bulk_out,contrast)
s = sum(params);
total = [s bulk_out bulk_in ; 4 5 6];
save debugMfileVars.mat
end
Antworten (3)
James Tursa
am 18 Okt. 2017
I don't see how this can possibly work, even when called from main( ). It appears you are writing into invalid memory. E.g.,
outp = zeros(2,3);
coder.ceval('matlabCallFun', params, paramsLen, funName, pathCall, bulkIn, bulkOut, contrast, coder.wref(outp));
In the above lines, it appears you have pre-allocated a total of 6 double elements for "outp". Yet in your C code there is this:
void matlabCallFun(double params[], int arrayLen, char *funName, char *pathCall, double bulkIn, double bulkOut, double contrast, double *sum) {
:
memcpy(sum, s, 8*sizeof(double));
So it appears in your C code you are writing 8 double elements. Seems like this would run off the end of valid memory.
And in your main( ) example you have this:
int main(int argc, char** argv) {
:
double *s;
:
matlabCallFun(params, paramsLen, funName, pathCall, bulkIn, bulkOut, contrast, s);
So here it appears you are passing an un-initialized pointer into matlabCallFun, whereupon matlabCallFun attempts to write 8 doubles to this invalid memory location.
Bottom line is I don't see how this could work in either case.
2 Kommentare
James Tursa
am 19 Okt. 2017
This latest posted example answers the 8 vs 6 issue. But it does not address the uninitialized pointer issue that I mention above in your main( ) example. In that case the pointer "s" is declared but given no value ... it is uninitialized. It is then passed into your matlabCallFun routine and used as the target for a memcpy. So you are writing to an invalid address, and anything bad can happen at that point.
Arwel
am 20 Okt. 2017
2 Kommentare
Arwel
am 20 Okt. 2017
James Tursa
am 20 Okt. 2017
Bearbeitet: James Tursa
am 20 Okt. 2017
Some comments:
1) The prototype for memcpy has (void *) for the pointer arguments. You get conversions from any object pointer to (void *) automatically, so there is no need to explicitly cast these pointers in your memcpy calls. E.g., this line
memcpy((void *)mxGetPr(PARAMS), (void *)params, arrayLen*sizeof(double));
can be this line instead, which maybe is more readable (at least to me anyway):
memcpy( mxGetPr(PARAMS), params, arrayLen*sizeof(double) );
2) There is a simpler way to create double scalar mxArray variables than what you are doing. E.g. these lines:
BULKIN = mxCreateDoubleMatrix(1,1,mxREAL);
memcpy((void *)mxGetPr(BULKIN), &bulkIn, 1*sizeof(double));
can be replaced with this line:
BULKIN = mxCreateDoubleScalar(bulkIn);
3) The ordering of the values appears to be exactly as expected to me. You create the variable "outp" as a 10x3 double and then pass the address of that into matlabCallFun via "coder.wref(outp)". Calculations are then done in the Engine with a call to debugMfile where the return variable is a 2D matrix total = [36 37 38; 4 5 6]. But MATLAB stores variable data in column ordered fashion. So these six values are actually stored in memory in the following order: 36, 4, 37, 5, 38, 6. Then back in the matlabCallFun code you copy these values into "outp" using a memcpy call ... which will simply retain the ordering in memory. Bottom line is I would expect these values to show up in the first column of "outp" in the same order that they are in memory in the "total" variable, and that is exactly what I am seeing. So this is all as expected and no surprise. If you want something else to show up you will need to code the value copying differently. I could advise you here, but I don't really know what you want. Is it your intention to have "outp" pre-allocated with more rows than necessary, and then get filled with "total" by row, so that the top part of "outp" will be a copy of "total"? Or what? If it is something like this, then you will need to pass in the size of "outp" to matlabCallFun so that your C code knows how to copy the values from "total" into the appropriate spots of "outp". Let me know if you need help with this.
4) You still have a coding error with writing to invalid memory. This seems to be a persistent problem with your code. In particular, note that "total" has only 6 elements, but you are still using that hardcoded 8 element memcpy. So you are reading off the end of the valid data block of memory. This is likely what is causing the seg fault. To fix this, code your value copying more robustly:
memcpy( sum, s, mxGetNumberOfElements(result)*sizeof(double) );
But this only solves half the problem, since the "sum" target of the copy might not be large enough to hold what is in "result". So you will probably want to adjust your code to ensure this doesn't happen. Either something to ensure "outp" is large enough to hold "total" in your m-code, or maybe pass in some sizing information to matlabCallFun so that this can be checked there and guard against invalid memory copies.
5) Not calling mxDestroyArray in and of itself will not result in a seg fault. The issue will be one of leaking memory and eventually running out of memory depending on the size of the mxArray variables involved. But, even though this is not the cause of the seg fault, you should get in the habit of destroying those mxArrays you create when you no longer need them in your code to avoid those memory leaks. In a mex routine, the memory leaks will be temporary and will be garbage collected when the mex routine returns to the caller. But in a true standalone Engine app, the memory leaks will be permanent until the program exits.
6) I am puzzled why some of your output is 0 and some is 0.0000. Typically the latter indicates that the value is not exactly 0. It is puzzling to me because "outp" was initialized to exactly 0's and I see nothing in your code that would change those trailing values to something else.
Arwel
am 9 Nov. 2017
Kategorien
Mehr zu Logical finden Sie in Hilfe-Center und File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!