LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

C DLL memory leak?

I'm building a C DLL for use with a VxWorks based Compact RIO 9205.  I'm also using LabVIEW 2009 (project requirement).  I've gotten some simple functions working and have checked out the examples and tutorial located here.  I'm newish to LabVIEW but a long time C/C++ developer so please bear with me.  In the examples provided by NI, I see an example piece of C code like this:

 

void ReturningValuesByReference_2DArrayOfIntegers (int rows, int cols, int ***newArray)
{
	int i, j;
	*newArray = malloc (sizeof(int) * rows);
	for (i=0; i<rows; i++)
	{
		(*newArray)[i] = malloc (sizeof(int) * cols);
		for (j=0; j<cols; j++)
			(*newArray)[i][j] = i+j;
	}
}

Now as a C developer on typical x86 based systems, this is a huge no-no.  It's a memory leak.  So trying to eliminate that issue, I allocated memory within LabVIEW instead (basically removing the malloc calls) but that ends up crashing the CompactRIO when the function is called.

 

So the point here is, I know on a typical x86 system this is a memory leak, as the function allocates memory and never deallocates it.  At least, it's up to the caller to do that work.  Does LabVIEW's Call Library Function Node handle any memory allocated inside the C function automatically?  Or is this just NI people doing a bad job of C programming?

0 Kudos
Message 1 of 8
(4,569 Views)

There's no magic in the Call Library Function Node; it works like calling a DLL in any other language. I don't think the example is NI doing a bad job of coding, it's just a code fragment that shows how to pass a 2D array by reference and not a full application. An experienced C programmer will notice, as you did, that pointers and memory allocation need to be managed properly. I wouldn't call this a "huge no-no;" it's only a memory leak if the function is called repeatedly without keeping track of the returned handle. In a real application there should be another function in the DLL that frees the memory. It's very common to have an initialize function that allocates memory and returns a handle, which is then passed to several more functions, and finally released in another function much later.

 

If you can share your code (LabVIEW and C) we can help debug why your attempts to allocate the memory in LabVIEW failed, but trying to get LabVIEW to allocate a 2D array the way a C compiler expects it to be arranged is not simple, especially because ** can indicate a lot of different things in C.

0 Kudos
Message 2 of 8
(4,541 Views)

It's not automatically a memory leak. You just shouldn't forget to also provide a freearray() function and call it from whoever called the other function successfully.

But this is definitely not meant to return a LabVIEW 2D array as that is completely different. A LabVIEW 2D array is a pointer to a single memory area which can take row * column elements.

void Returning2DArrayOfIntegers (int rows, int cols, int *newArray)
{
	int i, j;
	for (i=0; i<rows; i++)
	{
		for (j=0; j<cols; j++)
			newArray)[i * cols + j] = i+j;
	}
}

And you better make sure that you allocate the array with row * column elements on the LabVIEW diagram before passing it to this function!!!!

Rolf Kalbermatter
My Blog
0 Kudos
Message 3 of 8
(4,522 Views)

Thanks Nathan and Rolf.  Keep in mind these are static function calls, so perhaps I'm misunderstanding how allocating memory inside a function isn't automatically a memory leak.

 

I probably shouldn't have posted the 2D array example as that's not really what I'm doing.  Let me explain a little more in detail exactly what I'm doing and maybe that will help you guys help me.

 

The main overarching goal is offloading a lot of the LabVIEW in this project to C.  The C DLL will have a struct (complex, with sub structs, arrays, etc) which holds all the data.  The C DLL will also have a function which accepts this struct as input and transforms the data into a preformatted array of 16 bit unsigned ints.  I couldn't post all the code so I made a smaller example.  Here's my code (with a main included because I compile in linux as a test before cross compiling to vxworks and windows libraries).

 

hspacket.h

#include <iostream>

static const int NUMS_COUNT = 10;
static const int TEST_WORD_COUNT = 10;
struct pointCoords { unsigned short x; unsigned short y; }; struct shape { unsigned short width; unsigned short *nums; pointCoords center; };

hspacket.cpp

#include "hspacket.h"
void getTestPacket(unsigned short* words, shape* s) { words[0] = s->width; words[1] = s->center.x; words[2] = s->center.y; words[3] = s->nums[0]; words[4] = s->nums[1]; words[5] = s->nums[2]; words[6] = s->nums[3]; words[7] = s->nums[4]; words[8] = s->nums[5]; words[9] = s->nums[6]; } int main(int argc, char* argv[]) { unsigned short *words = (unsigned short*)malloc(TEST_WORD_COUNT * sizeof(unsigned short)); shape *s = (shape*)malloc(sizeof(shape)); s->width = 5; s->center.x = 2; s->center.y = 3; s->nums = (unsigned short*)malloc(NUMS_COUNT * sizeof(unsigned short)); for (unsigned int i = 0; i < NUMS_COUNT; ++i) s->nums[i] = 1; getTestPacket(words, s);
for (unsigned int i = 0; i < TEST_WORD_COUNT; ++i) std::cout << i << ": " << words[i] << std::endl; delete s->nums; delete s; delete words; return 0; }

This code compiles in linux and runs with the output (as expected):

0: 5
1: 2
2: 3
3: 1
4: 1
5: 1
6: 1
7: 1
8: 1
9: 1

And here's the LabVIEW side.  I cross compile the same code to a VxWorks .out file and a Windows .dll.

 

Note that the s->nums array just comes out as gibberish (assuming those are memory locations or something).  Since making the smaller example struct it doesn't seem to crash anymore, so I'll gradually add to it to ensure it works.  I'm not sure if I'm allocating correctly, but according to my coworker, simply entering 0 into each of the input words on the front panel automatically allocates them.

 

Also note the final struct will be have a lot more data in it but the principle is the same.

 

Thanks again for checking this out for me.

0 Kudos
Message 4 of 8
(4,491 Views)
I recommend that you read the "How LabVIEW stores data in memory" section in the LabVIEW help.

LabVIEW cannot automatically convert between an array embedded in a cluster and a pointer embedded in a struct. You'll have to implement that yourself, by including a pointer-sized integer in the cluster, then using GetValueByPointer or MoveBlock (see https://decibel.ni.com/content/docs/DOC-9091). Alternatively, if the embedded array is a fixed size, then declare it as such in the struct, and in the cluster replace the array with a cluster containing the correct number of elements.

I don't understand what this has to do with a memory leak.

The forum allows you to upload images directly into your post, no need to host them on an external site.
0 Kudos
Message 5 of 8
(4,483 Views)

Thanks for those comments Nathan.  The help section you recommended did help somewhat, especially to note that cross compilation with complex structs and clusters is virtually impossible in this instance.  Given the examples NI provides I'd hoped it would be possible.  It appears that a number of simpler parameter types instead of an all encompassing data structure will be required to accomplish what's needed.

 

The memory leak in question is explained in the first post of this thread.  The example C DLL functions NI gives are 100% memory leaks if directly used as static functions in C code.  I realize you don't agree but we can simply differ on that as far as I'm concerned.  My question was more related to the Call Library Function Node and whether it has its own "sandbox" of memory which gets cleaned up.  The code in question will be run at 1Hz, likely for multiple years, so even a small memory leak is unacceptable.

 

I typically use imgur to host photos on forums for two reasons:  1. I maintain control of the images, and 2. I feel like it's more polite to consider the forum's bandwidth.  If this forum doesn't desire its members to do so then I suggest they remove or disable the option.

0 Kudos
Message 6 of 8
(4,472 Views)

You need to be careful when starting to claim certain things. First you talk about C and static functions. In C a static function is simply a function that is only visible in its own module. Nothing about the function being static makes it suddenly create memory leaks. You are most likely mislead by something you learned in school about static object functions which, since they are static have no instance of an object to refer too. So if you allocate memory in such a function and are not careful you can indeed easily create memory leaks, but again nothing about the static ness of that function makes memory leask more likely than a normal object method. Yes an object method has access to the object structure and when allocating memory blocks and storing them in that object structure other object methods can access it too, but if you forget to free() that memory in your destructor you create a memory leak as well.

 

Your "static" method doesn't store the allocated memory pointers into a stack variable which will stop to exist after the function returns. Instead it stores it in the array pointer that is returned to the caller. Your caller is now able to access the data in those pointers AND responsible to deallocate it after it has finished accessing it! It's not a common use case because of various complications including having the allocation and deallocation in two different places, making errors more likely as a programmer is easily forgetting about this memory if he hasn't himself explicitedly allocated it first, but it's totally valid to design such an API. Just read about the getifaddrs() API just to name an example. It does the same and requires the caller to call freeifaddr() on the returned pointer after it has finished using the data in there. That is one of the other complications about such an API. If you create such an API you always should provide an according freexxx() function in your API too, since your code module may link to a different version of the C runtime than your caller, making malloc() in your module work on a different heap than the free() in the caller and causing all kind of nastyness. By providing an according freexxx() function you avoid that problem and you usually also know best how the structure needs to be deallocated as you also allocated it.  

 

And nothing in C++ would prevent an object method to return such a memory through a parameter either, although it is more convinient to let the object manage internally all the memory it allocates.

 

As to your considerations about wanting to write part of the "complex architecture" in C I would question the motives for that. IMHO the only two valid reasons for that are if you have already a significant existing and well tested code base in C(++) that you want to integrate in your LabVIEW software or you need to access lots of low level APIs. Anything else is most likely misguided. One of the more common reasons I hear is because of performance and in 99% of the cases that is simply bs. LabVIEW is not less performant if you throw the same amount of architectural design at it as you need to do when programming in C(++). Yes you can make bad choices in LabVIEW for data structures and still get it working albeit with an abominable performance. The same doesn't hold as true for C(++) code because if you start out with a bad architecture you may most likely never get it to work reliably. In addition while you  can concentrate in LabVIEW on the actual algorithmes, you have to worry in C(++) about proper memory allocation and deallocation, NULL pointer dereferencing, division by 0 exceptions, buffer overrun errors and much more before you can even start to work on your algorithme.

 

As to your problem about the structure you want to access, you need to ask yourself one question first: Do you want to access the elements in that structure directly from the LabVIEW diagram or can you allow for it to be an opaque pointer as far as LabVIEW is concerned and provide accessor functions in your DLL for everything you need to access?

 

In the first case you have to use native LabVIEW data elements. That are for scalars the according C type scalars and for arrays and strings LabVIEW handles. A LabVIEW handle is a pointer to a pointer to a memory area holding an int32 for each dimension, directly followed by the data elements. And LabVIEW handles MUST be allocated, resized and deallocated by LabVIEW memory manager functions. No other way of manipulating those handles is possible. It's not trvial but absolutely possible as I have done it several times already.

Rolf Kalbermatter
My Blog
0 Kudos
Message 7 of 8
(4,450 Views)

Rolf what you're saying is so long as you deallocate memory that's allocated then the function is not a memory leak.  That much is painfully obvious and nothing I "learned in school" taught me any different.

 

When I said "static" function call in C perhaps it was a poor choice of words.  What I meant was from the LabVIEW code via the function call node.  It seems to me like a static call of sorts.  My original question was directed at understanding the difference in the relationship of that node calling the function vs calling it from a plain old C main function.  I believe the answer was simpler than I thought.  Allocate/deallocate just like normal (although I still don't understand 100%).

 

You can question my motives for using C but that seems a bit narrow minded.  The simple answer to that question is that in my industry, code re-use is king.  Since I can cross compile this code to just about any platform in existence, it's a lot more re-usable.  Other example reasons could be (but aren't limited to):  Bringing in resources who know C to help on projects, easier source code management, easier separation of modular logic from platform dependent I/Os such as networking, file storage, etc.

 

The complex answer is more a C vs LabVIEW thing.  If it's not obvious by now I definitely prefer C/C++ to LabVIEW.  My coworker and I often joke with each other, with him calling what I do "text programming" and me calling his "box programming".  While I can admit they both have their merits, in general I could have an all day long conversation on why C++ kicks the pants off LabVIEW.  Your comment on the matter is basically the pot calling the kettle black.  You used the standard argument in any debate over software which is "if you know what you're doing then mine's better" which is ultimately pretty self defeating.  If you care to extend the debate on the subject I am fair game, but I think it's ultimately pointless.

 

I hope my comments aren't too annoying, but I can't abide when pointless, generalized assumptions of ignorance get tossed my way.  I very much appreciate the help on this "box programming".

0 Kudos
Message 8 of 8
(4,428 Views)