Problem debugging dll code I made for love2d projects

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
skaruts
Prole
Posts: 6
Joined: Tue Jun 12, 2018 11:20 pm

Problem debugging dll code I made for love2d projects

Post by skaruts »

(This is a cross-post from r/love2d)

I've written some native code for two of my projects (using the Nim bindings to Lua's C API), and I compiled it into dll files which get required from my Lua code. But I always have this issue that I can't easily debug it.

Whenever there's an error on the native code and it crashes my project, the error doesn't get printed out to the console. It's like the stdout that Love2d/lua use to print out errors isn't accessible to Nim (and I presume the same would happen if I was using C). So I'm quite in the dark while writing my own dynamic libraries.

I also can't print anything to the console from Nim code. I had to invoke Lua's print function through the C-API in order to work around that, but I can't work around not seeing errors.

I don't know if this is something that happens in C as well, or if it's somehow related to Nim. To be clear, I run my love2d projects from the command line, and I can see love/lua errors there just fine.

Does anyone know some way to get around this problem?


Just for completion, this is the nim code I'm using as base for my dlls:

Code: Select all

# lua's print function
proc print*[T](L:PState, args:varargs[T, `$`]) =
    L.getglobal("print")
    for i in 0..<len(args):
        L.pushstring(args[i])
    L.call(len(args), 0)


proc test(L:PState): cint {.cdecl.} =
    L.print("Nim test works")


# this gets called automatically during 'require'
# luaopen_ + libname (or else: ERROR)
proc luaopen_skfs(L:PState):cint {.cdecl, exportc, dynlib.} =
    var fns = [
        reg_func("test", test),
        reg_func()  # last empty (must be here)
    ]
    L.register("skfs", addr(fns[0]))
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: Problem debugging dll code I made for love2d projects

Post by ReFreezed »

Is there a way to manually change where Nim prints/echoes stuff? (I assume not, but otherwise you should be able to call GetStdHandle to get the correct file handle.) Are you using lovec.exe (instead of love.exe)? Can you share a project (with the DLL) where printing isn't working?
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
yal2du
Prole
Posts: 42
Joined: Wed Oct 13, 2021 5:41 pm

Re: Problem debugging dll code I made for love2d projects

Post by yal2du »

skaruts wrote: Sat Feb 19, 2022 3:11 pm I've written some native code for two of my projects (using the Nim bindings to Lua's C API), and I compiled it into dll files...

Whenever there's an error on the native code and it crashes my project, the error doesn't get printed out to the console. It's like the stdout that Love2d/lua use to print out errors isn't accessible to Nim (and I presume the same would happen if I was using C). So I'm quite in the dark while writing my own dynamic libraries.
I don't know anything about Nim but it looks like you can compile your .dll's with --debugger:native and run your program under gdb or the like to set breakpoints to see what is happening.

https://nim-lang.org/blog/2017/10/02/do ... g-gdb-lldb
skaruts
Prole
Posts: 6
Joined: Tue Jun 12, 2018 11:20 pm

Re: Problem debugging dll code I made for love2d projects

Post by skaruts »

Thanks for your answers. Both are actually helping.
ReFreezed wrote: Sat Feb 19, 2022 4:21 pm Is there a way to manually change where Nim prints/echoes stuff? (I assume not, but otherwise you should be able to call GetStdHandle to get the correct file handle.) Are you using lovec.exe (instead of love.exe)? Can you share a project (with the DLL) where printing isn't working?
At this point I forgot what the difference is between lovec.exe and love.exe, but using lovec.exe actually allows output from Nim. So that's a really good start. :)

The crashing error is still not showing up, though, so maybe something is missing. But...
yal2du wrote: Mon Feb 21, 2022 12:25 am I don't know anything about Nim but it looks like you can compile your .dll's with --debugger:native and run your program under gdb or the like to set breakpoints to see what is happening.
This seems promising. I actually didn't know about gdb (it's been a while since I read the docs fully, and I skipped this stuff). I'm still not being able to find the exact problem, but I'll tinker with it some more. I got a segmentation fault, but it's in Nim's standard library and the backtrace isn't going back far enough. But at least it's giving me some direction. Perhaps I have something to ask about in the Nim discord.
ReFreezed wrote: Sat Feb 19, 2022 4:21 pmCan you share a project (with the DLL) where printing isn't working?
I thought of that before, but my project is kinda big and messy at this point. I thought of trying to recreate the issue in a bare bones project, but to be honest, I'm a bit too burned out for that... :\
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Problem debugging dll code I made for love2d projects

Post by pgimeno »

skaruts wrote: Thu Feb 24, 2022 12:46 am This seems promising. I actually didn't know about gdb (it's been a while since I read the docs fully, and I skipped this stuff). I'm still not being able to find the exact problem, but I'll tinker with it some more. I got a segmentation fault, but it's in Nim's standard library and the backtrace isn't going back far enough. But at least it's giving me some direction. Perhaps I have something to ask about in the Nim discord.
Yeah, I don't know why, but some segfaults are silent instead of being reported as such. I've seen that frequently with Löve; not sure if it's muting the signal or what.

Can you post the backtrace?

skaruts wrote: Thu Feb 24, 2022 12:46 am I thought of that before, but my project is kinda big and messy at this point. I thought of trying to recreate the issue in a bare bones project, but to be honest, I'm a bit too burned out for that... :\
A bare bones Lua program that loads the DLL and calls the Nim function should be very short and easy, so it's a safe bet. If that doesn't reproduce the problem, you can give up on providing a test case.

I'm beginning to suspect that somehow the Nim library is not being initialized. Maybe ask if someone has experience writing a DLL in Nim that calls library functions.
User avatar
ReFreezed
Party member
Posts: 612
Joined: Sun Oct 25, 2015 11:32 pm
Location: Sweden
Contact:

Re: Problem debugging dll code I made for love2d projects

Post by ReFreezed »

skaruts wrote: Thu Feb 24, 2022 12:46 am At this point I forgot what the difference is between lovec.exe and love.exe, but using lovec.exe actually allows output from Nim. So that's a really good start. :)
love.exe is compiled as a windows/GUI application, while lovec.exe is compiled as a console application (which means the operating system automatically provides console functionality at startup). lovec.exe should be used whenever you run LÖVE from the command line.

(It's quite silly indeed that Windows works this way. Good old Microsoft making things more complicated than they need to be.)
Tools: Hot Particles, LuaPreprocess, InputField, (more) Games: Momento Temporis
"If each mistake being made is a new one, then progress is being made."
skaruts
Prole
Posts: 6
Joined: Tue Jun 12, 2018 11:20 pm

Re: Problem debugging dll code I made for love2d projects

Post by skaruts »

pgimeno wrote: Thu Feb 24, 2022 12:17 pmI'm beginning to suspect that somehow the Nim library is not being initialized. Maybe ask if someone has experience writing a DLL in Nim that calls library functions.
Well, the crash happens while using the lib, so I don't think there's a problem with loading it. And it doesn't always happen in the same way. I'll explain it better below.
pgimeno wrote: Thu Feb 24, 2022 12:17 pm A bare bones Lua program that loads the DLL and calls the Nim function should be very short and easy, so it's a safe bet. If that doesn't reproduce the problem, you can give up on providing a test case.
The problem is with recreating the crash in a simple project. To clarify, this is for a file-explorer to load images from within my love2d app, which is a font editor for text-based roguelikes (made also with my own two libs (fully in lua), one for a terminal emulator and another for an IM-GUI, and that's why it's big).

What the nim code does so far is just gathering a list of the names of directories and files in the current folder and return it in a table, sorted alphabetically and with the directory names before file names (and a ".." before everything, if the current directory isn't the root). On the lua side I keep the string for the current folder and that list of names, with which I make a list of UI buttons with those names, for navigating the explorer. (The Nim code also changes folders.)

But the crash doesn't happen right away. It always successfully lists files/folders the first time, and I can navigate into other folders. Sometimes when I navigate back out of some folder it crashes. Usually I can reproduce the crash by simply going in and out of my project's data folder, but still not always. (It does seem like it's when I navigate back, though. I'll have to double-check this, I just noticed it now.)

So it seems that to reproduce this I'd have to sort of replicate the basic behavior of my file explorer somehow on the lua side, in order to go back and forth until it crashes. I can't think of an easy way, though.
pgimeno wrote: Thu Feb 24, 2022 12:17 pm Can you post the backtrace?
Sure. For some reason the backtrace is actually pointing to my code now, which is nice. But it's not always the same line. I'll also post the Nim code below, cleaned up (it uses nim lua bindings).

Code: Select all

Thread 1 received signal SIGSEGV, Segmentation fault.
0x000000006788b45d in rawAlloc__system_4643 (a=a@entry=0x678ad6c8 <gch.system_5301+104>, requestedSize=requestedSize@entry=41)
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/alloc.nim:788
788             c.freeList = c.freeList.next
(gdb) bt
#0  0x000000006788b45d in rawAlloc__system_4643 (a=a@entry=0x678ad6c8 <gch.system_5301+104>, requestedSize=requestedSize@entry=41)
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/alloc.nim:788
#1  0x0000000067892dde in newObjRC1 (typ=typ@entry=0x678a5000 <strDesc__system_2412>, size=size@entry=25)
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/gc.nim:508
#2  0x0000000067892fbc in copyStringRC1 (src=src@entry=0x2b1d740)
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/sysstr.nim:134
#3  0x00000000678a1e1c in list_dir__skfs_21 (L=0x0)
    at I:/Programming/Lua/LOVE/roguelikes/_sillylib-lua/projects/font_editor/src/skfs.nim:72
#4  0x000007fee3f027a3 in ?? () from d:\apps\love\lua51.dll
#5  0x000000013f791910 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
In this backtrace it points to line 72 in my code (skfs.nim). But sometimes it's line 60. Both lines are in the first for loop (I annotated both in comments -- in the original code they're further apart because of debug prints and stuff).

The crash always happens after the code has successfully stored some file/folder names, somewhere half-way through the for loop. So it doesn't really seem like the problem is related to an invalid path or filename, but I could be wrong. It also doesn't seem related to any particular file type. It has happened with different ones.

Code: Select all

# skfs.nim
from os import nil
from algorithm as algo import nil
import luaw # a few simple abstractions I made (lua's c-api gets imported with it)

proc change_dir(L:PState):cint {.cdecl.} =
    let cur_dir:string = L.checkstring(1) # current directory
    let rel_dir:string = L.checkstring(2) # target directory (relative to current, not full path, should have no slashes)

    var new_dir:string
    if rel_dir == os.ParDir:
        new_dir = os.parent_dir(cur_dir)
    else:
        new_dir = os.join_path(cur_dir, rel_dir)

    L.pushstring(new_dir) # return new full path string (the lua code uses this string to call 'list_dir')
    return 1


proc string_sort(a, b: string):int = # used in 'list_dir' below
  if a < b: -1 else: 1


proc list_dir(L:PState):cint {.cdecl.} =
    let curr_dir = L.checkstring(1)
    
    L.newtable()                            # stack : [list]
    if not os.dir_exists(curr_dir): return 1

    os.set_current_dir(curr_dir)  # this seems necessary, couldn't list files without it

    var dirs:seq[string]
    var files:seq[string]

    if not os.is_root_dir(curr_dir): # if this isn't the root, add a ".." item to the list
        dirs.add(os.ParDir)

    for k, v in os.walk_dir(curr_dir):
        var name = os.extract_filename(v)                   # <-- line 60
        
        if   k == os.PathComponent.pcDir:  
            dirs.add(name)
        elif k == os.PathComponent.pcFile: 
            files.add(name)                                 # <-- line 72 
    
    # if len(dirs) > 0: algo.sort(dirs, string_sort)        # <-- line 85 (see below) 
    # if len(files) > 0: algo.sort(files, string_sort)
    
    # 'setfield' are a few helper procs that I made to help populate tables
    for i in 0 ..< len(dirs):
        # stack : [list]
        L.pushnumber(i+1)      # stack : [list, i]
        L.newtable()           # stack : [list, i, t]
        L.setfield("type", "dir")
        L.setfield("name", dirs[i])
        L.settable(-3)         # stack : [list]

    for i in 0 ..< len(files):
        # stack : [list]
        L.pushnumber(len(dirs)+i+1)      # stack : [list, i]
        L.newtable()  # stack : [list, i, t]
        L.setfield("type", "file")
        L.setfield("name", files[i])
        L.settable(-3)         # stack : [list]

    return 1


proc luaopen_skfs(L:PState):cint {.cdecl, exportc, dynlib.} =
    var fns = [
        reg_func("list_dir",   list_dir),
        reg_func("change_dir", change_dir),
        reg_func()  # last empty (must be here)
    ]
    L.register("skfs", addr(fns[0]))
Also, the lines where the names are sorted are commented out for now (right after the first for loop), because if I leave them in, then the crash seems to tend to happen there, instead of in the for loop.

With the sorting lines in, the backtrace is this:

Code: Select all

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000678a17f2 in cmpStrings (b=0x10, a=0x10) at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/strmantle.nim:16
16        let alen = a.len
(gdb) bt
#0  0x00000000678a17f2 in cmpStrings (b=0x10, a=0x10) at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/system/strmantle.nim:16
#1  string_sort__skfs_10 (a=0x10, b=0x10) at I:/Programming/Lua/LOVE/roguelikes/_sillylib-lua/projects/font_editor/src/skfs.nim:17
#2  0x00000000678971b8 in mergeAlt__skfs_95 (a=a@entry=0x7431060, aLen_0=aLen_0@entry=18, b=b@entry=0x742fa00, bLen_0=9, lo=lo@entry=14,
    m=m@entry=14, hi=hi@entry=15, cmp=..., order=order@entry=1 '\001')
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/pure/algorithm.nim:331
#3  0x00000000678979f4 in sort__skfs_78 (a=0x7431060, a@entry=0x10, aLen_0=18, cmp=..., order=order@entry=1 '\001')
    at C:/Users/Skaruts/.choosenim/toolchains/nim-1.6.4/lib/pure/algorithm.nim:413
#4  0x00000000678a255f in list_dir__skfs_21 (L=0x0)
    at I:/Programming/Lua/LOVE/roguelikes/_sillylib-lua/projects/font_editor/src/skfs.nim:85
#5  0x000007fee3a827a3 in ?? () from d:\apps\love\lua51.dll
#6  0x000000013fe51910 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

Line 85 is the first sorting line.

Even more weirdly, though, just now I had it crash with no backtrace (well, exiting, it seems), and "virtualFree failing!" is all it says about it. I have no idea what that is. (This was also using the sorting code.)

Code: Select all

virtualFree failing![Thread 4464.0xfec exited with code 1]
[Thread 4464.0x918 exited with code 1]
[Thread 4464.0x15fc exited with code 1]
[Thread 4464.0x1364 exited with code 1]
[Thread 4464.0x1018 exited with code 1]
[Thread 4464.0xf7c exited with code 1]
[Thread 4464.0x17a4 exited with code 1]
[Thread 4464.0x1458 exited with code 1]
[Thread 4464.0x1034 exited with code 1]
[Thread 4464.0xfe4 exited with code 1]
[Inferior 1 (process 4464) exited with code 01]
(gdb) bt
No stack.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Problem debugging dll code I made for love2d projects

Post by pgimeno »

Hm, I don't know Nim but what C compiler is it using under the hood? If it's not the one used to compile Löve, there might be problems.

Also, the intermittent reproducibility, the different locations and the backtrace suggest that the damage is already done by the time it crashes, probably depending on memory addresses and other random stuff. My guess is problems with allocation.
skaruts
Prole
Posts: 6
Joined: Tue Jun 12, 2018 11:20 pm

Re: Problem debugging dll code I made for love2d projects

Post by skaruts »

I also noticed some mentions of "gc" in the backtraces, and it does kinda seem like the problem is with the garbage collector.

I compiled the lib with --gc:markAndSweep (docs), which makes thread-local heaps, and seems like it's not crashing anymore.

I had tested the code yesterday as a standalone executable and it wasn't crashing ever, so that told me the code was probably fine.

Well, if no more crashes happen, I think the problem is solved.
Thank you all for the help. :)
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 9 guests