Skip to content

Commit

Permalink
started to implement endpoint to fetch task backtraces (#38)
Browse files Browse the repository at this point in the history
Title should be self-explanatory.

Extends the debug super-endpoint `/debug_engine` to handle task backtraces and also adds a `/task_backtraces` endpoint to collect task backtraces on-demand.
  • Loading branch information
d-netto authored May 31, 2024
1 parent a4453a6 commit a3ff7cd
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Julia process to interrogate its performance characteristics.
- `/heap_snapshot` endpoint to take a heap snapshot with `Profile.take_heap_snapshot`.
- Default query params: `/heap_snapshot?all_one=false`

**Task Backtraces**
- `/task_backtraces` endpoint to collect task backtraces. Only available in Julia v1.10+.
- Default query params: none.

## Examples

Start the server on your production process
Expand Down Expand Up @@ -80,3 +84,11 @@ $ curl '127.0.0.1:16825/heap_snapshot?all_one=false' --output prof1.heapsnapshot
And upload it in the [Chrome DevTools snapshot viewer](https://developer.chrome.com/docs/devtools/memory-problems/heap-snapshots/#view_snapshots) to explore the heap.
In Chrome `View > Developer > Developer Tools`, select the `Memory` tab, and press the `Load` button to upload the file.
### Task Backtraces
Collect task backtraces (requires Julia v1.10+):
```bash
$ curl '127.0.0.1:16825/task_backtraces' --output task_backtraces.txt
```
31 changes: 31 additions & 0 deletions src/ProfileEndpoints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,34 @@ end

end # if isdefined

###
### Task backtraces
###

function task_backtraces_endpoint(req::HTTP.Request)
@static if VERSION < v"1.10.0-DEV.0"
return HTTP.Response(501, "Task backtraces are only available in Julia 1.10+")
end
return handle_task_backtraces()
end

function handle_task_backtraces(stage_path = nothing)
@info "Starting Task Backtrace Profiling from ProfileEndpoints"
@static if VERSION < v"1.10.0-DEV.0"
return HTTP.Response(501, "Task backtraces are only available in Julia 1.10+")
end
if stage_path === nothing
stage_path = tempdir()
end
backtrace_file = joinpath(stage_path, "task_backtraces.txt")
open(backtrace_file, "w") do io
redirect_stderr(io) do
ccall(:jl_print_task_backtraces, Cvoid, ())
end
end
return HTTP.Response(200, backtrace_file)
end

###
### Debug super-endpoint
###
Expand Down Expand Up @@ -330,6 +358,8 @@ function debug_profile_endpoint_with_stage_path(stage_path = nothing)
parse(Bool, get(body, "pprof", default_pprof())),
stage_path
)
elseif profile_type == "task_backtraces"
return handle_task_backtraces(stage_path)
else
return HTTP.Response(400, "Unknown profile_type: $profile_type")
end
Expand All @@ -350,6 +380,7 @@ function register_endpoints(router; stage_path = nothing)
HTTP.register!(router, "/allocs_profile", allocations_profile_endpoint)
HTTP.register!(router, "/allocs_profile_start", allocations_start_endpoint)
HTTP.register!(router, "/allocs_profile_stop", allocations_stop_endpoint)
HTTP.register!(router, "/task_backtraces", task_backtraces_endpoint)
debug_profile_endpoint = debug_profile_endpoint_with_stage_path(stage_path)
HTTP.register!(router, "/debug_engine", debug_profile_endpoint)
end
Expand Down
26 changes: 26 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ const url = "http://127.0.0.1:$port"
@info "filename: $fname"
@test isfile(fname)
end

@testset "Debug endpoint task backtraces" begin
@static if VERSION >= v"1.10.0-DEV.0"
headers = ["Content-Type" => "application/json"]
payload = JSON3.write(Dict("profile_type" => "task_backtraces"))
req = HTTP.post("$url/debug_engine", headers, payload)
@test req.status == 200
fname = read(IOBuffer(req.body), String)
@info "filename: $fname"
@test isfile(fname)
end
end
end

@testset "Heap snapshot $query" for query in ("", "?all_one=true")
Expand Down Expand Up @@ -203,6 +215,20 @@ const url = "http://127.0.0.1:$port"
end
end

@testset "task backtraces" begin
@testset "task_backtraces endpoint" begin
@static if VERSION >= v"1.10.0-DEV.0"
req = HTTP.get("$url/task_backtraces", retry=false, status_exception=false)
@test req.status == 200
@test length(req.body) > 0

# Test whether the profile returned a valid file
data = read(IOBuffer(req.body), String)
@test isfile(data)
end
end
end

@testset "error handling" begin
let res = HTTP.get("$url/profile", status_exception=false)
@test 400 <= res.status < 500
Expand Down

0 comments on commit a3ff7cd

Please sign in to comment.