Skip to content

Commit 69f3a76

Browse files
authored
Improve NSight Systems activation by inspecting the session list. (#2638)
1 parent ef33505 commit 69f3a76

File tree

1 file changed

+86
-16
lines changed

1 file changed

+86
-16
lines changed

src/profile.jl

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ function find_nsys()
169169
return ENV["_"]
170170
else
171171
# look at a couple of environment variables that may point to NSight
172-
nsight = nothing
172+
nsys = nothing
173173
for var in ("LD_PRELOAD", "CUDA_INJECTION64_PATH", "NVTX_INJECTION64_PATH")
174174
haskey(ENV, var) || continue
175175
for val in split(ENV[var], Sys.iswindows() ? ';' : ':')
@@ -186,22 +186,57 @@ function find_nsys()
186186
error("Running under Nsight Systems, but could not find the `nsys` binary to start the profiler. Please specify using JULIA_CUDA_NSYS=path/to/nsys, and file an issue with the contents of ENV.")
187187
end
188188

189-
const __nsight = Ref{Union{Nothing,String}}()
190-
function nsight()
191-
if !isassigned(__nsight)
189+
const __nsys = Ref{Union{Nothing,String}}()
190+
function nsys()
191+
if !isassigned(__nsys)
192192
# find the active Nsight Systems profiler
193193
if haskey(ENV, "NSYS_PROFILING_SESSION_ID") && ccall(:jl_generating_output, Cint, ()) == 0
194-
__nsight[] = find_nsys()
195-
@assert isfile(__nsight[])
196-
@info "Running under Nsight Systems, CUDA.@profile will automatically start the profiler"
194+
__nsys[] = find_nsys()
197195
else
198-
__nsight[] = nothing
196+
__nsys[] = nothing
199197
end
200198
end
201199

202-
__nsight[]
200+
__nsys[]
203201
end
204202

203+
function nsys_sessions()
204+
sessions = Dict{Int,Dict{String,String}}()
205+
open(`$(nsys()) sessions list`, "r") do io
206+
header = Dict()
207+
for line in eachline(io)
208+
# parse the header
209+
if isempty(header)
210+
@assert startswith(line, r"\s+ID")
211+
colnames = split(line)[1:end-1] # ignore the final left-aligned column
212+
colranges = []
213+
for column in colnames
214+
push!(colranges, findfirst(Regex("\\s+\\b$column\\b"), line))
215+
end
216+
for (name, range) in zip(colnames, colranges)
217+
header[name] = range
218+
end
219+
220+
# parse the data
221+
else
222+
session = Dict()
223+
for (name, range) in header
224+
session[name] = lstrip(line[range])
225+
end
226+
227+
id = parse(Int, session["ID"])
228+
delete!(session, "ID")
229+
sessions[id] = session
230+
end
231+
end
232+
end
233+
return sessions
234+
end
235+
236+
nsys_session() = parse(Int, ENV["NSYS_PROFILING_SESSION_ID"])
237+
238+
nsys_state() = nsys_sessions()[nsys_session()]["STATE"]
239+
205240

206241

207242
"""
@@ -211,11 +246,50 @@ Enables profile collection by the active profiling tool for the current context.
211246
profiling is already enabled, then this call has no effect.
212247
"""
213248
function start()
214-
if nsight() !== nothing
215-
run(`$(nsight()) start --capture-range=cudaProfilerApi`)
216-
# it takes a while for the profiler to actually start tracing our process
249+
if nsys() !== nothing
250+
# by default, running under NSight Systems does not activate the profiler API-based
251+
# ranged collection; that's done by calling `nsys start --capture-range=cudaProfilerApi`.
252+
# however, as of recent we cannot do this anymore when already running under the
253+
# capturing `nsys profile`, so we need to detect the state and act accordingly.
254+
try
255+
state = nsys_state()
256+
257+
# `nsys profile`
258+
if state == "Collection"
259+
@warn """The application is already being profiled; starting the profiler is a no-op.
260+
261+
If you meant to profile a specific region, make sure to start NSight Systems in
262+
delayed mode (`nsys profile --start-later=true --capture-range=cudaProfilerApi`)
263+
or simply switch to the interactive `nsys launch` command."""
264+
return
265+
266+
# `nsys profile --start-later=true`
267+
elseif state == "DelayedCollection"
268+
@error """The application is running under a delayed profiling session which CUDA.jl cannot activate.
269+
270+
If you want `CUDA.@profile` to enable the profiler, make sure
271+
to pass `--capture-range=cudaProfilerApi` to `nsys profile`."""
272+
return
273+
274+
# `nsys profile --start-later=true --capture-range=cudaProfilerApi`
275+
elseif state == "StartRange"
276+
277+
# `nsys launch`
278+
elseif state == "Launched"
279+
run(`$(nsys()) start --capture-range=cudaProfilerApi`)
280+
281+
else
282+
error("Unexpected state: $state")
283+
end
284+
catch err
285+
@error "Failed to find the active profiling session ($(nsys_session())) in the session list:\n" * read(`$(nsys()) sessions list`, String) * "\n\nPlease file an issue." exception=(err,catch_backtrace())
286+
end
287+
288+
# it takes a while for the profiler to attach to our process
217289
sleep(0.01)
218290
end
291+
292+
# actually start the capture
219293
CUDA.cuProfilerStart()
220294
end
221295

@@ -227,10 +301,6 @@ profiling is already disabled, then this call has no effect.
227301
"""
228302
function stop()
229303
CUDA.cuProfilerStop()
230-
if nsight() !== nothing
231-
@info """Profiling has finished, open the report listed above with `nsys-ui`
232-
If no report was generated, try launching `nsys` with `--trace=cuda`"""
233-
end
234304
end
235305

236306

0 commit comments

Comments
 (0)