Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CImGui 3.0 segfault when plotting empty axis in Julia module #146

Closed
steffenhaug opened this issue Sep 11, 2024 · 4 comments
Closed

CImGui 3.0 segfault when plotting empty axis in Julia module #146

steffenhaug opened this issue Sep 11, 2024 · 4 comments

Comments

@steffenhaug
Copy link

This is a little bit of a weird one, but I have been able to reproduce it on Linux and macOS. It affects current master (3.0) using GLMakie 0.10.9 as well as GLMakie master.

If I'm plotting an empty axis in a function in a module, and call that function from outside the module module, I hit a segfault in Makies text layouting. In particular, this means it segfaults when compiled with PackageCompiler.jl, which is unfortunate.

Weirdly enough, if I instead call main() at the bottom of CImGuiRepro, the GUI launches and works fine, but only while compiling. Weirder again, if i call main() from CImGuiRepro.__init__, it segfaults again.

module CImGuiRepro

using GLMakie
import GLFW
import CImGui as ImGui
using CImGui.lib

fig = Figure()
ax = Axis(fig[1, 1])

function Plot()
  ImGui.Begin("Plot Window")
  ImGui.MakieFigure(
    "plot",
    fig;
    auto_resize_x=true,
    auto_resize_y=true,
    tooltip=false,
  )
  ImGui.End()
end

# PackageCompiler.jl entry point
function main()::Cint
  ImGui.set_backend(:GlfwOpenGL3)
  ctx = ImGui.CreateContext()

  io::Ptr{ImGuiIO} = ImGui.GetIO()
  io.IniFilename = C_NULL

  ImGui.render(
    Plot,
    ctx;
    window_size=(1200, 800),
    window_title="Makie Embed Repro",
    opengl_version=v"3.3",
  )

  return 0
end

end # module CImGuiRepro

Loading CImGuiRepro and calling main() leads to the following trace:

st@st-air CImGuiRepro % julia --project -e 'using CImGuiRepro; CImGuiRepro.main()'

[89418] signal (11.2): Segmentation fault: 11
in expression starting at none:1
unsafe_load at ./pointer.jl:119 [inlined]
unsafe_load at ./pointer.jl:119 [inlined]
unsafe_load at /Users/st/.julia/packages/FreeType/ATaWH/src/FreeType.jl:532 [inlined]
macro expansion at /Users/st/.julia/packages/FreeTypeAbstraction/wE79y/src/types.jl:163 [inlined]
macro expansion at ./lock.jl:267 [inlined]
getproperty at /Users/st/.julia/packages/FreeTypeAbstraction/wE79y/src/types.jl:162
family_name at /Users/st/.julia/packages/FreeTypeAbstraction/wE79y/src/findfonts.jl:45 [inlined]
fontname at /Users/st/.julia/packages/FreeTypeAbstraction/wE79y/src/findfonts.jl:122 [inlined]
insert_glyph! at /Users/st/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:293
glyph_index! at /Users/st/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:276
glyph_uv_width! at /Users/st/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:284 [inlined]
text_quads at /Users/st/.julia/packages/Makie/8h0bl/src/layouting/text_layouting.jl:345
#273 at /Users/st/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:583
jfptr_YY.273_56403 at /Users/st/.julia/compiled/v1.10/GLMakie/nfnZR_D73mh.dylib (unknown line)

... (a lot of lines omitted)

Adding anything to the axis prevents this. Even something silly like

fig = Figure()
ax = Axis(fig[1, 1])
lines!(ax, [0], [0])

Even though nothing is actually displayed, because there are not enough points to make a single line segment!

I thought maybe it was related to the automatic limits breaking down somehow when the plot is empty, but even manually setting the limits leads to a segfault:

fig = Figure()
ax = Axis(fig[1, 1], limits=(0, 1, 0, 1))
# axis left empty

I don't really know where to start looking for the problem, as the conditions to trigger it are so specific, and I don't have a very good understanding of Makie internals. I'm not sure if this is technically a Makie bug, or if there is some kind of setup we need to do manually when embedding Makie.

@JamesWrigley
Copy link
Member

The problem is likely the fact that fig is a global variable, creating it inside Plot() works:

module CImGuiRepro

using GLMakie
import GLFW
import CImGui as ImGui
using CImGui.lib

fig = nothing

function Plot()
    global fig
    if isnothing(fig)
        fig = Figure()
        ax = Axis(fig[1, 1])
    end

    ImGui.Begin("Plot Window")
    ImGui.MakieFigure(
        "plot",
        fig;
        auto_resize_x=true,
        auto_resize_y=true,
        tooltip=false,
    )
    ImGui.End()
end

# PackageCompiler.jl entry point
function main()::Cint
    ImGui.set_backend(:GlfwOpenGL3)
    ctx = ImGui.CreateContext()

    io::Ptr{ImGuiIO} = ImGui.GetIO()
    io.IniFilename = C_NULL

    ImGui.render(
        Plot,
        ctx;
        window_size=(1200, 800),
        window_title="Makie Embed Repro",
        opengl_version=v"3.3",
    )

    return 0
end

end # module CImGuiRepro

I'm guessing it's a similar issue to this: JuliaInterop/ZMQ.jl#241
i.e. a library initializes a global variable with a pointer during precompilation and it's no longer valid in subsequent sessions. I don't know why it's only happening with CImGui + Makie though, Makie's precompilation does similar things and that seems to work. @SimonDanisch, do you know what might be going wrong? Maybe we need to teardown Makie/Freetype or something?

@JamesWrigley
Copy link
Member

FWIW, this is the full stacktrace from my machine:

Stacktrace
$ julia --project=. -e 'using Scratch; Scratch.main()'        

[2618443] signal 11 (1): Segmentation fault
in expression starting at none:1
__memmove_avx_unaligned_erms at /lib64/libc.so.6 (unknown line)
macro expansion at /home/james/.julia/packages/FreeTypeAbstraction/wE79y/src/types.jl:164 [inlined]
macro expansion at ./lock.jl:273 [inlined]
getproperty at /home/james/.julia/packages/FreeTypeAbstraction/wE79y/src/types.jl:162
family_name at /home/james/.julia/packages/FreeTypeAbstraction/wE79y/src/findfonts.jl:45 [inlined]
fontname at /home/james/.julia/packages/FreeTypeAbstraction/wE79y/src/findfonts.jl:122 [inlined]
insert_glyph! at /home/james/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:293
glyph_index! at /home/james/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:276 [inlined]
glyph_uv_width! at /home/james/.julia/packages/Makie/8h0bl/src/utilities/texture_atlas.jl:284 [inlined]
text_quads at /home/james/.julia/packages/Makie/8h0bl/src/layouting/text_layouting.jl:345
#273 at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:583
unknown function (ip: 0x7f8d3d579e34)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
do_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/builtins.c:831
#map#236 at /home/james/.julia/packages/Makie/8h0bl/src/scenes.jl:167
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
do_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/builtins.c:831
map at /home/james/.julia/packages/Makie/8h0bl/src/scenes.jl:164
#272 at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:582
#210 at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:337
get! at ./dict.jl:458
cached_robj! at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:235
draw_atomic at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:572
insert! at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:355
#230 at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:360
unknown function (ip: 0x7f8d3d562ff2)
foreach at ./abstractarray.jl:3190
unknown function (ip: 0x7f8d3d53fe86)
insert! at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:357
#230 at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:360
unknown function (ip: 0x7f8d3d578532)
foreach at ./abstractarray.jl:3190
unknown function (ip: 0x7f8d3d53fe86)
insert! at /home/james/.julia/packages/GLMakie/GK1PF/src/drawing_primitives.jl:357
insertplots! at /home/james/.julia/packages/GLMakie/GK1PF/src/screen.jl:515
insertplots! at /home/james/.julia/packages/GLMakie/GK1PF/src/screen.jl:518
display_scene! at /home/james/.julia/packages/GLMakie/GK1PF/src/screen.jl:442
#display#351 at /home/james/.julia/packages/GLMakie/GK1PF/src/display.jl:9
display at /home/james/.julia/packages/GLMakie/GK1PF/src/display.jl:1 [inlined]
#display#1370 at /home/james/.julia/packages/Makie/8h0bl/src/display.jl:180 [inlined]
display at /home/james/.julia/packages/Makie/8h0bl/src/display.jl:177
unknown function (ip: 0x7f8d3d53f4c9)
#MakieFigure#1 at /home/james/.julia/dev/CImGui/ext/MakieIntegration.jl:130
MakieFigure at /home/james/.julia/dev/CImGui/ext/MakieIntegration.jl:116 [inlined]
Plot at /home/james/git/scratch/src/Scratch.jl:13
unknown function (ip: 0x7f8d3d52b7ff)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
jl_f__call_latest at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/builtins.c:875
#invokelatest#2 at ./essentials.jl:1054 [inlined]
invokelatest at ./essentials.jl:1051 [inlined]
#renderloop#3 at /home/james/git/CImGui.jl/ext/GlfwOpenGLBackend.jl:107
renderloop at /home/james/git/CImGui.jl/ext/GlfwOpenGLBackend.jl:54 [inlined]
#5 at /home/james/git/CImGui.jl/ext/GlfwOpenGLBackend.jl:176
unknown function (ip: 0x7f8d3d52eadf)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
start_task at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/task.c:1202
Allocations: 17411464 (Pool: 17410609; Big: 855); GC: 16
[1]    2618443 segmentation fault (core dumped)  julia --project=. -e 'using Scratch; Scratch.main()'

@steffenhaug
Copy link
Author

steffenhaug commented Sep 11, 2024

The problem is likely the fact that fig is a global variable, creating it inside Plot() works

Thanks! That's good advice; storing the plot globally is a silly thing to do anyways, when my program has a proper entry point. I should just set up the plots in main. I don't understand why when you add a lines! plot to the global figure that makes it stay valid though... Maybe that's just a coincidence.

I'm guessing it's a similar issue to this: JuliaInterop/ZMQ.jl#241 i.e. a library initializes a global variable with a pointer during precompilation and it's no longer valid in subsequent sessions.

That sounds very likely.

I don't know why it's only happening with CImGui + Makie though

FWIW I first encountered this segfault while trying update QMLMakie to support modern Makie versions, more or less following your implementation of Makie embedding in CImGui, and the docs you wrote in Makie about how to do it. Only after trying to port my debugging demo to CImGui did I notice that I got the exact same segfault here.

@JamesWrigley
Copy link
Member

Closing this because precompiling figures is unsafe and unlikely to be supported anytime soon: MakieOrg/Makie.jl#4571 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants