Skip to content

Commit

Permalink
Add an info popup and zoom buttons (#188)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Holy <[email protected]>
  • Loading branch information
IanButterworth and timholy authored Jan 16, 2022
1 parent c048a49 commit 4014d38
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 7 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ at the REPL. You'll see the result of Julia's `code_warntype` for that particula
which can help avoid confusion when opening several ProfileView windows
simultaneously.

- On Julia 1.8 ProfileView.view(expand_tasks=true) creates one tab per task.
Expanding by thread is on by default and can be disabled with `expand_threads=false`.

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

Expand Down
84 changes: 77 additions & 7 deletions src/ProfileView.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ macro profview(ex)
return quote
# disable the eventloop while profiling
# it will be restarted upon show
Gtk.enable_eventloop(false)
Gtk.auto_idle[] && Gtk.enable_eventloop(false)
Profile.clear()
@profile $(esc(ex))
view()
Expand Down Expand Up @@ -127,7 +127,7 @@ function view(data::Vector{UInt64}; lidict=nothing, kwargs...)
view(FlameGraphs.default_colors, data; lidict=lidict, kwargs...)
end
function view(; kwargs...)
Gtk.enable_eventloop(false)
Gtk.auto_idle[] && Gtk.enable_eventloop(false)
data, lidict = Profile.retrieve()
view(FlameGraphs.default_colors, data; lidict=lidict, kwargs...)
end
Expand Down Expand Up @@ -162,6 +162,7 @@ function viewgui(fcolor, gdict::NestedGraphDict; data=nothing, lidict=nothing, w
Gtk.GAccessor.scrollable(nb_threads, true)
Gtk.GAccessor.show_tabs(nb_threads, length(thread_tabs) > 1)
sort!(thread_tabs, by = s -> something(tryparse(Int, string(s)), 0)) # sorts thread_tabs as [all threads, 1, 2, 3 ....]

for thread_tab in thread_tabs
gdict_thread = gdict[thread_tab]
task_tabs = collect(keys(gdict_thread))
Expand All @@ -176,21 +177,41 @@ function viewgui(fcolor, gdict::NestedGraphDict; data=nothing, lidict=nothing, w
gsig = Observable(g) # allow substitution by the open dialog
c = canvas(UserUnit)
set_gtk_property!(widget(c), :expand, true)

f = Frame(c)
tb = Toolbar()
tb_open = ToolButton("gtk-open")
Gtk.GAccessor.tooltip_text(tb_open, "open")
tb_save_as = ToolButton("gtk-save-as")
Gtk.GAccessor.tooltip_text(tb_save_as, "save")
tb_zoom_fit = ToolButton("gtk-zoom-fit")
Gtk.GAccessor.tooltip_text(tb_zoom_fit, "zoom to fit")
tb_zoom_in = ToolButton("gtk-zoom-in")
Gtk.GAccessor.tooltip_text(tb_zoom_in, "zoom in")
tb_zoom_out = ToolButton("gtk-zoom-out")
Gtk.GAccessor.tooltip_text(tb_zoom_out, "zoom out")
tb_info = ToolButton("gtk-info")
Gtk.GAccessor.tooltip_text(tb_info, "ProfileView tips")

push!(tb, tb_open)
push!(tb, tb_save_as)
# FIXME: likely have to do `allkwargs` in the two below (add in C, combine, recur)
push!(tb, SeparatorToolItem())
push!(tb, tb_zoom_fit)
push!(tb, tb_zoom_out)
push!(tb, tb_zoom_in)
push!(tb, SeparatorToolItem())
push!(tb, tb_info)
# FIXME: likely have to do `allkwargs` in the open/save below (add in C, combine, recur)
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,g))
signal_connect(info_cb, tb_info, "clicked", Nothing, (), false, ())

bx = Box(:v)
push!(bx, tb)
push!(bx, f)
# don't use the actual taskid as the tab as it's very long
push!(nb_tasks, bx, task_tab_num == 1 ? task_tab : Symbol(task_tab_num - 1))
fdraw = viewprof(fcolor, c, gsig; kwargs...)
fdraw = viewprof(fcolor, c, gsig, (tb_zoom_fit, tb_zoom_out, tb_zoom_in); kwargs...)
GtkObservables.gc_preserve(nb_threads, c)
GtkObservables.gc_preserve(nb_threads, fdraw)
_c, _fdraw, _tb_open, _tb_save_as = c, fdraw, tb_open, tb_save_as
Expand Down Expand Up @@ -223,15 +244,16 @@ function viewgui(fcolor, gdict::NestedGraphDict; data=nothing, lidict=nothing, w
return win, _c, _fdraw, (_tb_open, _tb_save_as)
end

function viewprof(fcolor, c, gsig; fontsize=14)
function viewprof(fcolor, c, gsig, zoom_buttons; fontsize=14)
obs = on(gsig) do g
viewprof_func(fcolor, c, g, fontsize)
viewprof_func(fcolor, c, g, fontsize, zoom_buttons)
end
gsig[] = gsig[]
return obs
end

function viewprof_func(fcolor, c, g, fontsize)
function viewprof_func(fcolor, c, g, fontsize, zoom_buttons)
tb_zoom_fit, tb_zoom_out, tb_zoom_in = zoom_buttons
# From a given position, find the underlying tag
function gettag(tagimg, xu, yu)
x = ceil(Int, Float64(xu))
Expand All @@ -250,6 +272,9 @@ function viewprof_func(fcolor, c, g, fontsize)
img24 = img24[:,end:-1:1]
fv = XY(0.0..size(img24,1), 0.0..size(img24,2))
zr = Observable(ZoomRegion(fv, fv))
signal_connect(zoom_fit_cb, tb_zoom_fit, "clicked", Nothing, (), false, (zr))
signal_connect(zoom_out_cb, tb_zoom_out, "clicked", Nothing, (), false, (zr))
signal_connect(zoom_in_cb, tb_zoom_in, "clicked", Nothing, (), false, (zr))
sigrb = init_zoom_rubberband(c, zr)
sigpd = init_pan_drag(c, zr)
sigzs = init_zoom_scroll(c, zr)
Expand Down Expand Up @@ -353,6 +378,51 @@ function _save(selection, args...)
return nothing
end

@guarded function zoom_fit_cb(::Ptr, zr::Observable{ZoomRegion{T}}) where {T}
zr[] = GtkObservables.reset(zr[])
return nothing
end

@guarded function zoom_in_cb(::Ptr, zr::Observable{ZoomRegion{T}}) where {T}
setindex!(zr, zoom(zr[], 1/2))
return nothing
end

@guarded function zoom_out_cb(::Ptr, zr::Observable{ZoomRegion{T}}) where {T}
setindex!(zr, zoom(zr[], 2))
return nothing
end

@guarded function info_cb(::Ptr, ::Tuple)
# Note: Keep this updated with the readme
info = """
ProfileView.jl Interface Tips
----------------------------------------------
`Ctrl-q` and `Ctrl-w` close the window. You can also use `ProfileView.closeall()` to close all windows opened by ProfileView.
Left-clicking on a bar will cause information about this line to be printed in the REPL. This can be a convenient way to "mark" lines for later investigation.
Right-clicking on a bar calls the `edit()` function to open the line in an editor. (On a trackpad, use a 2-fingered tap.)
CTRL-clicking and dragging will zoom in on a specific region of the image. You can also control the zoom level with CTRL-scroll (or CTRL-swipe up/down).
CTRL-double-click to restore the full view.
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 includes icons to load and save profile data. 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.
After clicking on a bar, you can type `warntype_last()` and see the result of `code_warntype` for the call represented by that bar.
`ProfileView.view(windowname="method1")` allows you to name your window, which can help avoid confusion when opening several ProfileView windows simultaneously.
On Julia 1.8 `ProfileView.view(expand_tasks=true)` creates one tab per task. Expanding by thread is on by default and can be disabled with `expand_threads=false`.
"""
info_dialog(info)
return nothing
end

discardfirstcol(A) = A[:,2:end]
discardfirstcol(A::IndirectArray) = IndirectArray(A.index[:,2:end], A.values)

Expand Down

0 comments on commit 4014d38

Please sign in to comment.