Skip to content

Commit e49a006

Browse files
RobinBateman8080xTim
authored andcommitted
Reformatted debug mem leak doc (swiftlang#608)
* Reformatted and added spaces before backticks to correct content alignment. * Update memory-leaks-and-usage.md Removed spacing for a numbered bullet set to remove formatting as a code block. * Tidy up code formats --------- Co-authored-by: Tim <[email protected]>
1 parent 15ca0ca commit e49a006

File tree

1 file changed

+41
-19
lines changed

1 file changed

+41
-19
lines changed

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

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,37 @@ It’s important to note, however, that a gradual increase in memory usage over
1212

1313
Debugging memory leaks in Swift on macOS and Linux environments can be done using different tools and techniques, each with distinct strengths and usability.
1414

15-
Basic troubleshooting steps include:
15+
**Basic troubleshooting steps include:**
1616

1717
1. Using profiling tools, provided by the respective operating systems and development environments, to identify and analyze memory usage.
1818

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.
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.
2020

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.
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.
2222

2323
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/).
2424

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.
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.
2626

2727
3. Enabling debug memory allocation features allows you to get additional information about objects and their memory allocations.
2828

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.
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.
3030

31-
To enable Zombie Objects:
32-
1. Open your Xcode project.
33-
2. Go to the **Edit Scheme** menu by clicking on the scheme dropdown in the toolbar.
34-
3. In the scheme editor window, select the **Run** tab.
35-
4. Choose the **Diagnostics** tab.
36-
5. Under **Memory Management**, check the box next to **Enable Zombie Objects**.
31+
To enable Zombie Objects:
32+
1. Open your Xcode project.
33+
2. Go to the **Edit Scheme** menu by clicking on the scheme dropdown in the toolbar.
34+
3. In the scheme editor window, select the **Run** tab.
35+
4. Choose the **Diagnostics** tab.
36+
5. Under **Memory Management**, check the box next to **Enable Zombie Objects**.
3737

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

4040
### Troubleshooting
4141

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

44-
The following **example program** leaks memory. We are using it as an *example only* to illustrate the various troubleshooting methods mentioned below.
44+
The following **example program** leaks memory. We are using it as an *example only* to illustrate the various troubleshooting methods mentioned below.
45+
4546
```
4647
public class MemoryLeaker {
4748
var closure: () -> Void = { () }
@@ -67,6 +68,7 @@ func myFunctionDoingTheAllocation() {
6768
6869
myFunctionDoingTheAllocation()
6970
```
71+
7072
## Debugging leaks with Valgrind
7173
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.
7274

@@ -86,6 +88,7 @@ brew install valgrind
8688
Valgrind should be successfully installed on your system using the system package manager.
8789

8890
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+
8992
```
9093
valgrind --leak-check=full ./test
9194
```
@@ -95,11 +98,13 @@ valgrind --leak-check=full ./test
9598
1. Install Swift on your Linux system. You can download and install Swift from the [official website](https://swift.org/download/).
9699

97100
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+
98102
```
99103
sudo apt-get install valgrind
100104
```
101105

102106
3. Once Valgrind is installed, run the following command:
107+
103108
```
104109
valgrind --leak-check=full swift run
105110
```
@@ -139,6 +144,7 @@ The `valgrind` command analyzes the program for any memory leaks and shows the r
139144
```
140145

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

154160
To demangle the Swift symbols in the stack traces, run the `swift demangle` command:
161+
155162
```
156163
swift demangle <mangled_symbol>
157164
```
@@ -160,9 +167,10 @@ Replace `<mangled_symbol>` with the mangled symbol name shown in the stack trace
160167

161168
`swift demangle $s4test12MemoryLeakerCACycfC`
162169

163-
> Note: `swift demangle` is a Swift command line utility and should be available if you have the Swift toolchain installed.
170+
**Note:** `swift demangle` is a Swift command line utility and should be available if you have the Swift toolchain installed.
164171

165172
The utility will demangle the symbol and display a human-readable version as follows:
173+
166174
```
167175
==1== 32 bytes in 1 blocks are definitely lost in loss record 1 of 4
168176
==1== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
@@ -190,16 +198,19 @@ Here are the steps:
190198

191199
1. Open a terminal session and navigate to your Swift package directory.
192200
2. Set the `ASAN_OPTIONS` environment variable to enable AddressSanitizer and configure its behavior. You can do this by running the command:
201+
193202
```
194203
export ASAN_OPTIONS=detect_leaks=1
195204
```
196205

197206
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+
198208
```
199209
swift build --sanitize=address
200210
```
201211

202212
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+
203214
```
204215
=================================================================
205216
==478==ERROR: LeakSanitizer: detected memory leaks
@@ -213,31 +224,36 @@ Direct leak of 32 byte(s) in 1 object(s) allocated from:
213224
214225
SUMMARY: AddressSanitizer: 32 byte(s) leaked in 1 allocation(s).
215226
```
227+
216228
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).
217229

218230
However, you can symbolicate it using `llvm-symbolizer` or `addr2line` if you have `binutils` installed.
219231

220232
To install `binutils` for Swift on a server running Linux, follow these steps:
221233
1. Connect to your Swift server through SSH using a terminal.
222234
2. Update the package lists by running the following command:
235+
223236
```
224237
sudo apt update
225238
```
226239

227240
3. Install `binutils` by running the following command:
241+
228242
```
229243
sudo apt install binutils
230244
```
231245

232246
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.
233247

234248
You can now run the following command to demangle the symbols in the stack traces:
249+
235250
```
236251
# /tmp/test+0xc62ce
237-
addr2line -e /tmp/test -a 0xc62ce -ipf | swift demangle
252+
addr2line -e /tmp/test -a 0xc62ce -ipf | swift demangle
238253
```
239254

240255
In this example, the allocation that leaked is coming from:
256+
241257
```
242258
0x00000000000c62ce: test.myFunctionDoingTheAllocation() -> () at crtstuff.c:?
243259
```
@@ -256,13 +272,15 @@ A GUI front-end analyzer `heaptrack_gui` is available in addition to command lin
256272

257273
Using a different example, here’s a short how-to using [Ubuntu](https://www.swift.org/download/) to analyze transient usage.
258274
1. Install `heaptrack` by running this command:
275+
259276
```
260277
sudo apt-get install heaptrack
261278
```
262279

263280
2. Run the binary twice using `heaptrack`. The first run provides a baseline for `main`.
281+
264282
```
265-
> heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
283+
heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
266284
heaptrack output will be written to "/tmp/.nio_alloc_counter_tests_GRusAy/heaptrack.test_1000_autoReadGetAndSet.84341.gz"
267285
starting application, this might take some time...
268286
...
@@ -276,8 +294,9 @@ Heaptrack finished! Now run the following to investigate the data:
276294
```
277295

278296
3. Then run it a second time for the `feature branch` by changing the branch and recompiling.
297+
279298
```
280-
> heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
299+
heaptrack .build/x86_64-unknown-linux-gnu/release/test_1000_autoReadGetAndSet
281300
heaptrack output will be written to "/tmp/.nio_alloc_counter_tests_GRusAy/heaptrack.test_1000_autoReadGetAndSet.84372.gz"
282301
starting application, this might take some time...
283302
...
@@ -294,13 +313,15 @@ ubuntu@ip-172-31-25-161 /t/.nio_alloc_counter_tests_GRusAy>
294313
The output shows 673989 allocations in the `feature branch` version and 319347 in `main`, indicating a regression.
295314

296315
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:
316+
297317
```
298318
heaptrack_print -T -d heaptrack.test_1000_autoReadGetAndSet.84341.gz heaptrack.test_1000_autoReadGetAndSet.84372.gz | swift demangle
299319
```
300320

301-
> Note: `-T` outputs the temporary allocations, providing transient allocations and not leaks. If leaks are detected, remove `-T`.
321+
**Note:** `-T` outputs the temporary allocations, providing transient allocations and not leaks. If leaks are detected, remove `-T`.
302322

303323
Scroll down to see the transient allocations (output may be long):
324+
304325
```
305326
MOST TEMPORARY ALLOCATIONS
306327
307740 temporary allocations of 290324 allocations in total (106.00%) from
@@ -339,6 +360,7 @@ swift_slowAlloc
339360
```
340361

341362
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+
342364
```
343365
NIO.URing.getEnvironmentVar(Swift.String) -> Swift.String?
344366
at /home/ubuntu/swiftnio/swift-nio/Sources/NIO/LinuxURing.swift:291
@@ -348,7 +370,7 @@ NIO.URing._debugPrint(@autoclosure () -> Swift.String) -> ()
348370

349371
In this example, the debug prints are only for testing and would be removed from the code before the branch is merged.
350372

351-
> 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.
373+
**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.
352374

353375
### Limitations
354376

0 commit comments

Comments
 (0)