Author
Topic: About debugging a plug-in (Read 6224 times)

I have several questions about debugging a plug-in, once again... I feel a bit sorry to post so much lately... I hope that may be of some interest for some other people, still!

First I used the plug-in tester provided on the website: there was a strange error that popped by using it. It said that the parameters were not initialized with the default values. And indeed: in the constructor, I did not use the default values. This however is a bit cumbersome: each time one wants to modify the default values for a parameter, it has to be changed in two places. Is there a way of doing so automatically? What if the parameter is not initialized? how about with another value?

Then, I was wondering whether vamp-plugin-tester was performing some memory checks. That would be a great addition! Otherwise, is it possible to use valgrind for instance to do so? How would you recommend to use it?

Actually, what bothers me, for the latter matter, is that my plug-in needs some arrays, which probably should be freed at some stage... Thing is, I m not so sure about the whole work-flow of the plug-in (maybe the documentation could be a bit more precise about that...). When is the destructor called? the reset? For instance, when a user starts a session, then starts the plug-in as a layer, uses it and closes the layer. When he calls the plug-in as a new layer, is the previous instance called or is it a new instance?

So many questions... I feel sorry about that, since I might have missed the answers in the documentation, but please bear with me!

Best regards,

Jean-Louis

----------added 2010/12/13: Just added a second output feature to my plug-in. Launching Sonic Visualiser on it or even the plug-in tester crashes right at the beginning: I figured out that, at the start, SV tries to get some information on the outputDescriptor (from the getOutputDescriptors method), in which I was using attributes that were not initialized yet.

There is something somewhat related to that matter in the plug-in skeleton: handling m_blockSize differently whether it s 0. The comment says:

// Just so as not to return "1". This is the bin count that // would result from a block size of 1024, which is a likely // default -- but the host should always set the block size // before querying the bin count for certain.Well, I'd say that when starting SV, the block size is not set and the program still calls this method... That's probably not a big issue in the most general cases, in my case, since I am setting the labels for each bin of the output vector, it so happens that, with random values for my attributes (parameters), I end up writing a bit anywhere in memory... Just thought this should be made clear.

Some of this, including perhaps the bit about output descriptors, is either explicitly documented or at least "implied" by the Vamp plugins programmers' guide (http://vamp-plugins.org/guide.pdf) in the section "What can depend on a parameter?" But this doesn't cover most of your questions.

First, you're right that the default parameter values need to be set twice -- or else you need a separate variable for the default value, which you assign the parameter variable from. This is something of a deficiency in the specification, as it makes hosts easier to write but plugins harder, which is the wrong way around.

For memory checks, I would certainly use valgrind, assuming you are on a platform on which it is supported. Just stick the valgrind command at the start of your vamp-plugin-tester command line ("valgrind vamp-plugin-tester [options...]") The default behaviour of valgrind, with no fancy options, will show you most of what you need to know, though it's also worth trying the --leak-check=full option to valgrind to find memory leaks for you.

As for plugin lifecycle, Sonic Visualiser generally calls the constructor and destructor quite often -- for example when scanning plugins on startup it will construct each, query its outputs (but _not_ call initialise: the idea is that construction should be cheap and initialise should do the real work) and then destroy the plugin again. SV will also generally destroy and recreate at times when you might expect reset() to be used, as in your example with multiple layers -- the plugin will be destroyed there rather than simply reset. In fact I have a suspicion that SV never calls reset() at all, although some hosts will. Your plugin will need to behave the same in either situation.

About the guide: I ve gone through it, but could not find answers to some of my questions... I actually found the provided examples more useful than the documentation itself. That's why I thought a post would be good, to share my experience with others and also to keep trace of it!

valgrind: I ll try it, but for some reasons, when I tried it, it was taking ages to compute - far more than necessary. I guess vamp-plugin-tester also needs to be compiled with -g enabled?

outputs: another question! Let's say a vamp plug-in can output several features, is there a way, in SV, for instance, to obtain all of these outputs from one run, or do I need to run several instances of the plug-in so as to extract each of the desired outputs?

Computational software under Valgrind does indeed take a very long time to run. A very, very long time. That's presumably because of the immense amount of book-keeping work it needs to do for every single memory access.

You'll need to compile vamp-plugin-tester with -g if you want line numbers and suchlike in your Valgrind output for those parts of the stack trace that appear within vamp-plugin-tester. It probably won't make any difference to whether you get line numbers in the lines related to your plugin -- that should depend only on whether your plugin was compiled with -g or not.

SV cannot extract more than one output in a single run. (Well, technically it always extracts more than one output -- since a Vamp plugin always calculates all of its outputs at once -- but it won't display more than one output as a result.)

SV cannot extract more than one output in a single run. (Well, technically it always extracts more than one output -- since a Vamp plugin always calculates all of its outputs at once -- but it won't display more than one output as a result.)

Is this a limitation of Vamp plug-ins in general, or would it be something possible within SV in the (hopefully near) future?

For instance, the possibility of choosing which of the outputs should be kept, and display them as new panes and/or as new layers, would be a great addition to SV, if that would be technically possible!

Cheers, and happy new year to everyone involved and interested in SV, Vamp plug-ins, music signal processing or just music!

-- Performing test: B2 Invalid or dubious timestamp usagevamp-plugin-tester(2548,0xa03f4540) malloc: *** error for object 0x90ce18ae: pointer being freed was not allocated*** set a breakpoint in malloc_error_break to debugvamp-plugin-tester(2548,0xa03f4540) malloc: *** error for object 0x4001e0: pointer being freed was not allocated*** set a breakpoint in malloc_error_break to debugAfter which the plug-in still seems to have gone on with its work... Is that normal? is this a problem with the plug-in, or with the plug-in tester?

I know I get to talk a bit too much about that plug-in... I ll soon send an announcement for it, as soon as the article that goes with it is accepted.

For now, I still need help! Well, it basically works on Linux and Mac, with some bugs, but I guess I cannot really do much at the moment (valgrind tells me I want to use too much memory so it stops before saying anything useful).

Now, I just tried under Windows. I used to have a version which worked - but which was slow because during initialization, I generate big matrices that in the last version, I manually entered. Anyway, it works fine for Linux and Mac, and now, under Windows, with a cross compiled version, when I launch SV, it tells me:

This application has requested the Runtime to terminate it in an unusual way. Please contact ...Trying with the provided windows vamp-plugin-tester, I get the same error, and just before, something that goes:

terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_allocAny idea what might have gone wrong, and if I can solve this problem? I am only asking whether there would be an obvious answer (not much hope). Otherwise, I know I might need to check better the linux version, maybe some bugs may give a hint about that particular problem. But I still think it s a bit strange that it should crash right from the start under windows, while the other versions can work almost without bugs...

vamp-plugin-tester(2548,0xa03f4540) malloc: *** error for object 0x4001e0: pointer being freed was not allocated*** set a breakpoint in malloc_error_break to debugAfter which the plug-in still seems to have gone on with its work... Is that normal? is this a problem with the plug-in, or with the plug-in tester?

This is a problem with the plugin, but one that the tester can't detect -- the message probably comes from a debug version of the C library which the plugin is linked with. This is the sort of thing that Valgrind would detect and show a more thorough explanation of, if you could get it to run satisfactorily. It means just what it says -- at some point you are freeing a pointer that you didn't allocate -- but unfortunately that on its own isn't necessarily going to help you much to find the cause. Review all places where a pointer may be freed or deleted.

Quote

Anyway, it works fine for Linux and Mac, and now, under Windows, with a cross compiled version, when I launch SV, it tells me:

This application has requested the Runtime to terminate it in an unusual way. Please contact ...Trying with the provided windows vamp-plugin-tester, I get the same error, and just before, something that goes:

terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc

That error means that operator new() failed -- you requested allocation of more memory than was available. That may mean you have tried to allocate far more memory than you actually needed for some reason, or that you simply don't have enough memory -- or it might indicate a more subtle programming error such as unsigned integer underflow. For example, something like

may cause that exception to be thrown if either n or m is unsigned and n is smaller than m.

As this is a C++ exception, it's possible to catch it rather than permit the program to crash -- but it's usually a waste of time; there's not really any reliable way to recover from running out of memory, especially if you don't know at what point in the program the failure happens.

See if you can run it under a debugger and get a stack trace for the crash -- the innermost stack frames should show where the exception was thrown.

terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc

That error means that operator new() failed -- you requested allocation of more memory than was available. That may mean you have tried to allocate far more memory than you actually needed for some reason, or that you simply don't have enough memory -- or it might indicate a more subtle programming error such as unsigned integer underflow. For example, something like

may cause that exception to be thrown if either n or m is unsigned and n is smaller than m.

As this is a C++ exception, it's possible to catch it rather than permit the program to crash -- but it's usually a waste of time; there's not really any reliable way to recover from running out of memory, especially if you don't know at what point in the program the failure happens.

What surprises me is that it occurs while loading the program, and not when calling the plug-in. Furthermore, this error did not (yet...) appear with my tests under Linux and MacOsX. Could it be that under Linux and OSX, the memory management allows bigger allocations? Is there any known way to allow more under Windows (at compilation or from Windows)? At least trying this before looking for something else...

Quote

See if you can run it under a debugger and get a stack trace for the crash -- the innermost stack frames should show where the exception was thrown.

Any experience with a debugger under Windows? Or does gdb (or ddd) work with wine?

vamp-plugin-tester(2548,0xa03f4540) malloc: *** error for object 0x4001e0: pointer being freed was not allocated*** set a breakpoint in malloc_error_break to debugAfter which the plug-in still seems to have gone on with its work... Is that normal? is this a problem with the plug-in, or with the plug-in tester?

This is a problem with the plugin, but one that the tester can't detect -- the message probably comes from a debug version of the C library which the plugin is linked with. This is the sort of thing that Valgrind would detect and show a more thorough explanation of, if you could get it to run satisfactorily. It means just what it says -- at some point you are freeing a pointer that you didn't allocate -- but unfortunately that on its own isn't necessarily going to help you much to find the cause. Review all places where a pointer may be freed or deleted.

size_t n = m_blockSize / 2 + 1;m_powerSpectrum = new float[n];... // filling in the array and ... // after processing, putting the result in the desired feature set

Since this array has been allocated, I guess I should free that memory at some stage... but when would be the best time to do so? It seems that freeing it at the end of process makes the plug-in crash, and some of the errors I talked before might come from freeing these arrays in the destructor, or in the reset method.

Does this make sense, or should I look for some other potential reason? I tried again with valgrind, but I seem to ask too much memory (by the way, is that something bad for the plug-in, if I want more memory than valgrind can use? I mean, should I expect problems after, when using the plug-in in SV?).

Quote

Quote

Anyway, it works fine for Linux and Mac, and now, under Windows, with a cross compiled version, when I launch SV, it tells me:

This application has requested the Runtime to terminate it in an unusual way. Please contact ...Trying with the provided windows vamp-plugin-tester, I get the same error, and just before, something that goes:

terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc

That error means that operator new() failed -- you requested allocation of more memory than was available. That may mean you have tried to allocate far more memory than you actually needed for some reason, or that you simply don't have enough memory -- or it might indicate a more subtle programming error such as unsigned integer underflow. For example, something like

may cause that exception to be thrown if either n or m is unsigned and n is smaller than m.

See if you can run it under a debugger and get a stack trace for the crash -- the innermost stack frames should show where the exception was thrown.

It would seem that the error was caused by some unlucky combinations, at the plug-in first call. That seems related to a problem I talked about some time ago, with the initialisation of the parameters.

In order to provide the binNames, in the getOutputDescriptors method, I was filling-in a vector of strings, for which I assumed the size could not exceed 10 characters. At the first call, however, the values of blockSize and inputSampleRate make the strings a bit longer. Since I was using them to fill my array of char, I was basically writing a bit everywhere, which seemed not to worry SV under Linux or Mac, but which caused SV under Windows to crash right away. Does this sound like a normal behavior for SV, under these different OS's?

I m still running some tests to check whether some more bugs appear... I must admit I have gone through some strange crashes, like some sets of parameters that (randomly) led to unexpected crashes. Hard to trace back... Does the plug-in tester also test several parameter configurations? That would be an interesting feature.

size_t n = m_blockSize / 2 + 1;m_powerSpectrum = new float[n];... // filling in the array and ... // after processing, putting the result in the desired feature set

Since this array has been allocated, I guess I should free that memory at some stage... but when would be the best time to do so?

If m_powerSpectrum is a member of the plugin class and is allocated in the initialise method, then it should be freed in the destructor.

Note that, in this case, it should also be assigned to zero in the constructor, or else the destructor will crash if initialise is never called -- it is OK to delete[] a null pointer, unlike free() in C; but it's never OK to delete a pointer that has been declared but never initialised at all.

In the reset method, then, you could either delete and reallocate the pointer, or more likely you could reset the contents of its array to zero (since its intended size would be unchanged), or even leave them unchanged if their values don't matter.

If powerSpectrum was a variable allocated in process() and then used only in process(), then it should be deleted before process() returned -- but you'd obviously have to be sure to have copied all of the data you needed out of it before deleting it.

In order to provide the binNames, in the getOutputDescriptors method, I was filling-in a vector of strings, for which I assumed the size could not exceed 10 characters. At the first call, however, the values of blockSize and inputSampleRate make the strings a bit longer. Since I was using them to fill my array of char, I was basically writing a bit everywhere, which seemed not to worry SV under Linux or Mac, but which caused SV under Windows to crash right away. Does this sound like a normal behavior for SV, under these different OS's?

It sounds like you just got lucky under Linux and OS/X. Possibly it would have crashed later if the data had been deleted.

Generally it's a very bad idea to use fixed-size string arrays for data that you can't be totally confident of the length of -- this is a big cause of a very common class of security-related buffer overflow errors and, while that's maybe not going to be a practical issue in this code, it means it's something well worth learning to avoid in general.

Also, if your "strings" are actually char pointers allocated using new char[] or even malloc(), then it's a bad idea to put them into a vector because the vector will be unable to delete them and recover the memory when its destructor is called (as they are not objects with destructors themselves).

Generally, if you want a vector of strings in C++, you should always use a vector of std::string. If you want to "print" float or int values into the strings, consider using a std::ostringstream to build them.

I hope I'm not barking up the wrong tree here -- I'm making some rather wild guesses about what your data structure actually is, so my apologies if I've misunderstood!