Module programming, execution and debugging

indeed. you should be fixing the problem, not the symptoms. converting to std::array only fixes the symptoms.

1 Like

What do you mean by ‘only fixes the symptoms’ ?

The approach I suggested is by using std::array he could have an exception thrown if an out of bound access happens, therefore making it way easier to locate the origin of the problem. That’s what I do if I would encounter that kind of problem in a large codebase. But maybe there’s an easiest approach ?

simply replacing c-style array access won’t:

#include <array>
#include <iostream>

int main() {
  std::array<int, 3> a1{ {1, 2, 3} };

  std::cout << a1[0] << a1[1] << a1[2] << a1[3] << std::endl;

  return 0;
}

prints: 1230

and ignores the out of bounds array by virtue of simply returning 0.

against 1100 array instances? likely, yes :slight_smile: heck, even building valgrind to run in msys2, or running a local linux vm could end up being easier/cleaner than trying to scalpel in 1100 array changes.

Didn’t say that; you should read my original suggestion. " One possible workaround is to replace C-style arrays with std::array , and accessing them with the ->at() method, which will do the boundary check for you."

Still only a few month in C++ dev myself so I didn’t know valgrind, will look into it.

I looked for the original suggestion, was having an issue finding it. still, given the number of array accesses quoted, that’s going to be a huge undertaking and likely a pretty large performance hit (essentially the same as running with asan), and still ends up just masking the issue in the end (operator[] access in std::array uses pointer math, but could still end up being a cache spoiler, whereas at() includes the bounds-checking code and will both be a cache spoiler and a ton of extra instructions for each access).

Sure it’s a bit of an overhead, but given that most code seems to run at frame rate, not at sample rate, I guess it could be just fine.

until you want to run more than one module, then you’re hamstrung because a single module you want to use is not performing well, and you end up not being able to use it.

Yeah, there is no easy and perfect approach. I did not realize that std::array->at would throw an exception on out of bounds array access, but I was wondering what would happen on such. Receiving the exception message might be difficult from within the plugin DLL within Rack, right? There is the possibility that doing the std::array conversion step by step might allow me to find the culprit.

Since my module does no sample rate processing, I’m not too worried about performance with std::array using, any more than I would worry about performance doing the entire module in C++ rather than C. I know performance purists prefer C, but I’ve working on gigantic commercial 3D animation product development that was done almost entirely in C++. But, that’s just my opinion.

I’ve had Oracle VirtualBox Ubuntu Linux VM working on my computers before, but, since I am not a Linux developer, there would be a good bit of work to get up to speed on that.

I appreciate everyone’s suggestions. I’m not sure what I want to do on this. I do not have my code on GitHub, so it is not a trivial task to get it to you @jerrysv for asan build.

It is still just an educated guess that I have an out of bounds array write error. Nothing crashes, but I have seen some variables’ values change inexplicably as shown in the DEBUG() log. I have tons of DEBUG() statements in the code right now to aid in trying to find this bug. But, yes, it is still a needle in a haystack. As mentioned elsewhere, I have not put the code on GitHub yet.

I recall you have the array in the middle of a class/struct’s members. If you move the array to the end of the members, you might be able to avoid some issues when writing past the end of the array. (By chance there might be a few bytes of memory there that won’t be shared with anything else.) Of course that should just be done as a test to see if it reduces/removes the bugs, it is not an actual fix to the problem. You just have to try to find where the invalid array accesses are…

Haven’t had time to read past the original post.

People do this all the time. GDB/LLDB catches overruns but only when a segfault occurs. Valgrind works on Linux and Mac. Dr Memory works on Windows. Still, they’re not smart enough to catch issues like

struct Foo {
	float a[4];
	float b[4];
};

Foo foo;
foo.a[4];

If you’re only manipulating data inside your class, your process() method is automatically re-entrant. If you’re modifying global values, you need to do so atomically or with locking (which is discouraged on the audio thread unless you really know what you’re doing). Rack does not call process() simultaneously on the same instance. But in multi-thread mode, it can be called simultaneously with different instances.

The UI world should only communicate with the DSP world, not the other way around, and you need to make these communications thread-safe. For example, if your widget sets module->myMode and if your module’s process() method accesses it twice, its value could change.

This is also true for ParamWidgets and other things. In other words, params[...].getValue() might change values during your process() method. I’ve personally never needed to read param values twice, because I can just set its value to a local variable once, which will never change.

2 Likes

Thanks @Vortico , I will install Dr. Memory tomorrow since I develop under Windows. Out of bounds array access is surely the most likely culprit. I will continue to concentrate on that and only move on to other possible problems once I am confident in the arrays. Unfortunately, since I wrote Meander in C for DOS and Windows between 1988 and 2001, I could have had array problems long standing. More likely, as I copied over a couple of thousand lines of C code as-is from Meander and then tweaked it for Rack, I may have inadvertently got an array dimension wrong.

if it’s any consolation, I had some C code from ~1989-1992 that had been running without issue on SunOS and other BSD based unixes. revisited about 25 years later to weird memory issues. couldn’t track them down with gdb, lldb, or valgrind (it had originally passed fine with purify, if you remember that software), and found a ton of issues with asan. was quite amazed it ever ran after that … :confused:

Yeah, Meander for Windows has never crashed or malfunctioned since my last development work on it in 2001, but that is no guarantee that it was ever perfect. It was developed for 16 bit Windows. My work in 2001 made it a 32 bit app. I have not done anything to make it a 64 bit app. Yep, amazing that it still runs. I did rebuild it a couple of years ago successfully in order to make a minor MIDI port change.

I wrote an array audit function today that checks at various points of module execution to see if any arrays have invalid values in them and logs it if they do. This evening the auditor is finding 4 such problems that I introduced earlier in the week. Hopefully that will turn out to be the source of this weeks problem. I did identify one problem last week and fixed it and did not see the problem again until yesterday. There is hope for old software developed by old programmers:)

I’m actually surprised you didn’t cheat a little and modify types and add operator[] :slight_smile:

I installed Dr Memory and got it working. It is producing the log file and I see errors associated with plugin.dll, but can’t see any with my .cpp file name and line numbers. So, I tried adding -ggdb compile flags in my makefile , thinking that this would be enough to get debug symbols and line numbers into my plugin.dll and I would see then in the Dr Memory log. Is this the right way to do this under Windows and MinGW?

BTW, for this test I have a Rack patch with only my Meander module loaded. Also I’m just using the release Rack.exe build.

Thanks.

I think I’m good! My audit function allowed me to walk my primary structures and arrays and report any invalid values. Since many of Meanders’ arrays have elements that are indices into other arrays, it is pretty easy to check that those are valid indices for specific target arrays. The audit found 4 problems that I had induced earlier in the week, which I fixed. Based on this, I also added code to initialize all such parameters and array elements to valid values upon startup, so that it is less immune to unexpected oversights.

Last week I found an array that was incorrectly dimensioned. With last weeks fix and today’s fix, I can no longer induce the buggy behavior, no matter how much I crank around on value parameter and structural parameters while Meander is playing. I do have audio glitches arising with parameter control changes while running that I need to look more into.

I’ll continue trying to get Dr Memory working fully so I will have a tool for the future debugging:)

Thanks for your advice on all of this.

1 Like