Skip to content

Commit

Permalink
fix(profiling): remove use of static C++ [backport 2.7] (#8692)
Browse files Browse the repository at this point in the history
Backport 0c5b7ea from #8682 to 2.7.

libdd_wrapper library needs to strike a balance between using modern
(safe) C++ and adhering to the manylinux2014 spec. This is made slightly
complicated by the fact that manylinux2014 allows for more recent
compilers, so we can emit versioned symbols that rely on e.g.,
GLIBCXX_3.4.21 when GLIBCXX_3.4.19 is the limit.

In my laziness, instead of working around these checks, I just
statically-linked libstdc++. As you might guess, this causes symbol
visibility issues when end-users have a different c++. In particular, it
looks like the Locale portion of std::string destruction hardcodes a
`free()` which is invalid in certain other contexts, and it is precisely
that operation that gets ODR'd to oblivion. Whoopsies!

This patch removes the cases that I saw the emission of GLIBCXX_3.4.21.
Moving forward, I'll have to cook up the standard dev apparatus for
this.

Just to be super clear: since this patch removes statically-linking
libstdc++, any defects that arise in sofile generation will be caught by
the auditwheel phase of the build pipeline.

This fixes #8651 

## Checklist

- [x] Change(s) are motivated and described in the PR description
- [x] Testing strategy is described if automated tests are not included
in the PR
- [x] Risks are described (performance impact, potential for breakage,
maintainability)
- [x] Change is maintainable (easy to change, telemetry, documentation)
- [x] [Library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
are followed or label `changelog/no-changelog` is set
- [x] Documentation is included (in-code, generated user docs, [public
corp docs](https://github.com/DataDog/documentation/))
- [x] Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))
- [x] If this PR changes the public interface, I've notified
`@DataDog/apm-tees`.
- [x] If change touches code that signs or publishes builds or packages,
or handles credentials of any kind, I've requested a review from
`@DataDog/security-design-and-guidance`.

## Reviewer Checklist

- [x] Title is accurate
- [x] All changes are related to the pull request's stated goal
- [x] Description motivates each change
- [x] Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes
- [x] Testing strategy adequately addresses listed risks
- [x] Change is maintainable (easy to change, telemetry, documentation)
- [x] Release note makes sense to a user of the library
- [x] Author has acknowledged and discussed the performance implications
of this PR as reported in the benchmarks PR comment
- [x] 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)

Co-authored-by: David Sanchez <838104+sanchda@users.noreply.github.com>
  • Loading branch information
github-actions[bot] and sanchda committed Mar 15, 2024
1 parent 8b9f5a0 commit 52c0632
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 36 deletions.
7 changes: 0 additions & 7 deletions ddtrace/internal/datadog/profiling/dd_wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,8 @@ add_library(dd_wrapper SHARED
# Add common configuration flags
add_ddup_config(dd_wrapper)

# At present, C++17 is chosen as the minimum standard. This conflicts with the
# manylinux2014 standard upon which we must rely. We'll have to statically link
# libstdc++ to avoid this conflict, but need to remain mindful of symbol visibility
# and overall binary size.
target_compile_features(dd_wrapper PUBLIC cxx_std_17)

# Statically link libstdc++ to avoid manylinux2014 conflicts
target_link_options(dd_wrapper PRIVATE -static-libstdc++)

target_include_directories(dd_wrapper PRIVATE
include
${Datadog_INCLUDE_DIRS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>
#include <string_view>
#include <unordered_map>
#include <variant>

namespace Datadog {

Expand Down Expand Up @@ -40,7 +41,7 @@ class UploaderBuilder
static void set_url(std::string_view _url);
static void set_tag(std::string_view _key, std::string_view _val);

static Uploader build();
static std::variant<Uploader, std::string> build();
};

} // namespace Datadog
39 changes: 17 additions & 22 deletions ddtrace/internal/datadog/profiling/dd_wrapper/src/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,28 +269,23 @@ ddup_upload()

bool success = false;
{
// The borrow operation takes a reference, then locks the areas where
// the ddog_prof_Profile might be modified. We need to return it
ddog_prof_Profile upload_profile = Datadog::Sample::profile_borrow();

// We create a new uploader just for this operation
try {
auto uploader = Datadog::UploaderBuilder::build();

// NB, upload() cancels any inflight uploads in order to ensure only
// one is active at a time. This simplifies the fork/thread logic.
// This is usually fine, but when the user specifies a profiling
// upload interval less than the upload timeout, we have a potential
// backlog situation which isn't handled. This is against recommended
// practice, but it wouldn't be crazy to add a small backlog queue.
success = uploader.upload(upload_profile);
} catch (const std::exception& e) {
std::cerr << "Failed to create uploader: " << e.what() << std::endl;
}

// We're done with the profile
Datadog::Sample::profile_release();
Datadog::Sample::profile_clear_state();
// There are a few things going on here.
// * profile_borrow takes a reference in a way that locks the areas where the profile might
// be modified. It gets released and cleared after uploading.
// * Uploading cancels inflight uploads. There are better ways to do this, but this is what
// we have for now.
auto uploader = Datadog::UploaderBuilder::build();
struct
{
void operator()(Datadog::Uploader& uploader)
{
uploader.upload(Datadog::Sample::profile_borrow());
Datadog::Sample::profile_release();
Datadog::Sample::profile_clear_state();
}
void operator()(std::string& err) { std::cerr << "Failed to create uploader: " << err << std::endl; }
} visitor;
std::visit(visitor, uploader);
}
return success;
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ join(const std::vector<std::string>& vec, const std::string& delim)
});
}

Uploader
std::variant<Uploader, std::string>
UploaderBuilder::build()
{
// Setup the ddog_Exporter
Expand Down Expand Up @@ -130,10 +130,9 @@ UploaderBuilder::build()
}
}

// If any mistakes were made, report on them now and throw
if (!reasons.empty()) {
ddog_Vec_Tag_drop(tags);
throw std::runtime_error("Error initializing exporter, missing or bad configuration: " + join(reasons, ", "));
return "Error initializing exporter, missing or bad configuration: " + join(reasons, ", ");
}

// If we're here, the tags are good, so we can initialize the exporter
Expand All @@ -145,10 +144,10 @@ UploaderBuilder::build()
if (res.tag == DDOG_PROF_EXPORTER_NEW_RESULT_OK) {
ddog_exporter = res.ok;
} else {
const std::string errmsg = err_to_msg(&res.err, "Error initializing exporter");
std::string errmsg = err_to_msg(&res.err, "Error initializing exporter");
ddog_Error_drop(&res.err); // errmsg contains a copy of res.err, OK to drop
throw std::runtime_error(errmsg);
return errmsg;
}

return { url, ddog_exporter };
return Uploader{ url, ddog_exporter };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
profiling: This fixes a ``free(): invalid pointer`` error which would arise as a result of incorrectly
linking the C++ runtime.

0 comments on commit 52c0632

Please sign in to comment.