Skip to content

Commit 1707e13

Browse files
authored
Prevent stack overflow in Profile (#31893)
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.
1 parent 5b637df commit 1707e13

File tree

1 file changed

+27
-23
lines changed

1 file changed

+27
-23
lines changed

stdlib/Profile/src/Profile.jl

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -560,28 +560,33 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI
560560
return root
561561
end
562562

563-
# Print a "branch" starting at a particular level. This gets called recursively.
564-
function tree(io::IO, bt::StackFrameTree, level::Int, cols::Int, fmt::ProfileFormat, noisefloor::Int)
565-
level > fmt.maxdepth && return
566-
isempty(bt.down) && return
567-
# Order the line information
568-
nexts = collect(values(bt.down))
569-
lilist = collect(frame.frame for frame in nexts)
570-
counts = collect(frame.count for frame in nexts)
571-
# Generate the string for each line
572-
strs = tree_format(lilist, counts, level, cols)
573-
# Recurse to the next level
574-
for i in liperm(lilist)
575-
down = nexts[i]
576-
count = down.count
577-
count < fmt.mincount && continue
578-
count < noisefloor && continue
579-
str = strs[i]
580-
println(io, isempty(str) ? "$count unknown stackframe" : str)
581-
noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0
582-
tree(io, down, level + 1, cols, fmt, noisefloor_down)
563+
# Print the stack frame tree starting at a particular root. Uses a worklist to
564+
# avoid stack overflows.
565+
function tree(io::IO, bt::StackFrameTree, cols::Int, fmt::ProfileFormat)
566+
worklist = [(bt, 0, 0, "")]
567+
while !isempty(worklist)
568+
(bt, level, noisefloor, str) = popfirst!(worklist)
569+
isempty(str) || println(io, str)
570+
level > fmt.maxdepth && continue
571+
isempty(bt.down) && continue
572+
# Order the line information
573+
nexts = collect(values(bt.down))
574+
lilist = collect(frame.frame for frame in nexts)
575+
counts = collect(frame.count for frame in nexts)
576+
# Generate the string for each line
577+
strs = tree_format(lilist, counts, level, cols)
578+
# Recurse to the next level
579+
for i in reverse(liperm(lilist))
580+
down = nexts[i]
581+
count = down.count
582+
count < fmt.mincount && continue
583+
count < noisefloor && continue
584+
str = strs[i]
585+
isempty(str) && (str = "$count unknown stackframe")
586+
noisefloor_down = fmt.noisefloor > 0 ? floor(Int, fmt.noisefloor * sqrt(count)) : 0
587+
pushfirst!(worklist, (down, level + 1, noisefloor_down, str))
588+
end
583589
end
584-
nothing
585590
end
586591

587592
function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, LineInfoDict}, cols::Int, fmt::ProfileFormat)
@@ -594,8 +599,7 @@ function tree(io::IO, data::Vector{UInt64}, lidict::Union{LineInfoFlatDict, Line
594599
warning_empty()
595600
return
596601
end
597-
level = 0
598-
tree(io, root, level, cols, fmt, 0)
602+
tree(io, root, cols, fmt)
599603
nothing
600604
end
601605

0 commit comments

Comments
 (0)