Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ We're always happy to help contributors with their pull requests.

## Final word

Many thanks to all of our contributors, and looking forward to seeing you on Github! :tada:
Many thanks to all of our contributors, and looking forward to seeing you on Github!
70 changes: 57 additions & 13 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,107 @@
# ddprof

The Datadog Native Profiler for Linux
The Datadog Native Profiler for Linux.

## Overview

`ddprof` is a commandline utility for engaging kernel-mediated telemetry of an application and forwarding the resulting information to the Datadog backend. In several ways, it's similar to the `perf record` tool.
`ddprof` is a command-line utility to gather profiling data. After install you will continuously see where your application is spending CPU and memory allocations.
The data will be available in the `/profiling` section of the [Datadog UI](https://app.datadoghq.com/).

## Quick Start

Our official documentation is available [here](https://docs.datadoghq.com/profiler/enabling/ddprof/?tab=environmentvariables).
Our pre-built binaries are compatible with both musl and glibc. You should not need to recompile `ddprof` from source.

### From binary
### From binary [Recommended]

Check out our Release page for prebuilt binaries. Download the desired binary, making sure to mark it executable `chmod +x ./ddprof`.
Refer to [commands](docs/Commands.md) for the commands supported by `ddprof`. Example :
An installation guide is available [here](https://docs.datadoghq.com/profiler/enabling/ddprof/?tab=environmentvariables).
Check out our Release page for our [latest](https://github.com/DataDog/ddprof/releases/tag/latest) release. Download the release and extract `ddprof`.
Instrumenting your application should be as simple as adding `ddprof` in front of your usual command line.

To install the profiler, check out our [installation-helpers](#Installation-helpers) bellow.

The following command will run `ddprof` with the default settings (CPU and allocations)

```bash
./ddprof -S my_native_service ./run.sh
ddprof -S service_name_for_my_program ./my_program arg1 arg2
```

Profiling data shows up in the `/profiling` section of your Datadog UI. Specifying a service name will help you select your profiling data.
Refer to [commands](docs/Commands.md) for a more advanced usage of `ddprof`.

### From source

Checkout our build section [here](./docs/Build.md).

### Prerequisites
## Prerequisites

The Datadog Native Profiler for Linux has only been tested on kernel 4.15 above. It may be supported by older kernels, but your mileage may vary. One can verify the kernel version by running `uname`:
### Perf event paranoid

The target machine must have `perf_event_paranoid` set to 2 or lower.

```bash
uname -r
# needs to be less than or equal to 2
cat /proc/sys/kernel/perf_event_paranoid
```

In addition, the target machine must have `perf_event_paranoid` set to 2 or lower OR `CAP_SYS_ADMIN` enabled.
Here is an example adding a startup configuration to your system. This requires a system restart.

```bash
# needs to be less than or equal to 2
cat /proc/sys/kernel/perf_event_paranoid
sudo sh -c 'echo kernel.perf_event_paranoid=2 > /etc/sysctl.d/perf_event_paranoid_2.conf'
```

Alternatively you can use `CAP_SYS_ADMIN` or `sudo` as a one off test mechanism, more in the [Troubleshooting](./docs/Troubleshooting.md) section.
Don't hesitate to [reach-out](#Reaching-out) if you are not able to use our profiler!

### Agent installation

It is recommended to have an agent setup on the system you are profiling.
By default the profiler will target `localhost:8126` (the default trace agent endpoint). The `DD_TRACE_AGENT_URL` environment variable can be used to override this setting.

## Installation helpers

### Ubuntu / Debian

The following commands will download and install `ddprof` on Debian or Ubuntu distributions:

```bash
export ARCH=$(dpkg --print-architecture) # ARCH should hold amd64 or arm64
# ddprof requires xz-utils to uncompress the archive
sudo apt-get update && \
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y xz-utils curl jq && \
tag_name=$(curl -s https://api.github.com/repos/DataDog/ddprof/releases/latest | jq -r '.tag_name[1:]') && \
url_release="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/DataDog/ddprof/releases/download/v${tag_name}/ddprof-${tag_name}-${ARCH}-linux.tar.xz" && \
curl -L -o ddprof-${ARCH}-linux.tar.xz ${url_release} && \
tar xvf ddprof-${ARCH}-linux.tar.xz && \
sudo mv ddprof/bin/ddprof /usr/local/bin && \
rm -Rf ddprof-amd64-linux.tar.xz ./ddprof && \
ddprof --version
```

## Key Features

### Simplicity

`ddprof` is a wrapper, so using it should be as simple as injecting the binary into your container and wrapping your `run.sh` (or whatever) in it. `ddprof` will use environment variables if they are available, overriding them with commandline parameters if given.
`ddprof` is a wrapper, so using it should be as simple as injecting the binary into your container and wrapping your entrypoint.
`ddprof` will use environment variables if they are available, overriding them with commandline parameters if given.

### Safety

- Minimal interference to execution of instrumented processes
- `ddprof`'s Memory usage is sandboxed

### Allocation profiling

- By working in user space, `ddprof` can instrument allocations with low overhead

## Docs

Architectural showpieces and such will always be available in the `docs/` folder.

- [Build](./docs/Build.md)
- [Design](./docs/Design.md)
- [Automatically updated list of commads](./docs/Commands.md)
- [Troubleshooting](./docs/Troubleshooting.md)

## Reaching-out

Expand Down
5 changes: 5 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
0.10.1 to 0.11.0 (13/03/2023)
* JITDump support
* Fix crash when using custom stack (example: Fibers)
* Fix tag validation

0.9.3 to 0.10.1 (14/11/2022)
* Allocation profiling - instrument forks
* Universal binary - remove dependency on libc
Expand Down
61 changes: 5 additions & 56 deletions docs/Build.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# ddprof build

We do not recommend for users to recompile the application. The pre-built binaries should be compatible with your system.
Checkout the release page for our latest builds.

## Environment setup

ddprof is meant to build on Linux.
*Local builds on macos do not work (you don't have access to perf events).*
The dockerized environment will take care of installing all the dependencies.

### Native linux

Expand All @@ -13,14 +15,14 @@ Once all dependencies are installed, you can run the [Build Commands section](#b
### Docker

The [Dockerfile](../app/base-env/Dockerfile) contains all necessary dependencies to build the project.
Here is a script that mounts the `ddprof` folder within the build container.

```
./tools/launch_local_build.sh
```

Once inside the container, you can run the [Build Commands section](#build-commands).


## Build commands

### Building the native profiler
Expand All @@ -32,56 +34,3 @@ MkBuildDir Rel
RelCMake ../
make -j 4 .
```

### Building the benchmark (collatz)

A bench application will be built by default. Following CMake flag controls the build decision: `-DBUILD_BENCHMARKS=ON`.

## Speeding up builds

### Bypassing the use of shared docker volumes on MacOS

Docker can be used if you are not already on a linux environment. You need an ssh configuration as some repositories are private.
The following script create a docker container based on CI dockerfiles. It will:

- Use your ssh configuration
- Automatically pull down all dependencies (same as in CI)
- Sync your files with the docker environment

```bash
./tools/launch_local_build.sh
```

To speed up builds, we recommend usage of docker-sync (shared filesystems are very slow).

1 - create a docker-sync.yml file in the root of the repo.

```yml
version: "2"
syncs:
ddprof-sync:
sync_strategy: "native_osx"
src: "./"
host_disk_mount_mode: "cached"
```

2 - Then create a docker volume and launch docker-sync

```bash
docker volume create ddprof-sync
docker-sync start # launchs a watcher that syncs the files (takes a long time on first run)
```

3 - Use the docker build environment as usual (it will pick up the docker volume from the docker-sync file)

```bash
./tools/launch_local_build.sh
```

4 - You can stop and clean these volumes after usage

```bash
docker-sync stop
docker-sync clean
docker volume rm ddprof-sync
```
89 changes: 4 additions & 85 deletions docs/Design.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,10 @@
# Design

Design discussions.
Overview of the `ddprof` architecture

## Architecture

Roughly speaking, the profiler performs the following operations in sequence.
`ddprof` is a sample based profiler. It uses a mix of user space instrumentation and kernel APIs.
`ddprof` runs in a separate process and processes events through shared ring buffers.

* Instantiated by OS
* Processes options, environment variables, etc
* Initializes global objects and memory
* Creates a pipe (two linked sockets), setting the socket type to be a Unix
Domain Socket. This will be used for transferring file descriptors
* Sets up a pthreads barrier in a shared-memory region, with a shared
disposition (otherwise pthreads fails to use it properly) for coordination
* Calls fork() to create a child
* The child calls fork() and dies
* The original process iteratively calls `perf_event_open()` and sends the
resulting file descriptor to the grandchild using a unix domain socket, then
enters the pthread barrier. Iteration is done for each watcher, on each
CPU, since the `perf_event_open()` context is restricted.
* Grandchild receives the file descriptors, clears the thread barrier
* Original process closes the file descriptor and repeats until all watchers
have been enabled on all CPUs.
* Both processes close their unix domain sockets
* Grandchild creates one mmap() region to receive the `perf_event_open()`
ringbuffer
* Original process calls `execvp()` to become the target process with args
* Grandchild `poll()`s on received file descriptors to listen for events in
ringbuffer

## Overview

![img_fork_strategy](ddprof_archi_20210830.svg)

### Notes

* the grandchild does not read from stdio, so it should not be necessary to
close any of the underlying streams. But we could once we have better
logging facilities.
* We don't do anything to set the signal disposition of the grandchild or
original process.
* We should, but do not, do any resource isolation or limiting yet.

## Architecture painpoints

### Instrumentation Sequence

**Problem**
ddprof needs to enable instrumentation for the process it wraps. If this
fails, we want the target process to get launched anyway. It would also
be great if instrumentation happened after the profiler gets launched
(i.e., don't profile the profiler in the common case). Basically, we'd
like to:

* Minimize the permissions escalations required to instrument an application
* Ensure that hierarchical resource sandboxing interfaces, such as cgroups,
can be easily used in a large number of kernel versions to clamp ddprof
(i.e., don't rely on cool new cgroups v2 kernel v5.bignum features)
* Have an instrumentation sequence that could allow profiles to be collected
in a separate container entirely
* Suppress SIGCHLD in instrumented application if the profiler dies (SIGCHLD
can be used as a job control mechanism; we don't want to interfere, but
sometimes we can't help dying)
* A higher-order executor (for example, `strace ddprof app`) must receive the
PID of the _application_ and not the PID of ddprof through fork(). In other
words, the PID of the process must be the PID of the service, not the wrapper
* Isolate the instrumented application from hierarchical limits (e.g.,
those in `getrlimit()`

Of these goals, the first five are satisfied in the current implementation of
ddprof, with the last one being tricky to implement on containerized
environments without breaking containerization. We'll provide a discussion on
an alternative mechanism (option 3 above) which gets over this hurdle for
`perf_event_open()`-facilitated instrumentation.

## Ideas

### Alternative timing mode

For a variety of reasons, we thought of launching with `perf_event_open()`. We could also measure time using the standard `set_itimer()` approach. There are a few unfortunate consequences to this:

* itimers are mediated through Unix signals, which steal execution from the instrumented process (adds latency)
* signals have more skid than the kernel code, sometimes by a truly significant margin
* signals can interrupt syscalls, which can break client code
* signals don't follow forks
* have to implement new message passing system to bring samples up from children
* signal delivery is non-uniform through a thread pool--this isn't an academic point, sampling hugely favors the earliest-spawned thread
* users can over-write signal handlers

Some of this can be controlled for by implementing an LD_PRELOAD-type trick inside of a wrapper, which could catch `fork()` calls into libc and implement some other niceties, but I'm not sure how much effort this will be to support both glibc/musl across the major versions we have to support.
![wrapper_architecture.png](wrapper_architecture.png)
Loading