Skip to content

Commit

Permalink
Fix IO (fixes #73)
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Feb 10, 2020
1 parent 70dfbea commit f48c65f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 111 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ contribution to its total run time.
- You can pan the view by clicking and dragging, or by scrolling your
mouse/trackpad (scroll=vertical, SHIFT-scroll=horizontal).

- The toolbar at the top contains two icons to load and save profile
data, respectively. Clicking the save icon will prompt you for a
filename; you should use extension `*.jlprof` for any file you save.
Launching `ProfileView.view(nothing)` opens a blank
window, which you can populate with saved data by clicking on the
"open" icon.

**NOTE**: ProfileView does not support the old JLD-based `*.jlprof` files anymore.
Use the format provided by FlameGraphs v0.2 and higher.

## Command-line options

The `view` command has the following syntax:
Expand Down
32 changes: 0 additions & 32 deletions README_save.md

This file was deleted.

175 changes: 96 additions & 79 deletions src/ProfileView.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,28 @@ You have several options to control the output, of which the major ones are:
See [FlameGraphs](https://github.com/timholy/FlameGraphs.jl) for more information.
"""
function view(fcolor, data::Vector{UInt64}=Profile.fetch(); lidict=nothing, C=false, combine=true, recur=:off, pruned=FlameGraphs.defaultpruned, kwargs...)
function view(fcolor, data::Vector{UInt64}; lidict=nothing, C=false, combine=true, recur=:off, pruned=FlameGraphs.defaultpruned, kwargs...)
g = flamegraph(data; lidict=lidict, C=C, combine=combine, recur=recur, pruned=pruned)
g === nothing && return nothing
return view(fcolor, g; kwargs...)
return view(fcolor, g; data=data, lidict=lidict, kwargs...)
end
function view(data::Vector{UInt64}=Profile.fetch(); kwargs...)
view(FlameGraphs.default_colors, data; kwargs...)
function view(fcolor; kwargs...)
data, lidict = Profile.retrieve()
view(fcolor, data; lidict=lidict, kwargs...)
end
function view(data::Vector{UInt64}; lidict=nothing, kwargs...)
view(FlameGraphs.default_colors, data; lidict=lidict, kwargs...)
end
function view(; kwargs...)
data, lidict = Profile.retrieve()
view(FlameGraphs.default_colors, data; lidict=lidict, kwargs...)
end

# This method allows user to open a *.jlprof file
view(::Nothing; kwargs...) = view(FlameGraphs.default_colors, Node(NodeData(StackTraces.UNKNOWN, 0, 1:0)); kwargs...)

function view(fcolor, g::Node{NodeData}; kwargs...)
function view(fcolor, g::Node{NodeData}; data=nothing, lidict=nothing, kwargs...)
gsig = Signal(g) # allow substitution by the open dialog
# Display in a window
c = canvas(UserUnit)
set_gtk_property!(widget(c), :expand, true)
Expand All @@ -83,8 +95,8 @@ function view(fcolor, g::Node{NodeData}; kwargs...)
push!(tb, tb_open)
push!(tb, tb_save_as)
# FIXME: likely have to do `allkwargs` in the two below (add in C, combine, recur)
signal_connect(open_cb, tb_open, "clicked", Nothing, (), false, (widget(c),kwargs))
signal_connect(save_as_cb, tb_save_as, "clicked", Nothing, (), false, (widget(c),g,kwargs))
signal_connect(open_cb, tb_open, "clicked", Nothing, (), false, (widget(c),gsig,kwargs))
signal_connect(save_as_cb, tb_save_as, "clicked", Nothing, (), false, (widget(c),data,lidict))
win = Window("Profile", 800, 600)
push!(win, bx)
GtkReactive.gc_preserve(win, c)
Expand All @@ -94,7 +106,8 @@ function view(fcolor, g::Node{NodeData}; kwargs...)
delete!(window_wrefs, win)
end

viewprof(fcolor, c, g; kwargs...)
fdraw = viewprof(fcolor, c, gsig; kwargs...)
GtkReactive.gc_preserve(win, fdraw)

# Ctrl-w and Ctrl-q destroy the window
signal_connect(win, "key-press-event") do w, evt
Expand All @@ -107,99 +120,103 @@ function view(fcolor, g::Node{NodeData}; kwargs...)
Gtk.showall(win)
end

function viewprof(fcolor, c, g; fontsize=14)
img = flamepixels(fcolor, g)
tagimg = flametags(g, img)
# The first column corresponds to the bottom row, which is our fake root node. Get rid of it.
img, tagimg = img[:,2:end], tagimg[:,2:end]
img24 = reverse(RGB24.(img), dims=2)
fv = XY(0.0..size(img24,1), 0.0..size(img24,2))
zr = Signal(ZoomRegion(fv, fv))
sigrb = init_zoom_rubberband(c, zr)
sigpd = init_pan_drag(c, zr)
sigzs = init_zoom_scroll(c, zr)
sigps = init_pan_scroll(c, zr)
surf = Cairo.CairoImageSurface(img24)
sigredraw = draw(c, zr) do widget, r
ctx = getgc(widget)
set_coordinates(ctx, r)
rectangle(ctx, BoundingBox(r.currentview))
set_source(ctx, surf)
p = Cairo.get_source(ctx)
Cairo.pattern_set_filter(p, Cairo.FILTER_NEAREST)
fill(ctx)
end
lasttextbb = BoundingBox(1,0,1,0)
sigmotion = map(c.mouse.motion) do btn
# Repair image from ovewritten text
if c.widget.is_realized && c.widget.is_sized
ctx = getgc(c)
if Graphics.width(lasttextbb) > 0
r = value(zr)
set_coordinates(ctx, r)
rectangle(ctx, lasttextbb)
set_source(ctx, surf)
p = Cairo.get_source(ctx)
Cairo.pattern_set_filter(p, Cairo.FILTER_NEAREST)
fill(ctx)
end
# Write the info
xu, yu = btn.position.x, btn.position.y
sf = gettag(xu, yu)
if sf != StackTraces.UNKNOWN
str = string(basename(string(sf.file)), ", ", sf.func, ": line ", sf.line)
set_source(ctx, fcolor(:font))
Cairo.set_font_face(ctx, "sans-serif $(fontsize)px")
xi = value(zr).currentview.x
xmin, xmax = minimum(xi), maximum(xi)
lasttextbb = deform(Cairo.text(ctx, xu, yu, str, halign = xu < (2xmin+xmax)/3 ? "left" : xu < (xmin+2xmax)/3 ? "center" : "right"), -2, 2, -2, 2)
end
reveal(c)
end
end
function viewprof(fcolor, c, gsig; fontsize=14)
# From a given position, find the underlying tag
function gettag(xu, yu)
function gettag(tagimg, xu, yu)
x = ceil(Int, Float64(xu))
y = ceil(Int, Float64(yu))
Y = size(tagimg, 2)
x = max(1, min(x, size(tagimg, 1)))
y = max(1, min(y, Y))
tagimg[x,Y-y+1]
end
# Left-click prints the full path, function, and line to the console
# Right-click calls the edit() function
sigshow = map(c.mouse.buttonpress) do btn
if btn.button == 1 || btn.button == 3
ctx = getgc(c)
xu, yu = btn.position.x, btn.position.y
sf = gettag(xu, yu)
if sf != StackTraces.UNKNOWN
if btn.button == 1
println(sf.file, ", ", sf.func, ": line ", sf.line)
elseif btn.button == 3
edit(string(sf.file), sf.line)
map(gsig) do g
isempty(g.data.span) && return nothing
img = flamepixels(fcolor, g)
tagimg = flametags(g, img)
# The first column corresponds to the bottom row, which is our fake root node. Get rid of it.
img, tagimg = img[:,2:end], tagimg[:,2:end]
img24 = reverse(RGB24.(img), dims=2)
fv = XY(0.0..size(img24,1), 0.0..size(img24,2))
zr = Signal(ZoomRegion(fv, fv))
sigrb = init_zoom_rubberband(c, zr)
sigpd = init_pan_drag(c, zr)
sigzs = init_zoom_scroll(c, zr)
sigps = init_pan_scroll(c, zr)
surf = Cairo.CairoImageSurface(img24)
sigredraw = draw(c, zr) do widget, r
ctx = getgc(widget)
set_coordinates(ctx, r)
rectangle(ctx, BoundingBox(r.currentview))
set_source(ctx, surf)
p = Cairo.get_source(ctx)
Cairo.pattern_set_filter(p, Cairo.FILTER_NEAREST)
fill(ctx)
end
lasttextbb = BoundingBox(1,0,1,0)
sigmotion = map(c.mouse.motion) do btn
# Repair image from ovewritten text
if c.widget.is_realized && c.widget.is_sized
ctx = getgc(c)
if Graphics.width(lasttextbb) > 0
r = value(zr)
set_coordinates(ctx, r)
rectangle(ctx, lasttextbb)
set_source(ctx, surf)
p = Cairo.get_source(ctx)
Cairo.pattern_set_filter(p, Cairo.FILTER_NEAREST)
fill(ctx)
end
# Write the info
xu, yu = btn.position.x, btn.position.y
sf = gettag(tagimg, xu, yu)
if sf != StackTraces.UNKNOWN
str = string(basename(string(sf.file)), ", ", sf.func, ": line ", sf.line)
set_source(ctx, fcolor(:font))
Cairo.set_font_face(ctx, "sans-serif $(fontsize)px")
xi = value(zr).currentview.x
xmin, xmax = minimum(xi), maximum(xi)
lasttextbb = deform(Cairo.text(ctx, xu, yu, str, halign = xu < (2xmin+xmax)/3 ? "left" : xu < (xmin+2xmax)/3 ? "center" : "right"), -2, 2, -2, 2)
end
reveal(c)
end
end
# Left-click prints the full path, function, and line to the console
# Right-click calls the edit() function
sigshow = map(c.mouse.buttonpress) do btn
if btn.button == 1 || btn.button == 3
ctx = getgc(c)
xu, yu = btn.position.x, btn.position.y
sf = gettag(tagimg, xu, yu)
if sf != StackTraces.UNKNOWN
if btn.button == 1
println(sf.file, ", ", sf.func, ": line ", sf.line)
elseif btn.button == 3
edit(string(sf.file), sf.line)
end
end
end
end
append!(c.preserved, [sigrb, sigpd, sigzs, sigps, sigredraw, sigmotion, sigshow])
return nothing
end
append!(c.preserved, [sigrb, sigpd, sigzs, sigps, sigredraw, sigmotion, sigshow])
return nothing
end

@guarded function open_cb(::Ptr, settings::Tuple)
c, kwargs = settings
c, gsig, kwargs = settings
selection = open_dialog("Load profile data", toplevel(c), ("*.jlprof","*"))
isempty(selection) && return nothing
data, lidict = load(File(format"JLD", selection), "li", "lidict")
return view(data; lidict=lidict, kwargs...)
data, lidict = load(selection)
push!(gsig, flamegraph(data; lidict=lidict, kwargs...))
return nothing
end

@guarded function save_as_cb(::Ptr, profdata::Tuple)
c, data, lidict = profdata
selection = save_dialog("Save profile data as JLD file", toplevel(c), ("*.jlprof",))
selection = save_dialog("Save profile data as *.jlprof file", toplevel(c), ("*.jlprof",))
isempty(selection) && return nothing
FileIO.save(File(format"JLD", selection), "li", data, "lidict", lidict)
nothing
FileIO.save(File(format"JLPROF", selection), data, lidict)
return nothing
end

# include("precompile.jl")
Expand Down

0 comments on commit f48c65f

Please sign in to comment.