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 37The simplest invocation of valgrind is: 38 39```console 40valgrind node test.js 41``` 42 43with the output being: 44 45```console 46user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind node test.js 47==28993== Memcheck, a memory error detector 48==28993== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 49==28993== Using valgrind-3.13.0 and LibVEX; rerun with -h for copyright info 50==28993== Command: node test.js 51==28993== 52==28993== Use of uninitialised value of size 8 53==28993== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 54==28993== by 0x12F68A3: ??? (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 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 61==28993== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 62==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) 63==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) 64==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) 65==28993== 66--28993-- WARNING: unhandled amd64-linux syscall: 332 67--28993-- You may be able to write your own handler. 68--28993-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 69--28993-- Nevertheless we consider this a bug. Please report 70--28993-- it at http://valgrind.org/support/bug_reports.html. 71==28993== 72==28993== HEAP SUMMARY: 73==28993== in use at exit: 6,140 bytes in 23 blocks 74==28993== total heap usage: 12,888 allocs, 12,865 frees, 13,033,244 bytes allocated 75==28993== 76==28993== LEAK SUMMARY: 77==28993== definitely lost: 0 bytes in 0 blocks 78==28993== indirectly lost: 0 bytes in 0 blocks 79==28993== possibly lost: 304 bytes in 1 blocks 80==28993== still reachable: 5,836 bytes in 22 blocks 81==28993== suppressed: 0 bytes in 0 blocks 82==28993== Rerun with --leak-check=full to see details of leaked memory 83==28993== 84==28993== For counts of detected and suppressed errors, rerun with: -v 85==28993== Use --track-origins=yes to see where uninitialised values come 86``` 87 88This reports that Node.js is not _completely_ clean as there is some memory 89that was allocated but not freed when the process shut down. It is often 90impractical/not worth being completely clean in this respect. Modern 91operating systems will clean up the memory of the process after the 92shutdown while attempting to free all memory to get a clean 93report may have a negative impact on the code complexity and 94shutdown times. Node.js does a pretty good job only leaving on 95the order of 6KB that are not freed on shutdown. 96 97## An obvious memory leak 98 99Leaks can be introduced in native addons and the following is a simple 100example leak based on the "Hello world" addon from 101[node-addon-examples](https://github.com/nodejs/node-addon-examples). 102 103In this example, a loop which allocates ~1MB of memory and never frees it 104has been added: 105 106```cpp 107void* malloc_holder = nullptr; 108napi_value Method(napi_env env, napi_callback_info info) { 109 napi_status status; 110 napi_value world; 111 status = napi_create_string_utf8(env, "world", 5, &world); 112 assert(status == napi_ok); 113 114 // NEW LEAK HERE 115 for (int i=0; i < 1024; i++) { 116 malloc_holder = malloc(1024); 117 } 118 119 return world; 120} 121``` 122 123When trying to create a memory leak you need to ensure that 124the compiler has not optimized out the code that creates 125the leak. For example, by assigning the result of the allocation 126to either a global variable or a variable that will be read 127afterwards the compiler will not optimize it out along with 128the malloc and Valgrind will properly report the memory leak. 129If `malloc_holder` in the example above is made into a 130local variable then the compiler may freely remove 131it along with the allocations (since it is not used) 132and Valgrind will not find any leaks since they 133will no longer exist in the code being run. 134 135Running valgrind on this code shows the following: 136 137```console 138user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind node hello.js 139==1504== Memcheck, a memory error detector 140==1504== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 141==1504== Using V#algrind-3.13.0 and LibVEX; rerun with -h for copyright info 142==1504== Command: node hello.js 143==1504== 144==1504== Use of uninitialised value of size 8 145==1504== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 146==1504== by 0x12F68A3: ??? (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 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 153==1504== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 154==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) 155==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) 156==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) 157==1504== 158--1504-- WARNING: unhandled amd64-linux syscall: 332 159--1504-- You may be able to write your own handler. 160--1504-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 161--1504-- Nevertheless we consider this a bug. Please report 162--1504-- it at http://valgrind.org/support/bug_reports.html. 163world 164==1504== 165==1504== HEAP SUMMARY: 166==1504== in use at exit: 1,008,003 bytes in 1,032 blocks 167==1504== total heap usage: 17,603 allocs, 16,571 frees, 18,306,103 bytes allocated 168==1504== 169==1504== LEAK SUMMARY: 170==1504== definitely lost: 996,064 bytes in 997 blocks 171==1504== indirectly lost: 0 bytes in 0 blocks 172==1504== possibly lost: 3,304 bytes in 4 blocks 173==1504== still reachable: 8,635 bytes in 31 blocks 174==1504== of which reachable via heuristic: 175==1504== multipleinheritance: 48 bytes in 1 blocks 176==1504== suppressed: 0 bytes in 0 blocks 177==1504== Rerun with --leak-check=full to see details of leaked memory 178==1504== 179==1504== For counts of detected and suppressed errors, rerun with: -v 180==1504== Use --track-origins=yes to see where uninitialised values come from 181==1504== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 182``` 183 184Valgrind is reporting a problem as it shows 996,604 bytes as 185definitely lost and the question is how to find where that memory was 186allocated. The next step is to rerun as suggested in the 187output with `--leak-check=full`: 188 189``` bash 190user1@minikube1:~/valgrind/node-addon-examples/1_hello_world/napi$ valgrind --leak-check=full node hello.js 191==4174== Memcheck, a memory error detector 192==4174== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 193==4174== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info 194==4174== Command: node hello.js 195==4174== 196==4174== Use of uninitialised value of size 8 197==4174== at 0x12F2279: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 198==4174== by 0x12F68A3: ??? (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 0x12F3E9C: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 205==4174== by 0x12F3C77: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 206==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) 207==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) 208==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) 209==4174== 210--4174-- WARNING: unhandled amd64-linux syscall: 332 211--4174-- You may be able to write your own handler. 212--4174-- Read the file README_MISSING_SYSCALL_OR_IOCTL. 213--4174-- Nevertheless we consider this a bug. Please report 214--4174-- it at http://valgrind.org/support/bug_reports.html. 215world 216==4174== 217==4174== HEAP SUMMARY: 218==4174== in use at exit: 1,008,003 bytes in 1,032 blocks 219==4174== total heap usage: 17,606 allocs, 16,574 frees, 18,305,977 bytes allocated 220==4174== 221==4174== 64 bytes in 1 blocks are definitely lost in loss record 17 of 35 222==4174== at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 223==4174== by 0x9AEAD5: napi_module_register (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 224==4174== by 0x4010732: call_init (dl-init.c:72) 225==4174== by 0x4010732: _dl_init (dl-init.c:119) 226==4174== by 0x40151FE: dl_open_worker (dl-open.c:522) 227==4174== by 0x5D052DE: _dl_catch_exception (dl-error-skeleton.c:196) 228==4174== by 0x40147C9: _dl_open (dl-open.c:605) 229==4174== by 0x4E3CF95: dlopen_doit (dlopen.c:66) 230==4174== by 0x5D052DE: _dl_catch_exception (dl-error-skeleton.c:196) 231==4174== by 0x5D0536E: _dl_catch_error (dl-error-skeleton.c:215) 232==4174== by 0x4E3D734: _dlerror_run (dlerror.c:162) 233==4174== by 0x4E3D050: dlopen@@GLIBC_2.2.5 (dlopen.c:87) 234==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) 235==4174== 236==4174== 304 bytes in 1 blocks are possibly lost in loss record 27 of 35 237==4174== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 238==4174== by 0x40134A6: allocate_dtv (dl-tls.c:286) 239==4174== by 0x40134A6: _dl_allocate_tls (dl-tls.c:530) 240==4174== by 0x5987227: allocate_stack (allocatestack.c:627) 241==4174== by 0x5987227: pthread_create@@GLIBC_2.2.5 (pthread_create.c:644) 242==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) 243==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) 244==4174== by 0xA1C9A5: node::NodeMainInstance::CreateMainEnvironment(int*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 245==4174== by 0xA1CB42: node::NodeMainInstance::Run() (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 246==4174== by 0x9ACB67: node::Start(int, char**) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 247==4174== by 0x5BBFB96: (below main) (libc-start.c:310) 248==4174== 249==4174== 2,000 bytes in 2 blocks are possibly lost in loss record 33 of 35 250==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 251==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 252==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) 253==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) 254==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) 255==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 256==4174== by 0x12F68A3: ??? (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== 263==4174== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 264==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 265==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 266==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) 267==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) 268==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) 269==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 270==4174== by 0x12F68A3: ??? (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== 277==4174== LEAK SUMMARY: 278==4174== definitely lost: 997,064 bytes in 998 blocks 279==4174== indirectly lost: 0 bytes in 0 blocks 280==4174== possibly lost: 2,304 bytes in 3 blocks 281==4174== still reachable: 8,635 bytes in 31 blocks 282==4174== of which reachable via heuristic: 283==4174== multipleinheritance: 48 bytes in 1 blocks 284==4174== suppressed: 0 bytes in 0 blocks 285==4174== Reachable blocks (those to which a pointer was found) are not shown. 286==4174== To see them, rerun with: --leak-check=full --show-leak-kinds=all 287==4174== 288==4174== For counts of detected and suppressed errors, rerun with: -v 289==4174== Use --track-origins=yes to see where uninitialised values come from 290==4174== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0) 291``` 292 293This is the most interesting part of the report: 294 295```console 296==4174== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 297==4174== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 298==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 299==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) 300==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) 301==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) 302==4174== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 303==4174== by 0x12F68A3: ??? (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``` 310 311From the stack trace we can tell that the leak came from a native addon: 312 313```console 314==4174== by 0x9794979: Method(napi_env__*, napi_callback_info__*) (in /home/user1/valgrind/node-addon-examples/1_hello_world/napi/build/Release/hello.node) 315``` 316 317What we can't tell is where in the native addon the memory is being 318allocated. This is because by default the addon is compiled without 319the debug symbols which valgrind needs to be able to provide more 320information. 321 322## Enabling debug symbols to get more information 323 324Leaks may be either in addons or Node.js itself. The sections which 325follow cover the steps needed to enable debug symbols to get more info. 326 327### Native addons 328 329To enable debug symbols for all of your addons that are compiled on 330install use: 331 332```console 333npm install --debug 334``` 335 336Any options which are not consumed by npm are passed on to node-gyp and this 337results in the addons being compiled with the debug option. 338 339If the native addon contains pre-built binaries you will need to force 340a rebuild. 341 342```console 343npm install --debug 344npm rebuild 345``` 346 347The next step is to run valgrind after the rebuild. This time the information 348for the leaking location includes the name of the source file and the 349line number: 350 351```console 352==18481== 997,000 bytes in 997 blocks are definitely lost in loss record 35 of 35 353==18481== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 354>>>>> ==18481== by 0x9794989: Method(napi_env__*, napi_callback_info__*) (hello.cc:13) <<<<< 355==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) 356==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) 357==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) 358==18481== by 0x1376358: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 359==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 360==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 361==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 362==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 363==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 364==18481== by 0x12F68A3: ??? (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 365``` 366 367This new output shows us exactly where the leak is occurring in the file `hello.cc`: 368 369```cpp 370 6 void* malloc_holder = nullptr; 371 7 napi_value Method(napi_env env, napi_callback_info info) { 372 8 napi_status status; 373 9 napi_value world; 374 10 status = napi_create_string_utf8(env, "world", 5, &world); 375 11 assert(status == napi_ok); 376 12 for (int i=0; i< 1000; i++) { 377 13 malloc_holder = malloc(1000); // <<<<<< This is where we are allocating the memory that is not freed 378 14 } 379 15 return world; 380 16 } 381``` 382 383### Node.js binary 384 385If the leak is not in an addon and is instead in the Node.js binary itself, 386you may need to compile node yourself and turn on debug symbols. Looking at 387this entry reported by valgrind, with a release binary we see: 388 389```console 390 ==4174== 304 bytes in 1 blocks are possibly lost in loss record 27 of 35 391==4174== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 392==4174== by 0x40134A6: allocate_dtv (dl-tls.c:286) 393==4174== by 0x40134A6: _dl_allocate_tls (dl-tls.c:530) 394==4174== by 0x5987227: allocate_stack (allocatestack.c:627) 395==4174== by 0x5987227: pthread_create@@GLIBC_2.2.5 (pthread_create.c:644) 396==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) 397==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) 398==4174== by 0xA1C9A5: node::NodeMainInstance::CreateMainEnvironment(int*) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 399==4174== by 0xA1CB42: node::NodeMainInstance::Run() (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 400==4174== by 0x9ACB67: node::Start(int, char**) (in /home/user1/valgrind/node-v12.14.1-linux-x64/bin/node) 401==4174== by 0x5BBFB96: (below main) (libc-start.c:310) 402``` 403 404This gives us some information of where to look (`node::inspector::Agent::Start`) 405but not where in that function. We get more information than you might expect 406(or see by default with addons) because the Node.js binary exports many of 407its symbols using `-rdynamic` so that they can be used by addons. If the stack 408gives you enough information to track down where the leak is, that's great, 409otherwise the next step is to compile a debug build of Node.js. 410 411To get additional information with valgrind: 412 413* Check out the Node.js source corresponding to the release that you 414 want to debug. For example: 415```console 416git clone https://github.com/nodejs/node.git 417git checkout v12.14.1 418``` 419* Compile with debug enabled (for additional info see 420[building a debug build](https://github.com/nodejs/node/blob/v12.14.1/BUILDING.md#building-a-debug-build)). 421For example, on *nix: 422```console 423./configure --debug 424make -j4 425``` 426* Make sure to run with your compiled debug version of Node.js. Having used 427 `./configure --debug`, two binaries will have been built when `make` was run. 428 You must use the one which is in `out/Debug`. 429 430Running valgrind using the debug build of Node.js shows: 431 432```console 433==44112== 592 bytes in 1 blocks are possibly lost in loss record 26 of 27 434==44112== at 0x4C2BF79: calloc (vg_replace_malloc.c:762) 435==44112== by 0x4012754: _dl_allocate_tls (in /usr/lib64/ld-2.17.so) 436==44112== by 0x586287B: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so) 437==44112== by 0xFAB2D2: node::inspector::(anonymous namespace)::StartDebugSignalHandler() (inspector_agent.cc:140) 438==44112== by 0xFACB10: node::inspector::Agent::Start(std::string const&, node::DebugOptions const&, std::shared_ptr<node::HostPort>, bool) (inspector_agent.cc:777) 439==44112== by 0xE3A0BB: node::Environment::InitializeInspector(std::unique_ptr<node::inspector::ParentInspectorHandle, std::default_delete<node::inspector::ParentInspectorHandle> >) (node.cc:216) 440==44112== by 0xEE8F3E: node::NodeMainInstance::CreateMainEnvironment(int*) (node_main_instance.cc:222) 441==44112== by 0xEE8831: node::NodeMainInstance::Run() (node_main_instance.cc:108) 442==44112== by 0xE3CDEC: node::Start(int, char**) (node.cc:996) 443==44112== by 0x22D8BBF: main (node_main.cc:126) 444``` 445 446Now we can see the specific file name and line in the Node.js code which 447caused the allocation (inspector_agent.cc:140). 448