iOS16 [MXMetricManager deliverMetricPayload:] Crash

We have use MetricKit API when app was launched.


[[MXMetricManager sharedManager] addSubscriber:self];


the exception is here.

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread:  27

Last Exception Backtrace:
0   CoreFoundation                	0x18dbae248 __exceptionPreprocess + 164 (NSException.m:202)
1   libobjc.A.dylib               	0x186fc3a68 objc_exception_throw + 60 (objc-exception.mm:356)
2   CoreFoundation                	0x18dd19fec __NSFastEnumerationMutationHandler + 116 (NSEnumerator.m:130)
3   Foundation                    	0x18804d828 -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 76 (NSHashTable.m:577)
4   MetricKit                     	0x1fddf8868 -[MXMetricManager deliverMetricPayload:] + 408 (MXMetricManager.m:336)
5   Foundation                    	0x1880b66c8 __NSXPCCONNECTION_IS_CALLING_OUT_TO_EXPORTED_OBJECT_S1__ + 16 (NSXPCConnection.m:189)
6   Foundation                    	0x188686e34 -[NSXPCConnection _decodeAndInvokeMessageWithEvent:reply:flags:] + 1644 (NSXPCConnection.m:705)
7   Foundation                    	0x188688608 message_handler_message + 88 (NSXPCConnection.m:826)
8   Foundation                    	0x188688074 message_handler + 152 (NSXPCConnection.m:797)
9   libxpc.dylib                  	0x1da21a18c _xpc_connection_call_event_handler + 152 (connection.c:834)
10  libxpc.dylib                  	0x1da21a5bc _xpc_connection_mach_event + 992 (connection.c:1445)
11  libdispatch.dylib             	0x194ff609c _dispatch_client_callout4 + 20 (object.m:600)
12  libdispatch.dylib             	0x195012830 _dispatch_mach_msg_invoke + 468 (mach.c:2462)
13  libdispatch.dylib             	0x194ffd56c _dispatch_lane_serial_drain + 376 (inline_internal.h:0)
14  libdispatch.dylib             	0x19501354c _dispatch_mach_invoke + 448 (mach.c:2784)
15  libdispatch.dylib             	0x194ffd56c _dispatch_lane_serial_drain + 376 (inline_internal.h:0)
16  libdispatch.dylib             	0x194ffe214 _dispatch_lane_invoke + 436 (queue.c:3940)
17  libdispatch.dylib             	0x195008e10 _dispatch_workloop_worker_thread + 652 (queue.c:6846)
18  libsystem_pthread.dylib       	0x1da1c5df8 _pthread_wqthread + 288 (pthread.c:2618)
19  libsystem_pthread.dylib       	0x1da1c5b98 start_wqthread + 8

how to fix this issues?

Accepted Reply

The above indicates:

  • It’s a bug.

  • It’s reported as fixed in iOS 16.1 beta.

  • The workaround is to limit your app to a single subscriber.

What “further analysis” are you looking for?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • @eskimo,

    Can you elaborate on when the bug was introduced? I assume, 16.0.0. So presumably iOS 16.0.0 and any new iPad that shipped with 16.0.x before upgrading to 16.1?

    If my app code is using the subscriber for app exit data and memory statistics (metric memory metric), and a crash reporting tool using the diagnostics, it is lose-lose. Many crash reporting tools deliver their solution as a binary framework and don't allow customization of setup. So turning this off in an SDK would be tough.

Add a Comment

Replies

We have use MetricKit API when app was launched.

the exception is here.

Please post a full crash report. See Posting a Crash Report for advice on how to do that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Add a Comment

Here is my crash info. . Thanks your support.

We have the same crash。

Application Specific Information: *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSConcreteHashTable: 0x281e7f430> was mutated while being enumerated UserInfo:(null)'

We have the same crash

Interesting.

Any chance you can post a full crash report as well? The crash report from rbbtsn0w is a bit weird — it looks like some of the symbols in their code were deliberately obfuscated — and I might be able to learn more from a second report.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Seeing this here as we're getting alerted due to a spike in crashes due to this issue since the public release of iOS 16.

Full crash report attached.

The immediate cause of this crash is pretty easy to understand. Consider this backtrace:

0 CoreFoundation  … __exceptionPreprocess + 164 …
1 libobjc.A.dylib … objc_exception_throw + 60 …
2 CoreFoundation  … __NSFastEnumerationMutationHandler + 116 …
3 Foundation      … -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 76 …
4 MetricKit       … -[MXMetricManager deliverMetricPayload:] + 408 …
5 Foundation      … __NSXPCCONNECTION_IS_CALLING_OUT_TO_EXPORTED_OBJECT_S1__ + 16 …

Frame 5 is XPC delivering a message to MetricKit. That message contains MXMetricPayload values. MetricKit is in the process of iterating through its array of subscribers, passing that array to the -didReceiveMetricPayloads: method of each subscribers.

Frame 3 through 0 are characteristic of the was mutated while being enumerated exception discussed above. It’s likely that the collection in this case is MetricKit’s set of subscribers. So, you get this crash because you have either added or removed a subscriber while MetricKit is in the process of delivering metrics to its list of subscribers.

I’m not a deeply familiar with the MetricKit source code but this sounds like a bug to me. AFAICT MetricKit has appropriate serialisation for the APIs to add and remove subscribers, but not for its own internal use of the subscriber list.

I encourage you to file a bug about this, make sure to include a crash report. If you are able to reproduce this in your office, take a sysdiagnose log immediately after the crash and add that as well.

Please post your bug number, just for the record.

As to a workaround, how many subscribers does your app register? That is, if you set a symbolic breakpoint on -[MXMetricManager addSubscriber:], how many times is it called on launch?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I confirmed that [MXMetricManager addSubscriber] is called twice - it's invoked from another internal swift framework. Should there be only one subscriber? We had to remove a subscriber and release a version to stop the influx of crashes.

I submitted a bug using Feedback Assistant: https://feedbackassistant.apple.com/feedback/11525011

I see that it says "Potential fix identified - For a future OS update"

I confirmed that [MXMetricManager addSubscriber] is called twice - it's invoked from another internal swift framework.

Cool.

Should there be only one subscriber?

No. This is definitely our bug to fix.

We had to remove a subscriber and release a version to stop the influx of crashes.

That’s the correct workaround until we fix the bug.

I submitted a bug using Feedback Assistant [FB11525011]

Thanks!

My reading of that bug is that the fix is included in the current iOS 16.1b3 (20B5056e) seed. If you can find a way to reproduce this on 16.0, you could try testing with the beta seed to see if it still reproduces for you there.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Same problem, is there further analysis?

The above indicates:

  • It’s a bug.

  • It’s reported as fixed in iOS 16.1 beta.

  • The workaround is to limit your app to a single subscriber.

What “further analysis” are you looking for?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • @eskimo,

    Can you elaborate on when the bug was introduced? I assume, 16.0.0. So presumably iOS 16.0.0 and any new iPad that shipped with 16.0.x before upgrading to 16.1?

    If my app code is using the subscriber for app exit data and memory statistics (metric memory metric), and a crash reporting tool using the diagnostics, it is lose-lose. Many crash reporting tools deliver their solution as a binary framework and don't allow customization of setup. So turning this off in an SDK would be tough.

Add a Comment

I finally find my way to solve this problem, first share my analysis of this problem: when deliver Metric payloads to all subscribers we called -[MXMetricManager addSubscriber:], this cause the NSConcreteHashTable that save subscribers get mutated while being enumerated. It seems that the NSConcreteHashTable add subscribers or deliver Metric payloads in it's own thread, so just dispatch to main queue to call -[MXMetricManager addSubscriber:] not work. This is easy to reproduce, just keep add new subscriber(not equal to existed subscriber) in you code and simulate metric payload with Xcode. Finally I try to hook -[MXMetricManager addSubscriber:] and -[MXMetricManager removeSubscriber:] to a custom Class, this class hold a NSPointerArray of all subscribers, and dispatch to each subscribers when -didReceiveMetricPayloads: and -didReceiveDiagnosticPayloads: getting called.