Assertion failure - basher.d

Started by Forestidia86, December 28, 2017, 01:25:40 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Forestidia86

I had a crash while playing with the main message:

core.exception.AssertError@src\lix\skill\basher.d(101): Assertion failure

I have used my own (debug) build (of 0.9.6), so maybe I messed something up with it. I moved furthermore the folder to another place after the build but the error message still shows the path of the build place.

Edit: Debug build seems to crash consistently, release build from the same data doesn't. Debug build crashes consistently when basher finishes tunnel. That didn't happen with the test builds you provided. Did you do something with the basher code in the meantime? Or do I do something utterly wrong? At least release build doesn't crash.

Following the full message from log.txt:
(I have censored parts of the file paths but the important bits should be still there):

Lix version:  0.9.6
Session date: 2017-12-28 01:48:14
  1028.02 src\lix\skill\basher.d:101:
  1028.07 Assertion failure
  1028.28 0x005334DF in _d_assertp
0x00509085 in void lix.skill.basher.Basher.stopIfMovedDownTooFar() at C:\...\LixD-master0_9_6\LixD-master\src\lix\skill\basher.d(100)
0x00508D72 in void lix.skill.basher.Basher.perform() at C:\...\LixD-master0_9_6\LixD-master\src\lix\skill\basher.d(38)
0x00507E4B in void lix.perform.performUseGadgets(lix.lixxie.LixxieImpl*) at C:\...\LixD-master0_9_6\LixD-master\src\lix\perform.d(19)
0x0050796A in void lix.lixxie.LixxieImpl.perform(lix.fields.OutsideWorld*)
0x004CBB8E in D4game5modelQg9GameModel13updateLixxiesMFZ15performUnmarkedMFE3net2ac9PhyuOrderZ9__lambda2MFCQDF961ACA6FFECAD39D41547C51BB029FD at C:\...\LixD-master0_9_6\LixD-master\src\game\model\model.d(254)
0x004CB892 in void game.model.model.GameModel.updateLixxies().foreachLix(void delegate(game.tribe.Tribe, const(int), lix.lixxie.LixxieImpl*)) at C:\...\LixD-master0_9_6\LixD-master\src\game\model\model.d(216)
0x004CBB03 in void game.model.model.GameModel.updateLixxies().performUnmarked(net.ac.PhyuOrder) at C:\...\LixD-master0_9_6\LixD-master\src\game\model\model.d(250)
0x004CB7C3 in void game.model.model.GameModel.updateLixxies() at C:\...\LixD-master0_9_6\LixD-master\src\game\model\model.d(265)
0x004CB040 in void game.model.model.GameModel.advance() at C:\...\LixD-master0_9_6\LixD-master\src\game\model\model.d(94)
0x004CCD03 in void game.model.nurse.Nurse.updateOnce() at C:\...\LixD-master0_9_6\LixD-master\src\game\model\nurse.d(221)
0x004CC5E0 in void game.model.nurse.Nurse.updateTo(const(net.phyu.Phyu), const(game.model.cache.DuringTurbo)) at C:\...\LixD-master0_9_6\LixD-master\src\game\model\nurse.d(138)
0x004C4A55 in void game.core.speed.updatePhysicsAccordingToSpeedButtons(game.core.game.Game).upd(const(int), const(game.model.cache.DuringTurbo)) at C:\...\LixD-master0_9_6\LixD-master\src\game\core\speed.d(27)
0x004C49AF in void game.core.speed.updatePhysicsAccordingToSpeedButtons(game.core.game.Game) at C:\...\LixD-master0_9_6\LixD-master\src\game\core\speed.d(72)
0x004C1199 in void game.core.calc.implGameCalc(game.core.game.Game).noninputCalc() at C:\...\LixD-master0_9_6\LixD-master\src\game\core\calc.d(27)
0x004C10D8 in void game.core.calc.implGameCalc(game.core.game.Game) at C:\...\LixD-master0_9_6\LixD-master\src\game\core\calc.d(39)
0x004C3415 in void game.core.game.Game.calc() at C:\...\LixD-master0_9_6\LixD-master\src\game\core\game.d(195)
0x0042B383 in void basics.mainloop.MainLoop.calc() at C:\...\LixD-master0_9_6\LixD-master\src\basics\mainloop.d(265)
0x0042AC4F in void basics.mainloop.MainLoop.mainLoop() at C:\...\LixD-master0_9_6\LixD-master\src\basics\mainloop.d(103)
0x0050DD04 in int main.main(immutable(char)[][]).__lambda2() at C:\...\LixD-master0_9_6\LixD-master\src\main.d(39)
0x0053194F in extern (C) int allegro5.system.al_run_allegro(scope int delegate()).main_runner(int, char**) at C:\...\LixD-master\..\..\AppData\Roaming\dub\packages\allegro-4.0.1_5.2.0\allegro\allegro5\system.d(45)
0x00531933 in int allegro5.system.al_run_allegro(scope int delegate()) at C:\...\LixD-master\..\..\AppData\Roaming\dub\packages\allegro-4.0.1_5.2.0\allegro\allegro5\system.d(62)
0x0050DCB7 in _Dmain at C:\...\LixD-master0_9_6\LixD-master\src\main.d(35)
0x0053EDFB in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().__lambda1()
0x0053EDBF in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll()
0x0053ECC0 in _d_run_main
0x0050DD30 in main at C:\...\LixD-master0_9_6\LixD-master\src\verify\counter.d(7)
0x0057D8F9 in mainCRTStartup
0x76AB337A in BaseThreadInitThunk
0x77519882 in RtlInitializeExceptionChain
0x77519855 in RtlInitializeExceptionChain

Simon

Thanks for reporting! Copied to github issue #275.

That assert has never fired on me in 1-2 years. I've taken a quick look at the source, but don't remember the exact basher mechanics to jugde this. I should re-understand the entire basher's working.

Was this during a level where we can easily repro the terrain for the basher? Or did this happen in an intricate solution where we don't know what exactly happened? Sadly, I merely save the editor's map on throws, not the game's level/replay...

-- Simon

Forestidia86

#2
As I said in the edit: It crashes consistently when miner basher finishes tunnel. Just try to bash in "Any Way You Want".
Attached replay where it would crash for me in debug build.

Simon

Debug builds have asserts compiled into the code. Asserts are logical conditions that, if violated, terminate the application with a stacktrace like you posted. They're designed to catch logic errors (bugs, unjustified assumptions) in the code, not to sanitize user input.

Release builds don't have asserts compiled into the code, for best performance.

Your replay doesn't crash in my Linux build. :lix-mouth: I've downloaded the attached replay from reply #2, built v0.9.6 in debug mode, and played the replay back both on the included level and the pointed-to level. No crash, hm.

But -- I've now built v0.9.6 with the Windows toolchain, run the executable in Wine, and there I get the crash very reliably. :lix-scared: Exciting, will investigate.

-- Simon

Simon

It's a memory-management bug in Lix. I violate D's type system to squeeze performance out of the skill code, allowing different skill objects to share the same memory inside a lix. This is slightly faster than allocating lots of small objects per skill because I can now put all lixes and all jobs into one continuous chunk of memory.

The basher terminates and becomes walker, replacing the memory with walker memory, but then continues to execute basher code that assumes it reads basher memory, not walker memory. It would instead be correct to stop all basher code once the skill changes. This bug exists since about 0.8.3.

Thanks for the good report, will fix for next version!

-- Simon

Forestidia86

Only out of curiosity: Why didn't the test versions (0.9.2) you provided crash?

Simon

#6
The compiler is free to leave memory uninitialized when the compiler believes that this memory won't be read anyway. Depending on how the executable is laid out in memory, which changes between versions, some memory that is later copied might hold different values across versions. The memory that eventually fails the assert is not part of the executable; it's allocated RAM during program run, but garbage values (that were not intended to be read) were copied into it, or it was left uninitialized (unlikely because we could repro the crash easily).

The assert fails because a comparison between two integers fails. Even if, in 0.9.2, one side of the comparison was already garbage, maybe the garbage still compared correctly against the other integer, passing the assert.

Symptoms of memory corruption typically sound like magic: Sometimes, bugs come and go between application runs. Sometimes, values in the application behave seemingly randomly. Sometimes, bugs manifest on one system but not on another. Sometimes, bugs might only happen in release mode (nasty), or only in debug mode (better).

This type of bug shouldn't happen in normal D code, only when you deliberately reach for dark magic, you risk this. I've written about this particular form of memory corruption in a comparison of tagged union vs. normal OO state pattern; my 0.9.6 D code behaves like a tagged union with incorrect access.

-- Simon

Forestidia86