Skip to content

Commit

Permalink
Initial working commit
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jan 12, 2020
1 parent 1a126e6 commit 7467ef0
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 3 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
language: julia
os:
- linux
- osx
addons:
apt:
packages:
- librsvg2-bin
julia:
- 1.0
- 1
- nightly
matrix:
allow_failures:
Expand Down
6 changes: 6 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ uuid = "132c30aa-f267-4189-9183-c8a63c7e05e6"
authors = ["Tim Holy <tim.holy@gmail.com>"]
version = "0.1.0"

[deps]
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89"
Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
julia = "1"

Expand Down
126 changes: 125 additions & 1 deletion src/ProfileSVG.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,129 @@
module ProfileSVG

greet() = print("Hello World!")
using FlameGraphs, Colors, UUIDs, Profile
using Base.StackTraces: StackFrame
const FlameGraph = FlameGraphs.Node{FlameGraphs.NodeData}

export @profview

"""
@profview f(args...)
Clear the Profile buffer, profile `f(args...)`, and view the result graphically.
"""
macro profview(ex)
return quote
Profile.clear()
@profile $(esc(ex))
view()
end
end

struct FGConfig
g::FlameGraph
fcolor!
fontsize::Int
end

include("svgwriter.jl")

function view(fcolor!, data::Vector{UInt64}=Profile.fetch(); fontsize::Integer=12, kwargs...)
g = flamegraph(data; kwargs...)
FGConfig(g, fcolor!, fontsize)
end
function view(data::Vector{UInt64}=Profile.fetch(); fontsize::Integer=12, kwargs...)
view(FlameGraphs.default_colors, data; fontsize=fontsize, kwargs...)
end


function save(fcolor!, io::IO, g::FlameGraph; fontsize::Integer=12)
show(io, MIME("image/svg+xml"), FGConfig(g, fcolor!, fontsize))
end
function save(fcolor!, filename::AbstractString, g::FlameGraph; kwargs...)
open(filename, "w") do file
save(fcolor!, file, g; kwargs...)
end
return nothing
end
function save(fcolor!, io::IO; fontsize::Integer=12, kwargs...)
g = flamegraph(; kwargs...)
save(fcolor!, io, g; fontsize=fontsize)
end
function save(fcolor!, filename::AbstractString; kwargs...)
open(filename, "w") do file
save(fcolor!, file; kwargs...)
end
return nothing
end
save(io::IO, args...; kwargs...) = save(FlameGraphs.default_colors, io, args...; kwargs...)
save(filename::AbstractString, args...; kwargs...) = save(FlameGraphs.default_colors, filename, args...; kwargs...)


Base.showable(::MIME"image/svg+xml", fg::FGConfig) = true


function Base.show(io::IO, ::MIME"image/svg+xml", fg::FGConfig)
g, fcolor!, fontsize = fg.g, fg.fcolor!, fg.fontsize
ncols, nrows = length(g.data.span), FlameGraphs.depth(g)
leftmargin = rightmargin = 10
width = 1000
topmargin = 30
botmargin = 40
rowheight = 15
height = ceil(rowheight*nrows + botmargin + topmargin)
xstep = (width - (leftmargin + rightmargin)) / ncols
ystep = (height - (topmargin + botmargin)) / nrows
avgcharwidth = 6 # for Verdana 12 pt font

function printrec(io::IO, samples, xstart, xend, y, sf::StackFrame, rgb, fontsize)
width = xend - xstart
info = "$(sf.func) in $(sf.file):$(sf.line)"
shortinfo = "$(sf.func) in $(basename(string(sf.file))):$(sf.line)"
info = eschtml(info)
shortinfo = eschtml(shortinfo)
#if avgcharwidth*3 > width
# shortinfo = ""
#elseif length(shortinfo) * avgcharwidth > width
# nchars = int(width/avgcharwidth)-2
# shortinfo = eschtml(info[1:nchars] * "..")
#end
r = round(Integer, 255*red(rgb))
g = round(Integer, 255*green(rgb))
b = round(Integer, 255*blue(rgb))
print(io, """<rect vector-effect="non-scaling-stroke" x="$xstart" y="$y" width="$width" height="$ystep" fill="rgb($r,$g,$b)" rx="2" ry="2" data-shortinfo="$shortinfo" data-info="$info"/>\n""")
#if shortinfo != ""
println(io, """\n<text text-anchor="" x="$(xstart+4)" y="$(y+11.5)" font-size="$fontsize" font-family="Verdana" fill="rgb(0,0,0)" ></text>""")
# end
end

function eschtml(str)
s = replace(str, '<' => "&lt;")
s = replace(s, '>' => "&gt;")
s = replace(s, '&' => "&amp;")
s
end

function flamerects(fcolor!, io::IO, g, j, nextidx)
ndata = g.data
thiscolor = fcolor!(nextidx, j, ndata)
y = height - j*ystep - botmargin
xstart = (first(ndata.span)-1) * xstep + leftmargin
xend = ( last(ndata.span)-1) * xstep + leftmargin
printrec(io, length(ndata.span), xstart, xend, y, ndata.sf, thiscolor, fontsize)

for c in g
flamerects(fcolor!, io, c, j+1, nextidx)
end
return nothing
end

fig_id = string("fig-", replace(string(uuid4()), "-" => ""))
svgheader(io, fig_id, width=width, height=height)

nextidx = fill(1, nrows)
flamerects(fcolor!, io, g, 1, nextidx)

svgfinish(io, fig_id)
end

end # module
25 changes: 24 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
using ProfileSVG
using Test

# For these tests to work you need `rsvg-convert` installed.
# On Ubuntu this is `sudo apt install librsvg2-bin`.

function profile_test(n)
for i = 1:n
A = randn(100,100,20)
m = maximum(A)
Am = mapslices(sum, A; dims=2)
B = A[:,:,5]
Bsort = mapslices(sort, B; dims=1)
b = rand(100)
C = B.*b
end
end

@testset "ProfileSVG.jl" begin
# Write your own tests here.
profile_test(1) # to compile
@profview profile_test(10)
mktemp() do path, io
ProfileSVG.save(io)
flush(io)
# Validate the file by converting to PNG
str = read(`rsvg-convert $path`, String)
@test codeunits(str)[1:8] == UInt8[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
end
end

0 comments on commit 7467ef0

Please sign in to comment.