From d2e78089cedfac9669d6dea0518d114f296f0287 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 1 May 2019 14:16:57 -0400 Subject: [PATCH 1/2] Prevent stack overflow in Profile As a follow up to #31693, this fixes the other place in Profile that recurses over Profile data, causing stack overflows. This should fix a bunch of the recent intermittent CI faults on linux32. --- stdlib/Profile/src/Profile.jl | 45 ++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 3c3b6cb65ad00..0fafa94a8cf1f 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -560,26 +560,33 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI return root end -# Print a "branch" starting at a particular level. This gets called recursively. +# Print the stack frame tree starting at a particular root. Uses a worklist to +# avoid stack overflows. function tree(io::IO, bt::StackFrameTree, level::Int, cols::Int, fmt::ProfileFormat, noisefloor::Int) - level > fmt.maxdepth && return - isempty(bt.down) && return - # Order the line information - nexts = collect(values(bt.down)) - lilist = collect(frame.frame for frame in nexts) - counts = collect(frame.count for frame in nexts) - # Generate the string for each line - strs = tree_format(lilist, counts, level, cols) - # Recurse to the next level - for i in liperm(lilist) - down = nexts[i] - count = down.count - count < fmt.mincount && continue - count < noisefloor && continue - str = strs[i] - println(io, isempty(str) ? "$count unknown stackframe" : str) - noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0 - tree(io, down, level + 1, cols, fmt, noisefloor_down) + worklist = Tuple{StackFrameTree, Int, Int, Union{String, Nothing}}[ + (bt, level, noisefloor, nothing)] + while !isempty(worklist) + (bt, level, noisefloor, str) = popfirst!(worklist) + str !== nothing && println(io, str) + level > fmt.maxdepth && continue + isempty(bt.down) && continue + # Order the line information + nexts = collect(values(bt.down)) + lilist = collect(frame.frame for frame in nexts) + counts = collect(frame.count for frame in nexts) + # Generate the string for each line + strs = tree_format(lilist, counts, level, cols) + # Recurse to the next level + for i in liperm(lilist) + down = nexts[i] + count = down.count + count < fmt.mincount && continue + count < noisefloor && continue + str = strs[i] + isempty(str) && (str = "$count unknown stackframe") + noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0 + push!(worklist, (down, level+1, noisefloor_down, str)) + end end nothing end From 6639009916abf3178ed79031399c2e19b2249bfb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 May 2019 15:03:18 -0400 Subject: [PATCH 2/2] fixup! Prevent stack overflow in Profile --- stdlib/Profile/src/Profile.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 0fafa94a8cf1f..ffcc6ad10b05a 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -562,12 +562,11 @@ end # Print the stack frame tree starting at a particular root. Uses a worklist to # avoid stack overflows. -function tree(io::IO, bt::StackFrameTree, level::Int, cols::Int, fmt::ProfileFormat, noisefloor::Int) - worklist = Tuple{StackFrameTree, Int, Int, Union{String, Nothing}}[ - (bt, level, noisefloor, nothing)] +function tree(io::IO, bt::StackFrameTree, cols::Int, fmt::ProfileFormat) + worklist = [(bt, 0, 0, "")] while !isempty(worklist) (bt, level, noisefloor, str) = popfirst!(worklist) - str !== nothing && println(io, str) + isempty(str) || println(io, str) level > fmt.maxdepth && continue isempty(bt.down) && continue # Order the line information @@ -577,7 +576,7 @@ function tree(io::IO, bt::StackFrameTree, level::Int, cols::Int, fmt::ProfileFor # Generate the string for each line strs = tree_format(lilist, counts, level, cols) # Recurse to the next level - for i in liperm(lilist) + for i in reverse(liperm(lilist)) down = nexts[i] count = down.count count < fmt.mincount && continue @@ -585,10 +584,9 @@ function tree(io::IO, bt::StackFrameTree, level::Int, cols::Int, fmt::ProfileFor str = strs[i] isempty(str) && (str = "$count unknown stackframe") noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0 - push!(worklist, (down, level+1, noisefloor_down, str)) + pushfirst!(worklist, (down, level + 1, noisefloor_down, str)) end end - nothing end function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, LineInfoDict}, cols::Int, fmt::ProfileFormat) @@ -601,8 +599,7 @@ function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, Line warning_empty() return end - level = 0 - tree(io, root, level, cols, fmt, 0) + tree(io, root, cols, fmt) nothing end