Skip to content

Silent failure - no idea #5085

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

Closed
xzores opened this issue Apr 27, 2025 · 8 comments
Closed

Silent failure - no idea #5085

xzores opened this issue Apr 27, 2025 · 8 comments

Comments

@xzores
Copy link
Contributor

xzores commented Apr 27, 2025

Context

I am experiencing a silent run failure, it failes without any messages.
I can build the project, but when i run it nothing happens.

Observe:

PS D:\github\Voxel_game> odin run . -debug  
PS D:\github\Voxel_game> odin run . -debug
PS D:\github\Voxel_game> odin run . -debug
    Odin:    dev-2025-03-nightly:d011cb8
    OS:      Windows 11 Home Basic (version: 24H2), build 26100.3775
    CPU:     13th Gen Intel(R) Core(TM) i9-13900HX
    RAM:     32507 MiB
    Backend: LLVM 18.1.8

Expected Behavior

It should run

Current Behavior

It just dont do anything

Steps to Reproduce

I am not sure, here is what i got:

I have this function:

destroy :: proc (host_data : rawptr) {
	using host_data := cast(^Host_data)host_data;
	
	log.infof("Destroying host : %v", host_data);
	
	voxel_destroy_domain(terrain);
	
	delete(clients);
	free(host_data);
}

if i remove the line:

log.infof("Destroying host : %v", host_data);

then it runs, but notice that the code here is never executed.
If i do:

destroy :: proc (host_data : rawptr) {
	using host_data := cast(^Host_data)host_data;
	
	if false {
		log.infof("Destroying host : %v", host_data);
	}
	
	voxel_destroy_domain(terrain);
	
	delete(clients);
	free(host_data);
}

Then it still DOES NOT RUN!
I can print the members just fine, so if i do:

log.infof("Destroying host : %v", host_data.clients);

But not if i print host_data itself.

I have tried renaming host_data, it does not help.
I have tried removing using, it does not help
I have tried with fmt.printf and aprintf instead of log, it does not help.

I can print all of the member one by one, but not together, that is this works:

	if true {
		log.infof("Destroying host 1 : %#v", host_data.clients);
		log.infof("Destroying host 2 : %#v", host_data.terrain);
		log.infof("Destroying host 3 : %#v", host_data.tick_cnt);
	}

but this does not:

	if true {
		log.infof("Destroying host 1 : %#v", host_data);
	}

I have no idea what would cause this

@xzores
Copy link
Contributor Author

xzores commented Apr 27, 2025

to clarify, it still outputs a .exe file, it just dont run it. It seems something goes wrong before main.

@xzores
Copy link
Contributor Author

xzores commented Apr 27, 2025

Update it works if I do:

log.infof("Destroying host : %#v", host_data^);

that is if it is not a pointer

@Kelimion
Copy link
Member

Try running the generated executable and echo %ERRORLEVEL%, or running it under a debugger to see where it crashes. odin run can't magically make the generated executable print its output if it would fail when run regularly.

What makes you think "something goes wrong before main" if you're talking about changing something in destroy crashing or not crashing things?

@Kelimion
Copy link
Member

Where does that ^Host_data passed as a rawptr come from? Often if rearranging lines of code or commenting out a line or two makes things work, you have stack corruption.

Sounds like you're doing something like this:

new_host :: proc(width, height: int) -> ^Host_data {
    host := Host_data {
        terrain = generate_terrain(width, height),
    }
    return &host
}

main :: proc() {
    host := new_host(13, 42)
    destroy(host)
}

That works until you change your log statement or something else, reordering the stack layout in the process, and overwriting the data returned by new_host.

@xzores
Copy link
Contributor Author

xzores commented Apr 27, 2025

Its not a stack corruption as the function is never called, the values are also correct if printed sperately.

I dont know if this helps, but these are all the function which are involved with getting there. The project is big, like 55k lines, so i have a hard time pinning down issue.

I belive it failes before main because i dont get to main when the program is executed.

Ok, so this is how it works:

@(init, private)
init_register_host :: proc () {
	
	register_host("server_ingame", Host {
		init = init, //Returns data
		start = start,					//Starts?
		client_join = client_joined,		//Called when a client join	
		tick = tick,					//Called every tick
		client_leave = client_left, 	//Calledn when a client leaves
		destroy = destroy,				//Destroys the host_data returned by host.init
					
		tick_rate = 40, 							//Tricks per secound	
	});
	
}


hosts : map[string]Host;

register_host :: proc (name : string, host : Host) {
	
	hosts[name] = host;
}


close_host :: proc () {

	assert(engine_state.host_name != ""); 
	host_end();
	
	{ //Cleanup the host
		engine_state.host.destroy(engine_state.host_data);
	}
	
	engine_state.host = {};
	delete(engine_state.host_name);
	engine_state.host_name = "";
}

scene_destroy :: proc (scene_data : rawptr) {
	using scene_data := cast(^Ingame_scene)scene_data;
	defer free(scene_data);
	
	leave_host();
	close_host();
}


//This happens when the scene is recreated.
destroy_engine_scene :: proc () {
	using engine_state;
	
	clear(&engine_client.gizmos);
	
	log.infof("Destroy engine scene : %v", engine_state.scene_name);
	
	scene.destroy(scene_data); //This should join and wait for the host if applicable.
	
	delete(scene_state.key_bindings_lookup);
	delete(scene_state.events_map);
	delete(scene_state.voxel_domains); scene_state.voxel_domains = {};
	regui.destroy(&scene_state.gui_state);
	
	scene_state = {};
}


engine_main :: proc () {
	
	setup_engine(); //Setup function calls and such
	defer cleanup_engine();
	
	//Host is init later once a host_name has been set.
	
	{ //The game stuff
		using engine_state;
		
		defer delete(scene_name);
			
		Exe_mode :: enum {
			client,
			server,
		}
		
		mode : Exe_mode = .client; //TODO change to clients
		
		if len(os.args) > 1 {
			
			if os.args[1] == "-server" {
				mode = .server;
			} else if os.args[1] == "-client" {
				mode = .client;
			}
			else{
				panic("Wrong args passed, only -server allowed");
			}
		}
		
		switch mode {
			case .server:
				main_server();
				
			case .client:
				init_engine_client();
				init_engine_scene(); //call the init function call
				
				should_run, recreate := update_engine_client();
				for should_run {
					tracy.ZoneN("Frame loop");
					should_run, recreate = update_engine_client();
					
					if recreate {
						destroy_engine_scene();
						destroy_engine_host();
						init_engine_host();
						init_engine_scene();
					}
					
					{
						tracy.ZoneN("Free_all");
						free_all(context.temp_allocator);
					}
					
					if to_load != "" {
						tracy.ZoneN("Loading Scene");
						log.infof("Loading Scene : %v", to_load);
						
						delete(scene_name);
						scene_name = strings.clone(to_load);
						
						destroy_engine_scene();
						fmt.assertf(scene_name in scenes, "name '%v' is not a valid scene, scenes: %v", scene_name, scenes);
						scene = scenes[scene_name];
						init_engine_scene();
						
						delete(to_load)
						to_load = "";
					}
					
					tracy.FrameMark();
				}
				
				destroy_engine_scene();
				destroy_engine_client()
		}
	}
	
	free_all(context.temp_allocator);
}

main :: proc() {
	
	fmt.printf("Entering main\n");
	
	context.assertion_failure_proc = utils.init_stack_trace();
	defer utils.destroy_stack_trace();
	
	when USE_FILE_LOGGER {
		
		mode: int = 0
		when ODIN_OS == .Linux || ODIN_OS == .Darwin {
			mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IROTH
		}

		logh, logh_err := os.open("log.txt", (os.O_CREATE | os.O_TRUNC | os.O_RDWR), mode)

		//This crashes my program, idk why
		/*if logh_err == os.ERROR_NONE {
			os.stdout = logh
			os.stderr = logh
		}*/
		
		context.logger = utils.create_file_logger(logh, .Debug);
	}
	else {
		context.logger = utils.create_console_logger(.Debug);
	}
	
	when MEM_DEBUG {
		utils.init_tracking_allocators();
	}
	
	{
		when TRACY_ENABLE {
			tracy_alloc_data : tracy.ProfiledAllocatorData = {};
			tracy_alloc := tracy.MakeProfiledAllocator(&tracy_alloc_data, backing_allocator = context.allocator);
			context.allocator = tracy_alloc;
		}
		when MEM_DEBUG {
			tracker : ^mem.Tracking_Allocator;
			context.allocator = utils.make_tracking_allocator(tracker_res = &tracker); //This will use the backing allocator,
		}
		
		{
			tracy.SetThreadName("main");
			
			engine.engine_main();
		}
	}
	
	when USE_FILE_LOGGER {
		if logh_err == os.ERROR_NONE {
			log.destroy_file_logger(context.logger)
		}
	}
	
	free_all(context.temp_allocator);
	
	when MEM_DEBUG {
		utils.print_tracking_memory_results();
		utils.destroy_tracking_allocators();
	}
	
	fmt.printf("exited gracefully\n");
}

@xzores
Copy link
Contributor Author

xzores commented Apr 27, 2025

ok, i ran it in a debugger the issue was a completely different place.
I deleted some memory i should not:

@(init)
check_keys_match :: proc () {
	
	fields := reflect.enum_fields_zipped(render.Key_code);
	defer delete(fields); //This is the problem
	
	for f in fields {
		val, ok := reflect.enum_from_name(Key_code, f.name);
		fmt.assertf(ok, "There is a missing keycode: %v in common.Key_code", f);
		fmt.assertf(cast(i32)val == cast(i32)f.value, "There is a incorrect keycode: %v in common.Key_code", f);
	}
}```

@xzores xzores closed this as completed Apr 27, 2025
@Kelimion
Copy link
Member

Ah, @(init) procs would explain why it could crash before main. That wasn't obvious in the initial brief. Glad you found the bug.

@xzores
Copy link
Contributor Author

xzores commented Apr 27, 2025

this code had been there for like months, i dont know why i did not run into this before XD

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