Skip to content

Commit 8624dbf

Browse files
Fix number format starting over after a block of code (#617)
* Reformatted numbered lists due to Markdown's disruption of lists after fenced code blocks (triple backticks). * Corrected "Install binutils" numbering * removed valgrind option for macOS * Reformatted numbering I added spaces to nest the numbering sequences after the removal of macOS content for Valgrind. * Reformatted bullets and numbering * Formatted step 4 and verified number sequence * Cleaned up code formatting Added spaces between backticks and wording to align code formatting in the Heaptrack section. * Testing changes again... * Changed number sequence to steps
1 parent 663b3cd commit 8624dbf

File tree

1 file changed

+37
-69
lines changed

1 file changed

+37
-69
lines changed

documentation/server/guides/memory-leaks-and-usage.md

Lines changed: 37 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,35 @@ layout: page
44
title: Debugging Memory Leaks and Usage
55
---
66

7-
Debugging memory leaks and usage helps you identify and resolve issues related to memory management in an application.
7+
# Overview
88

9-
Memory leaks occur when memory is allocated but not properly deallocated, leading to a gradual increase in memory usage over time. This can severely impact an application's performance and stability.
9+
Debugging memory leaks and usage helps you identify and resolve issues related to memory management in an application. Memory leaks occur when memory is allocated but not properly deallocated, leading to a gradual increase in memory usage over time. This can severely impact an application's performance and stability.
1010

1111
It’s important to note, however, that a gradual increase in memory usage over time doesn’t always indicate a leak. Instead, it may be the memory profile of the application. For example, when an application’s cache gradually fills over time it shows the same gradual increase in memory. Accordingly, configuring the cache so it doesn’t expand beyond a designated limit will cause the memory usage to plateau. Additionally, allocator libraries don't always immediately return memory feedback to the system due to performance or other reasons. But it will stabilize over time.
1212

13+
## Tools and techniques
14+
1315
Debugging memory leaks in Swift on macOS and Linux environments can be done using different tools and techniques, each with distinct strengths and usability.
1416

15-
**Basic troubleshooting steps include:**
17+
### Basic troubleshooting includes:
18+
19+
* Using profiling tools.
20+
* Reviewing code and identifying potential leaks.
21+
* Enabling debug memory allocation features.
1622

17-
1. Using profiling tools, provided by the respective operating systems and development environments, to identify and analyze memory usage.
23+
**1. Using profiling tools** provided by the respective operating systems and development environments to identify and analyze memory usage.
1824

19-
**For macOS**, [Memory Graph Debugger](https://developer.apple.com/documentation/xcode/gathering-information-about-memory-use#Inspect-the-debug-memory-graph) and this [Detect and diagnose memory issues](https://developer.apple.com/videos/play/wwdc2021/10180/) video are helpful. You can also use the [Xcode Instruments](https://help.apple.com/instruments/mac/10.0/#/dev022f987b) tool for various profiling instruments including the [Allocations instrument](https://developer.apple.com/documentation/xcode/gathering-information-about-memory-use#Profile-your-app-using-the-Allocations-instrument) to track memory allocation and deallocation in your Swift code.
25+
*For macOS*: [Memory Graph Debugger](https://developer.apple.com/documentation/xcode/gathering-information-about-memory-use#Inspect-the-debug-memory-graph) and this [Detect and diagnose memory issues](https://developer.apple.com/videos/play/wwdc2021/10180/) video are helpful. You can also use the [Xcode Instruments](https://help.apple.com/instruments/mac/10.0/#/dev022f987b) tool for various profiling instruments including the [Allocations instrument](https://developer.apple.com/documentation/xcode/gathering-information-about-memory-use#Profile-your-app-using-the-Allocations-instrument) to track memory allocation and deallocation in your Swift code.
2026

21-
**For Linux**, you can use tools like [Valgrind](https://valgrind.org/) or [Heaptrack](https://github.com/KDE/heaptrack) to profile your application as shown in the examples below. Although these tools are primarily used for C/C++ code, they can also work with Swift.
27+
*For Linux*: You can use tools like [Valgrind](https://valgrind.org/) or [Heaptrack](https://github.com/KDE/heaptrack) to profile your application as shown in the examples below. Although these tools are primarily used for C/C++ code, they can also work with Swift.
2228

23-
2. Reviewing code and identifying potential leaks to examine your code for any potential areas where memory leaks may occur. Common sources of leaks include retained references or unbalanced retain-release cycles, which rarely apply to Swift since it performs [automatic reference counting (ARC)](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/).
29+
**2. Reviewing code and identifying potential leaks** to examine your code for any potential areas where memory leaks may occur. Common sources of leaks include retained references or unbalanced retain-release cycles, which rarely apply to Swift since it performs [automatic reference counting (ARC)](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/).
2430

25-
**Note:** Memory leaks can occur in Swift if there are substantial reference cycles between objects that involve closures or if objects hold references to external resources that are not released properly. However, the likelihood of such issues is significantly reduced through the automatic memory management's ability to add and remove references, making sources of leaks like retained references and unbalanced retain-release cycles less common in Swift code.
31+
> Note: Memory leaks can occur in Swift if there are substantial reference cycles between objects that involve closures or if objects hold references to external resources that are not released properly. However, the likelihood of such issues is significantly reduced through the automatic memory management's ability to add and remove references, making sources of leaks like retained references and unbalanced retain-release cycles less common in Swift code.
2632
27-
3. Enabling debug memory allocation features allows you to get additional information about objects and their memory allocations.
33+
**3. Enabling debug memory allocation features** allows you to get additional information about objects and their memory allocations.
2834

29-
**On macOS**, you can enable Zombie Objects using Xcode or use [MallocStackLogging](https://developer.apple.com/videos/play/wwdc2022/10106/) to detect over-released or accessed deallocated objects.
35+
*On macOS*: You can enable Zombie Objects using Xcode or use [MallocStackLogging](https://developer.apple.com/videos/play/wwdc2022/10106/) to detect over-released or accessed deallocated objects.
3036

3137
To enable Zombie Objects:
3238
1. Open your Xcode project.
@@ -35,9 +41,9 @@ To enable Zombie Objects:
3541
4. Choose the **Diagnostics** tab.
3642
5. Under **Memory Management**, check the box next to **Enable Zombie Objects**.
3743

38-
**On Linux**, Swift has built-in LeakSanitizer support that can be enabled using the `-sanitize=leak` compiler flag.
44+
*On Linux*: Swift has built-in LeakSanitizer support that can be enabled using the `-sanitize=leak` compiler flag.
3945

40-
### Troubleshooting
46+
## Troubleshooting
4147

4248
This section aims to provide you with helpful server-side troubleshooting techniques to debug leaks and usage using **Valgrind**, **LeakSanitizer**, and **Heaptrack**.
4349

@@ -69,48 +75,23 @@ func myFunctionDoingTheAllocation() {
6975
myFunctionDoingTheAllocation()
7076
```
7177

72-
## Debugging leaks with Valgrind
78+
### Debugging leaks with Valgrind
7379
Valgrind is an open-source framework for debugging and profiling Linux applications. It provides several tools, including Memcheck, which can detect memory leaks, invalid memory accesses, and other memory errors. Although Valgrind is primarily focused on C/C++ applications, it can also be used with Swift on Linux.
7480

75-
To debug memory leaks using Valgrind, install it on your system.
76-
77-
**For MacOS**:
78-
1. Open a Terminal session.
79-
2. [Install Homebrew](https://brew.sh/) if you haven't already.
80-
3. Once `Homebrew` is installed, run this command to install `valgrind`:
81-
82-
```bash
83-
brew install valgrind
84-
```
85-
86-
4. Enter your password if prompted to authorize the software installation and allow Homebrew to complete the installation process. Confirm the installation when requested.
87-
88-
Valgrind should be successfully installed on your system using the system package manager.
89-
90-
5. Once you've compiled your program (in this case, a binary named `test`), run the following `valgrind` command to enable full leak checking:
91-
92-
```
93-
valgrind --leak-check=full ./test
94-
```
95-
96-
**For Swift on Linux**:
81+
To debug memory leaks for Swift on Linux using Valgrind, install it on your system.
9782

9883
1. Install Swift on your Linux system. You can download and install Swift from the [official website](https://swift.org/download/).
99-
10084
2. Install Valgrind on your Linux system by using your package manager. For example, if you are using Ubuntu, you can run the following command:
101-
10285
```
10386
sudo apt-get install valgrind
10487
```
10588

10689
3. Once Valgrind is installed, run the following command:
107-
10890
```
10991
valgrind --leak-check=full swift run
11092
```
11193

112-
The `valgrind` command analyzes the program for any memory leaks and shows the relevant information about the leak, including the stack trace where the allocation occurred as shown below:
113-
94+
The `valgrind` command analyzes the program for any memory leaks and shows the relevant information about the leak, including the stack trace where the allocation occurred as shown below:
11495
```
11596
==1== Memcheck, a memory error detector
11697
==1== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
@@ -144,7 +125,6 @@ The `valgrind` command analyzes the program for any memory leaks and shows the r
144125
```
145126

146127
The following trace block (from above) indicates a memory leak.
147-
148128
```
149129
==1== 32 bytes in 1 blocks are definitely lost in loss record 1 of 4
150130
==1== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
@@ -158,7 +138,6 @@ The following trace block (from above) indicates a memory leak.
158138
However, since Swift uses name mangling for function and symbol names, the stack traces may not be straightforward to understand.
159139

160140
To demangle the Swift symbols in the stack traces, run the `swift demangle` command:
161-
162141
```
163142
swift demangle <mangled_symbol>
164143
```
@@ -170,7 +149,6 @@ Replace `<mangled_symbol>` with the mangled symbol name shown in the stack trace
170149
**Note:** `swift demangle` is a Swift command line utility and should be available if you have the Swift toolchain installed.
171150

172151
The utility will demangle the symbol and display a human-readable version as follows:
173-
174152
```
175153
==1== 32 bytes in 1 blocks are definitely lost in loss record 1 of 4
176154
==1== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
@@ -185,32 +163,29 @@ By analyzing the demangled symbols, we can understand which part of the code is
185163

186164
`test.myFunctionDoingTheAllocation` calling `test.MemoryLeaker.__allocating_init()`
187165

188-
### Limitations
166+
#### Limitations
189167

190168
* The `valgrind` command doesn’t understand the bit-packing used in many Swift data types like `String` or when `enums` are created with associated values. Consequently, using the `valgrind` command sometimes reports memory errors or leaks that do not exist, and false negatives occur when it fails to detect actual issues.
191169
* The `valgrind` command makes your program run exceptionally slow (possibly 100x slower), which may hinder your ability to reproduce the problem and analyze the performance.
192170
* Valgrind is primarily supported on Linux. Its support for other platforms, such as macOS or iOS, may be limited or nonexistent.
193171

194-
## Debugging leaks with LeakSanitizer
172+
### Debugging leaks with LeakSanitizer
195173
LeakSanitizer is a memory leak detector that is integrated into [AddressSanitizer](https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early). To debug memory leaks using LeakSanitizer with Address Sanitizer enabled on Swift, you will need to set the appropriate environment variable, compile your Swift package with the necessary options, and then run your application.
196174

197175
Here are the steps:
198176

199177
1. Open a terminal session and navigate to your Swift package directory.
200178
2. Set the `ASAN_OPTIONS` environment variable to enable AddressSanitizer and configure its behavior. You can do this by running the command:
201-
202179
```
203180
export ASAN_OPTIONS=detect_leaks=1
204181
```
205182

206183
3. Run `swift build` with the additional option to enable [Address Sanitizer](https://developer.apple.com/documentation/xcode/diagnosing-memory-thread-and-crash-issues-early):
207-
208184
```
209185
swift build --sanitize=address
210186
```
211187

212188
The build process will compile your code with AddressSanitizer enabled, which automatically looks for leaked memory blocks. If any memory leaks during the build are detected, it will output the information (similar to Valgrind) as shown in the example below:
213-
214189
```
215190
=================================================================
216191
==478==ERROR: LeakSanitizer: detected memory leaks
@@ -225,59 +200,55 @@ Direct leak of 32 byte(s) in 1 object(s) allocated from:
225200
SUMMARY: AddressSanitizer: 32 byte(s) leaked in 1 allocation(s).
226201
```
227202

228-
Currently, the output doesn’t provide a human-readable representation of the function names because [LeakSanitizer doesn't symbolicate stack traces on Linux](https://github.com/apple/swift/issues/55046).
229-
230-
However, you can symbolicate it using `llvm-symbolizer` or `addr2line` if you have `binutils` installed.
203+
Currently, the output doesn’t provide a human-readable representation of the function names because [LeakSanitizer doesn't symbolicate stack traces on Linux](https://github.com/apple/swift/issues/55046). However, you can symbolicate it using `llvm-symbolizer` or `addr2line` if you have `binutils` installed.
231204

232205
To install `binutils` for Swift on a server running Linux, follow these steps:
233-
1. Connect to your Swift server through SSH using a terminal.
234-
2. Update the package lists by running the following command:
235206

207+
Step 1: Connect to your Swift server through SSH using a terminal.
208+
209+
Step 2: Update the package lists by running the following command:
236210
```
237211
sudo apt update
238212
```
239213

240-
3. Install `binutils` by running the following command:
241-
214+
Step 3: Install `binutils` by running the following command:
242215
```
243216
sudo apt install binutils
244217
```
245218

246-
4. This will install `binutils` and its related tools for working with binaries, object files, and libraries, which can be useful for developing and debugging Swift applications on Linux.
219+
This will install `binutils` and its related tools for working with binaries, object files, and libraries, which can be useful for developing and debugging Swift applications on Linux.
247220

248221
You can now run the following command to demangle the symbols in the stack traces:
249-
250222
```
251223
# /tmp/test+0xc62ce
252224
addr2line -e /tmp/test -a 0xc62ce -ipf | swift demangle
253225
```
254226

255227
In this example, the allocation that leaked is coming from:
256-
257228
```
258229
0x00000000000c62ce: test.myFunctionDoingTheAllocation() -> () at crtstuff.c:?
259230
```
260231

261-
### Limitations
232+
#### Limitations
262233

263234
* LeakSanitizer may not be as effective in detecting and reporting all types of memory leaks in Swift code compared to languages like C or C++.
264235
* False positives occur when LeakSanitizer reports a memory leak that does not exist.
265236
* LeakSanitizer is primarily supported on macOS and Linux. While it is possible to use LeakSanitizer on iOS or other platforms that support Swift, there may be limitations or platform-specific issues that need to be considered.
266237
* Enabling Address Sanitizer and LeakSanitizer in your Swift project can have a performance impact. It is recommended to use LeakSanitizer for targeted analysis and debugging rather than continuously running it in production environments.
267238

268-
## Debugging transient memory usage with Heaptrack
239+
### Debugging transient memory usage with Heaptrack
269240
[Heaptrack](https://github.com/KDE/heaptrack) is an open-source heap memory profiler tool that helps find and analyze memory leaks and usage with less overhead than Valgrind. It also allows for analyzing and debugging transient memory usage in your application. However, it may significantly impact performance by overloading the allocator.
270241

271242
A GUI front-end analyzer `heaptrack_gui` is available in addition to command line access. The analyzer allows for diffing between two different runs of your application to troubleshoot variations in `malloc` behavior between the `feature branch` and `main`.
272243

273244
Using a different example, here’s a short how-to using [Ubuntu](https://www.swift.org/download/) to analyze transient usage.
274-
1. Install `heaptrack` by running this command:
275245

246+
Step 1: Install `heaptrack` by running this command:
276247
```
277248
sudo apt-get install heaptrack
278249
```
279250

280-
2. Run the binary twice using `heaptrack`. The first run provides a baseline for `main`.
251+
Step 2: Run the binary twice using `heaptrack`. The first run provides a baseline for `main`.
281252

282253
```
283254
heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
@@ -293,7 +264,7 @@ Heaptrack finished! Now run the following to investigate the data:
293264
heaptrack --analyze "/tmp/.nio_alloc_counter_tests_GRusAy/heaptrack.test_1000_autoReadGetAndSet.84341.gz"
294265
```
295266

296-
3. Then run it a second time for the `feature branch` by changing the branch and recompiling.
267+
Step 3: Then run it a second time for the `feature branch` by changing the branch and recompiling.
297268

298269
```
299270
heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
@@ -310,18 +281,16 @@ Heaptrack finished! Now run the following to investigate the data:
310281
ubuntu@ip-172-31-25-161 /t/.nio_alloc_counter_tests_GRusAy>
311282
```
312283

313-
The output shows 673989 allocations in the `feature branch` version and 319347 in `main`, indicating a regression.
314-
315-
4. Run the following command to analyze the output as a diff from these runs using `heaptrack_print` and pipe it through `swift demangle` for readability:
284+
The output shows `673989` allocations in the `feature branch` version and `319347` in `main`, indicating a regression.
316285

286+
Step 4: Run the following command to analyze the output as a diff from these runs using `heaptrack_print` and pipe it through `swift demangle` for readability:
317287
```
318288
heaptrack_print -T -d heaptrack.test_1000_autoReadGetAndSet.84341.gz heaptrack.test_1000_autoReadGetAndSet.84372.gz | swift demangle
319289
```
320290

321291
**Note:** `-T` outputs the temporary allocations, providing transient allocations and not leaks. If leaks are detected, remove `-T`.
322292

323293
Scroll down to see the transient allocations (output may be long):
324-
325294
```
326295
MOST TEMPORARY ALLOCATIONS
327296
307740 temporary allocations of 290324 allocations in total (106.00%) from
@@ -360,7 +329,6 @@ swift_slowAlloc
360329
```
361330

362331
Looking at the output above, we can see the extra transient allocations were due to extra debug printing and querying of environment variables as shown below:
363-
364332
```
365333
NIO.URing.getEnvironmentVar(Swift.String) -> Swift.String?
366334
at /home/ubuntu/swiftnio/swift-nio/Sources/NIO/LinuxURing.swift:291
@@ -372,7 +340,7 @@ In this example, the debug prints are only for testing and would be removed from
372340

373341
**Tip:** Heaptrack can also be [installed on an RPM-based distribution](https://rhel.pkgs.org/8/epel-x86_64/heaptrack-1.2.0-7.el8.x86_64.rpm.html) to debug transient memory usage. You may need to consult the distribution's documentation for the specific repository setup steps. When Heaptrack is installed correctly, it should display its version and usage information.
374342

375-
### Limitations
343+
#### Limitations
376344

377345
* It's important to note that Heaptrack was primarily designed for C and C++ applications, so its support for Swift applications is limited.
378346
* While Heaptrack can provide insights into memory allocations and deallocations in a Swift application, it may not capture certain Swift-specific memory management mechanisms like Swift's built-in Instruments profiler.

0 commit comments

Comments
 (0)