Skip to content

Commit 81e4561

Browse files
committed
bpftrace proof of concept for contextvars based thread correlation
1 parent 5307dd0 commit 81e4561

4 files changed

Lines changed: 118 additions & 0 deletions

File tree

uprobe-contextvars/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM archlinux:base-20251019.0.436919
2+
RUN pacman -Syu --noconfirm bpftrace debuginfod python uv
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Docker compose file for bpftrace and some python webapps
2+
3+
services:
4+
bpftrace:
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
8+
privileged: true
9+
pid: "host"
10+
network_mode: "host"
11+
volumes:
12+
- .:/app
13+
- /sys/:/sys/
14+
working_dir: /app
15+
environment:
16+
- DEBUGINFOD_URLS=https://debuginfod.archlinux.org
17+
entrypoint: ["bpftrace", "-v", "print_calls.bt"]
18+
19+
20+
python:
21+
build:
22+
context: .
23+
dockerfile: Dockerfile
24+
environment:
25+
- PYTHONUNBUFFERED=1
26+
volumes:
27+
- .:/app
28+
working_dir: /app
29+
entrypoint: ["uv", "run", "--script", "plain.py"]
30+
depends_on:
31+
- bpftrace

uprobe-contextvars/plain.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# /// script
2+
# requires-python = ">=3.13"
3+
# dependencies = [
4+
# "requests",
5+
# ]
6+
# ///
7+
8+
import asyncio
9+
10+
import requests
11+
12+
13+
async def make_request() -> None:
14+
# Runs the blocking IO in a thread pool
15+
status_code = await asyncio.to_thread(
16+
lambda: requests.get("https://github.com").status_code
17+
)
18+
print(status_code)
19+
20+
21+
async def main() -> None:
22+
while True:
23+
await make_request()
24+
await asyncio.sleep(2)
25+
26+
27+
asyncio.run(main())

uprobe-contextvars/print_calls.bt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#define YELLOW "\033[33m"
2+
#define NC "\033[0m" // No Color (resets the terminal color)
3+
4+
// These are the official C API for enter/exiting a context, but is not called by the cpython
5+
// internals. Something like uvloop might use this.
6+
uprobe:/usr/lib/libpython3.13.so:PyContext_Enter {
7+
printf("%stid=%s%s %s %s\n", YELLOW, tid, NC, func, args);
8+
}
9+
10+
uprobe:/usr/lib/libpython3.13.so:PyContext_Exit {
11+
printf("%stid=%s%s %s %s\n", YELLOW, tid, NC, func, args);
12+
}
13+
14+
// This is the C implementation of contextvars.run() python function
15+
// https://github.com/python/cpython/blob/v3.13.9/Python/context.c#L648-L650. This plus the
16+
// PyContext_Enter/PyContext_Exit is probably the most reliable.
17+
uprobe:/usr/lib/libpython3.13.so:context_run* {
18+
printf("%stid=%s%s %s %s\n", YELLOW, tid, NC, func, args);
19+
}
20+
uretprobe:/usr/lib/libpython3.13.so:context_run* {
21+
printf("%stid=%s%s return %s\n", YELLOW, tid, NC, func);
22+
}
23+
24+
// -----------
25+
26+
// These might be useful for tracking parentage of new Context objects
27+
/**
28+
uretprobe:/usr/lib/libpython3.13.so:PyContext_CopyCurrent {
29+
printf("%s retval = %#x\n", func, retval);
30+
}
31+
uretprobe:/usr/lib/libpython3.13.so:PyContext_Copy {
32+
printf("%s retval = %#x\n", func, retval);
33+
}
34+
*/
35+
36+
// These might be useful for watching the trace context for OTel python instrumented code
37+
/*
38+
uprobe:/usr/lib/libpython3.13.so:PyContextVar_Get {
39+
printf("%s %s\n", func, args);
40+
}
41+
uprobe:/usr/lib/libpython3.13.so:PyContextVar_Set {
42+
printf("%s %s\n", func, args);
43+
}
44+
*/
45+
46+
// These are called by PyContext_Enter/PyContext_Exit and context_run internally. It seems to
47+
// get inlined in many production builds of python (e.g. on Debian and uv managed python), so
48+
// might not be reliable.
49+
//
50+
// See https://github.com/python/cpython/blob/v3.13.9/Python/context.c#L112-L113
51+
/*
52+
uprobe:/usr/lib/libpython3.13.so:_PyContext_Enter {
53+
printf("tid=%s %s %s\n", tid, func, args);
54+
}
55+
uprobe:/usr/lib/libpython3.13.so:_PyContext_Exit {
56+
printf("tid=%s %s %s\n", tid, func, args);
57+
}
58+
*/

0 commit comments

Comments
 (0)