Skip to main content

analyze-crash

Native crash log analysis for dd-trace-py

インストールへ移動

ソース情報

リポジトリ
DataDog/dd-trace-py
ソースの最終更新活動
2026年6月5日 14:32
検出された SKILL.md の言語
英語
スター
650
フォーク
552

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
2 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
analyze-crash
description
Native crash log analysis for dd-trace-py
argument-hint
<crash-uuid-or-paste-log>
disable-model-invocation
false
context
fork
agent
general-purpose
allowed-tools
["Bash","Read","Grep","Glob","TodoWrite","WebSearch"]
# Native Crash Stack Trace Analysis for dd-trace-py You are analyzing a native crash captured by dd-trace-py's crashtracker. Perform a comprehensive investigation to help engineers understand and triage the crash. The crashtracker captures **all process-level native crashes**, so the crashing code may or may not be in dd-trace-py itself — investigate the code and patterns thoroughly before making the attribution call. ## Input Processing The user will provide crash data in one of two ways: ### Option A: Crash UUID (preferred) The user provides a crash UUID from Datadog crash telemetry. Use the fetch script bundled with this skill to retrieve and process the full crash log: ```bash cd .claude/skills/analyze-crash && dd-auth .venv/bin/python fetch_crash.py <UUID> [--tracer-version X.Y.Z] [--days 14] ``` This fetches the crash log from Datadog, processes the stack frames (demangling C++ symbols, resolving addresses against `/proc/self/maps`), and outputs structured JSON with: - **Metadata**: tracer version, runtime version, service, org, platform, architecture, signal - **Processed stack**: each frame with demangled symbol, binary file, offset, source file/line - **Binary mappings**: summary of loaded shared objects - **Datadog log link**: direct URL to the crash event If the user provides only a UUID with no other context, ask: > What is the crash UUID? Any additional filters (tracer version, org ID, language)? **Setup** (one-time): run `cd .claude/skills/analyze-crash && uv sync` to create the venv and install dependencies. **Authentication**: use `dd-auth` as a command wrapper — it injects Datadog API credentials into the environment for the wrapped process. ### Option B: Pasted crash log or stack trace The user pastes a raw crash log or stack trace directly. The format is typically: ``` 0x7f6e81c51fb7 PyUnicode_AsUTF8AndSize 0x7f6e769d7850 pycbc::Connection::open_bucket(_object*) 0x7f6e81ccd60c 0x7f6e81c53239 _PyObject_Call ... ``` Frames may have: hex address only, hex address + symbol, or symbol + `.so` name + offset. Parse all available information. If the paste includes full crash JSON (with `error.stack` and `files./proc/self/maps`), extract the metadata and processed stack from it. ### Determine the source revision The crash may come from a released wheel or a branch other than `main`. GitHub links must point at the exact revision that built the crashing binary so that line numbers match. **Ask the user**: Before starting analysis, ask: > Do you know the commit SHA or release tag for the dd-trace-py version that crashed? > (e.g., `v2.18.0`, `abc1234`). If not, I'll use `main`. **Resolution order** (use the first that succeeds): 1. **User-provided value** — commit SHA or tag supplied in the prompt or in response to the question above. Use it verbatim as `GIT_REF`. 2. **Crash metadata** — if the crash was fetched via UUID and includes a `tracer_version` field (e.g., `2.18.0`), try the corresponding git tag (`v2.18.0`). Verify it exists: `git rev-parse --verify "v2.18.0" 2>/dev/null`. 3. **Version string in the stack trace** — if the wheel filename or ddtrace version appears in the trace (e.g., `ddtrace-2.18.0`), try the corresponding git tag. 4. **Current checkout** — if the local repo is not on `main`, use `git rev-parse HEAD` to get the current SHA. 5. **Fallback** — use `main`. Store the resolved value as `GIT_REF` and use it in all GitHub links for the rest of the analysis. State which ref you are using and why at the top of the report (in Additional Context). ## Analysis Workflow ### GitHub Link Generation When referencing files in dd-trace-py, always provide clickable GitHub links using the resolved `GIT_REF` (see "Determine the source revision" above). **Format**: ``` [filename:line](https://github.com/DataDog/dd-trace-py/blob/{GIT_REF}/path/to/file#Lline) ``` **Examples** (assuming `GIT_REF=v2.18.0`): - Single line: `[_threads.cpp:226](https://github.com/DataDog/dd-trace-py/blob/v2.18.0/ddtrace/internal/_threads.cpp#L226)` - Range: `[_threads.cpp:226-252](https://github.com/DataDog/dd-trace-py/blob/v2.18.0/ddtrace/internal/_threads.cpp#L226-L252)` **Path construction**: base URL is `https://github.com/DataDog/dd-trace-py/blob/{GIT_REF}/`, then append the repo-relative path (strip any build-specific prefixes like `/home/runner/work/`, `/usr/local/lib/`, etc.). --- ### Phase 1: Parse & Classify Stack Frames Extract all stack frames and classify each one using the categories below. #### Frame Classification **CPython Runtime** — the Python interpreter itself: - `.so` name: `python3.X`, `libpython3.X.so`, `libpython3.X.so.1.0` - Symbols: `_PyEval_EvalFrameDefault`, `PyGILState_Ensure`, `take_gil`, `new_threadstate`, `PyEval_RestoreThread`, `PyEval_SaveThread`, `Py_RunMain`, `PyObject_Call*`, `_Py_*`, `PyThread_exit_thread`, `_PyRuntime`, `_PyThreadState_*` **dd-trace-py Native (_threads)** — periodic thread / GIL management: - `.so` name: `_threads.cpython-*.so` - Symbols: `PeriodicThread_start`, `PeriodicThread_stop`, `PeriodicThread_join`, `PeriodicThread__periodic`, `PeriodicThread__before_fork`, `PeriodicThread__after_fork`, `PeriodicThread__on_shutdown`, `PeriodicThread_awake`, `PeriodicThread_dealloc`, `GILGuard`, `AllowThreads`, `PyRef`, `execute_native_thread_routine` **dd-trace-py Native (_native / Rust)** — crashtracker, data pipeline, tracer utilities: - `.so` name: `_native.cpython-*.so` (under `ddtrace/internal/native/`) - Symbols: `crashtracker_*`, `TraceExporterPy::send`, `pyo3::*`, `rust_begin_unwind`, `rust_panic`, `libdd_crashtracker::*`, `datadog_profiling_ffi::*`, `ffe::*`, `ddsketch::*` **dd-trace-py Native (_memalloc)** — memory profiler: - `.so` name: `_memalloc.cpython-*.so` - Symbols: `memalloc_*`, `Datadog::Sample*` (when called from memalloc context) **dd-trace-py Native (_ddup / _stack / libdd_wrapper)** — profiling upload/sampling: - `.so` names: `_ddup.cpython-*.so`, `_stack.cpython-*.so`, `libdd_wrapper.cpython-*.so` - Symbols: `ddup_*`, `Datadog::Profile*`, `Datadog::Sample*`, `Datadog::Uploader*`, `Datadog::Sampler*`, `Datadog::EchionSampler*`, `Datadog::StackRenderer*`, `echion_*` **dd-trace-py IAST/AppSec Native** — security instrumentation: - `.so` name: `_native.cpython-*.so` (under `ddtrace/appsec/`), `_stacktrace.cpython-*.so` - Symbols: `taint_*`, `TaintEngine*`, `tainted_ops::*` **dd-trace-py Cython** — Python-level profiling helpers: - `.so` names: `_threading.cpython-*.so`, `_exception.cpython-*.so`, `_sampler.cpython-*.so`, `_lock.cpython-*.so`, `_task.cpython-*.so`, `_encoding.cpython-*.so` - Symbols: Cython-generated (often `__pyx_*` prefixes or module function names) **Third-Party Native (NOT dd-trace-py)** — known third-party libs: - `libev-*.so` / `libev.so.4` — gevent's embedded libev (NOT dd-trace-py) - `_native__lib.cpython-*.so` — Pyroscope's native extension (path contains `pyroscope/`) **Note:** Pyroscope crashes appear in dd-trace-py crash telemetry because the crashtracker captures all process-level crashes. `_native__lib` (double underscore, in `pyroscope/`) is ALWAYS Pyroscope, never dd-trace-py. dd-trace-py's module is `_native` (single underscore, in `ddtrace/internal/native/`). - `anyhow::error::object_drop`, `py_spy::*`, `goblin::*` — Pyroscope/py-spy Rust code - Customer application code, third-party Python extensions **OS/libc** — system libraries: - `libc.so.6`, `libpthread.so`, `libm.so` - Symbols: `gsignal`, `abort`, `pthread_exit`, `pthread_mutex_lock`, `__clone3`, `start_thread`, `cfree`, `free`, `malloc` Create a classification table: | # | Type | Symbol / Location | Description | |---|------|------------------|-------------| | 0 | {type} | {symbol} | {brief description} | | ... | | | | --- ### Phase 2: Locate dd-trace-py Source Code For each dd-trace-py frame that can be mapped to source code: 1. **Map `.so` name to source file**: - `_threads.cpython-*.so` → `ddtrace/internal/_threads.cpp` - `_native.cpython-*.so` (in `ddtrace/internal/native/`) → `src/native/` (Rust) - `_memalloc.cpython-*.so` → `ddtrace/profiling/collector/_memalloc.cpp` - `_ddup.cpython-*.so` → `ddtrace/internal/datadog/profiling/ddup/` - `_stack.cpython-*.so` → `ddtrace/internal/datadog/profiling/stack/` - `libdd_wrapper.cpython-*.so` → `ddtrace/internal/datadog/profiling/dd_wrapper/` 2. **Find the function** in the source file using Glob/Bash grep. 3. **Read code with context**: find the function body, read 10-15 lines before the relevant line, mark the crash point, read 5-10 lines after. Show enough to understand what the code is doing. Format: ``` ### Frame N: {symbol} ([{file}:{line}](GitHub link)) ​```cpp // {file}:{start}-{end} {code with relevant line marked: // >>> CRASH POINT <<<} ​``` **Analysis**: {what this code does and why it may have failed} ``` --- ### Phase 3: Apply General Crash Heuristics dd-trace-py maintains two complementary native code safety references: - `docs/native-code-review.md` — full incident-derived rules with historical PR links (10 sections) - `.cursor/rules/native-code.mdc` — compact triage checklist with trigger symbols and stop conditions Read **both files now**. Use their framework to classify the crash. The categories below summarize the key stack-trace signals; refer to the docs for full rationale and PR history. #### 3.1 — GIL Lifecycle During Finalization **Stack signals**: `PyGILState_Ensure`, `PyEval_RestoreThread`, `take_gil`, `new_threadstate`, `PyThread_exit_thread`, `pthread_exit`, `abort`, `gsignal`, `__forced_unwind`, `abi::__cxa_throw`, `std::terminate` — especially combined with thread-entry frames like `execute_native_thread_routine` or Rust `extern "C"` boundaries. **What to check**: - Is there a `py_is_finalizing()` / `is_finalizing()` check immediately before each GIL acquire/restore call? These checks are inherently TOCTTOU — finalization can begin in the window between the check and the call. - Does the crash involve C++ RAII unwinding? `pthread_exit` on glibc throws `abi::__forced_unwind`; any C++ destructor or `catch(...)` that does not re-throw it will cause `std::terminate` → `SIGABRT`. On musl (Alpine Linux), `pthread_exit` uses `longjmp` instead — `catch(abi::__forced_unwind&)` never fires. - For Rust/pyo3 code: `__forced_unwind` cannot propagate through `extern "C"` boundaries. Rust code that calls `PyEval_RestoreThread` (or uses `py.detach()`) needs its own finalization check — the C++ caller's `try/catch` cannot protect it. **CPython version behavior** (from `docs/native-code-review.md`): | CPython | Behavior when `take_gil()` detects finalization | |---------|------------------------------------------------| | 3.12 (all) | `PyThread_exit_thread()` → `pthread_exit()` → crash | | 3.13.0–3.13.7 | Same as 3.12 | | 3.13.8+ | `PyThread_hang_thread()` → `pause()` forever (hang, not crash) |
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る