Skip to content

Commit

Permalink
Add corner-cases tests for manually constructed profiles; ensure we c…
Browse files Browse the repository at this point in the history
…atch every sample

Fix the bugs in handling those cases :)
  • Loading branch information
NHDaly committed Oct 20, 2023
1 parent 2d72715 commit b3c5763
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
23 changes: 20 additions & 3 deletions src/PProf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,19 @@ function pprof(data::Union{Nothing, Vector{UInt}} = nothing,
1, # events
]

lastwaszero = false # (Legacy: used when has_meta = false)

lastwaszero = true # (Legacy: used when has_meta = false)

# The Profile data buffer is a big array, with each sample appended one after the other.
# Each sample now looks like this:
# | ip | ip | ip | meta1 | meta2 | meta3 | meta4| 0x0 | 0x0 |
# We iterate backwards, starting from the end, so that we don't encounter the metadata
# and mistake it for more ip addresses. For each sample, we skip the zeros, consume the
# metadata, then continue scanning the ip addresses, and when we hit another end of a
# block, we finish the sample we just consumed.
idx = length(data)
meta = nothing
while idx > 0
# We handle the very first sample after the loop.
if has_meta && Profile.is_block_end(data, idx)
if meta !== nothing
# Finish last block
Expand All @@ -181,7 +189,7 @@ function pprof(data::Union{Nothing, Vector{UInt}} = nothing,
]
idx -= (Profile.nmeta + 2) # skip all the metas, plus the 2 nulls that end a block.
continue
elseif !has_meta && idx != length(data) && data[idx] == 0
elseif !has_meta && data[idx] == 0
# Avoid creating empty samples
# ip == 0x0 is the sentinel value for finishing a backtrace (when meta is disabled), therefore finising a sample
# On some platforms, we sometimes get two 0s in a row for some reason...
Expand Down Expand Up @@ -278,6 +286,15 @@ function pprof(data::Union{Nothing, Vector{UInt}} = nothing,
push!(location_id, ip)
end
end
if length(data) > 0
# Finish the very last sample
if has_meta
push!(samples, Sample(;location_id = reverse!(location_id), value = value, label = meta))
else
push!(samples, Sample(;location_id = reverse!(location_id), value = value))
end
location_id = Vector{eltype(data)}()
end

# If from_c=false funcs and locs should NOT contain C functions
prof = PProfile(
Expand Down
34 changes: 34 additions & 0 deletions test/PProf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,40 @@ function load_prof_proto(file)
open(io->decode(ProtoDecoder(GzipDecompressorStream(io)), PProf.perftools.profiles.Profile), file, "r")
end

@testset "Corner Cases" begin
@testset "non-meta profile" begin

@testset "0 sample profile" begin
prof = load_prof_proto(pprof(UInt64[], out=tempname(), web=false))
@test length(prof.sample) == 0
end
@testset "1 sample profile" begin
prof = load_prof_proto(pprof(UInt64[0xdeadbeef,0], out=tempname(), web=false))
@test length(prof.sample) == 1
end

@testset "2 sample, 1 location profile" begin
prof = load_prof_proto(pprof(UInt64[0xdeadbeef,0, 0xdeadbeef, 0], out=tempname(), web=false))
@test length(prof.sample) == 2
@test length(prof.location) == 1
end
end
@testset "with-meta profile" begin
@testset "1 sample profile" begin
data = UInt64[0xdeadbeef, 1, 1, 1, 1, 0, 0]
prof = load_prof_proto(pprof(data, out=tempname(), web=false))
@test length(prof.sample) == 1
end

@testset "2 sample 1 location profile" begin
data = UInt64[0xdeadbeef, 1, 1, 1, 1, 0, 0, 0xdeadbeef, 1, 1, 1, 1, 0, 0]
prof = load_prof_proto(pprof(data, out=tempname(), web=false))
@test length(prof.sample) == 2
@test length(prof.location) == 1
end
end
end

@testset "with_c" begin
Profile.clear()

Expand Down

0 comments on commit b3c5763

Please sign in to comment.