Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading large chuck files before turning on audio causes Max to crash #11

Open
HighHarmonics2 opened this issue May 1, 2024 · 22 comments

Comments

@HighHarmonics2
Copy link
Contributor

I have a larger project with multiple chuck files, 12+ object instances, and a large event handler function. The Max patch is also large, with 2 separate instances of chuck~.

When I start the Max audio first, then load (run) the chuck program files, all is well.
When I load the chuck program files first, then start the audio, Max crashes.

I can reproduce the crash.

This is not a priority. I just need to remember to always start audio in Max before loading chuck files.
But it is worth considering. I would generally not expect to require the audio driver to be on first.

This problem does not occur with smaller chuck programs.

I may be operating outside the norm with 2 separate instances of chuck~ in a single Max patch.

@shakfu
Copy link
Owner

shakfu commented May 2, 2024

@HighHarmonics2 Thanks for this report. Let's try to figure what is causing the crash:

First thing Is that after it you crashes see if there is an error report that you can get on macOS. If you get one, can you just show what is on the top part of the traceback so we can see where it crashes specifically.

Also it would useful to figure out if it's due to the large size of the chuck file or the fact that you are running two instances of chuck~ or the combination thereof: i.e. two instances each running large files?

  • If you run the same large chuck file with only 1 chuck instance does it crash?
  • If you reduce the size of the chuck files with 2 instance running does it still crash?
  • Can you switch one of the instances off and retest to check if it is a specific instance.

Maybe you can shrink it to the smallest case which is causing the crash and then we can be better informed on where to begin addressing it.

@HighHarmonics2
Copy link
Contributor Author

I created a new Max patch and put in just one chuck~ with a subpatch with multiple run <filepath> messages.
No 2nd chuck~ and none of the Max objects from the original patch. So it is as stripped down it can be. The one large file has dependencies on all the other files (public class files), so I can't strip it down further.

It had the same behavior.

Here is the top of the crash report:

Process:               Max [3134]
Path:                  /Applications/Max.app/Contents/MacOS/Max
Identifier:            com.cycling74.Max
Version:               8.6.2 (d076223e34e) (8.6.2)
Code Type:             X86-64 (Native)
Parent Process:        launchd [1]
User ID:               501

Date/Time:             2024-05-01 20:54:25.7310 -0700
OS Version:            macOS 12.7.4 (21H1123)
Report Version:        12
Anonymous UUID:        1FB03842-39AD-0FE0-4DB0-94B48A00E427

Sleep/Wake UUID:       BA960FB5-D7BC-43C5-AC8C-A015FD8B086F

Time Awake Since Boot: 8500 seconds
Time Since Wake:       1270 seconds

System Integrity Protection: enabled

Crashed Thread:        56  com.apple.audio.IOThread.client

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000000
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    Namespace SIGNAL, Code 11 Segmentation fault: 11
Terminating Process:   exc handler [3134]

VM Region Info: 0 is not in any region.  Bytes before following region: 4423823360
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  
      __TEXT                      107ae3000-1087b3000    [ 12.8M] r-x/r-x SM=COW  ...nts/MacOS/Max

Thread 0:: CrBrowserMain Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	    0x7ff805d6593a mach_msg_trap + 10
1   libsystem_kernel.dylib        	    0x7ff805d65ca8 mach_msg + 56
2   CoreFoundation                	    0x7ff805e6929d __CFRunLoopServiceMachPort + 319
3   CoreFoundation                	    0x7ff805e67928 __CFRunLoopRun + 1276
4   CoreFoundation                	    0x7ff805e66d6c CFRunLoopRunSpecific + 562
5   HIToolbox                     	    0x7ff80eb195e6 RunCurrentEventLoopInMode + 292
6   HIToolbox                     	    0x7ff80eb1934a ReceiveNextEventCommon + 594
7   HIToolbox                     	    0x7ff80eb190e5 _BlockUntilNextEventMatchingListInModeWithFilter + 70
8   AppKit                        	    0x7ff8088a5aa9 _DPSNextEvent + 927
9   AppKit                        	    0x7ff8088a4166 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1394
10  AppKit                        	    0x7ff808896818 -[NSApplication run] + 586
11  Chromium Embedded Framework   	       0x10cc78376 ChromeAppModeStart_v7 + 7312070
12  Chromium Embedded Framework   	       0x10cc76711 ChromeAppModeStart_v7 + 7304801
13  Chromium Embedded Framework   	       0x10cc1eafa ChromeAppModeStart_v7 + 6945354
14  Chromium Embedded Framework   	       0x10cbe22bf ChromeAppModeStart_v7 + 6697487
15  Chromium Embedded Framework   	       0x10922c4fa cef_zip_reader_create + 412538
16  Max                           	       0x107c9de02 MaxCefEventLoopHandler::runMessageLoop() + 18
17  Max                           	       0x10821e01e juce::JUCEApplicationBase::main() + 206
18  Max                           	       0x10821df33 juce::JUCEApplicationBase::main(int, char const**) + 83
19  dyld                          	       0x1184e152e start + 462

@shakfu
Copy link
Owner

shakfu commented May 2, 2024

Thanks, it looks like the audio thread crashed for some reason. Is there anyway to run the same patch in vanilla chuck (i.e. outside of Max using command line chuck or miniaudicle)

@shakfu
Copy link
Owner

shakfu commented May 2, 2024

Otherwise, can you provide more details of the patch and/or the file which is causing the crash, to see if I can replicate it somehow.

@HighHarmonics2
Copy link
Contributor Author

HighHarmonics2 commented May 2, 2024

Yes, this runs fine locally. I have a variable localOn which sets the global values so I can develop and test outside of Max.

I've attached a zip file with all the files.

burst.ck is the main file and has details on running it. It has file dependencies on all the other .ck files. To run:

  • set localOn 1
  • command line: > chuck instr-FMenv.ck instr-NoiseEnv.ck event-glissBurst.ck pan.ck burst.ck

That should also give you sound output.

I've included two Max files. One is the bare patch with just the chuck files loader. The other is the full patch.
You will need to change the file paths in the run message boxes in the subpatch p loadChuckFiles. It doesn't work for me without the full path. I'm not sure why.

Both of these crash Max when I load chuck files first, then turn on audio, and work file when audio is on before loading chuck.

fyi - it is still possible this is something local to me. I've had some problems in the past with Max crashing - but nothing like this where I can reproduce it.
burstCrashTest.zip

Thanks for looking into this. Hopefully it yields something helpful.

@shakfu
Copy link
Owner

shakfu commented May 2, 2024

Ok thanks. I'll check it out.

@shakfu
Copy link
Owner

shakfu commented May 2, 2024

Hi @HighHarmonics2

I think the problem was related to the file loading... I had a crash initially. I made some consistency changes in the chuck_tilde dev code and also added the feature to search for chuck files in the directory of the active patcher which simplifies things considerably. I also tweaked your patch a little bit (see attached)... but in the end it worked for me without issues. The code could use some cleanup, but in the interest of you being able to test / use it, I have committed it to the main branch.

Let me know if it works for you.

burstCrashTest-v2.zip

@HighHarmonics2
Copy link
Contributor Author

Thanks! I'm tied up the rest of the day but will get to it as soon as I can.

Searching in the current directory is a nice improvement.

@HighHarmonics2
Copy link
Contributor Author

No luck. I continue to have the same crash results.

The new run commands from a local dir work fine, so I know I have your latest.
I also did a fresh install of Max 8.6.2, including the step to remove ~/Library/Application Support/Cycling '74 with all the history, preferences, authentication, etc.

@shakfu
Copy link
Owner

shakfu commented May 3, 2024

Hi @HighHarmonics2

I just tried it again without it crashing (I'm on an M1 MacBook Air). I followed this sequence:

  1. Open burstCrashTest-FullPatch.maxpat
  2. Presentation Mode to get the ui
  3. Turn on the ezdac~
  4. Click on the load button to load the chuck files
  5. Turn on the toggle below main
  6. Turn on the toggle below shaper
  7. At this point I have audio and just select from the preferences
  8. Play with some of the other ui options

No issue with any of the above sequence... Are you following a different sequence which is leading to a crash?

@HighHarmonics2
Copy link
Contributor Author

HighHarmonics2 commented May 3, 2024

No - see my 1st comment:

When I start the Max audio first, then load (run) the chuck program files, all is well.
When I load the chuck program files first, then start the audio, Max crashes.

So, to get a crash do this:

  1. Open either .maxpat file
  2. Presentation Mode to get the ui
  3. Click on the load button to load the chuck files
  4. Turn on the ezdac~
    CRASH

I'm on an intel iMac - MacOS 12.7.4

@shakfu
Copy link
Owner

shakfu commented May 3, 2024

Ok, now I can reproduce it. I got the exact same crash. While it refers to the crash thread as:

Crashed Thread:        60  com.apple.audio.IOThread.client

The given crash traceback is as follows:

Thread 60 Crashed:: com.apple.audio.IOThread.client
0   chuck~                        	       0x115052cac Chuck_Globals_Manager::get_global_event(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) + 44 (chuck_globals.cpp:1320)
1   chuck~                        	       0x11505f330 Chuck_Instr_Alloc_Word_Global::execute(Chuck_VM*, Chuck_VM_Shred*) + 476 (chuck_instr.cpp:4162)
2   chuck~                        	       0x1150cb8e4 Chuck_VM_Shred::run(Chuck_VM*) + 100 (chuck_vm.cpp:2230)
3   chuck~                        	       0x1150cb5bc Chuck_VM::compute() + 88 (chuck_vm.cpp:567)
4   chuck~                        	       0x1150cce78 Chuck_VM::run(long, float const*, float*) + 124 (chuck_vm.cpp:648)
5   chuck~                        	       0x115017788 ck_perform64(_ck*, object*, double**, long, double**, long, long, long, void*) + 484 (chuck_tilde.cpp:1684)
6   MaxAudioAPIImpl               	       0x10bd84fb8 dspchain_tick + 336
7   MaxAudioAPIImpl               	       0x10bd70178 plugrunner_tick + 204
8   MaxAudioAPIImpl               	       0x10bd6573c plugrunner_process + 208
9   MaxAudioAPIImpl               	       0x10bd6423c patchernode_process + 408
10  MaxAudioAPIImpl               	       0x10bd60708 mixerengine_process_fun + 76
11  Max                           	       0x1047f6460 linklist_funall_imp + 376
12  MaxAudioAPIImpl               	       0x10bd5ffc8 mixerengine_processiovector + 924
13  MaxAudioAPIImpl               	       0x10bd81024 ad_process + 228
14  ad_coreaudio                  	       0x10bc12d60 adcoreaudio_callback + 516
15  Max                           	       0x104a68ff0 juce::CoreAudioClasses::CoreAudioInternal::audioCallback(AudioTimeStamp const*, AudioTimeStamp const*, AudioBufferList const*, AudioBufferList*) + 748
16  Max                           	       0x104a68cc4 juce::CoreAudioClasses::CoreAudioInternal::audioIOProc(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*) + 28
17  CoreAudio                     	       0x185f14fb8 HALC_ProxyIOContext::IOWorkLoop() + 9508
18  CoreAudio                     	       0x185f1235c invocation function for block in HALC_ProxyIOContext::HALC_ProxyIOContext(unsigned int, unsigned int) + 108
19  CoreAudio                     	       0x1860984e4 HALC_IOThread::Entry(void*) + 88
20  libsystem_pthread.dylib       	       0x1836a6f94 _pthread_start + 136
21  libsystem_pthread.dylib       	       0x1836a1d34 thread_start + 8

I think it has to do with how the audio thread starts and stops in chuck~: it assumes that the normal uncrashing sequence of audio on first then run:

void ck_dsp64(t_ck* x, t_object* dsp64, short* count, double samplerate,
              long maxvectorsize, long flags)
{
    // post("sample rate: %f", samplerate);
    // post("maxvectorsize: %d", maxvectorsize);

    delete[] x->in_chuck_buffer;
    delete[] x->out_chuck_buffer;

    x->in_chuck_buffer = new float[maxvectorsize * x->channels];
    x->out_chuck_buffer = new float[maxvectorsize * x->channels];

    memset(x->in_chuck_buffer, 0.f,
           sizeof(float) * maxvectorsize * x->channels);
    memset(x->out_chuck_buffer, 0.f,
           sizeof(float) * maxvectorsize * x->channels);

    object_method(dsp64, gensym("dsp_add64"), x, ck_perform64, 0, NULL);
}


void ck_perform64(t_ck* x, t_object* dsp64, double** ins, long numins,
                  double** outs, long numouts, long sampleframes, long flags,
                  void* userparam)
{
    float* in_ptr = x->in_chuck_buffer;
    float* out_ptr = x->out_chuck_buffer;
    long n = sampleframes; // n = 64

    if (ins) {
        for (int i = 0; i < n; i++) {
            for (int chan = 0; chan < numins; chan++) {
                *(in_ptr++) = ins[chan][i];
            }
        }
    }

    x->chuck->run(x->in_chuck_buffer, x->out_chuck_buffer, n);

    for (int i = 0; i < n; i++) {
        for (int chan = 0; chan < numouts; chan++) {
            outs[chan][i] = *out_ptr++;
        }
    }
}

In ck_dsp64(), memory is allocated just before the audio thread is about to run. In your case, you are running somehow and I suppose something is trying to fill memory which is not yet allocated.. hence a crash.

Since the crashing point is given as the get_global_event function, can you please switch off all chuck global event triggering and rerun the patch?

@HighHarmonics2
Copy link
Contributor Author

Ok - I had two attempts, the 2nd disables more code and doesn't crash. The first still crashes at thread with get_global_event.

First attempt - same crash
This disables events, but still instantiates the event objects.
in burst.ck:

  • comment out lines 215 - 221 The spork calls invoke the fmHandler function which is where the event is started (line 76 e => now;
  • comment line 252 (event trigger)
  • line 58 0 => localOn;

Second attempt - runs without crash
In addition, disable the event object instantiation:

  • comment lines 210-211
  • comment lines 236-238 (variable assignment to event objects has to be disabled

@shakfu
Copy link
Owner

shakfu commented May 4, 2024

@HighHarmonics2

I spent some time yesterday and today trying to figure this out.

  1. In one experiment, I don't start chuck vm by default (there's one vm per chuck~ instance) and added a vm message to indicate the status of the vm. Previously the vm is initialized and running by default, whereas after my change, the vm is initialized but not running by default and it is only run when chuck->run(), the audio processing block, is called in the Max audio thread. I thought this would solve it but it didn't, the same crash happens after loading and then switch on audio in Max.

  2. Another variant of (1) is that I clear globals just after loading and before running the audio thread in Max. This crashed as well.

  3. Another experiment is that I load the patch, then clear vm and then open audio on Max. This works and forces a user reload of the patch, after which everything works.

Would it be possible get a minimum reproducible case where with one change in the code it starts working and if that is reversed it's stops working. I was having difficulties getting this from my side even with your instructions.

I think this issue has something to do with events (as per the error traceback) and also threads, so it's going to take time to figure out. I'd like to have a minimal reproducible case to share with Chuck core developers. I noted in the comments in the chuck code around the event buffer that there was a crash previously circa 1.3 which was fixed with the addition of the event buffer, perhaps this crash is related to startup assumptions made around this which were broken with the combination of Max / chuck~

In any case, until this is figured out, the question is how do we fix this critical bug now? Here are some ideas:

  1. The most drastic solution is to automatically clearvm before running the audio thread in Max

  2. Check if the audio thread is running via int sys_getdspstate(void); and if it is not then give an error if run or add is called, stating that the audio thread should be running before running or adding chuck code.

  3. Figure out the conditions under which the minimal reproducible case applies and then check for those and take appropriate measures...

I'd like to avoid (1), potentially start with (2) and hopefully, once we have understood the minimal reproducible case more clearly, end up with (3) or with another direct solution which address the problem at its source.

@shakfu
Copy link
Owner

shakfu commented May 4, 2024

@HighHarmonics2 I've applied the fix as per solution (2) in my prior post, and committed the change to main branch.. It's looks like scotch-tape but at least it works for now.

@shakfu
Copy link
Owner

shakfu commented May 4, 2024

On second thoughts, I will turn this into a switch that you can use for practical purposes, but I would like it off by default in order to catch more instances which crashes can occur.

@HighHarmonics2
Copy link
Contributor Author

  1. Check if the audio thread is running via int sys_getdspstate(void); and if it is not then give an error if run or add is called, stating that the audio thread should be running before running or adding chuck code.

This makes sense - detect the fault condition and prevent the crash.

I can work on a minimum reproducible solution. All that is needed is a .ck file with a minimal audio chain, an event, and a few globals to control the event from Max. Then a max patch that doesn't do much except trigger the event and change the globals. I don't think this needs multiple files, public classes, lots of globals, etc. But I'll make sure I can crash it reliably.

It's great to have you working hard to get this fixed - full crashes in Max are awful, particularly to users who might not be prepared for it. But just so you know - I'm not blocked by this for my own chuck-max work. As long as audio is on before loading chuck files, everything runs fine.

I'm offline the rest of today - but will try to test and respond again later tonight or tomorrow.

@HighHarmonics2
Copy link
Contributor Author

crash.zip

Here is a ck + maxpat that can be used to test / validate. crash.ck has usage instructions in the comments. You can set the eventOn variable to 1 for events to function. 0 => int eventOn will disable the spork~ call for the eventHandler function. That is the part of the code that causes the crash.

crash.maxpat is basic, with just three global parameters and an event trigger.

@shakfu
Copy link
Owner

shakfu commented May 5, 2024

@HighHarmonics2

Thanks for the new crash examples.

FYI, I have added a not-so-well named attribute, run_needs_audio, to switch on/off the constraint of being able to run/add shreds when audio is off.

@shakfu
Copy link
Owner

shakfu commented May 5, 2024

I've reduced the crash case down to the following simplest possible case (I think):

First, the chuck file:

class CustomEvent extends Event {}

global CustomEvent e;

while (true)
{
    1::second => now;
}

if global CustomEvent e; is declared after the class then it causes the crash. It does not matter whether CustomEvent is a normal or public class, the crash is the same.

Now for the patch:

Screenshot 2024-05-05 at 9 51 35 AM

If the chuck file is as above, and in the patch the (clear vm) message or its synonym, (reset), is sent before the run crash.ck or add crash.ckmessage then a crash in the Max host occurs.

crash-case.zip

I have attached a zipped folder with the different crash/nocrash scenarios.

In summary this is clearly an obscure bug, which you have somehow discovered in your patching!

I really don't have an answer of why this is occurring, and the best I can offer right now is this run_needs_audio switch which prevents the crash.

Perhaps chuck core dev can shed some light on what's going here...

@HighHarmonics2
Copy link
Contributor Author

fyi - run_needs_audio is working well for me.

@shakfu
Copy link
Owner

shakfu commented May 7, 2024

@HighHarmonics2

Glad it's working for you. It's really a stop-gap measure until we can figure what's going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants