I’m synthesising audio on an iPhone using additive synthesis and my app, written in Objective C, crashes while playing audio. The synthesis engine worked in a previous version of the app and with an earlier iOS. I'm currently testing it for iOS 10.3.3 using Xcode 8.3.3.
EDIT - in the current version, the view controller sends MIDI note on messages to communicate with the synthesis engine. The previous version did not use MIDI commands.
The app synthesises two sounds: a bell and a chorus of sine tones. It does this smoothly and without transients. However it will crash if one sound is playing while attempting to play the other. Yet it never crashes if the same sound is repeated or if the user stops the chorus sound before playing the bell sound (or vice versa).
The bell tone has 11 partials of fixed frequency, each partial shaped by an amplitude envelope. The chorus tone has 5 partials each with a pitch shift envelope and an amplitude envelope. Envelope duration is calculated at runtime from when a user triggers a sound until the end of the last active state, as shown in the diagram (below).
The crash happens consistently under two conditions.
Firstly, a crash can be caused by playing a bell while a chorus sound is still playing, in which case Xcode goes into debug looking for Amplitude partial 6, a partial that would not be used if the buffer was still trying to access a chorus sound which only has 5 partials.
The diagram shows what should happen if two sound events played in the first state (between 0:00 and 0:24) are followed by a third sound played in the following state. The envelope duration of all three is calculated to end at 1:36. Normally, the bell sound (between 0:24 and 0:48) would last until 1:36, unless a player interrupts it by playing another sound as happens with the previous two sounds. However, being the first sound played in a new state, playing it sends the app into debug.
Alternately, a crash can also be caused by playing a chorus sound while a bell is still playing, in which Xcode goes into debug looking at Pitch shift partial 1, a partial that would never be used if the buffer was still trying to access a bell sound where pitch is fixed.
In both examples, it looks as if my code fails when it tries to access a buffer that was active prior to the change of state. But I can't find the reason. I tried changing the buffer size but that made little difference (it just happened to be set to 5 when I produced the logs below).
Below are two methods I use to service buffer input for bell and chorus synthesis, the typedef that defines buffer states and a debug log for a typical bell crash and chorus crash. I need some feedback on these before diving into the routine where audio is synthesised into the output buffer. I'm happy to share that too but hopefully the problem might already be obvious for someone else.
The app almost works but for the past few weeks I’ve broken the code and fixed it several times without getting closer to solving this problem.
My code is an extension of an extremely helpful AudioBufferPlayer and Synth example created by Mattias Hollejman and refined by Mario Diana.
UPDATE
I added a new method called showPark to produce a clearer log showing the state of the audio buffer as each live event arrives. When audio buffer size is 3, the log clearly shows the buffer filling after three events leaving no where to go for all subsequent events.
The problem is not evident in the first 24 seconds because the synthesis parameters remain the same. However after 24 seconds, when event 6 happens, things change and the synthesis engine cannot access parameters it needs to synthesise a bell sound. I think this explains how the crash happens. On that basis I'm trying to work out the best way to fix the problem and I welcome any suggestions from anyone who knows what to do.
Here is a crash log.
crash log (latest)
2017-08-01 13:48:59.330 SatGam2[1169:1226966] PlayView loaded (selectedFamily:1 selectedPlayerID:9)
2017-08-01 13:48:59.331 SatGam2[1169:1226966] ENABLED (player 9: state 0)
2017-08-01 13:49:01.071 SatGam2[1169:1226966]
2017-08-01 13:49:01.072 SatGam2[1169:1226966] EVENT 1
2017-08-01 13:49:01.072 SatGam2[1169:1226966]
2017-08-01 13:49:01.072 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:01.073 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:01.073 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:01.073 SatGam2[1169:1226966] update parkPointer [1]
2017-08-01 13:49:01.073 SatGam2[1169:1226966]
2017-08-01 13:49:01.074 SatGam2[1169:1226966] Risset Chorus playing MIDI note number: 1 length: 95
2017-08-01 13:49:01.074 SatGam2[1169:1226966] [playChorus - buffer: 0 state: 0]
2017-08-01 13:49:03.873 SatGam2[1169:1226966] EVENT 2
2017-08-01 13:49:03.874 SatGam2[1169:1226966]
2017-08-01 13:49:03.874 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 1 State: ToneEventStatePressed
2017-08-01 13:49:03.874 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:03.875 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:03.875 SatGam2[1169:1226966] update parkPointer [2]
2017-08-01 13:49:03.875 SatGam2[1169:1226966]
2017-08-01 13:49:03.875 SatGam2[1169:1226966] Risset Chorus playing MIDI note number: 3 length: 92
2017-08-01 13:49:03.876 SatGam2[1169:1226966] [playChorus - buffer: 1 state: 0]
2017-08-01 13:49:06.180 SatGam2[1169:1226966] EVENT 3
2017-08-01 13:49:06.180 SatGam2[1169:1226966]
2017-08-01 13:49:06.180 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 1 State: ToneEventStatePressed
2017-08-01 13:49:06.180 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 3 State: ToneEventStatePressed
2017-08-01 13:49:06.181 SatGam2[1169:1226966] MIDI Status: 0 MIDI Note: 0 State: ToneEventStateInactive
2017-08-01 13:49:06.181 SatGam2[1169:1226966] update parkPointer [0]
2017-08-01 13:49:06.181 SatGam2[1169:1226966]
2017-08-01 13:49:06.181 SatGam2[1169:1226966] Risset Chorus playing MIDI note number: 5 length: 90
2017-08-01 13:49:06.182 SatGam2[1169:1226966] [playChorus - buffer: 2 state: 0]
2017-08-01 13:49:10.162 SatGam2[1169:1226966] EVENT 4
2017-08-01 13:49:10.162 SatGam2[1169:1226966]
2017-08-01 13:49:10.163 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 1 State: ToneEventStatePressed
2017-08-01 13:49:10.163 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 3 State: ToneEventStatePressed
2017-08-01 13:49:10.163 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 5 State: ToneEventStatePressed
2017-08-01 13:49:10.163 SatGam2[1169:1226966] update parkPointer [1]
2017-08-01 13:49:10.163 SatGam2[1169:1226966]
2017-08-01 13:49:10.164 SatGam2[1169:1226966] Risset Chorus playing MIDI note number: 7 length: 86
2017-08-01 13:49:11.352361+1000 SatGam2[1169:1228409] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
2017-08-01 13:49:13.041 SatGam2[1169:1226966]
2017-08-01 13:49:13.042 SatGam2[1169:1226966] EVENT 5
2017-08-01 13:49:13.043 SatGam2[1169:1226966]
2017-08-01 13:49:13.043 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 1 State: ToneEventStatePressed
2017-08-01 13:49:13.043 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 3 State: ToneEventStatePressed
2017-08-01 13:49:13.043 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 5 State: ToneEventStatePressed
2017-08-01 13:49:13.044 SatGam2[1169:1226966] update parkPointer [2]
2017-08-01 13:49:13.044 SatGam2[1169:1226966]
2017-08-01 13:49:13.044 SatGam2[1169:1226966] Risset Chorus playing MIDI note number: 9 length: 83
2017-08-01 13:49:23.332 SatGam2[1169:1226966] ENABLED (player 9: state 1)
2017-08-01 13:49:25.228 SatGam2[1169:1226966] EVENT 6
2017-08-01 13:49:25.228 SatGam2[1169:1226966]
2017-08-01 13:49:25.229 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 1 State: ToneEventStatePressed
2017-08-01 13:49:25.229 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 3 State: ToneEventStatePressed
2017-08-01 13:49:25.229 SatGam2[1169:1226966] MIDI Status: 144 MIDI Note: 5 State: ToneEventStatePressed
2017-08-01 13:49:25.229 SatGam2[1169:1226966] update parkPointer [0]
2017-08-01 13:49:25.229 SatGam2[1169:1226966]
2017-08-01 13:49:25.230 SatGam2[1169:1226966] Risset Bell playing MIDI note number: 0 length: 71
SatGam2 was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) bt
* thread #9, name = 'com.apple.coreaudio.AQClient', stop reason = EXC_BAD_ACCESS (code=1, address=0xb174)
* frame #0: 0x000a1ea5 SatGam2`-[Synth amplitudeLookupPartial6:](self=<unavailable>, _cmd="amplitudeLookupPartial6:", n=<unavailable>) at Synth.m:988 [opt]
frame #1: 0x000a1519 SatGam2`-[Synth fillBuffer:frames:](self=0x7a831a00, _cmd="fillBuffer:frames:", buffer=<unavailable>, frames=<unavailable>) at Synth.m:805 [opt]
frame #2: 0x000a516c SatGam2`__39-[PlayViewController startAudioSession]_block_invoke((null)=0x7ce7f8e0, buffer=<unavailable>, audioFormat=AudioStreamBasicDescription # 0xb0422ab8) at PlayViewController.m:397 [opt]
frame #3: 0x000a3aa8 SatGam2`PlayCallback(inUserData=0x7946f4c0, inAudioQueue=<unavailable>, inBuffer=<unavailable>) at AudioBufferPlayer.m:30 [opt]
(the rest of the log is the same as logs shown below)
Here is the code for the new method
- (void)showPark:(uint8_t)midiStatus data1:(uint8_t)mapped_MIDINoteNumber
{
// see if MIDI events are currently active and what kind of event
eventCount++;
NSLog(#"\n\n");
NSLog(#"EVENT %i", eventCount);
NSLog(#"\n");
for (int n = 0; n < MaxToneEvents; ++n)
{
// scan buffers
int a = _tones[n].state;
int b = _tones[n].midiStatus;
int c = _tones[n].midiNote;
NSLog(#" MIDI Status: %3d MIDI Note: %3d State: %#", b, c, [self getCurrent:(int)a]);
}
// update parkPointer
parkPointer = (parkPointer + 1) % MaxToneEvents;
NSLog(#" update parkPointer [%i]", parkPointer);
NSLog(#"\n");
}
-(NSString *)getCurrent:(int)state
{
switch(state){
case ToneEventStateInactive:
return #"ToneEventStateInactive";
case ToneEventStatePressed:
return #"ToneEventStatePressed";
case ToneEventStateReleased:
return #"ToneEventStateReleased";
default:
return #"null";
}
}
showPark is called from a method not shown previously so I'll include it here.
- (void)sendMIDIEvent:(uint8_t)midiStatus
data1:(uint8_t)mapped_MIDINoteNumber // dekany buttons mapped to MIDI Note Numbers 0-9
data2:(uint8_t)data2
{
// 1. check parking bay
[self showPark:(uint8_t)midiStatus data1:(uint8_t)mapped_MIDINoteNumber];
// 2. identify MIDI status
if (midiStatus == MIDI_Status_KeyOn_RissetChorus) // 144
{
// 3. pick up duration and send a message to playChorus
[self playChorus:(uint8_t)midiStatus
chorusPitch:(uint8_t)mapped_MIDINoteNumber
length:(int)noteLength];
}
else
{
if (midiStatus == MIDI_Status_KeyOn_RissetBell) // 145
{
// 4. pick up duration and send a message to playBell
[self playBell:(uint8_t)midiStatus
bellPitch:(uint8_t)mapped_MIDINoteNumber
length:(int)noteLength];
}
}
}
playBell
-(void)playBell:(uint8_t)midiStatus
bellPitch:(uint8_t)midiNoteNumber
length:(int)length;
{
hasPitchEnvelope = FALSE;
for (int n = 0; n < MaxToneEvents; ++n)
{
if (_tones[n].state == ToneEventStateInactive) // find an empty slot
{
_tones[n].state = ToneEventStatePressed;
_tones[n].frequency = _pitches[midiNoteNumber];
_tones[n].phase1 = 0.0f;
_tones[n].phase2 = 0.0f;
_tones[n].phase3 = 0.0f;
_tones[n].phase4 = 0.0f;
_tones[n].phase5 = 0.0f;
_tones[n].phase6 = 0.0f;
_tones[n].phase7 = 0.0f;
_tones[n].phase8 = 0.0f;
_tones[n].phase9 = 0.0f;
_tones[n].phase10 = 0.0f;
_tones[n].phase11 = 0.0f;
_tones[n].levelEnvelope1 = _levelPartialEnvelope1;
_tones[n].levelEnvelope2 = _levelPartialEnvelope2;
_tones[n].levelEnvelope3 = _levelPartialEnvelope3;
_tones[n].levelEnvelope4 = _levelPartialEnvelope4;
_tones[n].levelEnvelope5 = _levelPartialEnvelope5;
_tones[n].levelEnvelope6 = _levelPartialEnvelope6;
_tones[n].levelEnvelope7 = _levelPartialEnvelope7;
_tones[n].levelEnvelope8 = _levelPartialEnvelope8;
_tones[n].levelEnvelope9 = _levelPartialEnvelope9;
_tones[n].levelEnvelope10 = _levelPartialEnvelope10;
_tones[n].levelEnvelope11 = _levelPartialEnvelope11;
_tones[n].gain = 1.0f;
_tones[n].envStep = 0.0f;
_tones[n].envDelta = ENVELOPE_LENGTH / length; // GS Envelope
_tones[n].fadeOut = 1.0f;
return;
}
}
}
playChorus
- (void)playChorus:(uint8_t)midiStatus
chorusPitch:(uint8_t)midiNoteNumber
length:(int)length;
{
hasPitchEnvelope = TRUE;
for (int n = 0; n < MaxToneEvents; ++n)
{
if (_tones[n].state == ToneEventStateInactive) // find an empty slot
{
_tones[n].state = ToneEventStatePressed;
_tones[n].frequency = _pitches[midiNoteNumber];
_tones[n].phase1 = 0.0f;
_tones[n].phase2 = 0.0f;
_tones[n].phase3 = 0.0f;
_tones[n].phase4 = 0.0f;
_tones[n].phase5 = 0.0f;
_tones[n].phase6 = 0.0f;
_tones[n].phase7 = 0.0f;
_tones[n].phase8 = 0.0f;
_tones[n].phase9 = 0.0f;
_tones[n].phase10 = 0.0f;
_tones[n].phase11 = 0.0f;
_tones[n].levelEnvelope1 = _levelEnvelopeAboveOuter;
_tones[n].levelEnvelope2 = _levelEnvelopeAboveInner;
_tones[n].levelEnvelope3 = _levelEnvelopeCentreNote;
_tones[n].levelEnvelope4 = _levelEnvelopeBelowInner;
_tones[n].levelEnvelope5 = _levelEnvelopeBelowOuter;
_tones[n].pitchEnvelope1 = _pitchEnvelopeAboveOuter;
_tones[n].pitchEnvelope2 = _pitchEnvelopeAboveInner;
_tones[n].pitchEnvelope3 = _pitchEnvelopeCentreNote;
_tones[n].pitchEnvelope4 = _pitchEnvelopeBelowInner;
_tones[n].pitchEnvelope5 = _pitchEnvelopeBelowOuter;
_tones[n].gain = 1.0f;
_tones[n].envStep = 0.0f;
_tones[n].envDelta = ENVELOPE_LENGTH / length; // GS Envelope
_tones[n].fadeOut = 1.0f;
return;
}
}
}
ToneEvent
typedef enum
{
ToneEventStateInactive = 0, // ToneEvent is not used for playing a tone
ToneEventStatePressed, // ToneEvent is still playing normally
ToneEventStateReleased, // ToneEvent is released and ringing out
}
ToneEventState;
bell crash
2017-07-28 21:05:28.438 SatGam2[897:503237] ENABLED (player 4: state 1)
2017-07-28 21:05:28.502804+1000 SatGam2[897:504929] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
2017-07-28 21:05:30.511372+1000 SatGam2[897:504929] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
2017-07-28 21:05:30.852 SatGam2[897:503237] ToneEventStatePressed
2017-07-28 21:05:30.853 SatGam2[897:503237] ToneEventStateReleased
2017-07-28 21:05:30.853 SatGam2[897:503237] ToneEventStatePressed
2017-07-28 21:05:30.854 SatGam2[897:503237] ToneEventStatePressed
2017-07-28 21:05:30.854 SatGam2[897:503237] ToneEventStatePressed
2017-07-28 21:05:30.854 SatGam2[897:503237] noteLength 70
2017-07-28 21:05:30.855 SatGam2[897:503237] timeToGo 70 [RissetBells, from PlayViewController]
2017-07-28 21:05:30.855 SatGam2[897:503237] Risset Bell MIDI note number 1 : length 70
2017-07-28 21:05:30.855 SatGam2[897:503237] Clapper button 7 pressed
SatGam2 was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) bt
* thread #10, name = 'com.apple.coreaudio.AQClient', stop reason = EXC_BAD_ACCESS (code=1, address=0x9514)
* frame #0: 0x00071d82 SatGam2`-[Synth amplitudeLookupPartial6:](self=<unavailable>, _cmd="amplitudeLookupPartial6:", n=<unavailable>) at Synth.m:875 [opt]
frame #1: 0x000713f6 SatGam2`-[Synth fillBuffer:frames:](self=0x7e805a00, _cmd="fillBuffer:frames:", buffer=<unavailable>, frames=<unavailable>) at Synth.m:692 [opt]
frame #2: 0x000751e9 SatGam2`__39-[PlayViewController startAudioSession]_block_invoke((null)=0x7ae48160, buffer=<unavailable>, audioFormat=AudioStreamBasicDescription # 0xb0491ab8) at PlayViewController.m:393 [opt]
frame #3: 0x00073985 SatGam2`PlayCallback(inUserData=0x7c44ac60, inAudioQueue=<unavailable>, inBuffer=<unavailable>) at AudioBufferPlayer.m:30 [opt]
frame #4: 0x00a65e90 AudioToolbox`ClientAudioQueue::CallOutputCallback(AudioQueueBuffer*) + 336
frame #5: 0x00a69a99 AudioToolbox`ClientMessageHandler::OutputBufferComplete(unsigned int) + 137
frame #6: 0x00a5fc90 AudioToolbox`AQClientCallbackMessageReader::DispatchCallbacks(void const*, unsigned long) + 176
frame #7: 0x00a5f98d AudioToolbox`ClientAudioQueue::FetchAndDeliverPendingCallbacks(unsigned int) + 301
frame #8: 0x00a5fb78 AudioToolbox`AQCallbackReceiver_CallbackNotificationsAvailable + 152
frame #9: 0x009b071a AudioToolbox`_XCallbackNotificationsAvailable + 42
frame #10: 0x00c3b37b AudioToolbox`mshMIGPerform + 207
frame #11: 0x035e28b3 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 35
frame #12: 0x035e2822 CoreFoundation`__CFRunLoopDoSource1 + 498
frame #13: 0x035daccd CoreFoundation`__CFRunLoopRun + 2669
frame #14: 0x035d9fd4 CoreFoundation`CFRunLoopRunSpecific + 372
frame #15: 0x035d9e4b CoreFoundation`CFRunLoopRunInMode + 123
frame #16: 0x00a6a822 AudioToolbox`GenericRunLoopThread::Entry(void*) + 178
frame #17: 0x00c406b2 AudioToolbox`CAPThread::Entry(CAPThread*) + 96
frame #18: 0x06215047 libsystem_pthread.dylib`_pthread_body + 184
frame #19: 0x06214f8f libsystem_pthread.dylib`_pthread_start + 243
frame #20: 0x0621484a libsystem_pthread.dylib`thread_start + 34
(lldb)
chorus crash
2017-07-28 20:30:13.061 SatGam2[864:436890] ENABLED (player 4: state 2)
2017-07-28 20:30:13.227994+1000 SatGam2[864:438359] [aqme] 254: AQDefaultDevice (173): skipping input stream 0 0 0x0
2017-07-28 20:30:14.878 SatGam2[864:436890] ToneEventStatePressed
2017-07-28 20:30:14.878 SatGam2[864:436890] ToneEventStateReleased
2017-07-28 20:30:14.879 SatGam2[864:436890] ToneEventStatePressed
2017-07-28 20:30:14.879 SatGam2[864:436890] ToneEventStatePressed
2017-07-28 20:30:14.879 SatGam2[864:436890] ToneEventStatePressed
2017-07-28 20:30:14.879 SatGam2[864:436890] noteLength 47
2017-07-28 20:30:14.880 SatGam2[864:436890] timeToGo 47 [RissetChorus, from PlayViewController]
2017-07-28 20:30:14.880 SatGam2[864:436890] Risset Chorus MIDI note number 3 : length 47
2017-07-28 20:30:14.880 SatGam2[864:436890] Note button 4 pressed
SatGam2 was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) bt
* thread #10, name = 'com.apple.coreaudio.AQClient', stop reason = EXC_BAD_ACCESS (code=1, address=0xe62c)
* frame #0: 0x0007b728 SatGam2`-[Synth pitchShiftLookupPartial1:](self=0x7bb0d400, _cmd="pitchShiftLookupPartial1:", n=0) at Synth.m:762 [opt]
frame #1: 0x0007aeb6 SatGam2`-[Synth fillBuffer:frames:](self=0x7bb0d400, _cmd="fillBuffer:frames:", buffer=<unavailable>, frames=<unavailable>) at Synth.m:724 [opt]
frame #2: 0x0007f1e9 SatGam2`__39-[PlayViewController startAudioSession]_block_invoke((null)=0x7c74d5b0, buffer=<unavailable>, audioFormat=AudioStreamBasicDescription # 0xb01f9ab8) at PlayViewController.m:393 [opt]
frame #3: 0x0007d985 SatGam2`PlayCallback(inUserData=0x7b656430, inAudioQueue=<unavailable>, inBuffer=<unavailable>) at AudioBufferPlayer.m:30 [opt]
frame #4: 0x00a14e90 AudioToolbox`ClientAudioQueue::CallOutputCallback(AudioQueueBuffer*) + 336
frame #5: 0x00a18a99 AudioToolbox`ClientMessageHandler::OutputBufferComplete(unsigned int) + 137
frame #6: 0x00a0ec90 AudioToolbox`AQClientCallbackMessageReader::DispatchCallbacks(void const*, unsigned long) + 176
frame #7: 0x00a0e98d AudioToolbox`ClientAudioQueue::FetchAndDeliverPendingCallbacks(unsigned int) + 301
frame #8: 0x00a0eb78 AudioToolbox`AQCallbackReceiver_CallbackNotificationsAvailable + 152
frame #9: 0x0095f71a AudioToolbox`_XCallbackNotificationsAvailable + 42
frame #10: 0x00bea37b AudioToolbox`mshMIGPerform + 207
frame #11: 0x035988b3 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 35
frame #12: 0x03598822 CoreFoundation`__CFRunLoopDoSource1 + 498
frame #13: 0x03590ccd CoreFoundation`__CFRunLoopRun + 2669
frame #14: 0x0358ffd4 CoreFoundation`CFRunLoopRunSpecific + 372
frame #15: 0x0358fe4b CoreFoundation`CFRunLoopRunInMode + 123
frame #16: 0x00a19822 AudioToolbox`GenericRunLoopThread::Entry(void*) + 178
frame #17: 0x00bef6b2 AudioToolbox`CAPThread::Entry(CAPThread*) + 96
frame #18: 0x0621f047 libsystem_pthread.dylib`_pthread_body + 184
frame #19: 0x0621ef8f libsystem_pthread.dylib`_pthread_start + 243
frame #20: 0x0621e84a libsystem_pthread.dylib`thread_start + 34
(lldb)
Related
I use backtrace and backtrace_symbols to collect the call stack when my app crashed.
void InstallSignalHandler(void)
{
signal(SIGHUP, SignalExceptionHandler);
signal(SIGINT, SignalExceptionHandler);
signal(SIGQUIT, SignalExceptionHandler);
signal(SIGABRT, SignalExceptionHandler);
signal(SIGILL, SignalExceptionHandler);
signal(SIGSEGV, SignalExceptionHandler);
signal(SIGFPE, SignalExceptionHandler);
signal(SIGBUS, SignalExceptionHandler);
signal(SIGPIPE, SignalExceptionHandler);
}
void SignalExceptionHandler(int signal)
{
NSMutableString *mstr = [[NSMutableString alloc] init];
[mstr appendString:#"Stack:\n"];
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i <frames; ++i) {
[mstr appendFormat:#"%s\n", strs[i]];
}
NSLog(#"%#", mstr);
free(strs);
}
When I check the log in console, I just find the log miss the function that caused the crash.The function is:
+ (void)testCrash
{
int *nullPointer = NULL;
*nullPointer = 2019;
}
And the log in console is:
0 TestApp 0x0000000101d1e040 SignalExceptionHandler + 160
1 libsystem_platform.dylib 0x000000011002bb5d _sigtramp + 29
2 ??? 0x0000000000000000 0x0 + 0
3 TestApp 0x00000001019bbc6f __39+[MyCrashTesting showInViewController:]_block_invoke + 303
4 UIKit 0x000000010b09a559 -[UIAlertController _invokeHandlersForAction:] + 105
5 UIKit 0x000000010b09af5e __103-[UIAlertController _dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:]_block_invoke.461 + 16
6 UIKit 0x000000010ae42ca2 -[UIPresentationController transitionDidFinish:] + 1346
7 UIKit 0x000000010ae46b12 __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.436 + 183
8 UIKit 0x000000010ba2a3b4 -[_UIViewControllerTransitionContext c
I have thought the function name "testCrash" should be in the top of the log. Does I do something wrong?
Since this is a very simple method with no arguments and no ARC heavylifting inside clang the Objective-C compiler is able to do an optimization (-O1 is enough to achieve that) of removing the message runtime call.
You can prevent this behavior with:
+ (void)testCrash __attribute__((optnone))
{
int *nullPointer = NULL;
*nullPointer = 2019;
}
The lesson learnt here is you should never rely on particular stacktrace to achieve something during your program execution. Diagnostics is fine as long as you understand the optimizing principles you've just encountered.
I'm running a load test with wrk2 as a job on Jenkins. I'd like to send the results of the load test to Graylog but I only want to store the Requests/Sec and average latency.
Here's what the output looks like:
Running 30s test # https://example.com
1 threads and 100 connections
Thread calibration: mean lat.: 8338.285ms, rate sampling interval: 19202ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 16.20s 6.17s 29.64s 65.74%
Req/Sec 5.00 0.00 5.00 100.00%
Latency Distribution (HdrHistogram - Recorded Latency)
50.000% 15.72s
75.000% 20.81s
90.000% 24.58s
99.000% 29.13s
99.900% 29.66s
99.990% 29.66s
99.999% 29.66s
100.000% 29.66s
Detailed Percentile spectrum:
Value Percentile TotalCount 1/(1-Percentile)
4497.407 0.000000 1 1.00
7561.215 0.100000 11 1.11
11100.159 0.200000 22 1.25
12582.911 0.300000 33 1.43
14565.375 0.400000 44 1.67
15720.447 0.500000 54 2.00
16416.767 0.550000 60 2.22
17301.503 0.600000 65 2.50
18464.767 0.650000 71 2.86
19185.663 0.700000 76 3.33
20807.679 0.750000 81 4.00
21479.423 0.775000 84 4.44
22347.775 0.800000 87 5.00
22527.999 0.825000 90 5.71
23216.127 0.850000 93 6.67
23478.271 0.875000 95 8.00
23805.951 0.887500 96 8.89
24723.455 0.900000 98 10.00
25067.519 0.912500 99 11.43
25395.199 0.925000 101 13.33
26525.695 0.937500 102 16.00
26525.695 0.943750 102 17.78
26705.919 0.950000 103 20.00
28065.791 0.956250 104 22.86
28065.791 0.962500 104 26.67
28377.087 0.968750 105 32.00
28377.087 0.971875 105 35.56
28475.391 0.975000 106 40.00
28475.391 0.978125 106 45.71
28475.391 0.981250 106 53.33
29130.751 0.984375 107 64.00
29130.751 0.985938 107 71.11
29130.751 0.987500 107 80.00
29130.751 0.989062 107 91.43
29130.751 0.990625 107 106.67
29655.039 0.992188 108 128.00
29655.039 1.000000 108 inf
#[Mean = 16199.756, StdDeviation = 6170.105]
#[Max = 29638.656, Total count = 108]
#[Buckets = 27, SubBuckets = 2048]
----------------------------------------------------------
130 requests in 30.02s, 13.44MB read
Socket errors: connect 0, read 0, write 0, timeout 1192
Requests/sec: 4.33
Transfer/sec: 458.47KB
Does anyone know how I could go about extracting Requests/sec (at the bottom) and the latency average to send as JSON parameters?
The expected output would be: "latency": 16.2, "requests_per_second": 4.33
You didn't provide the expected output so your question isn't clear but is this what you want?
$ awk 'BEGIN{a["Latency"]; a["Requests/sec:"]} ($1 in a) && ($2 ~ /[0-9]/){print $1, $2}' file
Latency 16.20s
Requests/sec: 4.33
Updated based on you adding expected output to your question:
$ awk '
BEGIN { map["Latency"]="latency"; map["Requests/sec:"]="requests_per_second" }
($1 in map) && ($2 ~ /[0-9]/) { printf "%s\"%s\": %s", ofs, map[$1], $2+0; ofs=", " }
END { print "" }
' file
"latency": 16.2, "requests_per_second": 4.33
I have linux application on embedded linux that uses pthread library. From time to time pthread "manager" thread get signal 33 and crashed.
How do I know who is sending the signal?
Strace log:
...
18:15:07 getppid() = 30
18:15:07 poll([{fd=31, events=POLLIN}], 1, 2000) = 0
18:15:09 getppid() = 30
18:15:09 poll([{fd=31, events=POLLIN}], 1, 2000) = 0
18:15:11 getppid() = 30
18:15:11 poll([{fd=31, events=POLLIN}], 1, 2000) = 0
18:15:13 getppid() = 30
18:15:13 poll([{fd=31, events=POLLIN}], 1, 2000) = -1 EINTR (Interrupted system call)
18:15:13 --- SIGRT_1 (Unknown signal 33) # 0 (0) ---
18:15:13 getppid() = 30
18:15:13 wait4(-1, [WIFSIGNALED(s) && WTERMSIG(s) == SIGSEGV], WNOHANG|__WCLONE, NULL) = 68
18:15:13 --- SIGSEGV (Segmentation fault) # 0 (0) ---
Signal 33 is used internally by the pthreads library (it is referred to as SIGSETXID, and is raised by the library for every thread whenever one of the uids or gids of the process are changed).
It should not cause a thread to crash. Why do you believe this signal is responsible?
Could someone please explain to me why in the following code (using r25630 Windows), the value of iInsertTot at line 241 is null, or more to the point, why is line 234 ("return iInsertTot;") not executed and therefore at line 241, iInsertTot is null. The value of iInsertTot at lines 231/232 is an integer. While I can and probably should code this differently, I thought that I would try and see if it worked, because my understanding of Futures and Chaining was that it would work. I have used “return” in a similar way before and it worked, but I was returning null in those cases (eg. line 201 below).
/// The problem lines are :
233 fUpdateTotalsTable().then((_) {
234 return iInsertTot;
235 });
While running in the debugger, it appears that line 234 “return iInsertTot;” is never actually executed. Running from command line has the same result.
The method being called on line 233 (fUpdateTotalsTable) is something I am just in the process of adding, and it consists basically of sync code at this stage. However, the debugger appears to go through it correctly.
I have included the method “fUpdateTotalsTable()” (line 1076) just in case that is causing a problem.
Lines 236 to 245 have just been added, however just in case that code is invalid I have commented those lines out and run with the same problem occurring.
218 /*
219 * Process Inserts
220 */
221 }).then((_) {
222 sCheckpoint = "fProcessMainInserts";
223 ogPrintLine.fPrintForce ("Processing database ......");
224 int iMaxInserts = int.parse(lsInput[I_MAX_INSERTS]);
225 print ("");
226 return fProcessMainInserts(iMaxInserts, oStopwatch);
227 /*
228 * Update the 'totals' table with the value of Inserts
229 */
230 }).then((int iReturnVal) {
231 int iInsertTot = iReturnVal;
232 sCheckpoint = "fUpdateTotalsTable (insert value)";
233 fUpdateTotalsTable().then((_) {
234 return iInsertTot;
235 });
236 /*
237 * Display totals for inserts
238 */
239 }).then((int iInsertTot) {
240 ogTotals.fPrintTotals(
241 "${iInsertTot} rows inserted - Inserts completed",
242 iInsertTot, oStopwatch.elapsedMilliseconds);
243
244 return null;
245 /*
192 /*
193 * Clear main table if selected
194 */
195 }).then((tReturnVal) {
196 if (tReturnVal)
197 ogPrintLine.fPrintForce("Random Keys Cleared");
198 sCheckpoint = "Clear Table ${S_TABLE_NAME}";
199 bool tClearTable = (lsInput[I_CLEAR_YN] == "y");
200 if (!tFirstInstance)
201 return null;
202 return fClearTable(tClearTable, S_TABLE_NAME);
203
204 /*
205 * Update control row to increment count of instances started
206 */
207 }).then((_) {
1073 /*
1074 * Update totals table with values from inserts and updates
1075 */
1076 async.Future<bool> fUpdateTotalsTable() {
1077 async.Completer<bool> oCompleter = new async.Completer<bool>();
1078
1079 String sCcyValue = ogCcy.fCcyIntToString(ogTotals.iTotAmt);
1080
1081 print ("\n********* Total = ${sCcyValue} \n");
1082
1083 oCompleter.complete(true);
1084 return oCompleter.future;
1085 }
Your function L230-235 does not return anything and that's why your iInsertTot is null L239. To make it work you have to add a return at line 233.
231 int iInsertTot = iReturnVal;
232 sCheckpoint = "fUpdateTotalsTable (insert value)";
233 return fUpdateTotalsTable().then((_) {
234 return iInsertTot;
235 });
At random times, my app crashes with this crash log.
A magic solution would be ideal, but some help on where to start debugging this would also be helpful. The app is quite difficult to debug as it is a GPS based app, so if I know what to look for, I can build some kind of stress test.
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x3789c88f __exceptionPreprocess + 163
1 libobjc.A.dylib 0x31911259 objc_exception_throw + 33
2 AVFoundation 0x309301d9 -[AVPlayer _attachItem:andPerformOperation:withObject:] + 249
3 AVFoundation 0x309300db -[AVPlayer _insertItem:afterItem:] + 27
4 AVFoundation 0x30943b9d -[AVQueuePlayer insertItem:afterItem:] + 137
5 MediaPlayer 0x364a68b7 __block_global_2 + 199
6 libdispatch.dylib 0x34a4bc59 _dispatch_call_block_and_release + 13
7 libdispatch.dylib 0x34a4dee7 _dispatch_main_queue_callback_4CF$VARIANT$mp + 195
8 CoreFoundation 0x3786f2ad __CFRunLoopRun + 1269
9 CoreFoundation 0x377f24a5 CFRunLoopRunSpecific + 301
10 CoreFoundation 0x377f236d CFRunLoopRunInMode + 105
11 GraphicsServices 0x379eb439 GSEventRunModal + 137
12 UIKit 0x309fecd5 UIApplicationMain + 1081
13 App 0x000b5b6f main (main.m:14)
I am playing a variety of movies (mov (H.264, 1024 x 576, AAC, 44100 Hz, Stereo (L R)) and sounds (MP3 and WAV), sometimes mixed.
Movies are played using the MPMoviePlayer, sounds use the AVAudioPlayer.
Happens on both ios 5 and 6. (iPad)
--- edit: adding code ----
Video is prepared here:
- (void) prepareLayer:(NSDictionary*) aUserInfo{
mMoviePlayerController.contentURL = [aUserInfo objectForKey:#"contenturl"]; // set content
[mMoviePlayerController prepareToPlay];
mMoviePlayerController.shouldAutoplay = NO; // prevent autoplay
mMovieShouldLoop = NO;
[mWrapper setOpacity: 0]; // hide video view (we don't want to see the previous video file)
}
mWrapper is used to encapsulate the video in a cocos2d node
- (void) showLayer {
[mWrapper setVisible: YES];
[mMoviePlayerController pause];
mRetryCounter = 0;
mMovieStarted = NO;
self.visible = YES;
}
- (void) updateLayer {
if (!self.visible) { return; } // not visible, so not updating (prevents accidental playing of video)
if (mWarningHidden && !mMovieStarted) // if warning is hidden and the movieStarted flag is false
{
if (mMoviePlayerController.playbackState == MPMoviePlaybackStatePlaying) { // if movie did start successfully.
mMovieStarted = YES; // set flag
[mWrapper setOpacity: 255]; // show video view
}
else { // if movie is not playing yet
if (mRetryCounter == 0) { // if this is the first try
[[[CCDirector sharedDirector] openGLView] addGestureRecognizer:mSwipe]; // enable swipe gesture
}
[mMoviePlayerController play]; // start playing
if (mMovieShouldLoop) { // and set loop or not
if (mMoviePlayerController.repeatMode != MPMovieRepeatModeOne) {
mMoviePlayerController.repeatMode = MPMovieRepeatModeOne;
}
} else {
if (mMoviePlayerController.repeatMode != MPMovieRepeatModeNone) {
mMoviePlayerController.repeatMode = MPMovieRepeatModeNone;
}
}
if (mMoviePlayerController.playbackState != MPMoviePlaybackStatePlaying && mRetryCounter > 5) { // if movie not playing and retried for 5 times,
mMoviePlayerController.contentURL = [[NSBundle mainBundle] URLForResource:#"novideo" withExtension:#"m4v"]; // switch to movie of static, indicating something went wrong
[mMoviePlayerController play]; // and play that.
}
if (mRetryCounter > 10) {
mMovieStarted = YES; // if movie still doesn't run after switched to static and retrying that for 5 times, skip it.
NSLog(#"A Movie was skipped after trying to play it for 10 times.");
}
mRetryCounter++; // count retries.
}
}
}
-- edit: removed the sound portion of the code snippets as they were not part of the problem, and changed the title to reflect the problem is in the MPMoviePlayerController, not in the AVPlayer.
I worked around the problem by using the lower level AVPlayer instead of the MPMoviePlayer. This solved my problem completely.