Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 3 additions & 10 deletions src/spirv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,6 @@ end

# translate to SPIR-V
input = bitcode(mod)
dump_input() = let path = tempname(cleanup=false) * ".bc"
write(path, input)
path
end
spirv = if target.backend === :llvm
# compile in-process through libspirv. the back-end derives the triple from the
# options; unlike `llc`, it rejects unknown extensions instead of ignoring them.
Expand Down Expand Up @@ -355,24 +351,21 @@ end
error("Unsupported SPIR-V back-end $(repr(target.backend)); expected :llvm or :khronos.")
end

dump_output(output) = let path = tempname(cleanup=false) * ".spv"
write(path, output)
path
end
dump_files(output) = join(dump_failure(".bc" => input, ".spv" => output), " and ")

if target.validate
message = spirv_validate(spirv)
if message !== nothing
error("""Failed to validate generated SPIR-V: $message
If you think this is a bug, please file an issue and attach $(dump_input()) and $(dump_output(spirv)).""")
If you think this is a bug, please file an issue and attach $(dump_files(spirv)).""")
end
end

if target.optimize
optimized = spirv_optimize(spirv)
if optimized === nothing
error("""Failed to optimize generated SPIR-V.
If you think this is a bug, please file an issue and attach $(dump_input()) and $(dump_output(spirv)).""")
If you think this is a bug, please file an issue and attach $(dump_files(spirv)).""")
end
spirv = optimized
end
Expand Down
37 changes: 35 additions & 2 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ function external_result(backend::ExternalBackend, status, what::String, message
diagnostics::Vector{String}, input::Vector{UInt8};
warn=(msg)->@warn(msg))
if status != 0
path = tempname(cleanup=false) * ".bc"
write(path, input)
path, = dump_failure(".bc" => input)
msg = what
message == C_NULL || (msg *= ":\n" * take_message(backend, message))
isempty(diagnostics) || (msg *= "\n" * join(diagnostics, "\n"))
Expand Down Expand Up @@ -214,6 +213,40 @@ macro safe_show(exs...)
end


## failure dumps

# Write the inputs and outputs of a failed compilation step (`extension => data` pairs, which
# share a random base name) to files that can be attached to a bug report, and return their
# paths. Such failures often only happen on CI, so make the files downloadable there: on
# GitHub Actions they are written to `$RUNNER_TEMP/gpucompiler-dumps` for an
# `actions/upload-artifact` step to pick up, and on Buildkite they are uploaded as artifacts.
function dump_failure(artifacts::Pair{String}...)
on_github = get(ENV, "GITHUB_ACTIONS", "false") == "true" && haskey(ENV, "RUNNER_TEMP")
dir = on_github ? mkpath(joinpath(ENV["RUNNER_TEMP"], "gpucompiler-dumps")) : tempdir()
stem = tempname(dir; cleanup=false)
paths = String[]
for (ext, data) in artifacts
path = stem * ext
write(path, data)
push!(paths, path)
end

if on_github
println("::notice title=GPUCompiler failure dump::wrote $(join(basename.(paths), ", ")) to $dir")
elseif get(ENV, "BUILDKITE", "false") == "true"
for path in paths
try
run(`buildkite-agent artifact upload $path`)
catch err
@safe_warn "Failed to upload $path as a Buildkite artifact" exception=err
end
end
end

return paths
end


## safe deprecation warnings

const depwarn_lock = Threads.SpinLock()
Expand Down
50 changes: 50 additions & 0 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,56 @@ end
end
end

@testset "failure dumps" begin
# files of one dump share a base name, wherever they are written
function check_dump(paths, dir)
@test length(paths) == 2
@test all(path -> dirname(path) == dir, paths)
@test first.(splitext.(paths))[1] == first.(splitext.(paths))[2]
@test read(paths[1]) == UInt8[1, 2, 3]
@test read(paths[2], String) == "output"
rm.(paths)
end

withenv("GITHUB_ACTIONS" => nothing, "BUILDKITE" => nothing) do
paths = GPUCompiler.dump_failure(".bc" => UInt8[1, 2, 3], ".spv" => "output")
check_dump(paths, tempdir())
end

# on GitHub Actions, next to the runner's other temporary files, with a notice
mktempdir() do runner_temp
withenv("GITHUB_ACTIONS" => "true", "RUNNER_TEMP" => runner_temp,
"BUILDKITE" => nothing) do
paths, output = mktemp() do log, io
paths = redirect_stdout(io) do
GPUCompiler.dump_failure(".bc" => UInt8[1, 2, 3], ".spv" => "output")
end
flush(io)
paths, read(log, String)
end
dir = joinpath(runner_temp, "gpucompiler-dumps")
@test startswith(output, "::notice")
@test all(path -> occursin(basename(path), output), paths)
check_dump(paths, dir)

# failures of the external back-ends point at their dump
backend = GPUCompiler.ExternalBackend("libdoesnotexist", "Test")
err = try
redirect_stdout(devnull) do
GPUCompiler.external_result(backend, 1, "Failed to compile", Cstring(C_NULL),
String[], UInt8[1, 2, 3])
end
nothing
catch err
err
end
@test err isa ErrorException
m = match(r"attach (\S+\.bc)\.", err.msg)
@test m !== nothing && dirname(m[1]) == dir && read(m[1]) == UInt8[1, 2, 3]
end
end
end

using Logging

safe_log() = GPUCompiler.@safe_debug "safe_log"
Expand Down
Loading