Skip to content

refactor(crashtracker): use PyO3 for crashtracker module and crashtracker_exe #12690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 99 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
e6c3101
conditional compile
taegyunkim Mar 7, 2025
8e17854
crashtracker receiver
taegyunkim Mar 10, 2025
889e680
builds
taegyunkim Mar 10, 2025
e2b38de
python script for crahstracker_exe
taegyunkim Mar 10, 2025
1b44820
fix rust ci
taegyunkim Mar 10, 2025
370217b
fix lint:fmt
taegyunkim Mar 10, 2025
0dff240
update module path for crashtracker_exe
taegyunkim Mar 10, 2025
a24b457
atomic init and status func
taegyunkim Mar 10, 2025
8c99a8c
add defs in .pyi
taegyunkim Mar 10, 2025
59a32bd
Use rust crashtracker
taegyunkim Mar 11, 2025
effc567
fix mypy errors
taegyunkim Mar 11, 2025
de926e7
fix tests
taegyunkim Mar 11, 2025
cdc2732
make crashtracker a feature
taegyunkim Mar 11, 2025
609316d
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 11, 2025
89d0917
bypass sccache for better error messages
taegyunkim Mar 11, 2025
63a14c7
try except for importing crashtracker
taegyunkim Mar 11, 2025
a7e5ea1
also handle the case from crashtracker_exe
taegyunkim Mar 11, 2025
1be2faf
reduce CARGO_BUILD_JOBS as jobs are killed with -9 from hatch based jobs
taegyunkim Mar 11, 2025
d4b8a6c
add timeout_ms in telemetry
taegyunkim Mar 11, 2025
4df5f66
revive tests
taegyunkim Mar 11, 2025
1e85d45
optimize for size
taegyunkim Mar 11, 2025
cfad44e
remove type annotation to pass test on windows
taegyunkim Mar 11, 2025
674e02b
fix fmt
taegyunkim Mar 11, 2025
68e73e0
set CARGO_BUILD_JOBS=4 for some hatch based test suites
taegyunkim Mar 11, 2025
93e29bf
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 11, 2025
3c5f779
set CARGO_BUILD_JOBS=4 for some hatch based test suites
taegyunkim Mar 11, 2025
1919acb
just set it to 4 from gitlab.yml
taegyunkim Mar 11, 2025
c286ed7
try this?
taegyunkim Mar 11, 2025
b352e28
reduce to 4
taegyunkim Mar 11, 2025
4d257ef
try setting rustc_flags
taegyunkim Mar 13, 2025
fd09afc
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 13, 2025
ad20fe2
Squashed commit of the following:
taegyunkim Mar 13, 2025
b613854
try opt-level "z"
taegyunkim Mar 13, 2025
552b9a7
mac specific rustc_flags
taegyunkim Mar 13, 2025
615fcb8
cmake format
taegyunkim Mar 13, 2025
94ac23f
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 13, 2025
10d8870
Revert "Squashed commit of the following:"
taegyunkim Mar 13, 2025
2cab420
set codegen-units=1 for further optimization in binary size
taegyunkim Mar 13, 2025
5dd34ef
Discard changes to hatch.toml
taegyunkim Mar 13, 2025
f996c9a
Discard changes to .gitlab/tests.yml
taegyunkim Mar 13, 2025
4ba5b19
cleanup
taegyunkim Mar 13, 2025
28de979
enable sccache
taegyunkim Mar 13, 2025
d61a480
simple wrap with sccache
taegyunkim Mar 13, 2025
a2861b6
format
taegyunkim Mar 13, 2025
80a2367
update crashtracker_exe finding logic
taegyunkim Mar 13, 2025
9042a8c
typo
taegyunkim Mar 13, 2025
b1f7ac3
remove anyhow
taegyunkim Mar 13, 2025
23b77ef
use interior mutability and Option<Box<T>>
taegyunkim Mar 13, 2025
ff878d7
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 14, 2025
92654db
format
taegyunkim Mar 14, 2025
1a614c5
py_limited_api=cp38 in src/native
taegyunkim Mar 14, 2025
8fb4b35
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Mar 14, 2025
3fe212b
remove Box<_>
taegyunkim Mar 14, 2025
d2b1faf
use try_borrow_mut from on_fork
taegyunkim Mar 14, 2025
05418e9
use try_borrow_mut in init
taegyunkim Mar 14, 2025
f57180a
remove not(unix) cfg
taegyunkim Mar 14, 2025
f81da06
typo
taegyunkim Mar 16, 2025
b854d33
error handling
taegyunkim Mar 16, 2025
3ea4cf2
wrap the module with unix
taegyunkim Mar 16, 2025
e05f0c2
fix compile
taegyunkim Mar 16, 2025
60f435a
use anyhow feature
taegyunkim Mar 16, 2025
c158be7
adjust types
taegyunkim Mar 17, 2025
8383012
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 7, 2025
471062f
format
taegyunkim May 7, 2025
1349486
minor changes
taegyunkim May 7, 2025
ede9ae3
keep opt-level as is
taegyunkim May 7, 2025
2155624
remove implementation detail
taegyunkim May 7, 2025
629aff5
format rust
taegyunkim May 7, 2025
1a733a1
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 8, 2025
fab0087
dont expose crashtracker_exe
taegyunkim May 8, 2025
6133fb5
just copy all
taegyunkim May 8, 2025
20a5539
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 8, 2025
e34a368
enable test
taegyunkim May 8, 2025
d2336f3
format
taegyunkim May 8, 2025
b61d3e5
check for availability
taegyunkim May 8, 2025
3a62c76
add line back
taegyunkim May 8, 2025
87a580d
disable vpa for now
taegyunkim May 8, 2025
7a7c4c2
update flags
taegyunkim May 8, 2025
0f19c8b
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 9, 2025
98efaeb
Discard changes to .gitlab-ci.yml
taegyunkim May 9, 2025
834e417
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 12, 2025
1a8f639
wrap with try
taegyunkim May 12, 2025
b0dcd39
format
taegyunkim May 12, 2025
159973c
back to command
taegyunkim May 12, 2025
f944c90
revert back
taegyunkim May 13, 2025
911d7ef
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 13, 2025
fca2740
strip even more
taegyunkim May 13, 2025
498fe69
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 19, 2025
d336c9e
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 22, 2025
3c8e32a
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 27, 2025
2a740cf
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim May 30, 2025
6d1601a
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Jun 3, 2025
d6010ac
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Jun 4, 2025
f14f2b1
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Jun 6, 2025
43f0b73
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Jun 6, 2025
c53401a
check for availability
taegyunkim Jun 6, 2025
b1e4436
Merge branch 'main' into taegyunkim/prof-11491-crasht-pyo3
taegyunkim Jun 8, 2025
4c36b80
try installing command
taegyunkim Jun 8, 2025
4e58e3a
try opt-level s
taegyunkim Jun 8, 2025
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ ddtrace/profiling/collector/stack.c
ddtrace/profiling/exporter/pprof.c
ddtrace/profiling/_build.c
ddtrace/internal/datadog/profiling/ddup/_ddup.cpp
ddtrace/internal/datadog/profiling/crashtracker/crashtracker_exe*
ddtrace/internal/_encoding.c
ddtrace/internal/_rand.c
ddtrace/internal/_tagset.c
Expand Down
17 changes: 17 additions & 0 deletions ddtrace/commands/crashtracker_exe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

try:
from ddtrace.internal.native._native import crashtracker_receiver
except ImportError:

def crashtracker_receiver() -> None:
print("Crashtracker receiver not available.")


def main():
crashtracker_receiver()


if __name__ == "__main__":
main()
180 changes: 135 additions & 45 deletions ddtrace/internal/core/crashtracking.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,158 @@
from typing import Callable
import platform
import shutil
from typing import Dict
from typing import Optional

from ddtrace import config
from ddtrace import version
from ddtrace.internal.datadog.profiling import crashtracker
from ddtrace.internal import forksafe
from ddtrace.internal.compat import ensure_text
from ddtrace.internal.runtime import get_runtime_id
from ddtrace.internal.runtime import on_runtime_id_change
from ddtrace.settings._agent import config as agent_config
from ddtrace.settings.crashtracker import config as crashtracker_config
from ddtrace.settings.profiling import config as profiling_config
from ddtrace.settings.profiling import config_str


is_available: bool = crashtracker.is_available
failure_msg: str = crashtracker.failure_msg
is_started: Callable[[], bool] = crashtracker.is_started
is_available = True
try:
from ddtrace.internal.native._native import CrashtrackerConfiguration
from ddtrace.internal.native._native import CrashtrackerMetadata
from ddtrace.internal.native._native import CrashtrackerReceiverConfig
from ddtrace.internal.native._native import CrashtrackerStatus
from ddtrace.internal.native._native import StacktraceCollection
from ddtrace.internal.native._native import crashtracker_init
from ddtrace.internal.native._native import crashtracker_on_fork
from ddtrace.internal.native._native import crashtracker_status
except ImportError:
is_available = False


@on_runtime_id_change
def _update_runtime_id(runtime_id: str) -> None:
crashtracker.set_runtime_id(runtime_id)
def _get_tags(additional_tags: Optional[Dict[str, str]]) -> Dict[str, str]:
tags = {
"language": "python",
"runtime": "CPython",
"is_crash": "true",
"severity": "crash",
}

# Here we check for the presence of each tags, as the underlying Metadata
# object expects std::collections::HashMap<String, String> which doesn't
# support None values.
if config.env:
tags["env"] = config.env
if config.service:
tags["service"] = config.service
if config.version:
tags["version"] = config.version
runtime_id = get_runtime_id()
if runtime_id:
tags["runtime_id"] = runtime_id
runtime_version = platform.python_version()
if runtime_version:
tags["runtime_version"] = runtime_version
library_version = version.get_version()
if library_version:
tags["library_version"] = library_version

def add_tag(key: str, value: str) -> None:
if is_available:
crashtracker.set_tag(key, value)
for k, v in crashtracker_config.tags.items():
if k and v:
tags[k] = v

if additional_tags:
for k, v in additional_tags.items():
if k and v:
tags[k] = v

def start() -> bool:
if not is_available:
return False
if profiling_config.enabled:
tags["profiler_config"] = config_str(profiling_config)

import platform

crashtracker.set_url(crashtracker_config.debug_url or agent_config.trace_agent_url)
crashtracker.set_service(config.service)
crashtracker.set_version(config.version)
crashtracker.set_env(config.env)
crashtracker.set_runtime_id(get_runtime_id())
crashtracker.set_runtime_version(platform.python_version())
crashtracker.set_library_version(version.get_version())
crashtracker.set_create_alt_stack(bool(crashtracker_config.create_alt_stack))
crashtracker.set_use_alt_stack(bool(crashtracker_config.use_alt_stack))
if crashtracker_config.stacktrace_resolver == "fast":
crashtracker.set_resolve_frames_fast()
elif crashtracker_config.stacktrace_resolver == "full":
crashtracker.set_resolve_frames_full()
# Another thing to note is that these tag keys and values can be bytes
# objects, so we need to ensure that they are converted to strings as
# PyO3 can't convert bytes objects to Rust String type.
tags = {ensure_text(k): ensure_text(v) for k, v in tags.items()}

return tags


def _get_args(additional_tags: Optional[Dict[str, str]]):
dd_crashtracker_receiver = shutil.which("dd_crashtracker_receiver")
if dd_crashtracker_receiver is None:
print("Failed to find dd_crashtracker_receiver")
return (None, None, None)

if crashtracker_config.stacktrace_resolver is None:
stacktrace_resolver = StacktraceCollection.Disabled
elif crashtracker_config.stacktrace_resolver == "fast":
stacktrace_resolver = StacktraceCollection.WithoutSymbols
elif crashtracker_config.stacktrace_resolver == "safe":
crashtracker.set_resolve_frames_safe()
stacktrace_resolver = StacktraceCollection.EnabledWithSymbolsInReceiver
elif crashtracker_config.stacktrace_resolver == "full":
stacktrace_resolver = StacktraceCollection.EnabledWithInprocessSymbols
else:
crashtracker.set_resolve_frames_disable()
# This should never happen, as the value is validated in the crashtracker_config
# module.
print(f"Invalid stacktrace_resolver value: {crashtracker_config.stacktrace_resolver}")
stacktrace_resolver = StacktraceCollection.EnabledWithInprocessSymbols

if crashtracker_config.stdout_filename:
crashtracker.set_stdout_filename(crashtracker_config.stdout_filename)
if crashtracker_config.stderr_filename:
crashtracker.set_stderr_filename(crashtracker_config.stderr_filename)
# Create crashtracker configuration
config = CrashtrackerConfiguration(
[], # additional_files
crashtracker_config.create_alt_stack,
crashtracker_config.use_alt_stack,
5000, # timeout_ms
stacktrace_resolver,
crashtracker_config.debug_url or agent_config.trace_agent_url,
None, # unix_socket_path
)

# Add user tags
for key, value in crashtracker_config.tags.items():
add_tag(key, value)
# Create crashtracker receiver configuration
receiver_config = CrashtrackerReceiverConfig(
[], # args
{}, # env
dd_crashtracker_receiver, # path_to_receiver_binary
crashtracker_config.stderr_filename,
crashtracker_config.stdout_filename,
)

tags = _get_tags(additional_tags)

metadata = CrashtrackerMetadata("dd-trace-py", version.get_version(), "python", tags)

return config, receiver_config, metadata

if profiling_config.enabled:
add_tag("profiler_config", config_str(profiling_config))

# Only start if it is enabled
if crashtracker_config.enabled:
return crashtracker.start()
return False
def is_started() -> bool:
if not is_available:
return False
return crashtracker_status() == CrashtrackerStatus.Initialized


def start(additional_tags: Optional[Dict[str, str]] = None) -> bool:
if not is_available:
return False
if not crashtracker_config.enabled:
return False

try:
config, receiver_config, metadata = _get_args(additional_tags)
if config is None or receiver_config is None or metadata is None:
print("Failed to start crashtracker: failed to construct crashtracker configuration")
return False

crashtracker_init(config, receiver_config, metadata)

def crashtracker_fork_handler():
# We recreate the args here mainly to pass updated runtime_id after
# fork
config, receiver_config, metadata = _get_args(additional_tags)
if config is None or receiver_config is None or metadata is None:
print("Failed to restart crashtracker after fork: failed to construct crashtracker configuration")
return
crashtracker_on_fork(config, receiver_config, metadata)

forksafe.register(crashtracker_fork_handler)
except Exception as e:
print(f"Failed to start crashtracker: {e}")
return False
return True
7 changes: 0 additions & 7 deletions ddtrace/internal/datadog/profiling/build_standalone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ CLANGTIDY_CMD=${highest_clangxx/clang++/clang-tidy}
# Targets to target dirs
declare -A target_dirs
target_dirs["ddup"]="ddup"
target_dirs["crashtracker"]="crashtracker"
target_dirs["stack_v2"]="stack_v2"
target_dirs["dd_wrapper"]="dd_wrapper"

Expand Down Expand Up @@ -217,8 +216,6 @@ print_help() {
echo " stack_v2_test (also builds dd_wrapper_test)"
echo " ddup (also builds dd_wrapper)"
echo " ddup_test (also builds dd_wrapper_test)"
echo " crashtracker (also builds dd_wrapper)"
echo " crashtracker_test (also builds dd_wrapper_test)"
}

print_cmake_args() {
Expand Down Expand Up @@ -342,7 +339,6 @@ add_target() {
all|--)
targets+=("stack_v2")
targets+=("ddup")
targets+=("crashtracker")
;;
dd_wrapper)
# `dd_wrapper` is a dependency of other targets, but the overall structure is weird when it's given explicitly
Expand All @@ -355,9 +351,6 @@ add_target() {
ddup)
targets+=("ddup")
;;
crashtracker)
targets+=("crashtracker")
;;
*)
echo "Unknown target: $1"
exit 1
Expand Down
116 changes: 0 additions & 116 deletions ddtrace/internal/datadog/profiling/crashtracker/CMakeLists.txt

This file was deleted.

Loading
Loading