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

Properties spec failing when running on iOS target #3506

Closed
kirillzh opened this issue Apr 28, 2023 · 2 comments · Fixed by #3518
Closed

Properties spec failing when running on iOS target #3506

kirillzh opened this issue Apr 28, 2023 · 2 comments · Fixed by #3518
Labels
bug 🐛 Issues that report a problem or error in the code. multiplatform 🌐 This label is designated for issues and pull requests that concern the Kotest framework's compatibil

Comments

@kirillzh
Copy link

kirillzh commented Apr 28, 2023

Regression in Kotest 5.6.1

We have a kmp spec like this:

class MoneyTests : FunSpec({

  test("Properties") {
    checkAll(Exhaustive.enum<ISOTextCode>()) { currencyCode ->
      val currency = Currency.fromIsoTextCode(currencyCode)

      // Zero amount
      Money(currency, 0.0).isZero.shouldBeTrue()
      Money(currency, 0.0).isNegative.shouldBeFalse()
      Money(currency, 0.0).isPositive.shouldBeFalse()
      Money(currency, 0.0).isWholeNumber.shouldBeTrue()
      
      // ....
  }
}

It runs successfully on Kotest 5.5.4 but fails on Kotest 5.6.1 with following exception - specifically when targeting iOS platform, works fine on Android and JVM:

* What went wrong:
Execution failed for task ':shared:money:public:iosSimulatorArm64Test'.
> Test running process exited unexpectedly.
  Current test: Properties
  Process output:
   Child process terminated with signal 11: Segmentation fault


* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':shared:money:public:iosSimulatorArm64Test'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:149)
        at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:147)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:135)
        at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:337)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:324)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:317)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
Caused by: java.lang.Error: Test running process exited unexpectedly.
Current test: Properties
Process output:
 Child process terminated with signal 11: Segmentation fault
@kirillzh
Copy link
Author

kirillzh commented May 1, 2023

@jmecom's awesome findings:

Stacktrace:

(lldb) frame select 57114
frame #57114: 0x00000001049ab010 test.kexe`kfun:build.wallet.money.MoneyTests.$<init>$lambda$16$lambda$4COROUTINE$4.invokeSuspend#internal(_this=0x00000495491e2110, result=0x0000000104a80918) at MoneyTests.kt:41:5
   38  	class MoneyTests : FunSpec({
   39
   40  	  test("Properties") {
-> 41  	    checkAll(Exhaustive.enum<ISOTextCode>()) { currencyCode ->
   42  	      val currency = Currency.fromIsoTextCode(currencyCode)
   43
   44  	      // Zero amount
(lldb) frame select 57113
frame #57113: 0x0000000104993cb8 test.kexe`kfun:io.kotest.property#checkAll#suspend(genA=0x000004954901f5b0, property=0x00000495492072c0, $completion=0x00000495491e2110){0§<kotlin.Any?>}kotlin.Any at propertyTest1.kt:16:37
(lldb) frame select 57112
frame #57112: 0x00000001049758b4 test.kexe`kfun:io.kotest.property.PropTestConfig#<init>(_this=0x00000495491f33d0, seed=0x0000000000000000, minSuccess=0, maxFailure=0, shrinkingMode=0x0000000000000000, iterations=0x0000000000000000, listeners=0x0000000000000000, edgeConfig=0x0000000000000000, outputClassifications=false, labelsReporter=0x0000000000000000, constraints=0x0000000000000000, maxDiscardPercentage=0, $mask0=2047, $marker=0x0000000000000000){} at config.kt:107:22
(lldb) frame select 57111
frame #57111: 0x0000000104975380 test.kexe`kfun:io.kotest.property.PropertyTesting#<get-$instance>#static(){}io.kotest.property.PropertyTesting at config.kt:1:1
(lldb) frame select 57110
frame #57110: 0x0000000104a3a4e0 test.kexe`CallInitGlobalPossiblyLock + 512
test.kexe`CallInitGlobalPossiblyLock:
->  0x104a3a4e0 <+512>: b      0x104a3a510               ; <+560>
    0x104a3a4e4 <+516>: bl     0x104a428a8               ; symbol stub for: __cxa_begin_catch
    0x104a3a4e8 <+520>: ldur   x9, [x29, #-0x10]
    0x104a3a4ec <+524>: mov    w8, #0x3
(lldb) frame select 57109
frame #57109: 0x0000000104970f40 test.kexe`kfun:io.kotest.property.PropertyTesting.$init_global#internal at config.kt:1:1
(lldb) frame select 57109
frame #57109: 0x0000000104970f40 test.kexe`kfun:io.kotest.property.PropertyTesting.$init_global#internal at config.kt:1:1
(lldb) frame select 57108
frame #57108: 0x00000001049710b8 test.kexe`kfun:io.kotest.property.PropertyTesting#<init>(_this=0x0000049549062950){} at config.kt:16:34
(lldb) frame select 57106
frame #57106: 0x0000000104810114 test.kexe`kfun:io.kotest.mpp.atomics.AtomicReference#<init>(_this=0x00000495491c1ed0, initialValue=0x0000000104b3c6b0){} at AtomicReference.kt:5:27
(lldb) frame select 57105
frame #57105: 0x0000000104810114 test.kexe`kfun:io.kotest.mpp.atomics.AtomicReference#<init>(_this=0x00000495491c1eb0, initialValue=0x0000000104b3c6b0){} at AtomicReference.kt:5:27
(lldb) frame select 57104
frame #57104: 0x0000000104810114 test.kexe`kfun:io.kotest.mpp.atomics.AtomicReference#<init>(_this=0x00000495491c1e90, initialValue=0x0000000104b3c6b0){} at AtomicReference.kt:5:27
(lldb) frame select 57103
frame #57103: 0x0000000104810114 test.kexe`kfun:io.kotest.mpp.atomics.AtomicReference#<init>(_this=0x00000495491c1e70, initialValue=0x0000000104b3c6b0){} at AtomicReference.kt:5:27

Note the frame count. This supposed recursion in AtomicReference.kt happens 50,000+ times. Console.app crash reports shows:

-------------------------------------
Translated Report (Full Report Below)
-------------------------------------

Incident Identifier: 22DCF41A-67C3-41E6-B201-CF0982DDAEC9
CrashReporter Key:   8DE0440D-2375-E170-E4D7-4FC1E89C676D
Hardware Model:      MacBookPro18,2
Process:             test.kexe [398]
Path:                /Users/USER/*/test.kexe
Identifier:          test.kexe
Version:             ???
Code Type:           ARM-64 (Native)
Role:                Unspecified
Parent Process:      Exited process [395]
Coalition:           com.googlecode.iterm2 [167542]
Responsible Process: iTerm2 [10571]

Date/Time:           2023-04-29 16:17:49.7508 -0700
Launch Time:         2023-04-29 16:17:49.0482 -0700
OS Version:          macOS 12.6.5 (21G531)
Release Type:        User
Report Version:      104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_PROTECTION_FAILURE at 0x000000016d337ffc
Exception Codes: 0x0000000000000002, 0x000000016d337ffc
VM Region Info: 0x16d337ffc is in 0x169b34000-0x16d338000;  bytes after start: 58736636  bytes before end: 3
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      MALLOC_MEDIUM (reserved)    167800000-168000000    [ 8192K] rw-/rwx SM=NUL  ...(unallocated)
      GAP OF 0x1b34000 BYTES
--->  STACK GUARD                 169b34000-16d338000    [ 56.0M] ---/rwx SM=NUL  ... for thread 0
      Stack                       16d338000-16db34000    [ 8176K] rw-/rwx SM=PRV  thread 0
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: SIGNAL 11 Segmentation fault: 11
Terminating Process: exc handler [398]

Triggered by Thread:  0

Kernel Triage:
VM - pmap_enter failed with resource shortage
VM - pmap_enter failed with resource shortage
VM - pmap_enter failed with resource shortage
VM - pmap_enter failed with resource shortage
VM - pmap_enter failed with resource shortage


Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   test.kexe                     	       0x1026e13e0 mi_heap_malloc_zero_aligned_at + 288
1   test.kexe                     	       0x1026e9124 kotlin::mm::internal::ObjectFactoryStorage<8ul, kotlin::gc::AllocatorWithGC<kotlin::gc::Allocator, kotlin::gc::ConcurrentMarkAndSweep::ThreadData> >::Node::Create(kotlin::gc::AllocatorWithGC<kotlin::gc::Allocator, kotlin::gc::ConcurrentMarkAndSweep::ThreadData>&, unsigned long long) + 104
2   test.kexe                     	       0x1026e9124 kotlin::mm::internal::ObjectFactoryStorage<8ul, kotlin::gc::AllocatorWithGC<kotlin::gc::Allocator, kotlin::gc::ConcurrentMarkAndSweep::ThreadData> >::Node::Create(kotlin::gc::AllocatorWithGC<kotlin::gc::Allocator, kotlin::gc::ConcurrentMarkAndSweep::ThreadData>&, unsigned long long) + 104
3   test.kexe                     	       0x1026e0ff8 kotlin::mm::internal::ObjectFactoryStorage<8ul, kotlin::gc::AllocatorWithGC<kotlin::gc::Allocator, kotlin::gc::ConcurrentMarkAndSweep::ThreadData> >::Producer::Insert(unsigned long long) + 36
4   test.kexe                     	       0x102704b9c AllocInstance + 72
5   test.kexe                     	       0x1024e10ec kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 176 (AtomicReference.kt:5)
6   test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)
7   test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)
8   test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)
9   test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)
10  test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)
11  test.kexe                     	       0x1024e10fc kfun:io.kotest.mpp.atomics.AtomicReference#<init>(1:0){} + 192 (AtomicReference.kt:5)

Breaking down the crash log:
Screenshot 2023-05-01 at 5 12 32 PM

Root cause?

The actual code involved here is innocuous:
AtomicReference.kt#L4
config.kt#L4
AtomicProperty.kt#L6

checkAll is the “start” of the stack trace because it (eventually) causes code in config.kt to run, which initializes an AtomicProperty.

But in fact, if we remove all of the code in MoneyTests.kt except one test that does this:

	val test = AtomicProperty<Unit, Unit>()

…we crash in the same way. That stack trace snippet looks like:

…etc…
    frame #57122: 0x00000001048951e4 test.kexe`kfun:io.kotest.mpp.atomics.AtomicReference#<init>(_this=0x000004bb591c3770, initialValue=0x0000000104b71090){} at AtomicReference.kt:5:27
    frame #57123: 0x000000010489153c test.kexe`kfun:io.kotest.mpp.atomics.AtomicProperty#<init>(_this=0x000004bb59212350, defaultValue=0x0000000104b8c9d0){} at AtomicProperty.kt:8:32
    frame #57124: 0x00000001048916cc test.kexe`kfun:io.kotest.mpp.atomics.AtomicProperty#<init>(_this=0x000004bb59212350, defaultValue=0x0000000000000000, $mask0=1, $marker=0x0000000000000000){} at AtomicProperty.kt:6:1
    frame #57125: 0x00000001049fc3d0 test.kexe`kfun:build.wallet.money.MoneyTests.$<init>$lambda$1$lambda$0COROUTINE$0.invokeSuspend#internal(_this=0x000004bb591e26d0, result=0x0000000104ac4678) at MoneyTests.kt:46:16

So the cause of the crash is: Infinite recursion when initializing an AtomicProperty

@Kantis Kantis added bug 🐛 Issues that report a problem or error in the code. multiplatform 🌐 This label is designated for issues and pull requests that concern the Kotest framework's compatibil labels May 1, 2023
@Kantis
Copy link
Member

Kantis commented May 1, 2023

@sksamuel can we ditch AtomicProperty and AtomicReference since freezing is no longer needed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Issues that report a problem or error in the code. multiplatform 🌐 This label is designated for issues and pull requests that concern the Kotest framework's compatibil
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants