Skip to content

Add profiling sources#1606

Merged
morrisonlevi merged 27 commits into
masterfrom
levi/profiling-rs
Jul 18, 2022
Merged

Add profiling sources#1606
morrisonlevi merged 27 commits into
masterfrom
levi/profiling-rs

Conversation

@morrisonlevi

@morrisonlevi morrisonlevi commented Jun 8, 2022

Copy link
Copy Markdown
Collaborator

Description

This PR does not:

  • Alter the tracer in any way, including its build requirements.
  • Add new tests to CI, and will only fix/adjust lint jobs if necessary so that existing tracer jobs will not be affected by the profiling subdirectory.
  • Build out of the box with cargo build. You have to check out Datadog/libdatadog's main branch to profiling/libdatadog. I didn't want to add a git submodule without discussion and the crates for libdatadog are not yet published, so this is a compromise. Edit: libdatadog v0.7.0 has been released on github since then, so we can probably do something like libdatadog = { git = "https://github.com/DataDog/libdatadog", tag = "v0.7.0" }. I'll look into this soon.

This PR does:

  • Add sources for a PHP profiler written almost entirely in Rust. It is essentially a port of the existing C profiler found at Datadog/dd-prof-php.
  • Have some tests, but not many. As mentioned above, they are not run in CI.

Fixing these things shortcomings will likely come in further PRs, as this one is already at +2,481 additions and I expect it will take some time to be able to get it reviewed and merged, especially as it needs reviewed by both Rust and PHP experts who are busy working on their own things.

There are a variety of todo tasks that I do not expect to be handled in this PR, like unifying components/sapi with profiling/src/sapi.rs. However, there is one about zend_bool that I still need to audit. Given that I expect this PR to take a while to get fully reviewed and that it hasn't caused observable issues yet, I decided to open the PR and do this audit concurrently (and hopefully take no action except remove the todo).

Readiness checklist

  • Changelog has been added to the release document.
  • Tests added for this feature/bug.

Reviewer checklist

  • Appropriate labels assigned.
  • Milestone is set.

Changelog from v0.7

  • Add process_id and runtime_version tags.
  • Add support for changing env vars per request, such as per-directory env var settings in Apache.
  • Improve logging. Added a new log level trace, which is even more verbose than debug.
  • Stop sending a profile on every phpinfo() (or the equivalent command line option --ri datadog-profiling).

@bruceg bruceg left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have enough knowledge of the broader context to comment on the overall approach, so this review just reflects on the smaller details.

Comment thread profiling/Cargo.toml Outdated
Comment thread profiling/src/logging.rs Outdated
Comment thread profiling/build.rs
Comment thread profiling/build.rs Outdated
Comment thread profiling/build.rs
Comment thread profiling/src/lib.rs Outdated
Comment thread profiling/src/profiling.rs Outdated
Comment thread profiling/src/profiling.rs
Comment thread profiling/src/profiling.rs Outdated
Comment thread profiling/src/profiling.rs Outdated
Comment thread profiling/build.rs
Comment thread profiling/src/bindings.rs Outdated
Comment thread profiling/src/bindings.rs Outdated
Comment thread profiling/src/lib.rs
Comment thread profiling/src/lib.rs Outdated
Comment thread profiling/src/lib.rs Outdated
Comment thread profiling/src/lib.rs Outdated
Comment thread profiling/src/logging.rs Outdated
Comment thread profiling/src/profiling.rs
}

pub fn stop(self) {
// todo: what should be done when a thread panics?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally left TODO?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but also I really don't know what ought to be done. In general, I don't think anything can be done except to propagate the panic.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this is a Hard™ problem, and we build Mork with panic=abort for that reason.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

panic=abort seems nice, but to really get it right you need the stdlib to be rebuilt with it. I couldn't get that to work on Alpine, at the very least, though I haven't tried the latest Alpine release.

@mokomull mokomull left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really get a chance to look too hard at profiling.rs, but a few comments nonetheless. Mostly style and "a crate already exists to do that" flavoured suggestions.

Comment thread profiling/src/bindings.rs
Comment on lines +19 to +20
// todo: this a lie on some PHP versions; is it a problem even though zend_bool
// was always supposed to be 0 or 1 anyway?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread profiling/src/bindings.rs Outdated
Comment thread profiling/src/bindings.rs Outdated
Comment thread profiling/build.rs
.output()
.expect("Unable to run `php-config`. Is it in your PATH?");

let prefix = String::from_utf8(output.stdout).expect("only utf8 chars work");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you really wanted to support that you could use Path instead of Strings, but I'm pretty sure any user that's done that has already broken every ./configure script on their system :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember the details, but I actually tried to use Path and couldn't get all the plumbing I needed to work out. For instance, how do I convert the bytes from the output of php-config into a Path?

Comment thread profiling/build.rs
Comment thread profiling/src/php_ffi.h Outdated
Comment thread profiling/src/php_ffi.h
Comment on lines +75 to +83
// The strings will be interned but potentially only for the request, so be
// careful to not use them outside of a request (such as from other
// threads).
datadog_php_str env;
datadog_php_str service;
datadog_php_str version;
datadog_php_str trace_agent_host;
datadog_php_str trace_agent_port;
datadog_php_str trace_agent_url;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these change from request-to-request (particularly wondering about sapi_getenv() ... the libc getenv() shouldn't really change during execution).

If not, then perhaps we should hold a void* to a Box::into_raw() of a Rust structure so we don't have to bounce these strings through FFI so much just for them to end back up in a Vec<Tag>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can change from request to request, yes. For instance, in Apache you can configure env vars to have different values for different directories, think DD_SERVICE=myservice but in /admin it's DD_SERVICE=myservice-admin or something.

The code is partly set up the way it is because PHP's request globals have support for being tied to .ini configuration. I had previously tried to add .ini support, but hit an issue with certain configs like datadog.service being shared by the tracer and profiler, and the engine does not like this (it's not a C vs Rust issue, but a PHP extension issue). So, I pulled out all of the directly dead code but some of the influence is still there. I think I need to support .ini configuration so I'm not sure it's worth making the Rust side of things cleaner at the moment since I'm highly likely to undo that work for .ini. Dunno, I'll think about it.


#[derive(Debug, Clone)]
pub enum LabelValue {
Str(Cow<'static, str>),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to only ever be used for format!() so perhaps just take a String for simplicity?

}

pub fn stop(self) {
// todo: what should be done when a thread panics?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW this is a Hard™ problem, and we build Mork with panic=abort for that reason.

Comment thread profiling/src/sapi.rs Outdated
Comment on lines +23 to +38
static mut SAPIS: MaybeUninit<HashMap<&str, Sapi>> = MaybeUninit::uninit();
static ONCE: Once = Once::new();
ONCE.call_once(|| {
let sapis = HashMap::from([
("apache2handler", Sapi::Apache2Handler),
("cgi-fcgi", Sapi::CgiFcgi),
("cli", Sapi::Cli),
("cli-server", Sapi::CliServer),
("embed", Sapi::Embed),
("fpm-fcgi", Sapi::FpmFcgi),
("litespeed", Sapi::Litespeed),
("phpdbg", Sapi::PhpDbg),
("tea", Sapi::Tea),
]);
unsafe { SAPIS.write(sapis) };
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a job for the maplit crate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll take a look at maplit.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think before Rust 1.56 that maplit would be nice, but since then we have from and from_iter for HashMap. Using maplit would be changing out the (_, _), structure for _ => _, via macro, and I'm not sure how that's better? If it could do it in const contexts that would be awesome, but it can't. Am I missing something?

@morrisonlevi

Copy link
Copy Markdown
Collaborator Author

Thanks @mokomull, @blt , and @bruceg. I've addressed most of the feedback, though I'm still thinking about some of them like changing the globals struct.

@morrisonlevi

Copy link
Copy Markdown
Collaborator Author

Added a CODEOWNERS file as requested, but it's currently invalid as DataDog/profiling-php doesn't yet have write access.

bwoebi
bwoebi previously approved these changes Jul 13, 2022

@bwoebi bwoebi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that before a GA, we will still see proper ini handling.

In general the code reads pretty clean. Looks good to me for initial inclusion.

This reverts commit 3cc49a9.
This was only a temporary measure to not waste CI cycles.
@morrisonlevi
morrisonlevi merged commit 1ee5916 into master Jul 18, 2022
@morrisonlevi
morrisonlevi deleted the levi/profiling-rs branch July 18, 2022 17:00
@morrisonlevi morrisonlevi added this to the 0.77.0 milestone Jul 18, 2022
@morrisonlevi morrisonlevi added the profiling Relates to the Continuous Profiler label Jul 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

profiling Relates to the Continuous Profiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants