Hey, I just started out with Nim, and I'm currently working on my 2nd practice project. It renders a few points and lines between them based on a text that is parsed.
After a short while of playing around with the Program it will crash. The crashes are not deterministically reproducible. A text that will crash the program in one go wont in another. Non the less it never took me more than 1-2 min of playing around to produce a crash.
There are multiple ways how it crashes, and I gathered a few stack traces, and the corresponding code. If you don't see any obvious problems with the following example, I'll gladly provide the complete program (which currently only has 236 lines of code).
Btw, I'm using Nim 1.4 on Ubuntu 20.04 with GCC 9.3. Compilation via:
nim c -r session_planner.nim
I will just name the crashes from A to E:
Traceback (most recent call last)
/home/felix/git/session_planner/session_planner.nim(116) session_planner
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1120) run
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(358)
runMainLoop
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(194)
pWindowKeyPressSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(675) handleKeyDownEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1508) handleKeyDownEvent
/home/felix/git/session_planner/session_planner.nim(111) :anonymous
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(399)
alert
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(922)
pControlDrawSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(815) handleDrawEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1865) handleDrawEvent
/home/felix/git/session_planner/session_planner.nim(97) :anonymous
/home/felix/git/session_planner/session_planner.nim(51) twoElemSubSets
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(447) newSeq
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(439) newObj
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/alloc.nim(787) rawAlloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed:
'/home/felix/git/session_planner/session_planner '
func twoElemSubSets[T](s: Popable[T]): seq[(T, T)]=
if s.len < 2:
return
var s = s
while s.len > 2:
let cur = s.pop
for it in s:
result.add((cur, it))
result.add((s.pop, s.pop)) # <-- here
This one might have been compiled to cpp, not sure anymore
traceback (most recent call last)
/home/felix/git/session_planner/session_planner.nim(116) session_planner
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1120) run
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(358) runMainLoop
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(194) pWindowKeyPressSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(675) handleKeyDownEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1508) handleKeyDownEvent
/home/felix/git/session_planner/session_planner.nim(108) :anonymous
/home/felix/git/session_planner/parser.nim(125) parseGraph
/home/felix/git/session_planner/parser.nim(80) parseTopLvl
/home/felix/git/session_planner/parser.nim(66) parseTopLvl
/home/felix/git/session_planner/parser.nim(78) parseTopLvl
/home/felix/git/session_planner/parser.nim(9) getLineAt
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/strutils.nim(1907) join
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/strmantle.nim(283) nimCharToStr
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(439) newObj
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc_common.nim(423) prepareDealloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: '/home/felix/git/session_planner/session_planner '
This one definetly with c again.
Traceback (most recent call last)
/home/felix/git/session_planner/session_planner.nim(116) session_planner
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1120) run
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(358) runMainLoop
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(194) pWindowKeyPressSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(675) handleKeyDownEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1508) handleKeyDownEvent
/home/felix/git/session_planner/session_planner.nim(108) :anonymous
/home/felix/git/session_planner/parser.nim(127) parseGraph
/home/felix/git/session_planner/parser.nim(80) parseTopLvl
/home/felix/git/session_planner/parser.nim(66) parseTopLvl
/home/felix/git/session_planner/parser.nim(78) parseTopLvl
/home/felix/git/session_planner/parser.nim(9) getLineAt
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/strutils.nim(1907) join
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/strmantle.nim(283) nimCharToStr
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(439) newObj
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/alloc.nim(787) rawAlloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: '/home/felix/git/session_planner/session_planner '
proc getLineAt(s: string, i: int): string=
let sub = s[0..i].reversed.join # <--- here
var
start = sub.find('\n', 1)
mend = s.find('\n', i)
if mend == -1:
mend = s.high + 1
if start == -1:
start = 0
else:
start = i - start
s[start..<mend]
Segmentation fault (core dumped)
Error: execution of an external program failed: '/home/felix/git/session_planner/session_planner '
Didnt do anything this time except for moving the window around
Traceback (most recent call last)
/home/felix/git/session_planner/session_planner.nim(116) session_planner
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1120) run
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(358) runMainLoop
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(922) pControlDrawSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(815) handleDrawEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1865) handleDrawEvent
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/sequtils.nim(952) :anonymous
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(461) newObjRC1
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/alloc.nim(787) rawAlloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: '/home/felix/git/session_planner/session_planner '
Traceback (most recent call last)
/home/felix/git/session_planner/session_planner.nim(116) session_planner
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1120) run
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(358) runMainLoop
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui/private/gtk3/platform_impl.nim(194) pWindowKeyPressSignal
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(675) handleKeyDownEvent
/home/felix/.nimble/pkgs/nigui-0.2.4/nigui.nim(1508) handleKeyDownEvent
/home/felix/git/session_planner/session_planner.nim(108) :anonymous
/home/felix/git/session_planner/parser.nim(136) parseGraph
/home/felix/git/session_planner/parser.nim(116) parseSessions
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tableimpl.nim(52) []=
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tableimpl.nim(23) rawInsert
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(175) genericSeqAssign
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(139) genericAssign
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(100) genericAssignAux
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(128) genericAssignAux
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(26) genericAssignAux
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(22) genericAssignAux
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/assign.nim(75) genericAssignAux
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/gc.nim(435) newObjNoInit
/home/felix/.choosenim/toolchains/nim-1.4.0/lib/system/alloc.nim(787) rawAlloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: '/home/felix/git/session_planner/session_planner '
proc parseSessions(s: string): Table[string, HashSet[string]]=
let tokens = parseTopLvl(s.strip)
for i in countup(0, tokens.high, 2):
let points = tokens[i + 1]
result[tokens[i]] = points.splitLines. # <-- Here
filterIt(not it.startswith("#") and it.len > 0).
join(" ").split().filterIt(it.len > 0).toHashSet
I tried to find out whether the problems are in the parser, or in nigui. I wrote a small program (nigui_test, also in the repo), that renders the same graph (just much larger) based on random, and I added some tests for the parser (in the bottom of parser.nim). The Gui works without problems, and the parser works without problems. If I use the session_planner program to apply the parser test cases by hand, it will most of the time crash before I can even finish the first for loop:
for i in 2..5:
discard parseGraph(text % toSeq(1..i).join(" "))
So I'm wondering: what could cause two things that work well by them selves to crash like that when they're put together?So I'm wondering
When your parser code does not contain dangerous stuff like use of cast, addr, ptr then changes are good that it should not do random crashes.
But I assume niGui will contain all that, so chances for bugs are much larger unfortunately. A tiny bug can allow invalid memory modifications, which may hide for long time, but under some condition crash the program.
Maybe valgrind can help: Compile your program with --gc:arc and -d:useMalloc (hope nigui allows that) and run it though valgrid.
Unfortunately nigui uses gtk on linux, and gtk allocates internally stuff, so valgrind may complain about it, but still valgrind may detect other issues.
Or as niGui supports windows, try it on windows. May help to locate the problem.
I strongly assume that with --gc:arc the problem is only hidden but still exists.
Generally it is gc:arc which shows issues early, while gc:refc can hide memory issues until GC starts. So in old days when ARC was not available I often added GC_fullcollect() calls to the gintro code to enforce early memory garbage collection.
ARC uses destructors to free memory when it goes out of scope, while gc:refc uses finalizers. It can be that in niGui destructors work not properly and do not release all memory when it goes out of scope. That would be an explanation why it does not crash with ARC.
You may contact the niGui dev, I think it is Simon Krauter, and ask if niGui should work fine with default gc:refc and with new gc:arc.