• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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