Skip to content

Commit

Permalink
chore(profiling): output pprof file with `DD_PROFILING_{EXPORT_LIBDD_…
Browse files Browse the repository at this point in the history
…ENABLED, OUTPUT_PPROF}` (#10107)

Minimal and short-term change to export to file when
`DD_PROFILING_EXPORT_LIBDD_ENABLED` and `DD_PROFILING_OUTPUT_PPROF` are
set, similar to existing
[PprofFileExporter](https://github.com/DataDog/dd-trace-py/blob/322e0013bdf3a9e7db42fa6467815e9a7bfecd75/ddtrace/profiling/exporter/file.py#L10),
which is used when `DD_PROFILING_EXPORT_LIBDD_ENABLED` is turned off and
`DD_PROFILING_OUTPUT_PPROF` is set.

#9739 was a previous attempt to implement this in a more robust way.
Ideally, we'd want this logic to be in libdatadog which can then be
shared among language libraries.

Tested on DataDog/prof-correctness#46. Also
checked that the output profile is lz4 compressed as libdatadog does
that.

## Checklist
- [x] PR author has checked that all the criteria below are met
- The PR description includes an overview of the change
- The PR description articulates the motivation for the change
- The change includes tests OR the PR description describes a testing
strategy
- The PR description notes risks associated with the change, if any
- Newly-added code is easy to change
- The change follows the [library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
- The change includes or references documentation updates if necessary
- Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))

## Reviewer Checklist
- [x] Reviewer has checked that all the criteria below are met 
- Title is accurate
- All changes are related to the pull request's stated goal
- Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- Testing strategy adequately addresses listed risks
- Newly-added code is easy to change
- Release note makes sense to a user of the library
- If necessary, author has acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment
- Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
  • Loading branch information
taegyunkim authored Aug 13, 2024
1 parent 56f907a commit b019b50
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern "C"
void ddup_config_url(std::string_view url);
void ddup_config_max_nframes(int max_nframes);
void ddup_config_timeline(bool enable);
void ddup_config_output_filename(std::string_view filename);
void ddup_config_sample_pool_capacity(uint64_t capacity);

void ddup_config_user_tag(std::string_view key, std::string_view val);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "sample.hpp"
#include "types.hpp"

#include <atomic>
#include <memory>
#include <mutex>

Expand All @@ -24,9 +25,12 @@ class Uploader
static inline std::mutex upload_lock{};
std::string errmsg;
static inline std::unique_ptr<ddog_CancellationToken, DdogCancellationTokenDeleter> cancel;
std::string url;
static inline std::atomic<uint64_t> upload_seq{ 0 };
std::string output_filename;
std::unique_ptr<ddog_prof_Exporter, DdogProfExporterDeleter> ddog_exporter;

bool export_to_file(ddog_prof_EncodedProfile* encoded);

public:
bool upload(ddog_prof_Profile& profile);
static void cancel_inflight();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class UploaderBuilder
static inline std::string profiler_version;
static inline std::string url{ "http://localhost:8126" };
static inline ExporterTagset user_tags{};
static inline std::string output_filename{ "" };

static constexpr std::string_view language{ g_language_name };
static constexpr std::string_view family{ g_language_name };
Expand All @@ -39,6 +40,7 @@ class UploaderBuilder
static void set_profiler_version(std::string_view _profiler_version);
static void set_url(std::string_view _url);
static void set_tag(std::string_view _key, std::string_view _val);
static void set_output_filename(std::string_view _output_filename);

static std::variant<Uploader, std::string> build();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ ddup_config_timeline(bool enabled) // cppcheck-suppress unusedFunction
Datadog::SampleManager::set_timeline(enabled);
}

void
ddup_config_output_filename(std::string_view output_filename) // cppcheck-suppress unusedFunction
{
Datadog::UploaderBuilder::set_output_filename(output_filename);
}

void
ddup_config_sample_pool_capacity(size_t capacity) // cppcheck-suppress unusedFunction
{
Expand Down
40 changes: 38 additions & 2 deletions ddtrace/internal/datadog/profiling/dd_wrapper/src/uploader.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#include "uploader.hpp"
#include "libdatadog_helpers.hpp"

#include <errno.h> // errno
#include <fstream> // ofstream
#include <sstream> // ostringstream
#include <string.h> // strerror
#include <unistd.h> // getpid

using namespace Datadog;

void
Expand All @@ -12,10 +18,34 @@ DdogCancellationTokenDeleter::operator()(ddog_CancellationToken* ptr) const
}
}

Datadog::Uploader::Uploader(std::string_view _url, ddog_prof_Exporter* _ddog_exporter)
: url{ _url }
Datadog::Uploader::Uploader(std::string_view _output_filename, ddog_prof_Exporter* _ddog_exporter)
: output_filename{ _output_filename }
, ddog_exporter{ _ddog_exporter }
{
// Increment the upload sequence number every time we build an uploader.
// Upoloaders are use-once-and-destroy.
upload_seq++;
}

bool
Datadog::Uploader::export_to_file(ddog_prof_EncodedProfile* encoded)
{
// Write the profile to a file using the following format for filename:
// <output_filename>.<process_id>.<sequence_number>
std::ostringstream oss;
oss << output_filename << "." << getpid() << "." << upload_seq;
std::string filename = oss.str();
std::ofstream out(filename, std::ios::binary);
if (!out.is_open()) {
std::cerr << "Error opening output file " << filename << ": " << strerror(errno) << std::endl;
return false;
}
out.write(reinterpret_cast<const char*>(encoded->buffer.ptr), encoded->buffer.len);
if (out.fail()) {
std::cerr << "Error writing to output file " << filename << ": " << strerror(errno) << std::endl;
return false;
}
return true;
}

bool
Expand All @@ -32,6 +62,12 @@ Datadog::Uploader::upload(ddog_prof_Profile& profile)
}
ddog_prof_EncodedProfile* encoded = &result.ok; // NOLINT (cppcoreguidelines-pro-type-union-access)

if (!output_filename.empty()) {
bool ret = export_to_file(encoded);
ddog_prof_EncodedProfile_drop(encoded);
return ret;
}

// Build the request object
const ddog_prof_Exporter_File file = {
.name = to_slice("auto.pprof"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ Datadog::UploaderBuilder::set_tag(std::string_view _key, std::string_view _val)
}
}

void
Datadog::UploaderBuilder::set_output_filename(std::string_view _output_filename)
{
if (!_output_filename.empty()) {
output_filename = _output_filename;
}
}

std::string
join(const std::vector<std::string>& vec, const std::string& delim)
{
Expand Down Expand Up @@ -176,5 +184,5 @@ Datadog::UploaderBuilder::build()
return errmsg;
}

return Datadog::Uploader{ url, ddog_exporter };
return Datadog::Uploader{ output_filename, ddog_exporter };
}
7 changes: 7 additions & 0 deletions ddtrace/internal/datadog/profiling/ddup/_ddup.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ cdef extern from "ddup_interface.hpp":
void ddup_config_url(string_view url)
void ddup_config_max_nframes(int max_nframes)
void ddup_config_timeline(bint enable)
void ddup_config_output_filename(string_view output_filename)
void ddup_config_sample_pool_capacity(uint64_t sample_pool_capacity)

void ddup_config_user_tag(string_view key, string_view val)
Expand Down Expand Up @@ -95,6 +96,9 @@ cdef call_ddup_config_profiler_version(bytes profiler_version):
cdef call_ddup_config_user_tag(bytes key, bytes val):
ddup_config_user_tag(string_view(<const char*>key, len(key)), string_view(<const char*>val, len(val)))

cdef call_ddup_config_output_filename(bytes output_filename):
ddup_config_output_filename(string_view(<const char*>output_filename, len(output_filename)))


# Conversion functions
cdef uint64_t clamp_to_uint64_unsigned(value):
Expand Down Expand Up @@ -125,6 +129,7 @@ def config(
max_nframes: Optional[int] = None,
url: StringType = None,
timeline_enabled: Optional[bool] = None,
output_filename: StringType = None,
sample_pool_capacity: Optional[int] = None) -> None:

# Try to provide a ddtrace-specific default service if one is not given
Expand All @@ -138,6 +143,8 @@ def config(
call_ddup_config_version(ensure_binary_or_empty(version))
if url:
call_ddup_config_url(ensure_binary_or_empty(url))
if output_filename:
call_ddup_config_output_filename(ensure_binary_or_empty(output_filename))

# Inherited
call_ddup_config_runtime(ensure_binary_or_empty(platform.python_implementation()))
Expand Down
1 change: 1 addition & 0 deletions ddtrace/profiling/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def _build_default_exporters(self):
max_nframes=config.max_frames,
url=endpoint,
timeline_enabled=config.timeline_enabled,
output_filename=config.output_pprof,
sample_pool_capacity=config.sample_pool_capacity,
)
ddup.start()
Expand Down

0 comments on commit b019b50

Please sign in to comment.