1# Investigating memory leaks with Valgrind 2 3A Node.js process may run out of memory due to excessive consumption of 4native memory. Native Memory is memory which is not managed by the 5V8 Garbage collector and is allocated either by the Node.js runtime, its 6dependencies or native [addons](https://nodejs.org/docs/latest/api/n-api.html). 7 8This guide provides information on how to use Valgrind to investigate these 9issues on Linux platforms. 10 11## Valgrind 12 13[Valgrind](https://valgrind.org/docs/manual/quick-start.html) is a 14tool available on Linux distributions which can be used to investigate 15memory usage including identifying memory leaks (memory which is 16allocated and not freed) and other memory related problems 17like double freeing memory. 18 19To use Valgrind: 20 21* Be patient, running under Valgrind slows execution significantly 22 due to the checks being performed. 23* Reduce your test case to the smallest reproduce. Due to the slowdown it is 24 important to run the minimum test case in order to be able to do it in 25 a reasonable time. 26 27## Installation 28 29It is an optional package in most cases and must be installed explicitly. 30For example on Debian/Ubuntu: 31 32```console 33apt-get install valgrind 34``` 35 36## Invocation 37 38The simplest invocation of Valgrind is: 39 40```console 41valgrind node test.js 42``` 43 44with the output being: 45 46```console 47user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind node test.js 48==28993== Memcheck, a memory error detector 49==28993== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 50==28993== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info 51==28993== Command: node test.js 52==28993== 53==28993== Use of uninitialised value of size 8 54==28993== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 55==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 56==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 57==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 58==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 59==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 60==28993== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 61==28993== by 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 62==28993== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 63==28993== by 0xC7C9CF: v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 64==28993== by 0xC7CE87: v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 65==28993== by 0xB4CF3A: v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 66==28993== 67--28993-- WARNING: unhandled amd64-linux syscall: 332 68--28993-- You may be able to write your own handler. 69--28993-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 70--28993-- Nevertheless we consider this a bug. Please report 71--28993-- it at http://valgrind.org/support/bug_reports.html. 72==28993== 73==28993== HEAP SUMMARY: 74==28993== in use at exit: 6,140 bytes in 23 blocks 75==28993== total heap usage: 12,888 allocs, 12,865 frees, 13,033,244 bytes allocated 76==28993== 77==28993== LEAK SUMMARY: 78==28993== definitely lost: 0 bytes in 0 blocks 79==28993== indirectly lost: 0 bytes in 0 blocks 80==28993== possibly lost: 304 bytes in 1 blocks 81==28993== still reachable: 5,836 bytes in 22 blocks 82==28993== suppressed: 0 bytes in 0 blocks 83==28993== Rerun with --leak-check=full to see details of leaked memory 84==28993== 85==28993== For counts of detected and suppressed errors, rerun with: -v 86==28993== Use --track-origins=yes to see where uninitialised values come 87``` 88 89This reports that Node.js is not _completely_ clean as there is some memory 90that was allocated but not freed when the process shut down. It is often 91impractical/not worth being completely clean in this respect. Modern 92operating systems will clean up the memory of the process after the 93shutdown while attempting to free all memory to get a clean 94report may have a negative impact on the code complexity and 95shutdown times. Node.js does a pretty good job only leaving on 96the order of 6 KB that are not freed on shutdown. 97 98## An obvious memory leak 99 100Leaks can be introduced in native addons and the following is a simple 101example leak based on the "Hello world" addon from 102[node-addon-examples](https://github.com/nodejs/node-addon-examples). 103 104In this example, a loop which allocates approximately 1 MiB of memory and never 105frees it has been added: 106 107```cpp 108void* malloc_holder = nullptr; 109napi_value Method(napi_env env, napi_callback_info info) { 110 napi_status status; 111 napi_value world; 112 status = napi_create_string_utf8(env, "world", 5, &world); 113 assert(status == napi_ok); 114 115 // NEW LEAK HERE 116 for (int i=0; i < 1024; i++) { 117 malloc_holder = malloc(1024); 118 } 119 120 return world; 121} 122``` 123 124When trying to create a memory leak you need to ensure that 125the compiler has not optimized out the code that creates 126the leak. For example, by assigning the result of the allocation 127to either a global variable or a variable that will be read 128afterwards the compiler will not optimize it out along with 129the malloc and Valgrind will properly report the memory leak. 130If `malloc_holder` in the example above is made into a 131local variable then the compiler may freely remove 132it along with the allocations (since it is not used) 133and Valgrind will not find any leaks since they 134will no longer exist in the code being run. 135 136Running Valgrind on this code shows the following: 137 138```console 139user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind node hello.js 140==1504== Memcheck, a memory error detector 141==1504== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 142==1504== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info 143==1504== Command: node hello.js 144==1504== 145==1504== Use of uninitialised value of size 8 146==1504== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 147==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 148==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 149==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 150==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 151==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 152==1504== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 153==1504== by 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 154==1504== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 155==1504== by 0xC7C9CF: v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 156==1504== by 0xC7CE87: v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 157==1504== by 0xB4CF3A: v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 158==1504== 159--1504-- WARNING: unhandled amd64-linux syscall: 332 160--1504-- You may be able to write your own handler. 161--1504-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 162--1504-- Nevertheless we consider this a bug. Please report 163--1504-- it at http://valgrind.org/support/bug_reports.html. 164world 165==1504== 166==1504== HEAP SUMMARY: 167==1504== in use at exit: 1,008,003 bytes in 1,032 blocks 168==1504== total heap usage: 17,603 allocs, 16,571 frees, 18,306,103 bytes allocated 169==1504== 170==1504== LEAK SUMMARY: 171==1504== definitely lost: 996,064 bytes in 997 blocks 172==1504== indirectly lost: 0 bytes in 0 blocks 173==1504== possibly lost: 3,304 bytes in 4 blocks 174==1504== still reachable: 8,635 bytes in 31 blocks 175==1504== of which reachable via heuristic: 176==1504== multipleinheritance: 48 bytes in 1 blocks 177==1504== suppressed: 0 bytes in 0 blocks 178==1504== Rerun with --leak-check=full to see details of leaked memory 179==1504== 180==1504== For counts of detected and suppressed errors, rerun with: -v 181==1504== Use --track-origins=yes to see where uninitialised values come from 182==1504== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 183``` 184 185Valgrind is reporting a problem as it shows 996,604 bytes as 186definitely lost and the question is how to find where that memory was 187allocated. The next step is to rerun as suggested in the 188output with `--leak-check=full`: 189 190```bash 191user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind --leak-check=full node hello.js 192==4174== Memcheck, a memory error detector 193==4174== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 194==4174== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info 195==4174== Command: node hello.js 196==4174== 197==4174== Use of uninitialised value of size 8 198==4174== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 199==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 200==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 201==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 202==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 203==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 204==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 205==4174== by 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 206==4174== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 207==4174== by 0xC7C9CF: v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 208==4174== by 0xC7CE87: v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 209==4174== by 0xB4CF3A: v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 210==4174== 211--4174-- WARNING: unhandled amd64-linux syscall: 332 212--4174-- You may be able to write your own handler. 213--4174-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 214--4174-- Nevertheless we consider this a bug. Please report 215--4174-- it at http://valgrind.org/support/bug_reports.html. 216world 217==4174== 218==4174== HEAP SUMMARY: 219==4174== in use at exit: 1,008,003 bytes in 1,032 blocks 220==4174== total heap usage: 17,606 allocs, 16,574 frees, 18,305,977 bytes allocated 221==4174== 222==4174== 64 bytes in 1 blocks are definitely lost in loss record 17 of 35 223==4174== at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 224==4174== by 0x9AEAD5: napi_module_register (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 225==4174== by 0x4010732: call_init (dl-init.c:72) 226==4174== by 0x4010732: _dl_init (dl-init.c:119) 227==4174== by 0x40151FE: dl_open_worker (dl-open.c:522) 228==4174== by 0x5D052DE: _dl_catch_exception (dl-error-skeleton.c:196) 229==4174== by 0x40147C9: _dl_open (dl-open.c:605) 230==4174== by 0x4E3CF95: dlopen_doit (dlopen.c:66) 231==4174== by 0x5D052DE: _dl_catch_exception (dl-error-skeleton.c:196) 232==4174== by 0x5D0536E: _dl_catch_error (dl-error-skeleton.c:215) 233==4174== by 0x4E3D734: _dlerror_run (dlerror.c:162) 234==4174== by 0x4E3D050: dlopen@@GLIBC_2.2.5 (dlopen.c:87) 235==4174== by 0x9B29A0: node::binding::DLOpen(v8::FunctionCallbackInfo<v8::Value> const&)::{lambda(node::binding::DLib*)#1}::operator()(node::binding::DLib*) const (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 236==4174== 237==4174== 304 bytes in 1 blocks are possibly lost in loss record 27 of 35 238==4174== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 239==4174== by 0x40134A6: allocate_dtv (dl-tls.c:286) 240==4174== by 0x40134A6: _dl_allocate_tls (dl-tls.c:530) 241==4174== by 0x5987227: allocate_stack (allocatestack.c:627) 242==4174== by 0x5987227: pthread_create@@GLIBC_2.2.5 (pthread_create.c:644) 243==4174== by 0xAAF9DC: node::inspector::Agent::Start(std::string const&, node::DebugOptions const&, std::shared_ptr<node::HostPort>, bool) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 244==4174== by 0x9A8BE7: node::Environment::InitializeInspector(std::unique_ptr<node::inspector::ParentInspectorHandle, std::default_delete<node::inspector::ParentInspectorHandle> >) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 245==4174== by 0xA1C9A5: node::NodeMainInstance::CreateMainEnvironment(int*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 246==4174== by 0xA1CB42: node::NodeMainInstance::Run() (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 247==4174== by 0x9ACB67: node::Start(int, char**) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 248==4174== by 0x5BBFB96: (below main) (libc-start.c:310) 249==4174== 250==4174== 2,000 bytes in 2 blocks are possibly lost in loss record 33 of 35 251==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 252==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 253==4174== by 0x98F764: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 254==4174== by 0xBA6FC8: 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) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 255==4174== by 0xBA8DB6: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 256==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 257==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 258==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 259==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 260==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 261==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 262==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 263==4174== 264==4174== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 265==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 266==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 267==4174== by 0x98F764: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 268==4174== by 0xBA6FC8: 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) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 269==4174== by 0xBA8DB6: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 270==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 271==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 272==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 273==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 274==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 275==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 276==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 277==4174== 278==4174== LEAK SUMMARY: 279==4174== definitely lost: 997,064 bytes in 998 blocks 280==4174== indirectly lost: 0 bytes in 0 blocks 281==4174== possibly lost: 2,304 bytes in 3 blocks 282==4174== still reachable: 8,635 bytes in 31 blocks 283==4174== of which reachable via heuristic: 284==4174== multipleinheritance: 48 bytes in 1 blocks 285==4174== suppressed: 0 bytes in 0 blocks 286==4174== Reachable blocks (those to which a pointer was found) are not shown. 287==4174== To see them, rerun with: --leak-check=full --show-leak-kinds=all 288==4174== 289==4174== For counts of detected and suppressed errors, rerun with: -v 290==4174== Use --track-origins=yes to see where uninitialised values come from 291==4174== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0) 292``` 293 294This is the most interesting part of the report: 295 296```console 297==4174== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 298==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 299==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 300==4174== by 0x98F764: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 301==4174== by 0xBA6FC8: 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) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 302==4174== by 0xBA8DB6: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 303==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 304==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 305==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 306==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 307==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 308==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 309==4174== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 310``` 311 312From the stack trace we can tell that the leak came from a native addon: 313 314```console 315==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 316``` 317 318What we can't tell is where in the native addon the memory is being 319allocated. This is because by default the addon is compiled without 320the debug symbols which Valgrind needs to be able to provide more 321information. 322 323It is possible to hide leaks related to Node.js itself in future Valgrind runs 324using the suppression feature of Valgrind. 325 326## Generating a Valgrind suppression file 327 328Valgrind uses suppression files to hide issues found from the summary. Generate 329a log file with embedded suppressions using the `--gen-suppressions` and 330`--log-file` flags: 331 332```bash 333valgrind --leak-check=full \ 334 --gen-suppressions=all \ 335 --log-file=./valgrind-out.txt \ 336 node hello.js 337``` 338 339Valgrind will save the output to the log file specified. After each heap in the 340summary, Valgrind will include a suppression record: a structure that Valgrind 341can use to ignore specific memory issues. Suppression records can be saved to a 342suppression file which Valgrind can use in subsequent executions to hide various 343memory errors. This is an example of the suppression records from the previous 344call: 345 346```text 347{ 348 <insert_a_suppression_name_here> 349 Memcheck:Value8 350 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 351 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 352 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 353 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 354 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 355 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 356 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 357 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 358 obj:/home/kevin/.nvm/versions/node/v12.14.1/bin/node 359 fun:_ZN2v88internal12_GLOBAL__N_16InvokeEPNS0_7IsolateERKNS1_12InvokeParamsE 360 fun:_ZN2v88internal9Execution4CallEPNS0_7IsolateENS0_6HandleINS0_6ObjectEEES6_iPS6_ 361 fun:_ZN2v88Function4CallENS_5LocalINS_7ContextEEENS1_INS_5ValueEEEiPS5_ 362} 363{ 364 <insert_a_suppression_name_here> 365 Memcheck:Leak 366 match-leak-kinds: definite 367 fun:_Znwm 368 fun:napi_module_register 369 fun:call_init.part.0 370 fun:call_init 371 fun:_dl_init 372 fun:_dl_catch_exception 373 fun:dl_open_worker 374 fun:_dl_catch_exception 375 fun:_dl_open 376 fun:dlopen_doit 377 fun:_dl_catch_exception 378 fun:_dl_catch_error 379 fun:_dlerror_run 380} 381{ 382 <insert_a_suppression_name_here> 383 Memcheck:Leak 384 match-leak-kinds: possible 385 fun:calloc 386 fun:allocate_dtv 387 fun:_dl_allocate_tls 388 fun:allocate_stack 389 fun:pthread_create@@GLIBC_2.2.5 390 fun:_ZN4node9inspector5Agent5StartERKSsRKNS_12DebugOptionsESt10shared_ptrINS_8HostPortEEb 391 fun:_ZN4node11Environment19InitializeInspectorESt10unique_ptrINS_9inspector21ParentInspectorHandleESt14default_deleteIS3_EE 392 fun:_ZN4node16NodeMainInstance21CreateMainEnvironmentEPi 393 fun:_ZN4node16NodeMainInstance3RunEv 394 fun:_ZN4node5StartEiPPc 395 fun:(below main) 396} 397``` 398 399Create a file (eg. `node-12.14.1.supp`) with the contents of the suppression 400records, and run Valgrind with the suppression file previously created: 401 402```bash 403valgrind --leak-check=full \ 404 --suppressions=./node-12.14.1.supp \ 405 node hello.js 406``` 407 408Now, the Valgrind leak summary for suppressed issues are only mentioned as 409`suppressed` in the leak summary: 410 411```console 412==12471== HEAP SUMMARY: 413==12471== in use at exit: 8,067 bytes in 31 blocks 414==12471== total heap usage: 16,482 allocs, 16,451 frees, 17,255,689 bytes allocated 415==12471== 416==12471== LEAK SUMMARY: 417==12471== definitely lost: 0 bytes in 0 blocks 418==12471== indirectly lost: 0 bytes in 0 blocks 419==12471== possibly lost: 0 bytes in 0 blocks 420==12471== still reachable: 7,699 bytes in 29 blocks 421==12471== of which reachable via heuristic: 422==12471== multipleinheritance: 48 bytes in 1 blocks 423==12471== suppressed: 368 bytes in 2 blocks 424``` 425 426## Enabling debug symbols to get more information 427 428Leaks may be either in addons or Node.js itself. The sections which 429follow cover the steps needed to enable debug symbols to get more info. 430 431### Native addons 432 433To enable debug symbols for all of your addons that are compiled on 434install use: 435 436```console 437npm install --debug 438``` 439 440Any options which are not consumed by npm are passed on to node-gyp and this 441results in the addons being compiled with the debug option. 442 443If the native addon contains pre-built binaries you will need to force 444a rebuild. 445 446```console 447npm install --debug 448npm rebuild 449``` 450 451The next step is to run Valgrind after the rebuild. This time the information 452for the leaking location includes the name of the source file and the 453line number: 454 455```console 456==18481== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 457==18481== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 458>>>>> ==18481== by 0x9794989: Method(napi_env__*, napi_callback_info__*) (hello.cc:13) <<<<< 459==18481== by 0x98F764: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /home/user1/val grind/node-v12.14.1-linux-x64/bin/node) 460==18481== by 0xBA6FC8: 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::FunctionTem plateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 461==18481== by 0xBA8DB6: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 462==18481== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 463==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 464==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 465==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 466==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 467==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 468==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 469``` 470 471This new output shows us exactly where the leak is occurring in the file `hello.cc`: 472 473```cpp 474 6 void* malloc_holder = nullptr; 475 7 napi_value Method(napi_env env, napi_callback_info info) { 476 8 napi_status status; 477 9 napi_value world; 478 10 status = napi_create_string_utf8(env, "world", 5, &world); 479 11 assert(status == napi_ok); 480 12 for (int i=0; i< 1000; i++) { 481 13 malloc_holder = malloc(1000); // <<<<<< This is where we are allocating the memory that is not freed 482 14 } 483 15 return world; 484 16 } 485``` 486 487### Node.js binary 488 489If the leak is not in an addon and is instead in the Node.js binary itself, 490you may need to compile node yourself and turn on debug symbols. Looking at 491this entry reported by Valgrind, with a release binary we see: 492 493```console 494 ==4174== 304 bytes in 1 blocks are possibly lost in loss record 27 of 35 495==4174== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 496==4174== by 0x40134A6: allocate_dtv (dl-tls.c:286) 497==4174== by 0x40134A6: _dl_allocate_tls (dl-tls.c:530) 498==4174== by 0x5987227: allocate_stack (allocatestack.c:627) 499==4174== by 0x5987227: pthread_create@@GLIBC_2.2.5 (pthread_create.c:644) 500==4174== by 0xAAF9DC: node::inspector::Agent::Start(std::string const&, node::DebugOptions const&, std::shared_ptr<node::HostPort>, bool) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 501==4174== by 0x9A8BE7: node::Environment::InitializeInspector(std::unique_ptr<node::inspector::ParentInspectorHandle, std::default_delete<node::inspector::ParentInspectorHandle> >) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 502==4174== by 0xA1C9A5: node::NodeMainInstance::CreateMainEnvironment(int*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 503==4174== by 0xA1CB42: node::NodeMainInstance::Run() (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 504==4174== by 0x9ACB67: node::Start(int, char**) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 505==4174== by 0x5BBFB96: (below main) (libc-start.c:310) 506``` 507 508This gives us some information of where to look (`node::inspector::Agent::Start`) 509but not where in that function. We get more information than you might expect 510(or see by default with addons) because the Node.js binary exports many of 511its symbols using `-rdynamic` so that they can be used by addons. If the stack 512gives you enough information to track down where the leak is, that's great, 513otherwise the next step is to compile a debug build of Node.js. 514 515To get additional information with Valgrind: 516 517* Check out the Node.js source corresponding to the release that you 518 want to debug. For example: 519 520```console 521git clone https://github.com/nodejs/node.git 522git checkout v12.14.1 523``` 524 525* Compile with debug enabled (for additional info see 526 [building a debug build](https://github.com/nodejs/node/blob/v12.14.1/BUILDING.md#building-a-debug-build)). 527 For example, on \*nix: 528 529```console 530./configure --debug 531make -j4 532``` 533 534* Make sure to run with your compiled debug version of Node.js. Having used 535 `./configure --debug`, two binaries will have been built when `make` was run. 536 You must use the one which is in `out/Debug`. 537 538Running Valgrind using the debug build of Node.js shows: 539 540```console 541==44112== 592 bytes in 1 blocks are possibly lost in loss record 26 of 27 542==44112== at 0x4C2BF79: calloc (vg_replace_malloc.c:762) 543==44112== by 0x4012754: _dl_allocate_tls (in /usr/lib64/ld-2.17.so) 544==44112== by 0x586287B: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so) 545==44112== by 0xFAB2D2: node::inspector::(anonymous namespace)::StartDebugSignalHandler() (inspector_agent.cc:140) 546==44112== by 0xFACB10: node::inspector::Agent::Start(std::string const&, node::DebugOptions const&, std::shared_ptr<node::HostPort>, bool) (inspector_agent.cc:777) 547==44112== by 0xE3A0BB: node::Environment::InitializeInspector(std::unique_ptr<node::inspector::ParentInspectorHandle, std::default_delete<node::inspector::ParentInspectorHandle> >) (node.cc:216) 548==44112== by 0xEE8F3E: node::NodeMainInstance::CreateMainEnvironment(int*) (node_main_instance.cc:222) 549==44112== by 0xEE8831: node::NodeMainInstance::Run() (node_main_instance.cc:108) 550==44112== by 0xE3CDEC: node::Start(int, char**) (node.cc:996) 551==44112== by 0x22D8BBF: main (node_main.cc:126) 552``` 553 554Now we can see the specific file name and line in the Node.js code which 555caused the allocation (inspector\_agent.cc:140). 556 557We can examine that line (and its surrounding code) to 558find a solution for the memory leak. 559