• 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
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