stryker-mutator / stryker-js

Mutation testing for JavaScript and friends
https://stryker-mutator.io
Apache License 2.0
2.59k stars 250 forks source link

Potential Memory Leak #4318

Open maxowell opened 1 year ago

maxowell commented 1 year ago

Summary

Allocation failed - JavaScript heap out of memory during mutation testing on moderately sized projet.

I have consulted these similar issues, without being able to solve my problem :

Stryker config

{
    "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
    "packageManager": "npm",
    "reporters": [
        "html",
        "clear-text",
        "progress"
    ],
    "testRunner": "jest",
    "coverageAnalysis": "perTest",
    "mutate": [
        "server/**/*.js",
        "!server/**/*.spec.js"
    ]
}

Test runner config

{
    "jestrunner.debugOptions": {
        "env": {
            "NODE_OPTIONS": "--experimental-vm-modules"
        }
    },
    "jestrunner.jestCommand": "npm run test:jest"
}

Stryker environment

+-- @stryker-mutator/core@7.0.2
+-- @stryker-mutator/jest-runner@7.0.2
+-- stryker-cli@1.0.2
+-- jest@29.5.0

Test runner environment

npx cross-env NODE_OPTIONS=--experimental-vm-modules npx jest

Your Environment

software version(s)
node v16.18.1
npm 8.19.2
Operating System macOS Ventura 13.4 (Also tested on Windows 10, same error)

Add stryker.log

13:35:32 (62815) INFO ProjectReader Found 922 of 6004 file(s) to be mutated.
13:35:38 (62815) INFO Instrumenter Instrumented 922 source file(s) with 24529 mutant(s)

<--- Last few GCs --->

[62815:0x7fe04d100000]    63863 ms: Mark-sweep (reduce) 4061.4 (4114.1) -> 4061.3 (4073.3) MB, 49.7 / 0.0 ms  (+ 19.2 ms in 2 steps since start of marking, biggest step 19.2 ms, walltime since start of marking 715 ms) (average mu = 0.986, current mu = 0.9[62815:0x7fe04d100000]    64633 ms: Mark-sweep (reduce) 4221.0 (4233.1) -> 4221.0 (4232.1) MB, 80.6 / 0.0 ms  (average mu = 0.969, current mu = 0.895) allocation failure scavenge might not succeed

<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
 1: 0x1048555f5 node::Abort() (.cold.1) [/usr/local/bin/node]
 2: 0x10354af49 node::Abort() [/usr/local/bin/node]
 3: 0x10354b12e node::OOMErrorHandler(char const*, bool) [/usr/local/bin/node]
 4: 0x1036c23f0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
 5: 0x1036c23b3 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
 6: 0x103866095 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/usr/local/bin/node]
 7: 0x103864a1c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
 8: 0x1038712c0 v8::internal::Heap::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
 9: 0x103871341 v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
10: 0x1038383ee v8::internal::FactoryBase<v8::internal::Factory>::NewRawTwoByteString(int, v8::internal::AllocationType) [/usr/local/bin/node]
11: 0x10383ffe1 v8::internal::Factory::NewStringFromUtf8(v8::base::Vector<char const> const&, v8::internal::AllocationType) [/usr/local/bin/node]
12: 0x1036e414f v8::String::NewFromUtf8(v8::Isolate*, char const*, v8::NewStringType, int) [/usr/local/bin/node]
13: 0x10360f93c node::StringBytes::Encode(v8::Isolate*, char const*, unsigned long, node::encoding, v8::Local<v8::Value>*) [/usr/local/bin/node]
14: 0x10352bc90 void node::Buffer::(anonymous namespace)::StringSlice<(node::encoding)1>(v8::FunctionCallbackInfo<v8::Value> const&) [/usr/local/bin/node]
15: 0x10372ae49 v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) [/usr/local/bin/node]
16: 0x10372a916 v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) [/usr/local/bin/node]
17: 0x10372a08f v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
18: 0x103f9ecf9 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit [/usr/local/bin/node]
Abort trap: 6
nicojs commented 1 year ago

I wouldn't call 922 source files and 24529 mutants moderately sized 🙊 The entire code base of Stryker only has 7.7k mutants

Since Stryker seems to crash right after generating the mutants, I expect the memory problem to be in the main process of Stryker, not in any of the worker processes.

Could you try disabling disableTypeChecks:

{
  "disableTypeChecks": false
}

With disableTypeChecks, Stryker pulls all project files into memory (all 6004 of them). It does so right after instrumentation, so it seems like it could be the culprit.

maxowell commented 1 year ago

You're probably right, I graduated not long ago and this is one of the first projects I've been working on. So, to me, this is an "average" size 🥲

Your suggestion seems to be what caused the problem! The mutation tests are now running, there seems to be some more problems but those appear to be from my configuration, I believe I can fix them on my own.

I'm not sure if this is an intended feature or not, but in any case thank you for your support! :)

nicojs commented 1 year ago

You're welcome!

Yeah, pulling all of them into memory is needed to be able to disable type checking (we need to parse them using the typescript parser), but they don't need to stay in-memory.

Let's improve that using this issue