Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 27 additions & 23 deletions chapters/debugging.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
=== Introduction
This chapter goes into the various methods for finding and fixing bugs without disrupting the services in progress. We will explore testing techniques, tools, and frameworks that aid in testing and debugging your code. We'll also shed light on some common bug sources, such as deadlocks, message overflow, and memory issues, providing guidance on identifying and resolving these problems.

Debugging is the process of identifying and eliminating errors, or "bugs," from software. While Erlang offers step-by-step debugging tools like the link:http://erlang.org/doc/apps/debugger/debugger_chapter.html[_Debugger_], the most effective debugging methods often rely on Erlang's tracing facilities. These facilities will be thoroughly discussed in Chapter xref:CH-Tracing[]. In this chapter We will touch on system level tracing with dtrace and systemtap.
Debugging is the process of identifying and eliminating errors, or "bugs," from software. While Erlang offers step-by-step debugging tools like the link:http://erlang.org/doc/apps/debugger/debugger_chapter.html[_Debugger_], the most effective debugging methods often rely on Erlang's tracing facilities. These facilities will be thoroughly discussed in xref:CH-Tracing[]. In this chapter We will touch on system level tracing with DTrace and SystemTap.

This chapter also explores the concept of "Crash Dumps," which are human-readable text files generated by the Erlang Runtime System when an unrecoverable error occurs, such as running out of memory or reaching an emulator limit. Crash Dumps are invaluable for post-mortem analysis of Erlang nodes, and you will learn how to interpret and understand them.

In addition to these topics, this chapter will also discuss different testing methodologies, including EUnit and Common Test, which are crucial for ensuring the reliability and robustness of your code. The importance of mocking in testing will be examined, along with its best practices.

You will become acquainted with the "let it crash" principle and the ways to effectively implement it within your system. You'll gain insights into the workings of exceptions and supervisor tree design.

By the end of this chapter, you'll be equipped with the knowledge to systematically test your system and its individual components. You will be able to identify common mistakes and problems, and possibly even picking up some debugging philosophy along the way.
By the end of this chapter, you'll be equipped with the knowledge to systematically test your system and its individual components. You will be able to identify common mistakes and problems, and possibly even pick up some debugging philosophy along the way.


=== Debugging Philosophy
Expand Down Expand Up @@ -49,7 +49,7 @@ Check **process message queues** using:
process_info(Pid, messages).
```
A long message queue could indicate a performance bottleneck.

Inspect **ETS tables** and memory usage:
```erlang
ets:info(my_table, size).
Expand Down Expand Up @@ -163,7 +163,7 @@ process_request({error, _}) -> handle_error().

===== **4. Implementing Fail-Fast Mechanisms**

Erlang’s **Let It Crash** philosophy means processes should **fail quickly** when an error occurs instead of propagating invalid state.
Erlang’s **Let It Crash** philosophy means processes should **fail quickly** when an error occurs instead of propagating invalid state.

Example: Enforcing fail-fast behavior with guards:
```erlang
Expand Down Expand Up @@ -273,7 +273,7 @@ Memory leaks in Erlang often stem from:
- **Unbounded message queues**: Processes that receive but never consume messages.
- **Binary data accumulation**: Large binaries can cause high memory fragmentation.

===== How to detect memory leaks
===== How to Detect Memory Leaks

Check individual process memory usage:
```erlang
Expand Down Expand Up @@ -306,7 +306,7 @@ erlang:garbage_collect(Pid).
```
This reclaims memory used by binaries if the process is no longer referencing them.
This can be important in relaying processes that are not using the binaries anymore,
but they hang on to a reference to them. Remember that binaries are reference counted
but they hang on to a reference to them. Remember that binaries are reference counted
and live across processes.

**Monitor binary memory allocation**:
Expand All @@ -324,7 +324,7 @@ Erlang provides several **system flags** that control heap allocation behavior.
- Helps avoid frequent heap expansions if a process is expected to handle large amounts of data.
- Default is typically **233 words**, but increasing it slightly (e.g., **256** or **512**) can improve performance for processes that grow quickly.

**Example Usage**
**Example Usage**:
You can configure this setting for a process using:
```erlang
spawn_opt(fun() -> my_function() end, [{min_heap_size, 512}]).
Expand Down Expand Up @@ -512,12 +512,11 @@ Example trace output:
```
This allows you to track how values change throughout execution.

=== The next-genation debugger: EDB
=== The Next-Generation Debugger: EDB

The Erlang Debugger (EDB) is a modern, feature-rich debugger for Erlang applications. It provides a language server interface for setting breakpoints, inspecting variables, and stepping through code execution. See https://whatsapp.github.io/edb/
The Erlang Debugger (EDB) is a modern, feature-rich debugger for Erlang applications. It provides a language server interface for setting breakpoints, inspecting variables, and stepping through code execution. See https://github.com/WhatsApp/edb

In order to use EDB, you need to build Erlang from source with EDB support.
Future versions of Erlang OTP might be shipped with EDB support.
In order to use EDB prior to OTP 28, you need to build Erlang from source with EDB support.
Here is a guide on how to build Erlang from source with EDB support:

```bash
Expand All @@ -540,6 +539,7 @@ _build/default/bin/edb dap
This command launches EDB, allowing it to interface with your development environment through the DAP, providing a robust debugging experience.

Current State and Stability

At the time of writing, EDB is an early-stage, rapidly evolving tool. Its integration and usability, while promising, can still be challenging, particularly regarding IDE setup, node connections, and environment compatibility. Stability can vary significantly depending on OTP versions and developer tooling choices.

=== Crash Dumps in Erlang
Expand Down Expand Up @@ -599,18 +599,21 @@ This dump suggests that the system crashed due to a memory allocation failure (`
===== Key Sections in a Crash Dump

1. Slogan

Indicates the reason for the crash. Common slogans include:
- `eheap_alloc: Cannot allocate X bytes of memory` (Memory exhaustion)
- `Init terminating in do_boot ()` (Pobably an error in the boot script)
- `Init terminating in do_boot ()` (Probably an error in the boot script)
- `Could not start kernel pid` (Probably a bad argument in config)

2. System Information

Contains details about the runtime:
- `System version`: The Erlang/OTP version and build details
- `Compiled`: When the system was built
- `Taints`: Whether external native code (NIFs) are running

3. Memory Usage

Displays the memory distribution:

- `Total`: Total memory usage
Expand All @@ -620,16 +623,19 @@ This dump suggests that the system crashed due to a memory allocation failure (`
- `Code`: Loaded code memory footprint

4. Process List

Provides details about active processes, this section is crucial for identifying:

- Processes consuming excessive memory (`Stack+Heap` size)
- Processes stuck in an infinite loop (`Reductions` count abnormally high)
- Message queue overload (`Messages` field growing indefinitely)

5. Ports and Drivers

This lists open ports and drivers, which can be useful if external system interactions (files, sockets, databases) are suspected as crash causes.

6. Loaded Modules

This helps determine if dynamically loaded code (e.g., via `code:load_file/1`) caused the crash.


Expand Down Expand Up @@ -704,14 +710,14 @@ help you inspect the state of the system at the point of the crash.

=== Debugging the Runtime System

Understanding and diagnosing issues within the Erlang runtime system (BEAM) can be challenging due to its complexity. However, utilizing tools like the GNU Debugger (GDB) can significantly aid in this process. This section provides an overview of using GDB to debug the BEAM, including setting up the environment and employing GDB macros to streamline the debugging workflow.
Understanding and diagnosing issues within the Erlang runtime system (ERTS) can be challenging due to its complexity. However, utilizing tools like the GNU Debugger (GDB) can significantly aid in this process. This section provides an overview of using GDB to debug the BEAM, including setting up the environment and employing GDB macros to streamline the debugging workflow.

[[Using-GDB]]
==== Using GDB

GDB is a powerful tool for debugging applications at the machine level, offering insights into the execution of compiled programs. When applied to the BEAM, GDB allows developers to inspect the state of the Erlang virtual machine during execution or after a crash.

To effectively use GDB with the BEAM, it's beneficial to compile the Erlang runtime system with debugging symbols. This compilation provides detailed information during debugging sessions.
To effectively use GDB with the BEAM, it's beneficial to compile the Erlang runtime system with debugging symbols. This compilation provides detailed information during debugging sessions.

See <<Alternative Beam emulator builds>> for instructions on compiling and running
a version of Erlang with debugging information.
Expand Down Expand Up @@ -743,10 +749,10 @@ bin/cerl -rcore <core file>
```

NOTE: If you are comfortable with using the Emacs editor, you can use
`cerl` with the flags `-gdb` and `-core` (no leading `r`), which launch an
`cerl` with the flags `-gdb` and `-core` (no leading `r`), which launches an
Emacs instance to work as an IDE for the debugging session. (By setting
`EMACS=emacsclient` first, you can even make it run in an existing Emacs if
you have done a `M-x server-start`.) See the
you have done an `M-x server-start`.) See the
https://www.gnu.org/software/emacs/manual/html_node/emacs/GDB-Graphical-Interface.html[Emacs
GDB documentation] for more details.

Expand All @@ -770,7 +776,7 @@ gdb bin/x86_64-unknown-linux-gnu/beam.debug.smp 3140019
since those are just shell scripts that set up the proper environment
variables for the BEAM executable.)

Your OS might by default restrict attaching to running processes - even
Your OS might by default restrict attaching to running processeseven
those you own. How to reconfigure this is out of scope for this book.


Expand All @@ -793,7 +799,7 @@ etp-process-info <process_pointer>
```
Replace `<process_pointer>` with the actual pointer to the process control block (PCB) you're interested in. These macros simplify the process of extracting meaningful data from the BEAM's internal structures.

For a comprehensive guide on debugging the BEAM using GDB and employing these macros, refer to link:https://max-au.com/2022/03/29/debugging-the-beam/[Debugging the BEAM] and link:https://www.erlang.org/doc/system/debugging.html#debug-emulator[Debug emulator documentation]. These resources provide in-depth instructions and examples to assist you in effectively diagnosing and resolving issues within the Erlang runtime system.
For a comprehensive guide on debugging the BEAM using GDB and employing these macros, refer to link:https://max-au.com/2022/03/29/debugging-the-beam/[Debugging the BEAM] and link:https://www.erlang.org/doc/system/debugging.html#debug-emulator[Debug emulator] documentation. These resources provide in-depth instructions and examples to assist you in effectively diagnosing and resolving issues within the Erlang runtime system.


==== SystemTap and DTrace
Expand All @@ -807,7 +813,7 @@ Using these tools with Erlang can provide deep insights into the behavior of the
SystemTap and DTrace operate by inserting dynamically generated probes into running kernel and user-space applications. These probes capture real-time data, allowing developers to inspect and analyze program execution without stopping or modifying the application.

- **SystemTap**: Developed for Linux, SystemTap enables monitoring of kernel events, user-space programs, and runtime behavior using scripting. It is commonly used for profiling, fault detection, and system introspection.

- **DTrace**: Originally developed by Sun Microsystems for Solaris, DTrace provides similar tracing capabilities with a robust scripting language. It is widely used on macOS, FreeBSD, and SmartOS.

Both tools allow developers to measure function execution times, trace system calls, inspect memory usage, and capture event-based data critical for optimizing performance and debugging complex applications.
Expand All @@ -821,7 +827,7 @@ To use SystemTap and DTrace with Erlang, you need to enable the necessary tracin
SystemTap scripts rely on user-space markers embedded in the BEAM emulator. These markers allow SystemTap to hook into various internal events. To use SystemTap with Erlang:

- **Ensure SystemTap is installed** (on Linux distributions such as Ubuntu, Fedora, or CentOS):

```sh
sudo apt-get install systemtap systemtap-sdt-dev
```
Expand Down Expand Up @@ -865,7 +871,7 @@ This allows developers to observe function calls, detect bottlenecks, and debug

DTrace integrates directly with the BEAM runtime, offering deep visibility into system operations. It allows tracing function calls, memory allocation, garbage collection, and inter-process communication.

Dtrace works best on Solaris. There is a Linux version bundled with systemtap, but it is not as powerful as the Solaris version.
Dtrace works best on Solaris. There is a Linux version bundled with SystemTap, but it is not as powerful as the Solaris version.

On macOS, DTrace is pre-installed. On Ubuntu, it can be installed via:

Expand Down Expand Up @@ -897,5 +903,3 @@ sudo dtrace -s my_script.d
```

This provides a non-intrusive way to monitor the internal behavior of the BEAM virtual machine in real-time.


Loading